Simple communcation between threads

Started by
4 comments, last by DrTwox 15 years, 2 months ago
Hi all. I'm developing a simple music playing app (using C) that uses two threads. The main thread handles the GUI, the second thread decodes various audio formats (mp3, flac, etc) and sends the PCM data to the audio hardware. Currently to control the decoder thread, the GUI thread changes the value of a variable, and the decoder checks the value of this variable every quarter second or so. The decoder can also change this variable, e.g. While loading/initializing a file, the decoder sets the status to 'e_status_buffering'. The variable, decoderStatus, is protected by a mutex lock. The code to communicate between the two threads looks something like this:

/* Pseudocode-ish; not the real code! */
enum {
    e_status_buffering,
    e_status_stop,
    e_status_play,
    e_status_pause,
    e_status_nextTrack,
    e_status_prevTrack
} e_status;

e_status decoderStatus;
mutex_t lock; /* the lock init code isn't shown here */

void decoder_SetStatus(e_status status) {
    MutexLock(lock);
    decoderStatus = status;
    MutexUnlock(lock);
}

e_status decoder_GetStatus() {
    MutexLock(lock);
    e_status status = decoderStatus;
    MutexUnlock(lock);
    return status
}

Is this an acceptable method? What would be a better way to do this?
Advertisement
One problem I see with that is that there's no way to do an atomic "get and set" with the primitives you expose. If, when a thread is about to set the variable, it ever matters what the current state of the variable is, you're going to have a race condition. Of course, if it will always be OK to possibly have one thread have an outdated view of the variable, then that's not a problem.

In general, I find it better to express inter thread communication in terms of message-passing as opposed to state-setting whenever possible.
I don't see anything wrong with that.

Because all access to the shared variables is done from inside a mutex lock, you don't ever need to worry about atomic reads/writes, but in this case, I think it would even work without the mutex, as both a read and a write of 4 bytes of data (or less) are already atomic, but then you may need to add volatile to your variable definition, or use memory barriers, as otherwise a write to a different address that happens later on, may appear to have happened before this one from a different thread (but if you are going to do lockless threading, make really sure you know what you're doing, as it can yield really nasty bugs. If you don't feel comfortable with that, just keep it the way it currently is, and all should be fine:)).
Quote:Original post by quasar3d
but then you may need to add volatile to your variable definition... make really sure you know what you're doing
QFE. Volatile does not really do what you might think it does here - required reading.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by swiftcoder
Quote:Original post by quasar3d
but then you may need to add volatile to your variable definition... make really sure you know what you're doing
QFE. Volatile does not really do what you might think it does here - required reading.


Yes, I read that before, and for writing portable multi threading code, it's indeed pretty useless, but on systems with a simple memory model (like x86), volatile does make reads and writes more consistent (in a way that another thread cannot see the result of a write to one address happen, before it can see the result of an earlier write as well). It does certainly not make operations like + and - interlocked though.

Then again, I personally never use it, and just place memory barriers, which on x86 would only block the compiler from rearranging reads/writes over that barrier, but will also function as a real memory barrier on systems that need it, like XBox360

Then again, I believe the latest visual C++ compiler actually does insert memory barriers around volatile reads/writes, so then it would work on those platforms as well, but it certainly makes it a lot slower than needed, so memory barriers are still better:)
Thanks for the feedback! Much appreciated.

This topic is closed to new replies.

Advertisement