Re: RE : Apparent thread-safety bug in Glib 2.0 docs



On Sat, 2003-10-25 at 14:32, Joaquin Cuenca Abela wrote:
> Can you please elaborate a bit more how is a compiler suppossed to
> generate incorrect code in examples 3 & 4?
> They look perfectly fine to me.

Sure, no problem, this is a classic mistake C/C++ programmers make in
threaded environments (the problem doesn't exist in languages like Java
who's memory model is explicitly aware of threads). Let's look at a
simple getter/setter combination:


static int foo;

int getFoo() { return foo; }

void setFoo(int aFoo) { foo = aFoo; }

Now, let's say we have a thread that's in a loop waiting for foo to
reach zero:

while (getFoo() != 0) {
    /* do stuff */
}

All looks good so far right? Now let's make the getter's and setter's
thread safe:

static int foo;
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;

int getFoo() {
    int ret_val;
    g_static_mutex_lock(&mutex);
    ret_val = foo;
    g_static_mutex_unlock(&mutex);
    return ret_val;
}

void setFoo(int aFoo) {
    g_static_mutex_lock(&mutex);
    foo = aFoo;
    g_static_mutex_lock(&mutex);
}

Everything looks great now right? Only one thread can be reading or
writing to foo at a given time, so it's all nice and thread-safe right?

Wrong. Let's go back to our loop:

while (getFoo() != 0) {
    /* do stuff */
}

Now, when the compiler generates the code for this loop, it's quite
possible that it will stuff the value of foo into a register. Assuming
it doesn't have to flush the register, it will keep using the register
for any code that touches foo, in order to avoid the overhead of
accessing the system's memory. Sounds like a great idea right?

The problem comes when you are using threads. What if that while loop is
waiting for another thread to call setFoo(0)? That other thread is
working in it's own world of registers and main memory. Certainly there
is no way for the compiler to know that when that other thread invokes
setFoo(0) it needs to update the value in the register of the thread
doing the while loop.

Fortunately, C/C++ have a mechanism for informing a compiler that
something outside of the current thread of execution might update some
piece of memory. Originally, it was primarily used in places like device
drivers, where devices might update bits of memory. However, in a
multi-threaded world, this can happen simply by having multiple threads
manipulating the same chunk of memory. The solution is to use the
variable modifier "volatile", which tells the compiler that the value of
the variable may change at any time.

Typically, this means that the compiler always reads and writes values
to actual memory address, instead of to registers. It also means the
compiler doesn't do a number of other things which can cause similar
problems to the one I described, like instruction reordering. Either
way, you told the compiler that the value might change without it
knowing about it, so it does what it needs to do in order to avoid
creating a loop that never exits when another thread does setFoo(0).

-- 
Christopher Smith <x xman org>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]