public void TaskFromStateServer_ThrowsBeforeCreatingTask_EnsureIsLogged() { var exception = new InvalidOperationException("Some message"); var logger = Substitute.For <ILogger>(); var state = new TaskFromStateServer.State(wb => { wb.Release(); throw exception; }); using (var waitBlock = new WaitBlock()) { var waitBlockLocal = new[] { waitBlock }; using (var context = ApplicationContext.Create(application => application .ConfigureForUnitTest() .WithWaitBlock(waitBlockLocal[0]) .UseLiteServer(liteServer => liteServer .AddServer <TaskFromStateServer>()) .Services(services => services .Advanced(advanced => advanced .Register(kernel => logger) .Register(kernel => state))))) { context.Execute(nameof(LiteServerHost)); } } logger.Received(1).LogError(Arg.Is(exception)); }
public MyHost(IShutdown shutdown, ConsoleWriterQueue consoleWriterQueue, WaitBlock waitBlock, IRuntimeSettings runtimeSettings) { _shutdown = shutdown; _consoleWriterQueue = consoleWriterQueue; _waitBlock = waitBlock; _shouldHandleAsWindowsService = string.Equals(Boolean.TrueString, runtimeSettings["ShouldHandleAsWindowsService"]); }
public void TaskFromStateServer_TaskFromCanceled_NoErrors() { var logger = Substitute.For <ILogger>(); var state = new TaskFromStateServer.State(wb => { wb.Release(); return(Task.FromCanceled(new CancellationToken(true))); }); using (var waitBlock = new WaitBlock()) { var waitBlockLocal = new[] { waitBlock }; using (var context = ApplicationContext.Create(application => application .ConfigureForUnitTest() .WithWaitBlock(waitBlockLocal[0]) .UseLiteServer(liteServer => liteServer .AddServer <TaskFromStateServer>()) .Services(services => services .Advanced(advanced => advanced .Register(kernel => logger) .Register(kernel => state))))) { context.Execute(nameof(LiteServerHost)); } } }
public void SlackMessage_EndToEnd_Test() { using (var waitBlock = new WaitBlock()) { var factory = new MessageFactory(waitBlock); var waitBlockLocal = new[] { waitBlock }; using (IApplicationContext context = ApplicationContext.Create(application => application .ConfigureForUnitTest() .WithWaitBlock(waitBlockLocal[0]) .Services(services => services .Advanced(advanced => advanced .Register <ISlackBot, FakeSlackBot>() .Register <IRuntimeSettings>(kernel => new InMemoryRuntimeSettings() .Set("Slack.Enabled", "true")) .Register(kernel => factory))) .UseLiteServer(liteServer => liteServer .AddWorker <AddMessageToQueueAndExit>()) .UseSlack(slack => slack .MessageHandlers(messageHandlers => messageHandlers .Add <MessageHandler>()) .AddToLiteServer()))) { context.Execute(nameof(LiteServerHost)); } Assert.That(factory.Message.HandledCount, Is.EqualTo(1)); } }
public void RestartableServer_Failing_EnsureIsRestarted() { var logger = Substitute.For <ILogger>(); var state = new RestartableServer.State(2); using (var waitBlock = new WaitBlock()) { var waitBlockLocal = new[] { waitBlock }; using (var context = ApplicationContext.Create(application => application .ConfigureForUnitTest() .WithWaitBlock(waitBlockLocal[0]) .UseLiteServer(liteServer => liteServer .AddServer <RestartableServer>()) .Services(services => services .Advanced(advanced => advanced .Register(kernel => logger) .Register(kernel => state))))) { context.Execute(nameof(LiteServerHost)); } } Assert.AreEqual(state.NumberOfRestarts, state.Context?.FailedCount); logger.Received((int)state.NumberOfRestarts).LogError(Arg.Is <RestartableServer.CreateException>(x => true)); }
public void Advance() { WaitBlock wb; #if !USESPINLOCK lock (this) { #else bool taken = false; m_lock.Enter(ref taken); #endif // Advance the count this.value++; //Console.WriteLine("Advance {0}", this.value); // If we have waiters then we will wake them outside the lock wb = this.current; this.current = null; #if USESPINLOCK m_lock.Exit(); #else } #endif // Unblock the waiters if (wb != null) { NaiadTracing.Trace.RegionStart(NaiadTracingRegion.SetEvent); wb.ev.Set(); NaiadTracing.Trace.RegionStop(NaiadTracingRegion.SetEvent); } }
private E <CommandError> SendCommandBase(WaitBlock wb, TsCommand com) { scheduler.VerifyOwnThread(); if (status != TsClientStatus.Connecting && status != TsClientStatus.Connected) { return(CommandError.ConnectionClosed); } if (context is null) { throw new InvalidOperationException("context should be set"); } if (com.ExpectResponse) { var responseNumber = unchecked (++returnCode); var retCodeParameter = new CommandParameter("return_code", responseNumber); com.Add(retCodeParameter); msgProc.EnqueueRequest(retCodeParameter.Value, wb); } var message = com.ToString(); Log.Debug("[O] {0}", message); byte[] data = Tools.Utf8Encoder.GetBytes(message); var sendResult = context.PacketHandler.AddOutgoingPacket(data, PacketType.Command); if (!sendResult) { Log.Debug("packetHandler couldn't send packet: {0}", sendResult.Error); } return(R.Ok); }
private void RefreshWaitedObjects(ThreadStack threadStack) { List <WaitBlock> newWaitedNodes = new List <WaitBlock>(threadStack.WaitBlocks.Values); int countOld = r_waitedNodes.Count; int countNew = newWaitedNodes.Count; int to = countNew; if (countNew < countOld) { for (int i = countNew; i < countOld; ++i) { r_ownedNodes[countNew].Remove(); r_ownedNodes.RemoveAt(countNew); } } else if (countOld < countNew) { to = countOld; int index = countOld; for (int i = 0; i < (countNew - countOld); ++i) { WaitBlock objectAddress = newWaitedNodes[index]; ThreadStackWaitedOnNode waitedOnNode = new ThreadStackWaitedOnNode(r_manager, r_threadStackNode, index, objectAddress); r_waitedNodes.Add(waitedOnNode); ++index; } } for (int i = 0; i < to; ++i) { r_waitedNodes[i].Refresh(newWaitedNodes[i]); } }
private void SendCommandBase(WaitBlock wb, Ts3Command com) { lock (CommmandQueueLock) { if (com.ExpectResponse) { var retCode = new CommandParameter("return_code", returnCode); com.AppendParameter(retCode); msgProc.EnqueueRequest(retCode.Value, wb); returnCode++; } byte[] data = Util.Encoder.GetBytes(com.ToString()); lock (StatusLock) { if (wasExit) { throw new Ts3CommandException(new CommandError { Id = Ts3ErrorCode.custom_error, Message = "Connection closed" }); } packetHandler.AddOutgoingPacket(data, PacketType.Command); } } }
public Task WaitAsync(Mutex mutex) { WaitBlock block = new WaitBlock(mutex); _pending.Add(block); _pendingReady.Set(); return(block.Source.Task); }
public Task <IDisposable> WaitAsync(Mutex mutex) { var block = new WaitBlock(mutex); _pending.Add(block); _pendingReady.Set(); return(block.Source.Task.ContinueWith(_ => (IDisposable) new MutexLock(this, mutex))); }
public Message(WaitBlock waitBlock) { if (waitBlock == null) { throw new ArgumentNullException(nameof(waitBlock)); } _waitBlock = waitBlock; }
public override Task <R <T[], CommandError> > Send <T>(TsCommand com) { using var wb = new WaitBlock(msgProc.Deserializer); lock (sendQueueLock) { msgProc.EnqueueRequest(wb); SendRaw(com.ToString()); } return(wb.WaitForMessageAsync <T>()); }
public EventCount() { #if USESPINLOCK this.m_lock = new SpinLock(); Console.Error.WriteLine("Using EventCount with spinlock"); #else Console.Error.WriteLine("Using EventCount with monitor"); #endif this.value = 0; this.current = null; this.freelist = null; }
public override R <T[], CommandError> Send <T>(Ts3Command com) // Synchronous { using (var wb = new WaitBlock(msgProc.Deserializer, false)) { lock (sendQueueLock) { msgProc.EnqueueRequest(wb); SendRaw(com.ToString()); } return(wb.WaitForMessage <T>()); } }
protected override IEnumerable <T> SendCommand <T>(Ts3Command com) // Synchronous { using (var wb = new WaitBlock()) { lock (SendQueueLock) { msgProc.EnqueueRequest(wb); SendRaw(com.ToString()); } return(wb.WaitForMessage <T>()); } }
public LazyNotification SendSpecialCommand(Ts3Command com, params NotificationType[] dependsOn) { if (!com.ExpectResponse) { throw new ArgumentException("A special command must take a response"); } using (var wb = new WaitBlock(dependsOn)) { SendCommandBase(wb, com); return(wb.WaitForNotification()); } }
private E <CommandError> SendCommandBase(WaitBlock wb, Ts3Command com) { if (context.WasExit || com.ExpectResponse) { return(Util.TimeOutCommandError); } var message = com.ToString(); byte[] data = Util.Encoder.GetBytes(message); packetHandler.AddOutgoingPacket(data, PacketType.Command); return(R.Ok); }
/// <summary> /// Sends a command to the server. Commands look exactly like query commands and mostly also behave identically. /// <para>NOTE: Do not expect all commands to work exactly like in the query documentation.</para> /// </summary> /// <typeparam name="T">The type to deserialize the response to. Use <see cref="ResponseDictionary"/> for unknow response data.</typeparam> /// <param name="com">The raw command to send. /// <para>NOTE: By default does the command expect an answer from the server. Set <see cref="Ts3Command.ExpectResponse"/> to false /// if the client hangs after a special command (<see cref="SendCommand{T}"/> will return <code>null</code> instead).</para></param> /// <returns>Returns an enumeration of the deserialized and split up in <see cref="T"/> objects data. /// Or <code>null</code> if no reponse is expected.</returns> /// <exception cref="Ts3CommandException">When the response has an error code.</exception> public override IEnumerable <T> SendCommand <T>(Ts3Command com) { using (var wb = new WaitBlock()) { SendCommandBase(wb, com); if (com.ExpectResponse) { return(wb.WaitForMessage <T>()); } else { return(null); } } }
internal ThreadStackWaitedOnNode(ThreadStackNodeManager manager, TreeNode threadStackNode, int index, WaitBlock waitblock) { if (threadStackNode == null) throw new ArgumentNullException("r_threadStackNode"); if (manager == null) throw new ArgumentNullException("manager"); r_manager = manager; r_threadStackNode = threadStackNode; r_waitedOnNode = r_threadStackNode.Nodes.Insert(index, GetText(waitblock)); r_waitedOnNode.ImageKey = "hourglass.png"; r_waitedOnNode.SelectedImageKey = r_waitedOnNode.ImageKey; r_waitedOnNode.Tag = this; }
public async Task <R <LazyNotification, CommandError> > SendNotifyCommand(TsCommand com, params NotificationType[] dependsOn) { if (!com.ExpectResponse) { throw new ArgumentException("A special command must take a response"); } using var wb = new WaitBlock(msgProc.Deserializer, dependsOn); var result = SendCommandBase(wb, com); if (!result.Ok) { return(result.Error); } return(await wb.WaitForNotificationAsync()); }
public R <LazyNotification, CommandError> SendNotifyCommand(Ts3Command com, params NotificationType[] dependsOn) { if (!com.ExpectResponse) { throw new ArgumentException("A special command must take a response"); } using (var wb = new WaitBlock(false, dependsOn)) { var result = SendCommandBase(wb, com); if (!result.Ok) { return(result.Error); } return(wb.WaitForNotification()); } }
private void Run(CancellationToken token) { using (token.Register(() => _pendingReady.Set())) { var waiting = new List <WaitBlock>(); while (!token.IsCancellationRequested) { var waits = new WaitHandle[1 + waiting.Count]; waits[0] = _pendingReady; waiting.Select(w => (WaitHandle)w.Mutex).ToList().CopyTo(waits, 1); int index = WaitHandle.WaitAny(waits, TimeSpan.FromSeconds(1)); { Mutex toRelease; while (_releaseable.TryTake(out toRelease)) { toRelease.ReleaseMutex(); } } if (index == WaitHandle.WaitTimeout) { continue; } if (index == 0) { WaitBlock item; while (_pending.TryTake(out item)) { waiting.Add(item); } continue; } index--; { WaitBlock item = waiting[index]; waiting.RemoveAt(index); item.Source.TrySetResult(null); } } } }
/// <summary> /// Sends a command to the server. Commands look exactly like query commands and mostly also behave identically. /// <para>NOTE: Do not expect all commands to work exactly like in the query documentation.</para> /// </summary> /// <typeparam name="T">The type to deserialize the response to. Use <see cref="ResponseDictionary"/> for unknow response data.</typeparam> /// <param name="com">The command to send. /// <para>NOTE: By default does the command expect an answer from the server. Set <see cref="TsCommand.ExpectResponse"/> to false /// if the client hangs after a special command (<see cref="Send{T}(TsCommand)"/> will return a generic error instead).</para></param> /// <returns>Returns <code>R(OK)</code> with an enumeration of the deserialized and split up in <see cref="T"/> objects data. /// Or <code>R(ERR)</code> with the returned error if no response is expected.</returns> public override R <T[], CommandError> Send <T>(TsCommand com) { using (var wb = new WaitBlock(msgProc.Deserializer, false)) { var result = SendCommandBase(wb, com); if (!result.Ok) { return(result.Error); } if (com.ExpectResponse) { return(wb.WaitForMessage <T>()); } else { return(Array.Empty <T>()); } } }
protected override IEnumerable <T> SendCommand <T>(Ts3Command com) { var retCode = new CommandParameter("return_code", returnCode); if (com.ExpectResponse) { com.AppendParameter(retCode); } using (var wb = new WaitBlock()) { lock (CommmandQueueLock) { if (com.ExpectResponse) { msgProc.EnqueueRequest(retCode.Value, wb); returnCode++; } byte[] data = Util.Encoder.GetBytes(com.ToString()); lock (StatusLock) { if (wasExit) { throw new Ts3CommandException(new CommandError { Id = Ts3ErrorCode.custom_error, Message = "Connection closed" }); } packetHandler.AddOutgoingPacket(data, PacketType.Command); } } if (com.ExpectResponse) { return(wb.WaitForMessage <T>()); } else { return(null); } } }
public void SendMessageFromWorker_InMemory_GetsHandled() { using (var waitBlock = new WaitBlock()) { var message = new MyMessage(Guid.NewGuid()); var receivedMessages = new ConcurrentQueue <MyMessage>(); var waitBlockLocal = new[] { waitBlock }; using (var context = ApplicationContext.Create(application => application .ConfigureForUnitTest() .WithWaitBlock(waitBlockLocal[0]) .UseRebus(rebus => rebus .Bus((bus, kernel) => bus .Routing(routing => routing .TypeBased() .Map <MyMessage>("inputQueue")) .Transport(transport => transport .UseInMemoryTransport(new InMemNetwork(), "inputQueue"))) .Handlers(handlers => handlers .Handler <MyMessageHandler>()) .AddToLiteServer()) .UseLiteServer(liteServer => liteServer .AddWorker <SendMyMessageWorker>()) .Services(services => services .Advanced(advanced => advanced .Register(kernel => message) .Register(kernel => receivedMessages))))) { context.Execute(nameof(LiteServerHost)); } Assert.That(receivedMessages.Count, Is.EqualTo(1)); MyMessage receivedMessage; bool canDequeue = receivedMessages.TryDequeue(out receivedMessage); Assert.IsTrue(canDequeue); Assert.That(receivedMessage, Is.EqualTo(message)); Assert.IsTrue(receivedMessage.IsHandled); } }
/// <summary> /// Sends a command to the server. Commands look exactly like query commands and mostly also behave identically. /// <para>NOTE: Do not expect all commands to work exactly like in the query documentation.</para> /// </summary> /// <typeparam name="T">The type to deserialize the response to. Use <see cref="ResponseDictionary"/> for unknow response data.</typeparam> /// <param name="com">The command to send. /// <para>NOTE: By default does the command expect an answer from the server. Set <see cref="TsCommand.ExpectResponse"/> to false /// if the client hangs after a special command (<see cref="Send{T}(TsCommand)"/> will return a generic error instead).</para></param> /// <returns>Returns <code>R(OK)</code> with an enumeration of the deserialized and split up in <see cref="T"/> objects data. /// Or <code>R(ERR)</code> with the returned error if no response is expected.</returns> public override async Task <R <T[], CommandError> > Send <T>(TsCommand com) { using var wb = new WaitBlock(msgProc.Deserializer); var result = SendCommandBase(wb, com); if (!result.Ok) { return(result.Error); } if (com.ExpectResponse) { return(await wb.WaitForMessageAsync <T>()); } else { // This might not be the nicest way to return in this case // but we don't know what the response is, so this acceptable. return(CommandError.NoResult); } }
public void Nothing_To_Do() { var logger = Substitute.For <ILogger>(); using (var waitBlock = new WaitBlock()) { var waitBlockLocal = new[] { waitBlock }; using (var context = ApplicationContext.Create(application => application .ConfigureForUnitTest() .WithWaitBlock(waitBlockLocal[0]) .UseLiteServer(liteServer => liteServer.OnStartup(startup => { waitBlockLocal[0].Release(); })) .Services(services => services .Advanced(advanced => advanced .Register(kernel => logger))))) { context.Execute(nameof(LiteServerHost)); } } }
private bool TryEnqueue(WaitBlock w) { if (w.parker.TryLock()) { // // Move the locked wait block to the lock's queue. // mlock.EnqueueWaiter(w); return(true); } // // Mark the WaitBlock as unlinked so that WaitBlockQueue::Remove // returns immediately. // w.next = w; return(false); }
public async Task <R <IEnumerable <T>, CommandError> > SendCommandAsync <T>(Ts3Command com) where T : IResponse, new() { using (var wb = new WaitBlock(true)) { var result = SendCommandBase(wb, com); if (!result.Ok) { return(result.Error); } if (com.ExpectResponse) { return(await wb.WaitForMessageAsync <T>()); } else { // This might not be the nicest way to return in this case // but we don't know what the response is, so this acceptable. return(Util.NoResultCommandError); } } }
/// <summary> /// Sends a command to the server. Commands look exactly like query commands and mostly also behave identically. /// <para>NOTE: Do not expect all commands to work exactly like in the query documentation.</para> /// </summary> /// <typeparam name="T">The type to deserialize the response to. Use <see cref="ResponseDictionary"/> for unknow response data.</typeparam> /// <param name="com">The raw command to send. /// <para>NOTE: By default does the command expect an answer from the server. Set <see cref="Ts3Command.ExpectResponse"/> to false /// if the client hangs after a special command (<see cref="SendCommand{T}"/> will return <code>null</code> instead).</para></param> /// <returns>Returns <code>R(OK)</code> with an enumeration of the deserialized and split up in <see cref="T"/> objects data. /// Or <code>R(ERR)</code> with the returned error if no response is expected.</returns> public override R <IEnumerable <T>, CommandError> SendCommand <T>(Ts3Command com) { using (var wb = new WaitBlock(false)) { var result = SendCommandBase(wb, com); if (!result.Ok) { return(result.Error); } if (com.ExpectResponse) { return(wb.WaitForMessage <T>()); } else { // This might not be the nicest way to return in this case // but we don't know what the response is, so this acceptable. return(Util.NoResultCommandError); } } }
/// <summary> /// Unblocks a wait block. /// </summary> /// <param name="waitBlock">The wait block to unblock.</param> private void Unblock(WaitBlock* waitBlock) { int flags; // Clear the spinning flag. do { flags = waitBlock->Flags; } while (Interlocked.CompareExchange( ref waitBlock->Flags, flags & ~WaiterSpinning, flags ) != flags); if ((flags & WaiterSpinning) == 0) { #if RIGOROUS_CHECKS System.Diagnostics.Trace.Assert(_wakeEvent != IntPtr.Zero); #endif NativeMethods.NtReleaseKeyedEvent( _wakeEvent, new IntPtr(waitBlock), false, IntPtr.Zero ); } }
private static void InsertHeadList(WaitBlock* listHead, WaitBlock* entry) { WaitBlock* flink; flink = listHead->Flink; entry->Flink = flink; entry->Blink = listHead; flink->Blink = entry; listHead->Flink = entry; }
private static void InsertTailList(WaitBlock* listHead, WaitBlock* entry) { WaitBlock* blink; blink = listHead->Blink; entry->Flink = listHead; entry->Blink = blink; blink->Flink = entry; listHead->Blink = entry; }
internal void Refresh(WaitBlock waitblock) { r_waitedOnNode.Text = GetText(waitblock); }
private static bool RemoveEntryList(WaitBlock* entry) { WaitBlock* blink; WaitBlock* flink; flink = entry->Flink; blink = entry->Blink; blink->Flink = flink; flink->Blink = blink; return flink == blink; }
private static WaitBlock* RemoveHeadList(WaitBlock* listHead) { WaitBlock* flink; WaitBlock* entry; entry = listHead->Flink; flink = entry->Flink; listHead->Flink = flink; flink->Blink = listHead; return entry; }
/// <summary> /// Blocks on a wait block. /// </summary> /// <param name="waitBlock">The wait block to block on.</param> private void Block(WaitBlock* waitBlock) { int flags; // Spin for a while. for (int j = 0; j < _spinCount; j++) { if ((Thread.VolatileRead(ref waitBlock->Flags) & WaiterSpinning) == 0) break; } #if DEFER_EVENT_CREATION IntPtr wakeEvent; wakeEvent = Interlocked.CompareExchange(ref _wakeEvent, IntPtr.Zero, IntPtr.Zero); if (wakeEvent == IntPtr.Zero) { wakeEvent = this.CreateWakeEvent(); if (Interlocked.CompareExchange(ref _wakeEvent, wakeEvent, IntPtr.Zero) != IntPtr.Zero) NativeMethods.CloseHandle(wakeEvent); } #endif // Clear the spinning flag. do { flags = waitBlock->Flags; } while (Interlocked.CompareExchange( ref waitBlock->Flags, flags & ~WaiterSpinning, flags ) != flags); // Go to sleep if necessary. if ((flags & WaiterSpinning) != 0) { #if ENABLE_STATISTICS if ((waitBlock->Flags & WaiterExclusive) != 0) Interlocked.Increment(ref _acqExclSlpCount); else Interlocked.Increment(ref _acqShrdSlpCount); #endif if (NativeMethods.NtWaitForKeyedEvent( _wakeEvent, new IntPtr(waitBlock), false, IntPtr.Zero ) != 0) throw new Exception(Utils.MsgFailedToWaitIndefinitely); } }
/// <summary> /// Inserts a wait block into the waiters list. /// </summary> /// <param name="waitBlock">The wait block to insert.</param> /// <param name="position">Specifies where to insert the wait block.</param> private void InsertWaitBlock(WaitBlock* waitBlock, ListPosition position) { switch (position) { case ListPosition.First: InsertHeadList(_waitersListHead, waitBlock); break; case ListPosition.LastExclusive: InsertTailList(_firstSharedWaiter, waitBlock); break; case ListPosition.Last: InsertTailList(_waitersListHead, waitBlock); break; } }
private String GetText(WaitBlock waitblock) { return "Waiting (" + waitblock.ObjectWaitType.ToString() + ") on " + r_manager.GetValue(waitblock.ObjectAddress) + " with Key " + waitblock.WaitKey; }
void IMonitorLock.EnqueueWaiter(WaitBlock wb) { EnqueueLockedWaiter(wb); }
/// <summary> /// Wakes either one exclusive waiter or multiple shared waiters. /// </summary> private void Wake() { WaitBlock wakeList = new WaitBlock(); WaitBlock* wb; WaitBlock* exclusiveWb = null; wakeList.Flink = &wakeList; wakeList.Blink = &wakeList; _lock.Acquire(); try { bool first = true; while (true) { wb = _waitersListHead->Flink; if (wb == _waitersListHead) { int value; // No more waiters. Clear the waiters bit. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value & ~LockWaiters, value ) != value); break; } // If this is an exclusive waiter, don't wake // anyone else. if (first && (wb->Flags & WaiterExclusive) != 0) { exclusiveWb = RemoveHeadList(_waitersListHead); #if ENABLE_STATISTICS _exclusiveWaitersCount--; #endif break; } #if RIGOROUS_CHECKS // If this is not the first waiter we have looked at // and it is an exclusive waiter, then we have a bug - // we should have stopped upon encountering the first // exclusive waiter (previous block), so this means // we have an exclusive waiter *behind* shared waiters. if (!first && (wb->Flags & WaiterExclusive) != 0) { System.Diagnostics.Trace.Fail("Exclusive waiter behind shared waiters!"); } #endif // Remove the (shared) waiter and add it to the wake list. wb = RemoveHeadList(_waitersListHead); InsertTailList(&wakeList, wb); #if ENABLE_STATISTICS _sharedWaitersCount--; #endif first = false; } if (exclusiveWb == null) { // If we removed shared waiters, we removed all of them. // Reset the first shared waiter pointer. // Note that this also applies if we haven't woken anyone // at all; this just becomes a redundant assignment. _firstSharedWaiter = _waitersListHead; } } finally { _lock.Release(); } // If we removed one exclusive waiter, unblock it. if (exclusiveWb != null) { this.Unblock(exclusiveWb); return; } // Carefully traverse the wake list and wake each shared waiter. wb = wakeList.Flink; while (wb != &wakeList) { WaitBlock* flink; flink = wb->Flink; this.Unblock(wb); wb = flink; } }
/// <summary> /// Wakes multiple shared waiters. /// </summary> private void WakeShared() { WaitBlock wakeList = new WaitBlock(); WaitBlock* wb; wakeList.Flink = &wakeList; wakeList.Blink = &wakeList; _lock.Acquire(); try { wb = _firstSharedWaiter; while (true) { WaitBlock* flink; if (wb == _waitersListHead) { int value; // No more waiters. Clear the waiters bit. do { value = _value; } while (Interlocked.CompareExchange( ref _value, value & ~LockWaiters, value ) != value); break; } #if RIGOROUS_CHECKS // We shouldn't have *any* exclusive waiters at this // point since we started at _firstSharedWaiter. if ((wb->Flags & WaiterExclusive) != 0) System.Diagnostics.Trace.Fail("Exclusive waiter behind shared waiters!"); #endif // Remove the waiter and add it to the wake list. flink = wb->Flink; RemoveEntryList(wb); InsertTailList(&wakeList, wb); #if ENABLE_STATISTICS _sharedWaitersCount--; #endif wb = flink; } // Reset the first shared waiter pointer. _firstSharedWaiter = _waitersListHead; } finally { _lock.Release(); } // Carefully traverse the wake list and wake each waiter. wb = wakeList.Flink; while (wb != &wakeList) { WaitBlock* flink; flink = wb->Flink; this.Unblock(wb); wb = flink; } }