150 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* sync.cc: Synchronization functions for cygwin.
 | |
| 
 | |
|    This file implements the methods for controlling the "muto" class
 | |
|    which is intended to operate similarly to a mutex but attempts to
 | |
|    avoid making expensive calls to the kernel.
 | |
| 
 | |
|    Copyright 2000, 2001, 2002 Red Hat, Inc.
 | |
| 
 | |
|    Written by Christopher Faylor <cgf@cygnus.com>
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include <stdlib.h>
 | |
| #include <time.h>
 | |
| #include <sys/wait.h>
 | |
| #include <errno.h>
 | |
| #include <stdlib.h>
 | |
| #include "sync.h"
 | |
| #include "security.h"
 | |
| 
 | |
| muto NO_COPY muto_start;
 | |
| 
 | |
| #undef WaitForSingleObject
 | |
| 
 | |
| /* Constructor */
 | |
| muto *
 | |
| muto::init (const char *s)
 | |
| {
 | |
|   waiters = -1;
 | |
|   /* Create event which is used in the fallback case when blocking is necessary */
 | |
|   if (!(bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL)))
 | |
|     {
 | |
|       DWORD oerr = GetLastError ();
 | |
|       SetLastError (oerr);
 | |
|       return NULL;
 | |
|     }
 | |
|   name = s;
 | |
|   next = muto_start.next;
 | |
|   muto_start.next = this;
 | |
|   return this;
 | |
| }
 | |
| 
 | |
| #if 0 /* FIXME: Do we need this? mutos aren't destroyed until process exit */
 | |
| /* Destructor (racy?) */
 | |
| muto::~muto ()
 | |
| {
 | |
|   while (visits)
 | |
|     release ();
 | |
| 
 | |
|   HANDLE h = bruteforce;
 | |
|   bruteforce = NULL;
 | |
|   /* Just need to close the event handle */
 | |
|   if (h)
 | |
|     CloseHandle (h);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* Acquire the lock.  Argument is the number of milliseconds to wait for
 | |
|    the lock.  Multiple visits from the same thread are allowed and should
 | |
|    be handled correctly.
 | |
| 
 | |
|    Note: The goal here is to minimize, as much as possible, calls to the
 | |
|    OS.  Hence the use of InterlockedIncrement, etc., rather than (much) more
 | |
|    expensive OS mutexes.  */
 | |
| int
 | |
| muto::acquire (DWORD ms)
 | |
| {
 | |
|   DWORD this_tid = GetCurrentThreadId ();
 | |
| 
 | |
|   if (tid != this_tid)
 | |
|     {
 | |
|       /* Increment the waiters part of the class.  Need to do this first to
 | |
| 	 avoid potential races. */
 | |
|       LONG was_waiting = InterlockedIncrement (&waiters);
 | |
| 
 | |
|       /* This is deceptively simple.  Basically, it allows multiple attempts to
 | |
| 	 lock the same muto to succeed without attempting to manipulate sync.
 | |
| 	 If the muto is already locked then this thread will wait for ms until
 | |
| 	 it is signalled by muto::release.  Then it will attempt to grab the
 | |
| 	 sync field.  If it succeeds, then this thread owns the muto.
 | |
| 
 | |
| 	 There is a pathological condition where a thread times out waiting for
 | |
| 	 bruteforce but the release code triggers the bruteforce event.  In this
 | |
| 	 case, it is possible for a thread which is going to wait for bruteforce
 | |
| 	 to wake up immediately.  It will then attempt to grab sync but will fail
 | |
| 	 and go back to waiting.  */
 | |
|       if (tid != this_tid && (was_waiting || InterlockedExchange (&sync, 1) != 0))
 | |
| 	{
 | |
| 	  switch (WaitForSingleObject (bruteforce, ms))
 | |
| 	      {
 | |
| 	      case WAIT_OBJECT_0:
 | |
| 		goto gotit;
 | |
| 		break;
 | |
| 	      default:
 | |
| 		InterlockedDecrement (&waiters);
 | |
| 		return 0;	/* failed. */
 | |
| 	      }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
| gotit:
 | |
|   tid = this_tid;	/* register this thread. */
 | |
|   return ++visits;	/* Increment visit count. */
 | |
| }
 | |
| 
 | |
| /* Return the muto lock.  Needs to be called once per every acquire. */
 | |
| int
 | |
| muto::release ()
 | |
| {
 | |
|   DWORD this_tid = GetCurrentThreadId ();
 | |
| 
 | |
|   if (tid != this_tid || !visits)
 | |
|     {
 | |
|       SetLastError (ERROR_NOT_OWNER);	/* Didn't have the lock. */
 | |
|       return 0;	/* failed. */
 | |
|     }
 | |
| 
 | |
|   /* FIXME: Need to check that other thread has not exited, too. */
 | |
|   if (!--visits)
 | |
|     {
 | |
|       tid = 0;		/* We were the last unlocker. */
 | |
|       (void) InterlockedExchange (&sync, 0); /* Reset trigger. */
 | |
|       /* This thread had incremented waiters but had never decremented it.
 | |
| 	 Decrement it now.  If it is >= 0 then there are possibly other
 | |
| 	 threads waiting for the lock, so trigger bruteforce. */
 | |
|       if (InterlockedDecrement (&waiters) >= 0)
 | |
| 	(void) SetEvent (bruteforce); /* Wake up one of the waiting threads */
 | |
|     }
 | |
| 
 | |
|   return 1;	/* success. */
 | |
| }
 | |
| 
 | |
| /* Call only when we're exiting.  This is not thread safe. */
 | |
| void
 | |
| muto::reset ()
 | |
| {
 | |
|   visits = sync = tid = 0;
 | |
|   InterlockedExchange (&waiters, -1);
 | |
|   if (bruteforce)
 | |
|     {
 | |
|       CloseHandle (bruteforce);
 | |
|       bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, name);
 | |
|     }
 | |
| }
 |