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

Hi again,

I wasn't aware of the Interlocked functions.

I just examined the documentation for InterlockedIncrement and it states
that the variables must be aligned on a 32-bit boundary.

On Solaris the error code EINVAL is returned if either once_control or
init_routine is NULL. You should consider returning EINVAL (or other
appropriate error code) if once_contol isn't 32 bit aligned.

>>>>
int __pthread_once (pthread_once_t * once_control, void (*init_routine)
(void))
 {
   if ((once_control & 0x03) != 0) { // must be checked before use of
Interlocked...
     return EINVAL;
   }
   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;
}
<<<<

I do not understand why other threads should not block until
init_routine has been completed. The few uses of pthread_once that I
have seen allocate resources and initializes variables that are required
to be correct after pthread_once has completed, for the following code
the work correctly. If a thread could return from pthread_once without
an error and before init_routine was invoked then the code wouldn't
work. I can't direct you to the code I'm refering to 'cause I can't
remember where I seen it, sorry.

I did a man pthread_once on a machine running Irix 6.5. and came up with
this:

>>>>>
C SYNOPSIS
     #include <pthread.h>

     int pthread_once(pthread_once_t *once, void (*init)(void));

DESCRIPTION
     The pthread_once() function ensures that the function init is
called
     exactly once for all callers using the once initialization
variable.
     This variable is initialized with the constant PTHREAD_ONCE_INIT. 
While
     init is being executed all callers of pthread_once() will wait.

     If a thread is cancelled while executing the init function [see
     pthread_setcancelstate()], then pthread_once() will permit another
thread
     (possibly waiting) to make the call.
<<<<<

I have also found this:
http://lithos.gat.com/software/alpha/AA-Q2DPC-TKT1_html/thrd0152.html#pt_once_44

The man page for pthread_once for Solaris 8 doesn't mention anything
about the blocking behaviour.

René

Robert Collins wrote:
> 
> ----- 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