Mailing-List: contact cygwin-help AT sourceware DOT cygnus DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT sources DOT redhat DOT com Delivered-To: mailing list cygwin AT sources DOT redhat DOT com Message-ID: <3AD2CCDB.5620D68@mip.sdu.dk> Date: Tue, 10 Apr 2001 11:05:31 +0200 From: =?iso-8859-1?Q?Ren=E9=20M=F8ller?= Fonseca Organization: MIP, University of Southern Denmark X-Mailer: Mozilla 4.76 [en] (Windows NT 5.0; U) X-Accept-Language: en MIME-Version: 1.0 To: Robert Collins Cc: cygwin AT cygwin DOT com Subject: Re: contribution soapbox(was Re: G++ guru's please comment - Re: FW: pthread_create problem in Cygwin 1.1.8-2]) References: <3AD1A64D DOT 7DC36DF0 AT mip DOT sdu DOT dk> <012a01c0c0f1$baf65110$0200a8c0 AT lifelesswks> <3AD1D41D DOT 2FC62EF1 AT mip DOT sdu DOT dk> <008b01c0c13d$313003d0$0200a8c0 AT lifelesswks> Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit 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 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" > To: "Robert Collins" > Cc: > 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