Mail Archives: cygwin/2001/04/10/16:36:59
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" <fonseca AT mip DOT sdu DOT dk>
> To: "Robert Collins" <robert DOT collins AT itdomain DOT com DOT au>
> Cc: <cygwin AT cygwin DOT com>
> 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
- Raw text -