diff options
author | Christopher Faylor <me@cgf.cx> | 2000-02-17 19:38:33 +0000 |
---|---|---|
committer | Christopher Faylor <me@cgf.cx> | 2000-02-17 19:38:33 +0000 |
commit | 1fd5e000ace55b323124c7e556a7a864b972a5c4 (patch) | |
tree | dc4fcf1e5e22a040716ef92c496b8d94959b2baa /winsup/cygwin/sync.cc | |
parent | 369d8a8fd5e887eca547bf34bccfdf755c9e5397 (diff) | |
download | cygnal-1fd5e000ace55b323124c7e556a7a864b972a5c4.tar.gz cygnal-1fd5e000ace55b323124c7e556a7a864b972a5c4.tar.bz2 cygnal-1fd5e000ace55b323124c7e556a7a864b972a5c4.zip |
import winsup-2000-02-17 snapshot
Diffstat (limited to 'winsup/cygwin/sync.cc')
-rw-r--r-- | winsup/cygwin/sync.cc | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/winsup/cygwin/sync.cc b/winsup/cygwin/sync.cc new file mode 100644 index 000000000..89d92ce06 --- /dev/null +++ b/winsup/cygwin/sync.cc @@ -0,0 +1,112 @@ +/* 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 1999 Cygnus Solutions. + + 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 <stdlib.h> +#include <time.h> +#include <sys/wait.h> +#include <errno.h> +#include <stdlib.h> +#include "winsup.h" + +/* Constructor */ +muto::muto(int inh, const char *name) : sync (0), visits(0), waiters(-1), tid (0) +{ + /* Create event which is used in the fallback case when blocking is necessary */ + if (!(bruteforce = CreateEvent (inh ? &sec_all_nih : &sec_none_nih, FALSE, FALSE, name))) + { + DWORD oerr = GetLastError (); + SetLastError (oerr); + return; + } +} + +/* Destructor */ +muto::~muto () +{ + /* Just need to close the event handle */ + if (bruteforce) + CloseHandle (bruteforce); +} + +/* 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. */ +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 mutex. + + 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. */ + while (tid != this_tid && (was_waiting || InterlockedExchange (&sync, 1) != 0)) + { + switch (WaitForSingleObject (bruteforce, ms)) + { + case WAIT_OBJECT_0: + was_waiting = 0; + break; + default: + InterlockedDecrement (&waiters); + return 0; /* failed. */ + } + } + } + + 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. */ + 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. */ +} |