Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change new ThreadPool.QueueUserWorkItem method to be generic #16570

Merged
merged 1 commit into from Feb 27, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

Change new ThreadPool.QueueUserWorkItem method to be generic

  • Loading branch information...
stephentoub committed Feb 26, 2018
commit 19f1317c42133f8cfa19aebc19b29bf83426b2c2
@@ -108,7 +108,7 @@ public virtual void Send(SendOrPostCallback d, Object state)

public virtual void Post(SendOrPostCallback d, Object state)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
ThreadPool.QueueUserWorkItem(s => s.d(s.state), (d, state), preferLocal: false);
}


@@ -894,23 +894,19 @@ internal interface IThreadPoolWorkItem
void MarkAborted(ThreadAbortException tae);
}

internal sealed class QueueUserWorkItemCallback : IThreadPoolWorkItem
internal abstract class QueueUserWorkItemCallbackBase : IThreadPoolWorkItem
{
private WaitCallback _callback;
private readonly ExecutionContext _context;
private readonly Object _state;

#if DEBUG
private volatile int executed;

~QueueUserWorkItemCallback()
~QueueUserWorkItemCallbackBase()
{
Debug.Assert(
executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
"A QueueUserWorkItemCallback was never called!");
}

private void MarkExecuted(bool aborted)
protected void MarkExecuted(bool aborted)
{
GC.SuppressFinalize(this);
Debug.Assert(
@@ -919,109 +915,152 @@ private void MarkExecuted(bool aborted)
}
#endif

internal QueueUserWorkItemCallback(WaitCallback waitCallback, Object stateObj, ExecutionContext ec)
void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
{
_callback = waitCallback;
_state = stateObj;
_context = ec;
#if DEBUG
// This workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
// This counts as being executed for our purposes.
MarkExecuted(aborted: true);
#endif
}

void IThreadPoolWorkItem.ExecuteWorkItem()
public virtual void ExecuteWorkItem()
{
#if DEBUG
MarkExecuted(aborted: false);
#endif
// call directly if it is an unsafe call OR EC flow is suppressed
}
}

internal sealed class QueueUserWorkItemCallback : QueueUserWorkItemCallbackBase
{
private WaitCallback _callback;
private readonly object _state;
private readonly ExecutionContext _context;

internal static readonly ContextCallback s_executionContextShim = state =>
{
var obj = (QueueUserWorkItemCallback)state;
WaitCallback c = obj._callback;
Debug.Assert(c != null);
obj._callback = null;
c(obj._state);
};

internal QueueUserWorkItemCallback(WaitCallback callback, object state, ExecutionContext context)
{
_callback = callback;
_state = state;
_context = context;
}

public override void ExecuteWorkItem()
{
base.ExecuteWorkItem();
ExecutionContext context = _context;
if (context == null)
{
WaitCallback cb = _callback;
WaitCallback c = _callback;
_callback = null;
cb(_state);
c(_state);
}
else
{
ExecutionContext.RunInternal(context, ccb, this);
ExecutionContext.RunInternal(context, s_executionContextShim, this);
}
}
}

void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
internal sealed class QueueUserWorkItemCallback<TState> : QueueUserWorkItemCallbackBase
{
private Action<TState> _callback;
private readonly TState _state;
private readonly ExecutionContext _context;

internal static readonly ContextCallback s_executionContextShim = state =>
{
#if DEBUG
// this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
// This counts as being executed for our purposes.
MarkExecuted(aborted: true);
#endif
var obj = (QueueUserWorkItemCallback<TState>)state;
Action<TState> c = obj._callback;
Debug.Assert(c != null);
obj._callback = null;
c(obj._state);
};

internal QueueUserWorkItemCallback(Action<TState> callback, TState state, ExecutionContext context)
{
_callback = callback;
_state = state;
_context = context;
}

internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);

private static void WaitCallback_Context(Object state)
public override void ExecuteWorkItem()
{
QueueUserWorkItemCallback obj = (QueueUserWorkItemCallback)state;
WaitCallback wc = obj._callback;
Debug.Assert(null != wc);
wc(obj._state);
base.ExecuteWorkItem();
ExecutionContext context = _context;
if (context == null)
{
Action<TState> c = _callback;
_callback = null;
c(_state);
}
else
{
ExecutionContext.RunInternal(context, s_executionContextShim, this);
}
}
}

internal sealed class QueueUserWorkItemCallbackDefaultContext : IThreadPoolWorkItem
internal sealed class QueueUserWorkItemCallbackDefaultContext : QueueUserWorkItemCallbackBase
{
private WaitCallback callback;
private readonly Object state;

#if DEBUG
private volatile int executed;
private WaitCallback _callback;
private readonly object _state;

~QueueUserWorkItemCallbackDefaultContext()
internal static readonly ContextCallback s_executionContextShim = state =>
{
Debug.Assert(
executed != 0 || Environment.HasShutdownStarted || AppDomain.CurrentDomain.IsFinalizingForUnload(),
"A QueueUserWorkItemCallbackDefaultContext was never called!");
}

private void MarkExecuted(bool aborted)
var obj = (QueueUserWorkItemCallbackDefaultContext)state;
WaitCallback c = obj._callback;
Debug.Assert(c != null);
obj._callback = null;
c(obj._state);
};

internal QueueUserWorkItemCallbackDefaultContext(WaitCallback callback, object state)
{
GC.SuppressFinalize(this);
Debug.Assert(
0 == Interlocked.Exchange(ref executed, 1) || aborted,
"A QueueUserWorkItemCallbackDefaultContext was called twice!");
_callback = callback;
_state = state;
}
#endif

internal QueueUserWorkItemCallbackDefaultContext(WaitCallback waitCallback, Object stateObj)
public override void ExecuteWorkItem()
{
callback = waitCallback;
state = stateObj;
base.ExecuteWorkItem();
ExecutionContext.RunInternal(executionContext: null, s_executionContextShim, this); // null executionContext on RunInternal is Default context
}
}

void IThreadPoolWorkItem.ExecuteWorkItem()
{
#if DEBUG
MarkExecuted(aborted: false);
#endif
// null executionContext on RunInternal is Default context
ExecutionContext.RunInternal(executionContext: null, ccb, this);
}
internal sealed class QueueUserWorkItemCallbackDefaultContext<TState> : QueueUserWorkItemCallbackBase
{
private Action<TState> _callback;
private readonly TState _state;

void IThreadPoolWorkItem.MarkAborted(ThreadAbortException tae)
internal static readonly ContextCallback s_executionContextShim = state =>
{
#if DEBUG
// this workitem didn't execute because we got a ThreadAbortException prior to the call to ExecuteWorkItem.
// This counts as being executed for our purposes.
MarkExecuted(aborted: true);
#endif
var obj = (QueueUserWorkItemCallbackDefaultContext<TState>)state;
Action<TState> c = obj._callback;
Debug.Assert(c != null);
obj._callback = null;
c(obj._state);
};

internal QueueUserWorkItemCallbackDefaultContext(Action<TState> callback, TState state)
{
_callback = callback;
_state = state;
}

internal static readonly ContextCallback ccb = new ContextCallback(WaitCallback_Context);

private static void WaitCallback_Context(Object state)
public override void ExecuteWorkItem()
{
QueueUserWorkItemCallbackDefaultContext obj = (QueueUserWorkItemCallbackDefaultContext)state;
WaitCallback wc = obj.callback;
Debug.Assert(null != wc);
obj.callback = null;
wc(obj.state);
base.ExecuteWorkItem();
ExecutionContext.RunInternal(executionContext: null, s_executionContextShim, this); // null executionContext on RunInternal is Default context
}
}

@@ -1271,12 +1310,9 @@ bool executeOnlyOnce
}

public static bool QueueUserWorkItem(WaitCallback callBack) =>
QueueUserWorkItem(callBack, null, preferLocal: false);

public static bool QueueUserWorkItem(WaitCallback callBack, object state) =>
QueueUserWorkItem(callBack, state, preferLocal: false);
QueueUserWorkItem(callBack, null);

public static bool QueueUserWorkItem(WaitCallback callBack, object state, bool preferLocal)
public static bool QueueUserWorkItem(WaitCallback callBack, object state)
{
if (callBack == null)
{
@@ -1291,6 +1327,26 @@ public static bool QueueUserWorkItem(WaitCallback callBack, object state, bool p
new QueueUserWorkItemCallbackDefaultContext(callBack, state) :
(IThreadPoolWorkItem)new QueueUserWorkItemCallback(callBack, state, context);

ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: true);

return true;
}

public static bool QueueUserWorkItem<TState>(Action<TState> callBack, TState state, bool preferLocal)
{
if (callBack == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.callBack);
}

EnsureVMInitialized();

ExecutionContext context = ExecutionContext.Capture();

IThreadPoolWorkItem tpcallBack = (context != null && context.IsDefault) ?
new QueueUserWorkItemCallbackDefaultContext<TState>(callBack, state) :
(IThreadPoolWorkItem)new QueueUserWorkItemCallback<TState>(callBack, state, context);

ThreadPoolGlobals.workQueue.Enqueue(tpcallBack, forceGlobal: !preferLocal);

return true;
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.