/* 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. 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 "miscfuncs.h" #include "sync.h" #include "thread.h" #include "cygtls.h" #undef WaitForSingleObject muto NO_COPY lock_process::locker; void muto::grab () { tls = &_my_tls; } /* Constructor */ muto * muto::init (const char *s) { char *already_exists = (char *) InterlockedExchangePointer ((PVOID *) &name, (PVOID) s); if (already_exists) while (!bruteforce) yield (); else { waiters = -1; /* Create event which is used in the fallback case when blocking is necessary */ bruteforce = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL); if (!bruteforce) api_fatal ("couldn't allocate muto '%s', %E", s); } 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) { void *this_tls = &_my_tls; if (tls != this_tls) { /* Increment the waiters part of the class. Need to do this first to avoid potential races. */ LONG was_waiting = ms ? InterlockedIncrement (&waiters) : 0; while (was_waiting || InterlockedExchange (&sync, 1) != 0) switch (WaitForSingleObject (bruteforce, ms)) { case WAIT_OBJECT_0: was_waiting = 0; break; default: return 0; /* failed. */ } /* Have to do it this way to avoid a race */ if (!ms) InterlockedIncrement (&waiters); tls = this_tls; /* register this thread. */ } return ++visits; /* Increment visit count. */ } bool muto::acquired () { return tls == &_my_tls; } /* Return the muto lock. Needs to be called once per every acquire. */ int muto::release (_cygtls *this_tls) { if (tls != this_tls || !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) { tls = 0; /* We were the last unlocker. */ 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) SetEvent (bruteforce); /* Wake up one of the waiting threads */ else if (*name == '!') { CloseHandle (bruteforce); /* If *name == '!' and there are no other waiters, then this is the last time this muto will ever be used, so close the handle. */ #ifdef DEBUGGING bruteforce = NULL; #endif } } return 1; /* success. */ }