// Acquire the lock for read (shared) access public void LockRead() { lock (monitor) { // if there isn’t blocked writers and the resource isn’t being written, grant // read access immediately if (writeReqQueue.Count == 0 && state >= 0) { state++; // add one reader return; } // otherwise, enqueue a read access request LockReadRequest request = EnqueueReader(); // wait until request is granted, or the thread gives up due to interruption do { try { MonitorEx.Wait(monitor, monitor); // eq a Monitor.Wait(monitor); } catch (ThreadInterruptedException) { // if the requested shared access was granted, we must re-assert interrupt // exception, and return normally. if (request.done) { Thread.CurrentThread.Interrupt(); break; } // otherwise, we remove the request from the queue and re-throw the exception RemoveReader(); throw; } // if shared access was granted then return; otherwise, re-wait } while (!request.done); } }
private void SyncUnlink(DListNode waiter) { lock (_lock) { DListNode.RemoveIfInserted(waiter); MonitorEx.PulseAll(_lock, waiter); } }
// acquires the specified number of permits; returns false if times out public bool Acquire(int acquires, int timeout = Timeout.Infinite) { lock (monitor) { if (reqQueue.Count == 0 && CanAcquire(acquires)) { AcquireSideEffect(acquires); return(true); } Request request = new Request(acquires); reqQueue.AddLast(request); // enqueue "request" at the end of "reqQueue" TimeoutHolder th = new TimeoutHolder(timeout); do { if ((timeout = th.Value) <= 0) { reqQueue.Remove(request); // after remove the request of the current thread from queue, *it is possible* // that the current synhcronization state allows now to satisfy other queued // acquire(s). if (CurrentSynchStateAllowsAcquire()) { PerformPossibleAcquires(); } return(false); } try { MonitorEx.Wait(monitor, request, timeout); // *wait on a private condition variable* } catch (ThreadInterruptedException) { // if the acquire operation was already done, re-assert interrupt // and return normally; else remove request from queue and throw // ThreadInterruptedException. if (request.done) { Thread.CurrentThread.Interrupt(); return(true); } reqQueue.Remove(request); // after remove the request of the current thread from queue, *it is possible* // that the current synhcronization state allows now to satisfy other queued // acquire(s). if (CurrentSynchStateAllowsAcquire()) { PerformPossibleAcquires(); } throw; // ThreadInterruptedException } } while (!request.done); // the request was completed return(true); } }
// auxiliary method: grant access to the first waiting writer private void GrantAccessToAWaitingWriter() { if (writeReqQueue.Count > 0) { LinkedListNode <bool> reqNode = writeReqQueue.First; reqNode.Value = true; // set request.done to true state = -1; // exclusive lock was taken writeReqQueue.RemoveFirst(); MonitorEx.Pulse(monitor, reqNode); // notify the waiting writer } }
private void GrantAccessToOneWritter() { if (waitingWriters.Count > 0) { LinkedListNode <bool> writer = waitingWriters.First; // get first waiting writer waitingWriters.RemoveFirst(); // remove the writer from the wait queue writing = true; // set exclusive lock as taken writer.Value = true; // mark exclusive lock request as granted; MonitorEx.PulseAll(myLock, writer); // notify the specific writer } }
// auxiliary method: grant access to all waiting readers private bool GrantAccessToWaitingReaders() { if (WaitingReaders() > 0) { readReqQueue.done = true; // mark all lock read requests as done state += WaitingReaders(); // account with all new active readers ClearReaderQueue(); MonitorEx.PulseAll(monitor, monitor); // notify waiting readers return(true); } return(false); }
/* apenas pode ser invocado pelas threads que tenham * adquirido o semáforo para escrita, liberta o acesso para * escrita e, atomicamente, adquire acesso para leitura.*/ public void DowngradeWriter() { if (waitingReaders != null && waitingReaders.waiters > 0) { readers += waitingReaders.waiters; waitingReaders.done = true; // dar acesso aos leitores waitingReaders = null; // retirar os leitores de espera MonitorEx.PulseAll(myLock, myLock); // notificar todos os leitores } else { GrantAccessToOneWritter(); } }
// send a message to the queue public void Send(T sentMsg) { lock (monitor) { UpdateStateOnSend(sentMsg); if (reqQueue.Count > 0) { Request request = reqQueue.First.Value; reqQueue.RemovestFirst(); request.receivedMsg = ReceiveSideEffect(); request.done = true; // notify waiting thread on its private condition variable MonitorEx.Pulse(monitor, request); } } }
// perform the possible pending acquires private void PerformPossibleAcquires() { while (reqQueue.Count > 0) { Request request = reqQueue.First.Value; if (!CanAcquire(request.acquires)) { break; } // remove request from queue and satisfy it reqQueue.RemoveFirst(); AcquireSideEffect(request.acquires); request.done = true; MonitorEx.Pulse(monitor, request); // *specific thread notification* } }
// DownWrite adquirem a posse do semáforo para escrita public void DownWrite() { lock (myLock) { // nao existem leitores em espera e neinguem está a escrever e a fila de escritores está vazia, // o escritor tem acesso ao semáforo if (readers == 0 && !writing && waitingWriters.Count == 0) { writing = true; return; } // o escritor fica em espera LinkedListNode <bool> wrnode = waitingWriters.AddLast(false); do { try { MonitorEx.Wait(myLock, wrnode); } catch (ThreadInterruptedException) { // acesso foi garantido, o escritor retira-se if (wrnode.Value) { Thread.CurrentThread.Interrupt(); return; } // o escritor é removido da espera waitingWriters.Remove(wrnode); // garantir o acesso dos leitores ao semáforo if (!writing && waitingWriters.Count == 0 && waitingReaders != null) { if (waitingReaders != null && waitingReaders.waiters > 0) { readers += waitingReaders.waiters; waitingReaders.done = true; // dar acesso aos leitores waitingReaders = null; // retirar os leitores de espera MonitorEx.PulseAll(myLock, myLock); // notificar todos os leitores } } throw; } } while (!wrnode.Value); } }
// send a message to the queue (optimized) public void SendOptimized(T sentMsg) { lock (monitor) { if (reqQueue.Count > 0) { // deliver the message directly to a blocked thread Request request = reqQueue.First.Value; reqQueue.Remove(request); request.receivedMsg = sentMsg; request.done = true; // notify waiting thread on its private conditions variable MonitorEx.Pulse(monitor, request); } else { // no receiving thread, so the message is left in the respective queue UpdateStateDueToSend(sentMsg); } } }
// receive the next message from the queue public bool Receive(out T receivedMsg, int timeout = Timeout.Infinite) { lock (monitor) { if (reqQueue.Count == 0 && CanReceive()) { receivedMsg = ReceiveSideEffect(); return(true); } // add a request to the end of the reqQueue Request request = new Request(); reqQueue.AddLast(request); TimeoutHolder th = new TimeoutHolder(timeout); do { if ((timeout = th.Value) == 0) { // the specified time limit has expired. // Here we know that our request was not satisfied. reqQueue.Remove(request); receivedMsg = default(T); return false, } try { MonitorEx.Wait(monitor, request, timeout); //block on private condition variable } catch (ThreadInterruptedException) { // if the acquire operation was already done, re-assert interrupt // and return normally; else remove request from queue and throw // ThreadInterruptedException. if (request.done) { Thread.CurrentThread.Interrupt(); break; } reqQueue.Remove(request); throw; } } while (!request.done); receivedMsg = request.receivedMsg; return(true); } }
private readonly LinkedList <bool> waitingWriters = new LinkedList <bool>(); // escritores em espera (entra um de cada vez no semáforo) // DownRead adquirem a posse do semáforo para leitura public void DownRead() { lock (myLock) { // não existem escritores à espera e não há nenhum esritor a escrever, // o leitor ganha acesso independentemente dos outros leitores if (waitingWriters.Count == 0 && !writing) { readers++; return; } // o leitor fica em espera WaitingReaders rdnode; if ((rdnode = waitingReaders) == null) { waitingReaders = rdnode = new WaitingReaders(); } rdnode.waiters++; do { try { MonitorEx.Wait(myLock, myLock); } catch (ThreadInterruptedException) { // acesso foi garantido, o leitor retira-se if (rdnode.done) { Thread.CurrentThread.Interrupt(); return; } // o leitor é removido da espera if (--rdnode.waiters == 0) { waitingReaders = null; } throw; } } while (!rdnode.done); } }
// Acquire the lock for write (exclusive) access public void LockWrite() { lock (monitor) { // if the lock isn’t held for read nor for writing, grant the access immediately if (state == 0) { state = -1; // set state indicating exclusive lock held return; } // enqueue a request for exclusive access LinkedListNode <bool> requestNode = writeReqQueue.AddLast(false); // wait until request is granted, or the thread gives up due to interruption do { try { MonitorEx.Wait(monitor, requestNode); } catch (ThreadInterruptedException) { // if exclusive access was granted, then we re-assert exception, and return normally if (requestNode.Value) { Thread.CurrentThread.Interrupt(); break; } // othwewise, remove the request from the queue, and return throwing the exception. writeReqQueue.Remove(requestNode); // when a waiting writer gives up, we must grant shared access to all // waiting readers that has been blocked by this waiting writer if (writeReqQueue.Count == 0 && WaitingReaders() > 0 && state >= 0) { GrantAccessToWaitingReaders(); } throw; } // if the request was granted return, else re-wait } while (!requestNode.Value); } }
//--------------------------------------------------------------------- // Synchronous //-- public bool WaitEx(int timeout, CancellationToken ctk) { bool interrupted = false; Waiter <Data> waiter = null; bool ownsTheWaiter = false; lock (_lock) { if (currentPermits > 0) { currentPermits -= 1; return(true); } // if the current thread must block, check immediate cancelers if (timeout == 0) { return(false); } ctk.ThrowIfCancellationRequested(); // create a synchronous waiter node and insert it in the wait queue waiter = new SyncWaiter <Data>(new Data(), ctk, SyncUnlink); DListNode.AddLast(waiters, waiter); waiter.Enable(); // wrap timeout value with timeout holder, in order to support timeout adjust TimeoutHolder th = new TimeoutHolder(timeout); // wait until the request is staisfied, timeout expires or cancellation do { try { if ((timeout = th.Value) == 0) { ownsTheWaiter = TrySetStateAndRemoveWaiter(waiter, WaiterState.TIMED_OUT); break; } // wait on the monitor's condition variable MonitorEx.Wait(_lock, waiter, timeout); } catch (ThreadInterruptedException) { interrupted = true; ownsTheWaiter = TrySetStateAndRemoveWaiter(waiter, WaiterState.INTERRUPTED); break; } } while (waiter.State == WaiterState.WAITING); } // this processing is private of the current thread, so we can do it without // owning the lock. if (ownsTheWaiter) { waiter.CancelCancellation(); } if (waiter.State == WaiterState.INTERRUPTED) { throw new ThreadInterruptedException(); } if (interrupted) { Thread.CurrentThread.Interrupt(); } if (waiter.State == WaiterState.CANCELED) { throw new OperationCanceledException(ctk); } return(waiter.State == WaiterState.SUCCESS); }
// generic acquire operation; returns false when it times out public bool Acquire(AcquireArgs acquireArgs, out AcquireResult result, int timeout = Timeout.Infinite) { lock (monitor) { if (reqQueue.Count == 0 && CanAcquire(acquireArgs)) { result = AcquireSideEffect(acquireArgs); return(true); } Request request = new Request(acquireArgs); reqQueue.AddLast(request); // enqueue "request" at the end of the request queue TimeoutHolder th = new TimeoutHolder(timeout); do { if ((timeout = th.Value) == 0) { // the timeout limit has expired - here we are sure that the acquire resquest // is still pending. So, we remove the request from the queue and return failure. reqQueue.Remove(request); // After remove the request of the current thread from queue, *it is possible* // that the current synhcronization state allows now to satisfy other queued // acquire(s). if (CurrentSynchStateAllowsAquire()) { PerformPossibleAcquires(); } result = default(AcquireResult); return(false); } try { MonitorEx.Wait(monitor, request, timeout); // *wait on a private condition var* } catch (ThreadInterruptedException) { // the thread may be interrupted when the requested acquire operation // is already performed, in which case you can no longer give up if (request.done) { // re-assert the interrupt and return normally, indicating to the // caller that the operation was successfully completed Thread.CurrentThread.Interrupt(); break; } // remove the request from the queue and throw ThreadInterruptedException reqQueue.Remove(request); // After remove the request of the current thread from queue, *it is possible* // that the current synhcronization state allows now to satisfy other queued // acquire(s). if (CurrentSynchStateAllowsAquire()) { PerformPossibleAcquires(); } throw; // ThreadInterruptedException } } while (!request.done); // the request acquire operation completed successfully result = request.acquireResult; return(true); } }