[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.programming.threads

pthreas-win32 maybe unsafe with multiprocessor architectures

Tristan \(do not reply by email\)

7/23/2004 11:37:00 PM

if you are using pthread-win32 (Open Source POSIX Threads for Win32, see
http://sources.redhat.com/pthreads-win32... ), you should be aware of
this problem:.

by looking at the source code, i found that in some cases, several threads
may be accessing the same memory location without the necessary protection
(mutex). i think this can cause possible problems (such has crashes / memory
exceptions) on some multiprocessor architectures.

for example, here is a snipplet from pthread_once:

if (InterlockedIncrement (&(once_control->started)) == 0) {
(*init_routine) ();
once_control->done = TRUE;
} else {
while (!(once_control->done)) {
Sleep (0);
}
}

if called by several threads, one thread only will call init_routine while
the other thread(s) will wait until the init_routine returns in the first
thread, by polling the once_control->done variable.

on multiprocessor architectures, all the threads may be running concurrently
(i.e. really at the same time) and the variable may be accessed by two
processors simultanously, and this, as far as i know, can cause havoc (e.g.
fatal exceptions).

i think the only workaround it to protect this kind of variable with mutexs
and use critical sections. naturally it makes the code a bit more
complicated...

is my analysis correct ?


32 Answers

Mouse

7/24/2004 12:51:00 AM

0

> on multiprocessor architectures, all the threads may be running
concurrently
> (i.e. really at the same time) and the variable may be accessed by two
> processors simultanously, and this, as far as i know, can cause havoc
(e.g.
> fatal exceptions).

Its not going to crash here. Its a race-condition involving memory
visibility. So, the application could crash because it did not get a
consistent view of the data.




> i think the only workaround it to protect this kind of variable with
mutexs
> and use critical sections.

You can omit the mutexs completely by using atomic ops with the correct
memory barriers:

http://groups.google.com/groups?selm=XIRDc.192766%24Ly.83841%40attbi_s01&...
( read whole thread )

The DCL code I posted suffers from the M$ "mystery barrier". Does the
barriers get executed upon cas failure?


Joe Seigh

7/24/2004 12:53:00 AM

0



"Tristan (do not reply by email)" wrote:
>
> if you are using pthread-win32 (Open Source POSIX Threads for Win32, see
> http://sources.redhat.com/pthreads-win32... ), you should be aware of
> this problem:.
>
> by looking at the source code, i found that in some cases, several threads
> may be accessing the same memory location without the necessary protection
> (mutex). i think this can cause possible problems (such has crashes / memory
> exceptions) on some multiprocessor architectures.
>
> for example, here is a snipplet from pthread_once:
>
> if (InterlockedIncrement (&(once_control->started)) == 0) {
> (*init_routine) ();
> once_control->done = TRUE;
> } else {
> while (!(once_control->done)) {
> Sleep (0);
> }
> }
>
> if called by several threads, one thread only will call init_routine while
> the other thread(s) will wait until the init_routine returns in the first
> thread, by polling the once_control->done variable.
>
> on multiprocessor architectures, all the threads may be running concurrently
> (i.e. really at the same time) and the variable may be accessed by two
> processors simultanously, and this, as far as i know, can cause havoc (e.g.
> fatal exceptions).
>
> i think the only workaround it to protect this kind of variable with mutexs
> and use critical sections. naturally it makes the code a bit more
> complicated...
>
> is my analysis correct ?

No. Accessing the variable that way is perfectly safe. All they're doing
is reading a variable that is initialized to zero and just need to see
a non-zero value.

There is a bug though. They're missing an acquire memory barrier after
seeing once_control_done set to non zero and a release memory barrier
before setting it. Your basic bad DCL implementation.

Also, the reason they're probably not using a mutex is that mutexes and
critical sections in windows cannot be statically initialized.

Joe Seigh

Tristan \(do not reply by email\)

7/24/2004 1:21:00 AM

0

so are you saying that a thread reading an unprotected variable while
another thread could possibly write the same variable is always safe ?

i was under the impression that when two threads can possibly access
simultanously the same memory address (e.g. one reading and the other
writing), then on some multiprocessor architectures, this could produce a
fault.

you seem to be all very convinved that it cannot (ever) be a probem.

can you point me to a definitive document that explains why this is in fact
never a problem, and that it is absolutely 100% definitively garanteed that
this will work fine on all multiprocessor architectures ?


Tristan \(do not reply by email\)

7/24/2004 1:26:00 AM

0

> There is a bug though. They're missing an acquire memory barrier after
> seeing once_control_done set to non zero and a release memory barrier
> before setting it. Your basic bad DCL implementation.

I thought InterlockedIncrement was doing that ?

Maybe they should use InterlockedIncrementAcquire instead ?

>
> Also, the reason they're probably not using a mutex is that mutexes and
> critical sections in windows cannot be statically initialized.

yeah, i noticed that. a real pain. the only way would be to initialize the
needed critical sections in the pthread_init initialization routine, and the
only way to make sure that this one is called and called only once is to put
the library in a dll and call pthread_init in DLLMain.


David Schwartz

7/24/2004 1:58:00 AM

0


"Tristan (do not reply by email)" <do-not-reply-by-email@kissthecow.com>
wrote in message news:gUiMc.658$oi4.241@newssvr27.news.prodigy.com...

> yeah, i noticed that. a real pain. the only way would be to initialize
> the
> needed critical sections in the pthread_init initialization routine, and
> the
> only way to make sure that this one is called and called only once is to
> put
> the library in a dll and call pthread_init in DLLMain.

It can be trivially done using DCL in assembly language using two 32-bit
values that are initialized to zero. You basically just use one as a
spinlock that starts out unlocked and the other as a "we're definitely
finished with initialization" flag.

In pseudo-code:

if(definitely_finished==0)
{
acquire_spinlock();
if(!definitely_finished)
{
do_stuff();
definitely_finished=1;
}
release_spinlock();
}

Note that this must be competently implemented in assembly language with
the proper memory visibility semantics.

DS


Joe Seigh

7/24/2004 1:59:00 AM

0



SenderX wrote:
>

> The DCL code I posted suffers from the M$ "mystery barrier". Does the
> barriers get executed upon cas failure?

Actually, Itanium suffers from the mystery barrier. You ought to ask
in the Intel developer's forums. Pentium doesn't have the problem
since the lock prefix serializes the processor. If they change that,
they'll still have to keep the semantics the same.

Joe Seigh

Joe Seigh

7/24/2004 2:04:00 AM

0



"Tristan (do not reply by email)" wrote:
>
> so are you saying that a thread reading an unprotected variable while
> another thread could possibly write the same variable is always safe ?
>
> i was under the impression that when two threads can possibly access
> simultanously the same memory address (e.g. one reading and the other
> writing), then on some multiprocessor architectures, this could produce a
> fault.
>
> you seem to be all very convinved that it cannot (ever) be a probem.
>
> can you point me to a definitive document that explains why this is in fact
> never a problem, and that it is absolutely 100% definitively garanteed that
> this will work fine on all multiprocessor architectures ?

It's not a problem since the only way you can see a non zero value is if
somebody set it. If the accessing the variable is atomic then you'll see
the value that was set some time after it got set. If accessing is non-atomic
then you might see some undefined value if the store and read overlap but there
is no undefined value that would make the logic in this case incorrect. If
it was a pointer and accessing the pointer was non-atomic then you could get
into trouble using an undefined value as a pointer.

Joe Seigh

Joe Seigh

7/24/2004 2:24:00 AM

0



"Tristan (do not reply by email)" wrote:
>
> > There is a bug though. They're missing an acquire memory barrier after
> > seeing once_control_done set to non zero and a release memory barrier
> > before setting it. Your basic bad DCL implementation.
>
> I thought InterlockedIncrement was doing that ?
>
> Maybe they should use InterlockedIncrementAcquire instead ?

No, the memory barrier would be in the wrong place for DCL.
>
> >
> > Also, the reason they're probably not using a mutex is that mutexes and
> > critical sections in windows cannot be statically initialized.
>
> yeah, i noticed that. a real pain. the only way would be to initialize the
> needed critical sections in the pthread_init initialization routine, and the
> only way to make sure that this one is called and called only once is to put
> the library in a dll and call pthread_init in DLLMain.

You can use lock-free DCL to initialize a critical section

qfree_t * initFreeQueue() {
qfree_t *q;
qfree_t *oldq;

q = InterlockedCompareExchangePointer(&xq, NULL, NULL);

if (q == NULL) {
q = (qfree_t *)malloc(sizeof(qfree_t));
InitializeCriticalSection(&(q->cs));
q->qfree = NULL;
q->qcount = 0;
q->qcreate = 0;
q->qalloc = 0;
q->qdealloc = 0;

oldq = InterlockedCompareExchangePointer(&xq, q, NULL);

if (oldq != NULL) {
DeleteCriticalSection(&(q->cs));
free(q);
q = oldq;
}

}

return q;

}


This is on a pentium so it doesn't have the problem that Itanium appears to have
(which is probably just a documentation problem).

Joe Seigh

Joe Seigh

7/24/2004 2:45:00 AM

0




> You can use lock-free DCL to initialize a critical section
>
(snip)

If you don't like multiple concurrent intialization of singletons,
look who recommends it also. :)

http://groups.google.com/groups?as_umsgid=98m2b6%24nsp%241%40hplms2....

Joe Seigh

Tristan \(do not reply by email\)

7/24/2004 3:47:00 AM

0


"Joe Seigh" <jseigh_01@xemaps.com> wrote in message
news:4101C5AB.F31259A@xemaps.com...
>
>
> "Tristan (do not reply by email)" wrote:
> >
> > so are you saying that a thread reading an unprotected variable while
> > another thread could possibly write the same variable is always safe ?
> >
> > i was under the impression that when two threads can possibly access
> > simultanously the same memory address (e.g. one reading and the other
> > writing), then on some multiprocessor architectures, this could produce
a
> > fault.
> >
> > you seem to be all very convinved that it cannot (ever) be a probem.
> >
> > can you point me to a definitive document that explains why this is in
fact
> > never a problem, and that it is absolutely 100% definitively garanteed
that
> > this will work fine on all multiprocessor architectures ?
>
> It's not a problem since the only way you can see a non zero value is if
> somebody set it.

i was not refering to that.

i was refering to the fact that you can get a memory exception when
accessing a memory address while another thread (running on another
processor, in a multiprocessor architecture) accesses it.

i clearly remember seeing memory fault happen in this case, but that was not
under the windows operating system.

so i know that the case is possible, and that happens.

are you saying that it is garanteed somewhere (where is that written ?) that
under any flavor of the windows OS that uses multople processors, this
cannot happen ?

> If the accessing the variable is atomic

accessing a variable is not garanteed to be an atomic operation, is it ? it
certainely depends on the size of the variable, the instructions that the
compiler will use to access the variable (i.e. nothing guarantees that the
compiler will not access the two 16-bit words when you read a 32-0bit
variable) and the way the memory is accessed. accessing a double is likely
to not be an atomic operation.

so your statement "accessing the variable is atomic" is incorrect, as a
general statement.