void CreateLongRunningTask(XParameterizedThreadStart threadStartFunc) { this.task = Task.Factory.StartNew( () => { // We start the task running, then unleash it by signaling the readyToStart event. // This is needed to avoid thread reuse for tasks (see below) this.readyToStart.WaitOne(); // This is the first time we're using this thread, therefore the TLS slot must be empty if (currentThread != null) { Debug.WriteLine("warning: currentThread already created; OS thread reused"); Debug.Assert(false); } currentThread = this; threadStartFunc(this.startupParameter); this.completed.Set(); }, CancellationToken.None, // .NET always creates a brand new thread for LongRunning tasks // This is not documented but unlikely to ever change: // https://github.com/dotnet/corefx/issues/2576#issuecomment-126693306 TaskCreationOptions.LongRunning, TaskScheduler.Default); }
/// <summary> /// Cancels the task scheduled via <see cref="Watch"/>. /// </summary> public static void Unwatch(Thread thread, Action task) { Contract.Requires(thread != null); Contract.Requires(task != null); Schedule(thread, task, false); }
public override void ChannelRead(IChannelHandlerContext context, object message) { var t = _thread; if (t is null) { _thread = Thread.CurrentThread; } else { Assert.Same(t, Thread.CurrentThread); } IByteBuffer m = (IByteBuffer)message; int count = m.ReadableBytes / 4; for (int j = 0; j < count; j++) { int actual = m.ReadInt(); int expected = _inCnt++; Assert.Equal(expected, actual); context.FireChannelRead(actual); } m.Release(); }
protected SingleThreadEventExecutorOld(IEventExecutorGroup parent, string threadName, TimeSpan breakoutInterval, IQueue <IRunnable> taskQueue) : base(parent) { _firstTask = true; _emptyEvent = new ManualResetEventSlim(false, 1); _shutdownHooks = new HashSet <Action>(); _loopAction = Loop; _loopCoreAciton = LoopCore; _terminationCompletionSource = NewPromise(); _taskQueue = taskQueue; _preciseBreakoutInterval = PreciseTimeSpan.FromTimeSpan(breakoutInterval); _scheduler = new ExecutorTaskScheduler(this); _thread = new Thread(_loopAction); if (string.IsNullOrEmpty(threadName)) { _thread.Name = DefaultWorkerThreadName; } else { _thread.Name = threadName; } _thread.Start(); }
void DestroyDown(Thread currentThread, AbstractChannelHandlerContext ctx, bool inEventLoop) { // We have reached at tail; now traverse backwards. AbstractChannelHandlerContext headContext = this.head; while (true) { if (ctx == headContext) { break; } IEventExecutor executor = ctx.Executor; if (inEventLoop || executor.IsInEventLoop(currentThread)) { lock (this) { Remove0(ctx); this.CallHandlerRemoved0(ctx); } } else { executor.Execute((self, c) => ((DefaultChannelPipeline)self).DestroyDown(Thread.CurrentThread, (AbstractChannelHandlerContext)c, true), this, ctx); break; } ctx = ctx.Prev; inEventLoop = false; } }
/// <summary> /// Schedules the specified <see cref="Action"/> to run when the specified <see cref="Thread"/> dies. /// </summary> public static void Watch(Thread thread, Action task) { Contract.Requires(thread != null); Contract.Requires(task != null); Contract.Requires(thread.IsAlive); Schedule(thread, task, true); }
private bool ConfirmShutdownSlow() { if (!InEventLoop) { ThrowHelper.ThrowInvalidOperationException_Must_be_invoked_from_an_event_loop(); } CancelScheduledTasks(); if (0ul >= (ulong)_gracefulShutdownStartTime) { _gracefulShutdownStartTime = GetTimeFromStart(); } if (RunAllTasks() || RunShutdownHooks()) { if (IsShutdown) { // Executor shut down - no new tasks anymore. return(true); } // There were tasks in the queue. Wait a little bit more until no tasks are queued for the quiet period or // terminate if the quiet period is 0. // See https://github.com/netty/netty/issues/4241 if (0ul >= (ulong)Volatile.Read(ref v_gracefulShutdownQuietPeriod)) { return(true); } _taskQueue.TryEnqueue(WakeupTask); return(false); } long nanoTime = GetTimeFromStart(); if (IsShutdown || (nanoTime - _gracefulShutdownStartTime > Volatile.Read(ref v_gracefulShutdownTimeout))) { return(true); } if (nanoTime - _lastExecutionTime <= Volatile.Read(ref v_gracefulShutdownQuietPeriod)) { // Check if any tasks were added to the queue every 100ms. // TODO: Change the behavior of takeTask() so that it returns on timeout. _taskQueue.TryEnqueue(WakeupTask); Thread.Sleep(100); return(false); } // No tasks were added for last quiet period - hopefully safe to shut down. // (Hopefully because we really cannot make a guarantee that there will be no execute() calls by a user.) return(true); }
static void Schedule(Thread thread, Action task, bool isWatch) { _ = PendingEntries.TryEnqueue(new Entry(thread, task, isWatch)); if (SharedConstants.False >= (uint)Interlocked.CompareExchange(ref started, SharedConstants.True, SharedConstants.False)) { var watcherThread = new Thread(s => ((IRunnable)s).Run()); watcherThread.Start(watcher); _ = Interlocked.Exchange(ref ThreadDeathWatcher.watcherThread, watcherThread); } }
static void Schedule(Thread thread, Action task, bool isWatch) { PendingEntries.TryEnqueue(new Entry(thread, task, isWatch)); if (Interlocked.CompareExchange(ref started, 1, 0) == 0) { var watcherThread = new Thread(s => ((IRunnable)s).Run()); watcherThread.Start(watcher); ThreadDeathWatcher.watcherThread = watcherThread; } }
protected bool ConfirmShutdownSlow() { Debug.Assert(InEventLoop, "must be invoked from an event loop"); CancelScheduledTasks(); if (_gracefulShutdownStartTime == PreciseTimeSpan.Zero) { _gracefulShutdownStartTime = PreciseTimeSpan.FromStart; } if (RunAllTasks() || RunShutdownHooks()) { if (IsShutdown) { // Executor shut down - no new tasks anymore. return(true); } // There were tasks in the queue. Wait a little bit more until no tasks are queued for the quiet period or // terminate if the quiet period is 0. // See https://github.com/netty/netty/issues/4241 if (_gracefulShutdownQuietPeriod == PreciseTimeSpan.Zero) { return(true); } WakeUp(true); return(false); } PreciseTimeSpan nanoTime = PreciseTimeSpan.FromStart; if (IsShutdown || (nanoTime - _gracefulShutdownStartTime > _gracefulShutdownTimeout)) { return(true); } if (nanoTime - _lastExecutionTime <= _gracefulShutdownQuietPeriod) { // Check if any tasks were added to the queue every 100ms. // TODO: Change the behavior of takeTask() so that it returns on timeout. // todo: ??? WakeUp(true); Thread.Sleep(100); return(false); } // No tasks were added for last quiet period - hopefully safe to shut down. // (Hopefully because we really cannot make a guarantee that there will be no execute() calls by a user.) return(true); }
/// <summary> /// Cancels the task scheduled via <see cref="Watch"/>. /// </summary> public static void Unwatch(Thread thread, Action task) { if (thread is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.thread); } if (task is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task); } Schedule(thread, task, false); }
protected bool ConfirmShutdown() { if (!this.IsShuttingDown) { return(false); } Contract.Assert(this.InEventLoop, "must be invoked from an event loop"); this.CancelScheduledTasks(); if (this.gracefulShutdownStartTime == PreciseTimeSpan.Zero) { this.gracefulShutdownStartTime = PreciseTimeSpan.FromStart; } if (this.RunAllTasks() || this.RunShutdownHooks()) { if (this.IsShutdown) { // Executor shut down - no new tasks anymore. return(true); } // There were tasks in the queue. Wait a little bit more until no tasks are queued for the quiet period. this.WakeUp(true); return(false); } PreciseTimeSpan nanoTime = PreciseTimeSpan.FromStart; if (this.IsShutdown || (nanoTime - this.gracefulShutdownStartTime > this.gracefulShutdownTimeout)) { return(true); } if (nanoTime - this.lastExecutionTime <= this.gracefulShutdownQuietPeriod) { // Check if any tasks were added to the queue every 100ms. // TODO: Change the behavior of takeTask() so that it returns on timeout. // todo: ??? this.WakeUp(true); Thread.Sleep(100); return(false); } // No tasks were added for last quiet period - hopefully safe to shut down. // (Hopefully because we really cannot make a guarantee that there will be no execute() calls by a user.) return(true); }
/// <summary> /// Waits until the thread of this watcher has no threads to watch and terminates itself. /// Because a new watcher thread will be started again on <see cref="Watch"/>, /// this operation is only useful when you want to ensure that the watcher thread is terminated /// <strong>after</strong> your application is shut down and there's no chance of calling <see cref="Watch"/> /// afterwards. /// </summary> /// <param name="timeout"></param> /// <returns><c>true</c> if and only if the watcher thread has been terminated.</returns> public static bool AwaitInactivity(TimeSpan timeout) { Thread watcherThread = Volatile.Read(ref ThreadDeathWatcher.watcherThread); if (watcherThread is object) { _ = watcherThread.Join(timeout); return(!watcherThread.IsAlive); } else { return(true); } }
/// <summary> /// Waits until the thread of this watcher has no threads to watch and terminates itself. /// Because a new watcher thread will be started again on <see cref="Watch"/>, /// this operation is only useful when you want to ensure that the watcher thread is terminated /// <strong>after</strong> your application is shut down and there's no chance of calling <see cref="Watch"/> /// afterwards. /// </summary> /// <param name="timeout"></param> /// <returns><c>true</c> if and only if the watcher thread has been terminated.</returns> public static bool AwaitInactivity(TimeSpan timeout) { Thread watcherThread = ThreadDeathWatcher.watcherThread; if (watcherThread != null) { watcherThread.Join(timeout); return(!watcherThread.IsAlive); } else { return(true); } }
/// <summary> /// Schedules the specified <see cref="Action"/> to run when the specified <see cref="Thread"/> dies. /// </summary> public static void Watch(Thread thread, Action task) { if (thread is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.thread); } if (task is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.task); } //if (!thread.IsAlive) { ThrowHelper.ThrowArgumentException(); } Schedule(thread, task, true); }
/// <summary>Creates a new instance of <see cref="SingleThreadEventExecutor"/>.</summary> /// <param name="parent">the <see cref="IEventExecutorGroup"/> which is the parent of this instance and belongs to it.</param> /// <param name="threadFactory">the <see cref="IThreadFactory"/> which will be used for the used <see cref="Thread"/>.</param> /// <param name="addTaskWakesUp"><c>true</c> if and only if invocation of <see cref="AddTask(IRunnable)"/> will wake up the executor thread.</param> /// <param name="maxPendingTasks">the maximum number of pending tasks before new tasks will be rejected.</param> /// <param name="rejectedHandler">the <see cref="IRejectedExecutionHandler"/> to use.</param> protected SingleThreadEventExecutor(IEventExecutorGroup parent, IThreadFactory threadFactory, bool addTaskWakesUp, int maxPendingTasks, IRejectedExecutionHandler rejectedHandler) : this(parent, addTaskWakesUp, rejectedHandler) { if (threadFactory is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.threadFactory); } _maxPendingTasks = Math.Max(16, maxPendingTasks); _taskQueue = NewTaskQueue(_maxPendingTasks); _blockingTaskQueue = _taskQueue as IBlockingQueue <IRunnable>; _thread = NewThread(threadFactory); }
public override void ChannelRead(IChannelHandlerContext context, object message) { var t = _thread; if (t is null) { _thread = Thread.CurrentThread; } else { Assert.Same(t, Thread.CurrentThread); } int actual = (int)message; int expected = _inCnt++; Assert.Equal(expected, actual); }
public void Run() { for (;;) { this.FetchWatchees(); this.NotifyWatchees(); // Try once again just in case notifyWatchees() triggered watch() or unwatch(). this.FetchWatchees(); this.NotifyWatchees(); Thread.Sleep(1000); if (this.watchees.Count == 0 && PendingEntries.IsEmpty) { // Mark the current worker thread as stopped. // The following CAS must always success and must be uncontended, // because only one watcher thread should be running at the same time. bool stopped = Interlocked.CompareExchange(ref started, 0, 1) == 1; Contract.Assert(stopped); // Check if there are pending entries added by watch() while we do CAS above. if (PendingEntries.IsEmpty) { // A) watch() was not invoked and thus there's nothing to handle // -> safe to terminate because there's nothing left to do // B) a new watcher thread started and handled them all // -> safe to terminate the new watcher thread will take care the rest break; } // There are pending entries again, added by watch() if (Interlocked.CompareExchange(ref started, 1, 0) != 0) { // watch() started a new watcher thread and set 'started' to true. // -> terminate this thread so that the new watcher reads from pendingEntries exclusively. break; } // watch() added an entry, but this worker was faster to set 'started' to true. // i.e. a new watcher thread was not started // -> keep this thread alive to handle the newly added entries. } } }
protected SingleThreadEventExecutor(IEventExecutorGroup parent, string threadName, TimeSpan breakoutInterval, IQueue <IRunnable> taskQueue) : base(parent) { this.terminationCompletionSource = new TaskCompletionSource(); this.taskQueue = taskQueue; this.preciseBreakoutInterval = PreciseTimeSpan.FromTimeSpan(breakoutInterval); this.scheduler = new ExecutorTaskScheduler(this); this.thread = new Thread(this.Loop); if (string.IsNullOrEmpty(threadName)) { this.thread.Name = DefaultWorkerThreadName; } else { this.thread.Name = threadName; } this.thread.Start(); }
/// <summary>Creates a new instance of <see cref="SingleThreadEventExecutor"/>.</summary> /// <param name="parent">the <see cref="IEventExecutorGroup"/> which is the parent of this instance and belongs to it.</param> /// <param name="threadFactory">the <see cref="IThreadFactory"/> which will be used for the used <see cref="Thread"/>.</param> /// <param name="addTaskWakesUp"><c>true</c> if and only if invocation of <see cref="AddTask(IRunnable)"/> will wake up the executor thread.</param> /// <param name="taskQueue">The pending task queue.</param> /// <param name="rejectedHandler">the <see cref="IRejectedExecutionHandler"/> to use.</param> protected SingleThreadEventExecutor(IEventExecutorGroup parent, IThreadFactory threadFactory, bool addTaskWakesUp, IQueue <IRunnable> taskQueue, IRejectedExecutionHandler rejectedHandler) : this(parent, addTaskWakesUp, rejectedHandler) { if (threadFactory is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.threadFactory); } if (taskQueue is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.taskQueue); } _maxPendingTasks = DefaultMaxPendingExecutorTasks; _taskQueue = taskQueue; _blockingTaskQueue = taskQueue as IBlockingQueue <IRunnable>; _thread = NewThread(threadFactory); }
protected SingleThreadEventExecutor(IEventExecutorGroup parent, string threadName, TimeSpan breakoutInterval, IQueue <IRunnable> taskQueue) : base(parent) { _loopAction = Loop; _loopCoreAciton = LoopCore; _terminationCompletionSource = NewPromise(); _taskQueue = taskQueue; _preciseBreakoutInterval = PreciseTimeSpan.FromTimeSpan(breakoutInterval); _scheduler = new ExecutorTaskScheduler(this); _thread = new Thread(_loopAction); if (string.IsNullOrEmpty(threadName)) { _thread.Name = DefaultWorkerThreadName; } else { _thread.Name = threadName; } _thread.Start(); }
public override void ChannelRead(IChannelHandlerContext context, object message) { var t = _thread; if (t is null) { _thread = Thread.CurrentThread; } else { Assert.Same(t, Thread.CurrentThread); } IByteBuffer output = context.Allocator.Buffer(4); int m = ((int)message); int expected = _inCnt++; Assert.Equal(expected, m); output.WriteInt(m); context.FireChannelRead(output); }
void DestroyUp(AbstractChannelHandlerContext ctx, bool inEventLoop) { Thread currentThread = Thread.CurrentThread; AbstractChannelHandlerContext tailContext = this.tail; while (true) { if (ctx == tailContext) { this.DestroyDown(currentThread, tailContext.Prev, inEventLoop); break; } IEventExecutor executor = ctx.Executor; if (!inEventLoop && !executor.IsInEventLoop(currentThread)) { executor.Execute((self, c) => ((DefaultChannelPipeline)self).DestroyUp((AbstractChannelHandlerContext)c, true), this, ctx); break; } ctx = ctx.Next; inEventLoop = false; } }
public Entry(Thread thread, Action task, bool isWatch) { this.Thread = thread; this.Task = task; this.IsWatch = isWatch; }
/// <inheritdoc cref="IEventExecutor"/> public abstract bool IsInEventLoop(Thread thread);
public override bool IsInEventLoop(Thread thread) => true;
/// <inheritdoc cref="IEventExecutor"/> public override bool IsInEventLoop(Thread t) => this.thread == t;
public async Task TestStagedExecution() { IEventLoopGroup l = new DefaultEventLoopGroup(4, new DefaultThreadFactory("l")); IEventExecutorGroup e1 = new DefaultEventExecutorGroup(4, new DefaultThreadFactory("e1")); IEventExecutorGroup e2 = new DefaultEventExecutorGroup(4, new DefaultThreadFactory("e2")); ThreadNameAuditor h1 = new ThreadNameAuditor(); ThreadNameAuditor h2 = new ThreadNameAuditor(); ThreadNameAuditor h3 = new ThreadNameAuditor(true); IChannel ch = new LocalChannel(); // With no EventExecutor specified, h1 will be always invoked by EventLoop 'l'. ch.Pipeline.AddLast(h1); // h2 will be always invoked by EventExecutor 'e1'. ch.Pipeline.AddLast(e1, h2); // h3 will be always invoked by EventExecutor 'e2'. ch.Pipeline.AddLast(e2, h3); await l.RegisterAsync(ch); await ch.ConnectAsync(_localAddr); // Fire inbound events from all possible starting points. ch.Pipeline.FireChannelRead("1"); ch.Pipeline.Context(h1).FireChannelRead("2"); ch.Pipeline.Context(h2).FireChannelRead("3"); ch.Pipeline.Context(h3).FireChannelRead("4"); // Fire outbound events from all possible starting points. ch.Pipeline.WriteAsync("5").Ignore(); ch.Pipeline.Context(h3).WriteAsync("6").Ignore(); ch.Pipeline.Context(h2).WriteAsync("7").Ignore(); await ch.Pipeline.Context(h1).WriteAndFlushAsync("8"); await ch.CloseAsync(); // Wait until all events are handled completely. while (h1._outboundThreadNames.Count < 3 || h3._inboundThreadNames.Count < 3 || h1._removalThreadNames.Count < 1) { if (h1._exception.Value != null) { throw h1._exception.Value; } if (h2._exception.Value != null) { throw h2._exception.Value; } if (h3._exception.Value != null) { throw h3._exception.Value; } Thread.Sleep(10); } string currentName = Thread.CurrentThread.Name; try { // Events should never be handled from the current thread. Assert.DoesNotContain(currentName, h1._inboundThreadNames); Assert.DoesNotContain(currentName, h2._inboundThreadNames); Assert.DoesNotContain(currentName, h3._inboundThreadNames); Assert.DoesNotContain(currentName, h1._outboundThreadNames); Assert.DoesNotContain(currentName, h2._outboundThreadNames); Assert.DoesNotContain(currentName, h3._outboundThreadNames); Assert.DoesNotContain(currentName, h1._removalThreadNames); Assert.DoesNotContain(currentName, h2._removalThreadNames); Assert.DoesNotContain(currentName, h3._removalThreadNames); // Assert that events were handled by the correct executor. foreach (string name in h1._inboundThreadNames) { Assert.StartsWith("l-", name); } foreach (string name in h2._inboundThreadNames) { Assert.StartsWith("e1-", name); } foreach (string name in h3._inboundThreadNames) { Assert.StartsWith("e2-", name); } foreach (string name in h1._outboundThreadNames) { Assert.StartsWith("l-", name); } foreach (string name in h2._outboundThreadNames) { Assert.StartsWith("e1-", name); } foreach (string name in h3._outboundThreadNames) { Assert.StartsWith("e2-", name); } foreach (string name in h1._removalThreadNames) { Assert.StartsWith("l-", name); } foreach (string name in h2._removalThreadNames) { Assert.StartsWith("e1-", name); } foreach (string name in h3._removalThreadNames) { Assert.StartsWith("e2-", name); } // Assert that the events for the same handler were handled by the same thread. HashSet <string> names = new HashSet <string>(); names.UnionWith(h1._inboundThreadNames); names.UnionWith(h1._outboundThreadNames); names.UnionWith(h1._removalThreadNames); Assert.Single(names); names.Clear(); names.UnionWith(h2._inboundThreadNames); names.UnionWith(h2._outboundThreadNames); names.UnionWith(h2._removalThreadNames); Assert.Single(names); names.Clear(); names.UnionWith(h3._inboundThreadNames); names.UnionWith(h3._outboundThreadNames); names.UnionWith(h3._removalThreadNames); Assert.Single(names); // Count the number of events Assert.Single(h1._inboundThreadNames); Assert.Equal(2, h2._inboundThreadNames.Count); Assert.Equal(3, h3._inboundThreadNames.Count); Assert.Equal(3, h1._outboundThreadNames.Count); Assert.Equal(2, h2._outboundThreadNames.Count); Assert.Single(h3._outboundThreadNames); Assert.Single(h1._removalThreadNames); Assert.Single(h2._removalThreadNames); Assert.Single(h3._removalThreadNames); } catch (Exception) { //System.out.println("H1I: " + h1.inboundThreadNames); //System.out.println("H2I: " + h2.inboundThreadNames); //System.out.println("H3I: " + h3.inboundThreadNames); //System.out.println("H1O: " + h1.outboundThreadNames); //System.out.println("H2O: " + h2.outboundThreadNames); //System.out.println("H3O: " + h3.outboundThreadNames); //System.out.println("H1R: " + h1.removalThreadNames); //System.out.println("H2R: " + h2.removalThreadNames); //System.out.println("H3R: " + h3.removalThreadNames); throw; } finally { Task.WaitAll( l.ShutdownGracefullyAsync(), e1.ShutdownGracefullyAsync(), e2.ShutdownGracefullyAsync()); } }
protected virtual void TaskDelay(int millisecondsTimeout) { Thread.Sleep(millisecondsTimeout); }