From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 22066 invoked by alias); 8 Mar 2005 02:22:22 -0000 Mailing-List: contact pthreads-win32-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: pthreads-win32-owner@sources.redhat.com Received: (qmail 21881 invoked from network); 8 Mar 2005 02:22:04 -0000 Received: from unknown (HELO canyonero.dot.net.au) (202.147.68.14) by sourceware.org with SMTP; 8 Mar 2005 02:22:04 -0000 Received: from [203.24.160.100] (helo=ip-203-24-160-100.dot.net.au) by canyonero.dot.net.au with esmtp (Exim 3.35 #1 (Debian)) id 1D8UME-0002Hu-00 for ; Tue, 08 Mar 2005 13:22:02 +1100 Subject: Re: starvation in pthread_once? From: Ross Johnson To: Pthreads-Win32 list In-Reply-To: <1109978309.8332.70.camel@desk.home> References: <97ffb3105030301323c1bae1@mail.gmail.com> <1109978309.8332.70.camel@desk.home> Content-Type: text/plain Date: Tue, 08 Mar 2005 02:22:00 -0000 Message-Id: <1110248521.6900.44.camel@desk.home> Mime-Version: 1.0 Content-Transfer-Encoding: 7bit X-SW-Source: 2005/txt/msg00028.txt.bz2 All, I've redesigned pthread_once() and will shortly drop it into CVS and a new snapshot. Please take a look over the code below and let me know of any problems or improvements you find. The rationale is explained in the comments. This new version passes a new test I've added to the test suite that exercises the routine much more intensively than previously (although it doesn't play around with thread priorities yet). Thanks. Ross /* * Use a single global cond+mutex to manage access to all once_control objects. * Unlike a global mutex on it's own, the global cond+mutex allows faster * once_controls to overtake slower ones. Spurious wakeups can occur, but * can be tolerated. * * To maintain a separate mutex for each once_control object requires either * cleaning up the mutex (difficult to synchronise reliably), or leaving it * around forever. Since we can't make assumptions about how an application might * employ once_t objects, the later is considered to be unacceptable. * * Since this is being introduced as a bug fix, the global cond+mtx also avoids * a change in the ABI, maintaining backwards compatibility. * * The mutex should be an ERRORCHECK type to be sure we will never, in the event * we're cancelled before we get the lock, unlock the mutex when it's held by * another thread (possible with NORMAL/DEFAULT mutexes because they don't check * ownership). */ if (!once_control->done) { if (InterlockedExchange((LPLONG) &once_control->started, (LONG) 0) == -1) { (*init_routine) (); /* * Holding the mutex during the broadcast prevents threads being left * behind waiting. */ pthread_cleanup_push(pthread_mutex_unlock, (void *) &ptw32_once_control.mtx); (void) pthread_mutex_lock(&ptw32_once_control.mtx); once_control->done = PTW32_TRUE; (void) pthread_cond_broadcast(&ptw32_once_control.cond); pthread_cleanup_pop(1); } else { pthread_cleanup_push(pthread_mutex_unlock, (void *) &ptw32_once_control.mtx); (void) pthread_mutex_lock(&ptw32_once_control.mtx); while (!once_control->done) { (void) pthread_cond_wait(&ptw32_once_control.cond, &ptw32_once_control.mtx); } pthread_cleanup_pop(1); } } On Sat, 2005-03-05 at 10:18 +1100, Ross Johnson wrote: > On Thu, 2005-03-03 at 04:32 -0500, Gottlob Frege wrote: > > I'm concerned about the Sleep(0) in pthread_once: > > > > Thanks. It looks like this routine needs to be redesigned. > > Regards. > Ross > > > if (!once_control->done) > > { > > if (InterlockedIncrement (&(once_control->started)) == 0) > > { > > /* > > * First thread to increment the started variable > > */ > > (*init_routine) (); > > once_control->done = PTW32_TRUE; > > > > } > > else > > { > > /* > > * Block until other thread finishes executing the onceRoutine > > */ > > while (!(once_control->done)) > > { > > /* > > * The following gives up CPU cycles without pausing > > * unnecessarily > > */ > > Sleep (0); > > } > > } > > } > > > > IIRC, Sleep(0) does not relinquish time slices to lower priority > > threads. (Sleep(n) for n != 0 does, but 0 does not). So, if a lower > > priority thread is first in, followed closely by a higher priority > > one, the higher priority thread will spin on Sleep(0) *forever* > > because the lower, first thread will never get a chance to set done. > > > > So even Sleep(10) should be good enough. In theory, there could be > > enough higher priority threads in the system that the first thread > > still doesn't get in (ever?!), but unlikely. And that would probably > > mean a general design flaw of the calling code, not pthread_once. > > > > ? > >