Subject: Re: contribution soapbox(was Re: G++ guru's please comment - Re: FW: pthread_create problem in Cygwin 1.1.8-2])
From: "Robert Collins" <[email-masked]>
To: René Møller Fonseca <[email-masked]>
Cc: <[email-masked]>
Date: Tue, 10 Apr 2001 07:36:51 +1000

----- Original Message -----
From: "René Møller Fonseca" <[email-masked]>
To: "Robert Collins" <[email-masked]>
Cc: <[email-masked]>
Sent: Tuesday, April 10, 2001 1:24 AM
Subject: Re: contribution soapbox(was Re: G++ guru's please comment -
Re: FW: pthread_create problem in Cygwin 1.1.8-2])

> Hi Robert,
>
> The tricky part is to make pthread_once MT-safe without doing a busy
> wait.

I just had this pointed out by another reader as well. Win32 does
provide the tools to do this. I'm about to code it up and I'll drop the
revised version to the list..

>
> Your code is not MT-safe; more than one thread can enter init_routine.
>
> I'm not familiar with the requirements of pthread_once but I'm
expecting
> the following to be true:
>
> 1. Only one thread may enter init_routine (otherwise the "once" suffix
> doesn't make any sense).

Yes.

> 2. if init_routine is currently being run by some thread then all
other
> threads must block until the function has completed.

No. This could deadlock an application. Also pthread_once is cancellable
if init_routine is cancelable, which means that we cannot suspend the
other threads. Most of the pthreads functions are designed around the
early low level unix scheduling environment, thus they are often easily
implemented 1-1 with Win32 calls. The hard ones are ones that specify a
particular scheduling algorithm.

Rob

>
>
> Here is my implementation of pthread_once using busy waiting. If
> init_routine takes a long time then busy waiting becomes very bad :-(

Thanks for helping.

>
> >>>>
> typedef struct {unsigned int state; unsigned int lock;}
pthread_once_t;
>
> #define PTHREAD_ONCE_INIT {0, 0}

Unfortunately this is wrong. PTHREAD_ONCE_INIT has to take no
parameters. pthread_once_t is opaque to the user so pthread_once_init
can be anything we want.

>
> inline unsigned int pthread_once_try_lock(pthread_once_t*
once_control)
> {
>   register unsigned int previous;
>   asm volatile ("xchgl %0, %1" : "=&r" (previous), "=m"
> (once_control->lock) : "0" (1));
>   return !previous;
> }

Win32 has Interlocked* functions that expand to pretty much what you've
got there.

>
> int __pthread_once(pthread_once_t* once_control, void (*init_routine)
> (void)) {
>   if (!once_control->state) { // should init_routine possible be
called
> by this thread
>     while (!pthread_once_try_lock(once_control)); // wait for
exclusive
> lock
>       // only one thread can be here at any time
>       if (!once_control->state) { // should this thread run
init_routine
>         init_routine();
>         once_control->state = 1; // make sure init_routine is not
called
> again
>       }
>     once_control->lock = 0; // release lock
>   }
>   return 0;
> }
> <<<<
>
> Warning: I have not checked and optimized it thoroughly.
>
> P.S. I have put the code in the attachment!
>
> René
>

Here's my V2.
(PTHREAD_ONCE_INIT is defined as 0x0001)

int __pthread_once (pthread_once_t * once_control, void (*init_routine)
(void))
{
  if (InterlockedDecrement(once_control)!=0)
    {
      InterlockIncrement(once_control);
      return 0;
    }
    init_routine();
  /* cancel test will go here when canceability states are implemneted
*/
  /* if (init_routine was canceled)
      InterlockIncrement(once_control); */
  return 0;
}

As you can see, there's no busy wait. Given that reads are atomic, an
alternative implementation could check a flag, and then wait for a mutex
or condition variable, but there's no need for a mutex here.

Rob

--
Want to unsubscribe from this list?
Check out: http://cygwin.com/ml/#unsubscribe-simple