* Re: pthread_once suggestions
[not found] ` <1117074885.9478.98.camel@desk.home>
@ 2005-05-26 16:22 ` Gottlob Frege
2005-05-27 1:42 ` Vladimir Kliatchko
0 siblings, 1 reply; 2+ messages in thread
From: Gottlob Frege @ 2005-05-26 16:22 UTC (permalink / raw)
To: Ross Johnson; +Cc: Pthreads-Win32 list
To get me back into the loop, can someone post a version with these
latest ideas (pseudocode or whatever)?
On 5/25/05, Ross Johnson <Ross.Johnson@homemail.com.au> wrote:
> On Thu, 2005-05-26 at 01:42 +1000, Ross Johnson wrote:
> > On Wed, 2005-05-25 at 06:40 -0400, Vladimir Kliatchko wrote:
> > > Hi,
> > > I did not have much time to look into it, but from the top of my head it
> > > appears that the problem you pointed out is similar to the problem with the
> > > initializing thread not being canceled but completing while the thread B is
> > > suspended before creating the semaphore. This last problem was addressed by
> > > simply rechecking the 'done' flag. Can't we similarly recheck 'started'
> > > flag?
> >
> > I don't see why this won't work, but it's really late here so I will
> > continue to look at it tomorrow. If it does work then I may owe Gottlob
> > an apology because he also used this flag in this way, and I'm hoping I
> > had a good reason not to do the same, but I'm too tired to look tonight.
>
> Good morning,
>
> Sorry if I seem to be unnecessarily confusing this, but I need to
> satisfy myself that all the older issues can truly be set aside.
>
> Here's my explanation of why 'started' wasn't sufficient in the current
> version (but is now in your version):
>
> The current event based version uses a manual-reset event, which the new
> initter must reset (the alternative using auto-reset and daisy chaining
> resets was not acceptable) and priority manipulation was needed because
> of this. The cancelled state flag is useful here and elsewhere because
> it meant all of this extra complexity could be kept out of the no-
> cancellation paths efficiently.
>
> Your semaphore version removes the need for any extra post-cancellation
> processing by the new initter thread, and I think I can agree - it's now
> possible to simply recheck 'started' as you've now done.
>
> I think it's looking much much better now and I'm very exited at the
> prospects.
>
> Thanks.
> Ross
>
> > I would love to have been wrong all along.
> >
> > > See the attached code.
> > >
> > > --vlad
> > >
> > > PS.
> > > I did rerun all the regression tests but only on a single CPU system. The
> > > condition above I tested by sticking a 'sleep' call before creating the
> > > semaphore.
> > > Do you have a multi-CPU box around?
> >
> > Not I, but Tim Theisen runs the tests on his 4-way before I release a
> > new version. He was certainly picking up problems that ran fine on my
> > uni-processor.
> >
> > Regards.
> > Ross
> >
> > >
> > > -----Original Message-----
> > > From: Ross Johnson [mailto:Ross.Johnson@homemail.com.au]
> > > Sent: Wednesday, May 25, 2005 5:41 AM
> > > To: Vladimir Kliatchko
> > > Cc: Gottlob Frege
> > > Subject: Re: pthread_once suggestions
> > >
> > > On Wed, 2005-05-25 at 17:48 +1000, Ross Johnson wrote:
> > > > I think there is a problem after all.
> > > >
> > > > If thread A is running the init_routine and thread B is suspended
> > > > immediately before it is about to increment numSemaphoreUsers, and
> > > > thread A is cancelled, then: there may not be a semaphore yet for the
> > > > cancelled thread A to release; thread B will eventually resume, create
> > > > the semaphore, and then wait on it until a new thread C arrives to
> > > > finally complete the init_routine and release thread B.
> > > >
> > > > If there are only ever 2 threads then thread B will wait forever. But
> > > > even if there are more than 2 threads, thread B should not have to wait
> > > > until thread C arrives.
> > > >
> > > > So I think it's still necessary to have a flag to tell potential waiters
> > > > that the init_routine has been cancelled.
> > > >
> > > > Even so, I think the semaphore can still eliminate a lot of the current
> > > > complexity, especially the priority management.
> > >
> > > Hi Vlad,
> > >
> > > Sorry, it's all coming back to me - but slowly. If I add the cancelled
> > > state flag back in to prevent thread B waiting on the semaphore that it
> > > has just created (see above), then I seem to be returning to the
> > > situation where starvation occurs.
> > >
> > > If thread C arrives and sets 'started' to true first, but suspends
> > > before it can clear the cancelled flag, then if thread B (which has been
> > > woken) has a higher priority than thread C, it could busy loop and
> > > starve thread C of CPU, preventing it from ever clearing the cancelled
> > > flag. This is a problem that Gottlob noted during the previous effort to
> > > tame this routine using events. It's what ultimately forced the
> > > inclusion of the priority promotion code.
> > >
> > > Regards.
> > > Ross
> > >
> > > > Is this correct?
> > > >
> > > > Regards.
> > > > Ross
> > > >
> > > > On Wed, 2005-05-25 at 16:55 +1000, Ross Johnson wrote:
> > > > > On Tue, 2005-05-24 at 20:56 -0400, Vladimir Kliatchko wrote:
> > > > > > Dear Ross,
> > > > > > I was looking at the implementation of the pthread_once and came up
> > > > > > with what seemed a simpler and more robust implementation. I would
> > > > > > like to contribute this implementation. Pls, let me know what think.
> > > > > >
> > > > > > Thx,
> > > > > > --vlad
> > > > >
> > > > > Hi Vlad,
> > > > >
> > > > > This is great. It seems to have everything and I can't find fault with
> > > > > it - I'm still looking since I think there were some very obscure
> > > > > scenarios, but you seem to have fundamentally eliminated them.
> > > > >
> > > > > This is actually much closer to the simple [working] version posted by
> > > > > Gottlob Frege initially:-
> > > > > http://sources.redhat.com/ml/pthreads-win32/2005/msg00029.html
> > > > > which uses events but didn't include cancelation. Your's is even a
> > > > > little simpler I think.
> > > > >
> > > > > The advantage your semaphore has over the event is that you can release
> > > > > just one waiter after a cancellation but still release all waiters when
> > > > > done (PulseEvent is deprecated and dangerous and wasn't an option). If
> > > > > there are really no problems with it then I think you've legitimised the
> > > > > whole approach, as opposed to the named or reference counting mutex
> > > > > trick etc.
> > > > >
> > > > > And you've done it without changing pthread_once_t_ or PTHREAD_ONCE_INIT
> > > > > - so the ABI is unaffected. Fantastic!
> > > > >
> > > > > I presume you've already run the test suite, and once4.c in particular
> > > > > has passed? I'll let you know if any problems arise. Did you test it on
> > > > > a multi-processor system?
> > > > >
> > > > > Thank you.
> > > > > Ross
> > > > >
> > > > > > The implementation:
> > > > > > #define PTHREAD_ONCE_INIT { PTW32_FALSE, PTW32_FALSE, 0, 0}
> > > > > >
> > > > > >
> > > > > >
> > > > > > struct pthread_once_t_
> > > > > > {
> > > > > > int done;
> > > > > > int started;
> > > > > > int numSemaphoreUsers;
> > > > > > HANDLE semaphore;
> > > > > > };
> > > > > >
> > > > > > static void PTW32_CDECL
> > > > > > ptw32_once_init_routine_cleanup(void * arg)
> > > > > > {
> > > > > > pthread_once_t * once_control = (pthread_once_t *) arg;
> > > > > >
> > > > > > (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started,
> > > (LONG)PTW32_FALSE);
> > > > > >
> > > > > > if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /*
> > > MBR fence */
> > > > > > {
> > > > > > ReleaseSemaphore(once_control->semaphore, 1, NULL);
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > int
> > > > > > pthread_once (pthread_once_t * once_control, void (*init_routine)
> > > (void))
> > > > > > {
> > > > > > int result;
> > > > > > HANDLE sema;
> > > > > >
> > > > > > if (once_control == NULL || init_routine == NULL)
> > > > > > {
> > > > > > result = EINVAL;
> > > > > > goto FAIL0;
> > > > > > }
> > > > > > else
> > > > > > {
> > > > > > result = 0;
> > > > > > }
> > > > > >
> > > > > > while (!InterlockedExchangeAdd((LPLONG)&once_control->done, 0L)) /*
> > > Atomic Read */
> > > > > > {
> > > > > > if (!PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started,
> > > (LONG)PTW32_TRUE))
> > > > > > {
> > > > > >
> > > > > > #ifdef _MSC_VER
> > > > > > #pragma inline_depth(0)
> > > > > > #endif
> > > > > >
> > > > > > pthread_cleanup_push(ptw32_once_init_routine_cleanup,
> > > (void *) once_control);
> > > > > > (*init_routine)();
> > > > > > pthread_cleanup_pop(0);
> > > > > >
> > > > > > #ifdef _MSC_VER
> > > > > > #pragma inline_depth()
> > > > > > #endif
> > > > > >
> > > > > > (void)
> > > PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->done, (LONG)PTW32_TRUE);
> > > > > >
> > > > > > /*
> > > > > > * we didn't create the event.
> > > > > > * it is only there if there is someone waiting.
> > > > > > * Avoid using the global event_lock but still prevent
> > > SetEvent
> > > > > > * from overwriting any 'lasterror' if the event is
> > > closed before we
> > > > > > * are done with it.
> > > > > > */
> > > > > > if
> > > (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence
> > > */
> > > > > > {
> > > > > > ReleaseSemaphore(once_control->semaphore,
> > > once_control->numSemaphoreUsers, NULL);
> > > > > > }
> > > > > > }
> > > > > > else
> > > > > > {
> > > > > >
> > > InterlockedIncrement((LPLONG)&once_control->numSemaphoreUsers);
> > > > > > if
> > > (!InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR fence
> > > */
> > > > > > {
> > > > > > sema = CreateSemaphore(NULL, 0, INT_MAX, NULL);
> > > > > > if
> > > (PTW32_INTERLOCKED_COMPARE_EXCHANGE((PTW32_INTERLOCKED_LPLONG)&once_control-
> > > >semaphore,
> > > > > >
> > > (PTW32_INTERLOCKED_LONG)sema,
> > > > > >
> > > (PTW32_INTERLOCKED_LONG)0))
> > > > > > {
> > > > > > CloseHandle(sema);
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > * Check 'state' again in case the initting thread has
> > > finished or cancelled
> > > > > > * and left before seeing that there was an event to
> > > trigger.
> > > > > > */
> > > > > > if (InterlockedExchangeAdd((LPLONG)&once_control->done,
> > > 0L) ||
> > > > > > WaitForSingleObject(once_control->semaphore,
> > > INFINITE))
> > > > > > {
> > > > > > if (0 ==
> > > InterlockedDecrement((LPLONG)&once_control->numSemaphoreUsers))
> > > > > > {
> > > > > > /* we were last */
> > > > > > if ((sema = (HANDLE)
> > > PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->semaphore,
> > > > > >
> > > (LONG)0)))
> > > > > > {
> > > > > > CloseHandle(sema);
> > > > > > }
> > > > > > }
> > > > > > }
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > * ------------
> > > > > > * Failure Code
> > > > > > * ------------
> > > > > > */
> > > > > > FAIL0:
> > > > > > return (result);
> > > > > >
> > > > > > } /* pthread_once */
> > > > > >
> > >
>
>
^ permalink raw reply [flat|nested] 2+ messages in thread
* RE: pthread_once suggestions
2005-05-26 16:22 ` pthread_once suggestions Gottlob Frege
@ 2005-05-27 1:42 ` Vladimir Kliatchko
0 siblings, 0 replies; 2+ messages in thread
From: Vladimir Kliatchko @ 2005-05-27 1:42 UTC (permalink / raw)
To: 'Gottlob Frege'; +Cc: pthreads-win32
[-- Attachment #1: Type: text/plain, Size: 15093 bytes --]
Sure. Here is the code.
--vlad
#define PTHREAD_ONCE_INIT { 0, 0, 0, 0}
enum ptw32_once_state {
PTW32_ONCE_INIT = 0x0,
PTW32_ONCE_STARTED = 0x1,
PTW32_ONCE_DONE = 0x2
};
struct pthread_once_t_
{
int state;
int reserved;
int numSemaphoreUsers;
HANDLE semaphore;
};
static void PTW32_CDECL
ptw32_once_init_routine_cleanup(void * arg)
{
pthread_once_t * once_control = (pthread_once_t *) arg;
(void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state,
(LONG)PTW32_ONCE_INIT);
if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore,
0L)) /* MBR fence */
{
ReleaseSemaphore(once_control->semaphore, 1, NULL);
}
}
int
pthread_once (pthread_once_t * once_control, void (*init_routine) (void))
{
int result;
int state;
HANDLE sema;
if (once_control == NULL || init_routine == NULL)
{
result = EINVAL;
goto FAIL0;
}
else
{
result = 0;
}
while ((state = PTW32_INTERLOCKED_COMPARE_EXCHANGE(
(PTW32_INTERLOCKED_LPLONG)&once_control->state,
(PTW32_INTERLOCKED_LONG)PTW32_ONCE_STARTED,
(PTW32_INTERLOCKED_LONG)PTW32_ONCE_INIT)) !=
PTW32_ONCE_DONE)
{
if (PTW32_ONCE_INIT == state)
{
#ifdef _MSC_VER
#pragma inline_depth(0)
#endif
pthread_cleanup_push(ptw32_once_init_routine_cleanup,
(void *) once_control);
(*init_routine)();
pthread_cleanup_pop(0);
#ifdef _MSC_VER
#pragma inline_depth()
#endif
(void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state,
(LONG)PTW32_ONCE_DONE);
/*
* we didn't create the semaphore.
* it is only there if there is someone waiting.
*/
if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore,
0L)) /* MBR fence */
{
ReleaseSemaphore(once_control->semaphore,
once_control->numSemaphoreUsers, NULL);
}
}
else
{
InterlockedIncrement((LPLONG)&once_control->numSemaphoreUsers);
if (!InterlockedExchangeAdd((LPLONG)&once_control->semaphore,
0L)) /* MBR fence */
{
sema = CreateSemaphore(NULL, 0, INT_MAX, NULL);
if (PTW32_INTERLOCKED_COMPARE_EXCHANGE(
(PTW32_INTERLOCKED_LPLONG)&once_control->semaphore,
(PTW32_INTERLOCKED_LONG)sema,
(PTW32_INTERLOCKED_LONG)0))
{
CloseHandle(sema);
}
}
/*
* Check 'state' again in case the initting thread has finished or
cancelled and left before seeing that there was a semaphore.
*/
if (InterlockedExchangeAdd((LPLONG)&once_control->state,
0L) == PTW32_ONCE_STARTED)
{
WaitForSingleObject(once_control->semaphore, INFINITE);
}
if (0 ==
InterlockedDecrement((LPLONG)&once_control->numSemaphoreUsers))
{
/* we were last */
if ((sema = (HANDLE) PTW32_INTERLOCKED_EXCHANGE(
(LPLONG)&once_control->semaphore,
(LONG)0)))
{
CloseHandle(sema);
}
}
}
}
/*
* ------------
* Failure Code
* ------------
*/
FAIL0:
return (result);
} /* pthread_once */
-----Original Message-----
From: pthreads-win32-owner@sources.redhat.com
[mailto:pthreads-win32-owner@sources.redhat.com] On Behalf Of Gottlob Frege
Sent: Thursday, May 26, 2005 12:23 PM
To: Ross Johnson
Cc: Pthreads-Win32 list
Subject: Re: pthread_once suggestions
To get me back into the loop, can someone post a version with these
latest ideas (pseudocode or whatever)?
On 5/25/05, Ross Johnson <Ross.Johnson@homemail.com.au> wrote:
> On Thu, 2005-05-26 at 01:42 +1000, Ross Johnson wrote:
> > On Wed, 2005-05-25 at 06:40 -0400, Vladimir Kliatchko wrote:
> > > Hi,
> > > I did not have much time to look into it, but from the top of my head
it
> > > appears that the problem you pointed out is similar to the problem
with the
> > > initializing thread not being canceled but completing while the thread
B is
> > > suspended before creating the semaphore. This last problem was
addressed by
> > > simply rechecking the 'done' flag. Can't we similarly recheck
'started'
> > > flag?
> >
> > I don't see why this won't work, but it's really late here so I will
> > continue to look at it tomorrow. If it does work then I may owe Gottlob
> > an apology because he also used this flag in this way, and I'm hoping I
> > had a good reason not to do the same, but I'm too tired to look
tonight..
>
> Good morning,
>
> Sorry if I seem to be unnecessarily confusing this, but I need to
> satisfy myself that all the older issues can truly be set aside.
>
> Here's my explanation of why 'started' wasn't sufficient in the current
> version (but is now in your version):
>
> The current event based version uses a manual-reset event, which the new
> initter must reset (the alternative using auto-reset and daisy chaining
> resets was not acceptable) and priority manipulation was needed because
> of this. The cancelled state flag is useful here and elsewhere because
> it meant all of this extra complexity could be kept out of the no-
> cancellation paths efficiently.
>
> Your semaphore version removes the need for any extra post-cancellation
> processing by the new initter thread, and I think I can agree - it's now
> possible to simply recheck 'started' as you've now done.
>
> I think it's looking much much better now and I'm very exited at the
> prospects.
>
> Thanks.
> Ross
>
> > I would love to have been wrong all along.
> >
> > > See the attached code.
> > >
> > > --vlad
> > >
> > > PS.
> > > I did rerun all the regression tests but only on a single CPU system.
The
> > > condition above I tested by sticking a 'sleep' call before creating
the
> > > semaphore.
> > > Do you have a multi-CPU box around?
> >
> > Not I, but Tim Theisen runs the tests on his 4-way before I release a
> > new version. He was certainly picking up problems that ran fine on my
> > uni-processor.
> >
> > Regards.
> > Ross
> >
> > >
> > > -----Original Message-----
> > > From: Ross Johnson [mailto:Ross.Johnson@homemail.com.au]
> > > Sent: Wednesday, May 25, 2005 5:41 AM
> > > To: Vladimir Kliatchko
> > > Cc: Gottlob Frege
> > > Subject: Re: pthread_once suggestions
> > >
> > > On Wed, 2005-05-25 at 17:48 +1000, Ross Johnson wrote:
> > > > I think there is a problem after all.
> > > >
> > > > If thread A is running the init_routine and thread B is suspended
> > > > immediately before it is about to increment numSemaphoreUsers, and
> > > > thread A is cancelled, then: there may not be a semaphore yet for
the
> > > > cancelled thread A to release; thread B will eventually resume,
create
> > > > the semaphore, and then wait on it until a new thread C arrives to
> > > > finally complete the init_routine and release thread B.
> > > >
> > > > If there are only ever 2 threads then thread B will wait forever.
But
> > > > even if there are more than 2 threads, thread B should not have to
wait
> > > > until thread C arrives.
> > > >
> > > > So I think it's still necessary to have a flag to tell potential
waiters
> > > > that the init_routine has been cancelled.
> > > >
> > > > Even so, I think the semaphore can still eliminate a lot of the
current
> > > > complexity, especially the priority management.
> > >
> > > Hi Vlad,
> > >
> > > Sorry, it's all coming back to me - but slowly. If I add the cancelled
> > > state flag back in to prevent thread B waiting on the semaphore that
it
> > > has just created (see above), then I seem to be returning to the
> > > situation where starvation occurs.
> > >
> > > If thread C arrives and sets 'started' to true first, but suspends
> > > before it can clear the cancelled flag, then if thread B (which has
been
> > > woken) has a higher priority than thread C, it could busy loop and
> > > starve thread C of CPU, preventing it from ever clearing the cancelled
> > > flag. This is a problem that Gottlob noted during the previous effort
to
> > > tame this routine using events. It's what ultimately forced the
> > > inclusion of the priority promotion code.
> > >
> > > Regards.
> > > Ross
> > >
> > > > Is this correct?
> > > >
> > > > Regards.
> > > > Ross
> > > >
> > > > On Wed, 2005-05-25 at 16:55 +1000, Ross Johnson wrote:
> > > > > On Tue, 2005-05-24 at 20:56 -0400, Vladimir Kliatchko wrote:
> > > > > > Dear Ross,
> > > > > > I was looking at the implementation of the pthread_once and came
up
> > > > > > with what seemed a simpler and more robust implementation. I
would
> > > > > > like to contribute this implementation. Pls, let me know what
think.
> > > > > >
> > > > > > Thx,
> > > > > > --vlad
> > > > >
> > > > > Hi Vlad,
> > > > >
> > > > > This is great. It seems to have everything and I can't find fault
with
> > > > > it - I'm still looking since I think there were some very obscure
> > > > > scenarios, but you seem to have fundamentally eliminated them.
> > > > >
> > > > > This is actually much closer to the simple [working] version
posted by
> > > > > Gottlob Frege initially:-
> > > > > http://sources.redhat.com/ml/pthreads-win32/2005/msg00029.html
> > > > > which uses events but didn't include cancelation. Your's is even a
> > > > > little simpler I think.
> > > > >
> > > > > The advantage your semaphore has over the event is that you can
release
> > > > > just one waiter after a cancellation but still release all waiters
when
> > > > > done (PulseEvent is deprecated and dangerous and wasn't an
option). If
> > > > > there are really no problems with it then I think you've
legitimised the
> > > > > whole approach, as opposed to the named or reference counting
mutex
> > > > > trick etc.
> > > > >
> > > > > And you've done it without changing pthread_once_t_ or
PTHREAD_ONCE_INIT
> > > > > - so the ABI is unaffected. Fantastic!
> > > > >
> > > > > I presume you've already run the test suite, and once4.c in
particular
> > > > > has passed? I'll let you know if any problems arise. Did you test
it on
> > > > > a multi-processor system?
> > > > >
> > > > > Thank you.
> > > > > Ross
> > > > >
> > > > > > The implementation:
> > > > > > #define PTHREAD_ONCE_INIT { PTW32_FALSE, PTW32_FALSE, 0,
0}
> > > > > >
> > > > > >
> > > > > >
> > > > > > struct pthread_once_t_
> > > > > > {
> > > > > > int done;
> > > > > > int started;
> > > > > > int numSemaphoreUsers;
> > > > > > HANDLE semaphore;
> > > > > > };
> > > > > >
> > > > > > static void PTW32_CDECL
> > > > > > ptw32_once_init_routine_cleanup(void * arg)
> > > > > > {
> > > > > > pthread_once_t * once_control = (pthread_once_t *) arg;
> > > > > >
> > > > > > (void)
PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started,
> > > (LONG)PTW32_FALSE);
> > > > > >
> > > > > > if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore,
0L)) /*
> > > MBR fence */
> > > > > > {
> > > > > > ReleaseSemaphore(once_control->semaphore, 1, NULL);
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > int
> > > > > > pthread_once (pthread_once_t * once_control, void
(*init_routine)
> > > (void))
> > > > > > {
> > > > > > int result;
> > > > > > HANDLE sema;
> > > > > >
> > > > > > if (once_control == NULL || init_routine == NULL)
> > > > > > {
> > > > > > result = EINVAL;
> > > > > > goto FAIL0;
> > > > > > }
> > > > > > else
> > > > > > {
> > > > > > result = 0;
> > > > > > }
> > > > > >
> > > > > > while (!InterlockedExchangeAdd((LPLONG)&once_control->done,
0L)) /*
> > > Atomic Read */
> > > > > > {
> > > > > > if
(!PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started,
> > > (LONG)PTW32_TRUE))
> > > > > > {
> > > > > >
> > > > > > #ifdef _MSC_VER
> > > > > > #pragma inline_depth(0)
> > > > > > #endif
> > > > > >
> > > > > >
pthread_cleanup_push(ptw32_once_init_routine_cleanup,
> > > (void *) once_control);
> > > > > > (*init_routine)();
> > > > > > pthread_cleanup_pop(0);
> > > > > >
> > > > > > #ifdef _MSC_VER
> > > > > > #pragma inline_depth()
> > > > > > #endif
> > > > > >
> > > > > > (void)
> > > PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->done,
(LONG)PTW32_TRUE);
> > > > > >
> > > > > > /*
> > > > > > * we didn't create the event.
> > > > > > * it is only there if there is someone waiting.
> > > > > > * Avoid using the global event_lock but still
prevent
> > > SetEvent
> > > > > > * from overwriting any 'lasterror' if the event
is
> > > closed before we
> > > > > > * are done with it.
> > > > > > */
> > > > > > if
> > > (InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR
fence
> > > */
> > > > > > {
> > > > > > ReleaseSemaphore(once_control->semaphore,
> > > once_control->numSemaphoreUsers, NULL);
> > > > > > }
> > > > > > }
> > > > > > else
> > > > > > {
> > > > > >
> > > InterlockedIncrement((LPLONG)&once_control->numSemaphoreUsers);
> > > > > > if
> > > (!InterlockedExchangeAdd((LPLONG)&once_control->semaphore, 0L)) /* MBR
fence
> > > */
> > > > > > {
> > > > > > sema = CreateSemaphore(NULL, 0, INT_MAX,
NULL);
> > > > > > if
> > >
(PTW32_INTERLOCKED_COMPARE_EXCHANGE((PTW32_INTERLOCKED_LPLONG)&once_control-
> > > >semaphore,
> > > > > >
> > > (PTW32_INTERLOCKED_LONG)sema,
> > > > > >
> > > (PTW32_INTERLOCKED_LONG)0))
> > > > > > {
> > > > > > CloseHandle(sema);
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > * Check 'state' again in case the initting thread
has
> > > finished or cancelled
> > > > > > * and left before seeing that there was an event
to
> > > trigger.
> > > > > > */
> > > > > > if
(InterlockedExchangeAdd((LPLONG)&once_control->done,
> > > 0L) ||
> > > > > > WaitForSingleObject(once_control->semaphore,
> > > INFINITE))
> > > > > > {
> > > > > > if (0 ==
> > > InterlockedDecrement((LPLONG)&once_control->numSemaphoreUsers))
> > > > > > {
> > > > > > /* we were last */
> > > > > > if ((sema = (HANDLE)
> > > PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->semaphore,
> > > > > >
> > > (LONG)0)))
> > > > > > {
> > > > > > CloseHandle(sema);
> > > > > > }
> > > > > > }
> > > > > > }
> > > > > > }
> > > > > > }
> > > > > >
> > > > > > /*
> > > > > > * ------------
> > > > > > * Failure Code
> > > > > > * ------------
> > > > > > */
> > > > > > FAIL0:
> > > > > > return (result);
> > > > > >
> > > > > > } /* pthread_once
*/
> > > > > >
> > >
>
>
[-- Attachment #2: vk_pthread_once2.c --]
[-- Type: text/plain, Size: 3162 bytes --]
#define PTHREAD_ONCE_INIT { 0, 0, 0, 0}
enum ptw32_once_state {
PTW32_ONCE_INIT = 0x0,
PTW32_ONCE_STARTED = 0x1,
PTW32_ONCE_DONE = 0x2
};
struct pthread_once_t_
{
int state;
int reserved;
int numSemaphoreUsers;
HANDLE semaphore;
};
static void PTW32_CDECL
ptw32_once_init_routine_cleanup(void * arg)
{
pthread_once_t * once_control = (pthread_once_t *) arg;
(void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state,
(LONG)PTW32_ONCE_INIT);
if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore,
0L)) /* MBR fence */
{
ReleaseSemaphore(once_control->semaphore, 1, NULL);
}
}
int
pthread_once (pthread_once_t * once_control, void (*init_routine) (void))
{
int result;
int state;
HANDLE sema;
if (once_control == NULL || init_routine == NULL)
{
result = EINVAL;
goto FAIL0;
}
else
{
result = 0;
}
while ((state = PTW32_INTERLOCKED_COMPARE_EXCHANGE(
(PTW32_INTERLOCKED_LPLONG)&once_control->state,
(PTW32_INTERLOCKED_LONG)PTW32_ONCE_STARTED,
(PTW32_INTERLOCKED_LONG)PTW32_ONCE_INIT)) !=
PTW32_ONCE_DONE)
{
if (PTW32_ONCE_INIT == state)
{
#ifdef _MSC_VER
#pragma inline_depth(0)
#endif
pthread_cleanup_push(ptw32_once_init_routine_cleanup,
(void *) once_control);
(*init_routine)();
pthread_cleanup_pop(0);
#ifdef _MSC_VER
#pragma inline_depth()
#endif
(void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state,
(LONG)PTW32_ONCE_DONE);
/*
* we didn't create the semaphore.
* it is only there if there is someone waiting.
*/
if (InterlockedExchangeAdd((LPLONG)&once_control->semaphore,
0L)) /* MBR fence */
{
ReleaseSemaphore(once_control->semaphore,
once_control->numSemaphoreUsers, NULL);
}
}
else
{
InterlockedIncrement((LPLONG)&once_control->numSemaphoreUsers);
if (!InterlockedExchangeAdd((LPLONG)&once_control->semaphore,
0L)) /* MBR fence */
{
sema = CreateSemaphore(NULL, 0, INT_MAX, NULL);
if (PTW32_INTERLOCKED_COMPARE_EXCHANGE(
(PTW32_INTERLOCKED_LPLONG)&once_control->semaphore,
(PTW32_INTERLOCKED_LONG)sema,
(PTW32_INTERLOCKED_LONG)0))
{
CloseHandle(sema);
}
}
/*
* Check 'state' again in case the initting thread has finished or
cancelled and left before seeing that there was a semaphore.
*/
if (InterlockedExchangeAdd((LPLONG)&once_control->state,
0L) == PTW32_ONCE_STARTED)
{
WaitForSingleObject(once_control->semaphore, INFINITE);
}
if (0 ==
InterlockedDecrement((LPLONG)&once_control->numSemaphoreUsers))
{
/* we were last */
if ((sema = (HANDLE) PTW32_INTERLOCKED_EXCHANGE(
(LPLONG)&once_control->semaphore,
(LONG)0)))
{
CloseHandle(sema);
}
}
}
}
/*
* ------------
* Failure Code
* ------------
*/
FAIL0:
return (result);
} /* pthread_once */
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2005-05-27 1:42 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <0IH100J9YK9N5G@mta3.srv.hcvlny.cv.net>
[not found] ` <1117035766.9478.44.camel@desk.home>
[not found] ` <1117074885.9478.98.camel@desk.home>
2005-05-26 16:22 ` pthread_once suggestions Gottlob Frege
2005-05-27 1:42 ` Vladimir Kliatchko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).