// This is the worker delegate that is called on the Threadpool thread to fire the actual events. It sets the DelegateStarted flag so // the thread that queued the work to the threadpool knows it has started (since it does not want to block indefinitely on the task // to start). private static void ControlCDelegate(object data) { ControlCDelegateData controlCData = (ControlCDelegateData)data; controlCData.DelegateStarted = true; ConsoleCancelEventArgs args = new ConsoleCancelEventArgs(controlCData.ControlKey); controlCData.CancelCallbacks(null, args); controlCData.Cancel = args.Cancel; }
// Returns true if we've "handled" the break request, false if // we want to terminate the process (or at least let the next // control handler function have a chance). private static bool BreakEvent(int controlType) { // The thread that this gets called back on has a very small stack on 64 bit systems. There is // not enough space to handle a managed exception being caught and thrown. So, queue up a work // item on another thread for the actual event callback. if (controlType == Win32Native.CTRL_C_EVENT || controlType == Win32Native.CTRL_BREAK_EVENT) { // To avoid race between remove handler and raising the event ConsoleCancelEventHandler cancelCallbacks = Console._cancelCallbacks; if (cancelCallbacks == null) { return(false); } // Create the delegate ConsoleSpecialKey controlKey = (controlType == 0) ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak; ControlCDelegateData delegateData = new ControlCDelegateData(controlKey, cancelCallbacks); WaitCallback controlCCallback = new WaitCallback(ControlCDelegate); // Queue the delegate if (!ThreadPool.QueueUserWorkItem(controlCCallback, delegateData)) { BCLDebug.Assert(false, "ThreadPool.QueueUserWorkItem returned false without throwing. Unable to execute ControlC handler"); return(false); } // Block until the delegate is done. We need to be robust in the face of the work item not executing // but we also want to get control back immediately after it is done and we don't want to give the // handler a fixed time limit in case it needs to display UI. Wait on the event twice, once with a // timout and a second time without if we are sure that the handler actually started. TimeSpan controlCWaitTime = new TimeSpan(0, 0, 30); // 30 seconds delegateData.CompletionEvent.WaitOne(controlCWaitTime, false); if (!delegateData.DelegateStarted) { BCLDebug.Assert(false, "ThreadPool.QueueUserWorkItem did not execute the handler within 30 seconds."); return(false); } delegateData.CompletionEvent.WaitOne(); delegateData.CompletionEvent.Close(); return(delegateData.Cancel); } return(false); }
internal static bool HandleBreakEvent(ConsoleSpecialKey controlKey) { // The thread that this gets called back on has a very small stack on some systems. There is // not enough space to handle a managed exception being caught and thrown. So, run a task // on the threadpool for the actual event callback. // To avoid the race condition between remove handler and raising the event ConsoleCancelEventHandler cancelCallbacks = Console._cancelCallbacks; if (cancelCallbacks == null) { return(false); } var delegateData = new ControlCDelegateData(controlKey, cancelCallbacks); Task callBackTask = Task.Factory.StartNew( d => ((ControlCDelegateData)d).HandleBreakEvent(), delegateData, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); // Block until the delegate is done. We need to be robust in the face of the task not executing // but we also want to get control back immediately after it is done and we don't want to give the // handler a fixed time limit in case it needs to display UI. Wait on the task twice, once with a // timeout and a second time without if we are sure that the handler actually started. TimeSpan controlCWaitTime = new TimeSpan(0, 0, 30); // 30 seconds callBackTask.Wait(controlCWaitTime); if (!delegateData.DelegateStarted) { Debug.Assert(false, "The task to execute the handler did not start within 30 seconds."); return(false); } callBackTask.Wait(); return(delegateData.Cancel); }
// Returns true if we've "handled" the break request, false if // we want to terminate the process (or at least let the next // control handler function have a chance). private static bool BreakEvent(int controlType) { // The thread that this gets called back on has a very small stack on 64 bit systems. There is // not enough space to handle a managed exception being caught and thrown. So, queue up a work // item on another thread for the actual event callback. if (controlType == Win32Native.CTRL_C_EVENT || controlType == Win32Native.CTRL_BREAK_EVENT) { // To avoid ---- between remove handler and raising the event ConsoleCancelEventHandler cancelCallbacks = Console._cancelCallbacks; if (cancelCallbacks == null) { return false; } // Create the delegate ConsoleSpecialKey controlKey = (controlType == 0) ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak; ControlCDelegateData delegateData = new ControlCDelegateData(controlKey, cancelCallbacks); WaitCallback controlCCallback = new WaitCallback(ControlCDelegate); // Queue the delegate if (!ThreadPool.QueueUserWorkItem(controlCCallback, delegateData)) { Contract.Assert(false, "ThreadPool.QueueUserWorkItem returned false without throwing. Unable to execute ControlC handler"); return false; } // Block until the delegate is done. We need to be robust in the face of the work item not executing // but we also want to get control back immediately after it is done and we don't want to give the // handler a fixed time limit in case it needs to display UI. Wait on the event twice, once with a // timout and a second time without if we are sure that the handler actually started. TimeSpan controlCWaitTime = new TimeSpan(0, 0, 30); // 30 seconds delegateData.CompletionEvent.WaitOne(controlCWaitTime, false); if (!delegateData.DelegateStarted) { Contract.Assert(false, "ThreadPool.QueueUserWorkItem did not execute the handler within 30 seconds."); return false; } delegateData.CompletionEvent.WaitOne(); delegateData.CompletionEvent.Close(); return delegateData.Cancel; } return false; }
private static bool BreakEvent(int controlType) { if ((controlType != 0) && (controlType != 1)) { return false; } ConsoleCancelEventHandler cancelCallbacks = _cancelCallbacks; if (cancelCallbacks == null) { return false; } ConsoleSpecialKey controlKey = (controlType == 0) ? ConsoleSpecialKey.ControlC : ConsoleSpecialKey.ControlBreak; ControlCDelegateData state = new ControlCDelegateData(controlKey, cancelCallbacks); WaitCallback callBack = new WaitCallback(Console.ControlCDelegate); if (!ThreadPool.QueueUserWorkItem(callBack, state)) { return false; } TimeSpan timeout = new TimeSpan(0, 0, 30); state.CompletionEvent.WaitOne(timeout, false); if (!state.DelegateStarted) { return false; } state.CompletionEvent.WaitOne(); state.CompletionEvent.Close(); return state.Cancel; }