> rbb 01/09/01 00:29:07 > > Modified: threadproc/win32 thread.c > Log: > Avoid a rather nasty bug in the Windows apr_thread_once function. We > basically, check the value before we increment, and after. By checking > before, we take a hit of one if statement, but avoid a kernel call in > most cases. > Submitted by: Will Rowe <[EMAIL PROTECTED]> > > Revision Changes Path > 1.38 +11 -0 apr/threadproc/win32/thread.c > > Index: thread.c > =================================================================== > RCS file: /home/cvs/apr/threadproc/win32/thread.c,v > retrieving revision 1.37 > retrieving revision 1.38 > diff -u -r1.37 -r1.38 > --- thread.c 2001/09/01 07:10:25 1.37 > +++ thread.c 2001/09/01 07:29:07 1.38 > @@ -231,6 +231,17 @@ > APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, > void (*func)(void)) > { > + /* Quick escape hatch, and bug fix. The InterlockedIncrement > + * call is just incrementing a long int, so it has the potential > + * to wrap. Very unlikely, but still, that would be an almost > + * impossible bug to hunt. With this, we might see one or two > + * calls to InterlockedIncrement, but never enough to wrap the > + * long int. This also saves us a kernel call for most calls to > + * this function. > + */ > + if (control->value) { > + return APR_SUCCESS; > + } > InterlockedIncrement(&control->value); > if (control->value == 1) { > func();
This still isn't correct. Now we have a race condition that could result in the function never being called instead of once. This looks better IMHO: APR_DECLARE(apr_status_t) apr_thread_once(apr_thread_once_t *control, void (*func)(void)) { if (!InterlockedExchange(&control->value, 1)) func(); return APR_SUCCESS; } This way we also don't have the wrapping problem. Sander