public static bool QueueUserWorkItemHelper(WaitCallback callBack, object state, bool isSafe) { return(Helper.SimpleWrap <bool>( delegate(ClrSyncManager manager) { Helper.ThreadRoutineArg p = new Helper.ThreadRoutineArg(); p.s = new Original::Semaphore(0, 1); p.o = state; p.wcb = callBack; bool flag = isSafe ? Original.ThreadPool.QueueUserWorkItem(Helper.ThreadCreateWrapper(manager), p) : Original.ThreadPool.UnsafeQueueUserWorkItem(Helper.ThreadCreateWrapper(manager), p); if (flag) { ChessTask child = manager.TaskFork(); manager.RegisterTaskSemaphore(child, p.s, false); manager.TaskResume(child); } return flag; }, delegate() { return isSafe ? Original.ThreadPool.QueueUserWorkItem(callBack, state) : Original.ThreadPool.UnsafeQueueUserWorkItem(callBack, state); } )); }
public static object Invoke(object untypedMethod, object[] arguments) { return(Helper.SimpleWrap <object>( delegate(ClrSyncManager manager) { var method = (Method)untypedMethod; if (method.ShortName == "BeginInvoke") { // extract the delegate arguments, and callback Debug.Assert(arguments.Length >= 3); var delegateArguments = new object[arguments.Length - 3]; var callback = (AsyncCallback)arguments[arguments.Length - 2]; var asyncState = arguments[arguments.Length - 1]; var @delegate = (global::System.Delegate)arguments[0]; global::System.Array.Copy(arguments, 1, delegateArguments, 0, delegateArguments.Length); // create the IAsyncResult that everyone shares var asyncResult = new WrappedAsyncResult(asyncState); // we wrap the actual invoke // so that CHESS gets control, as usual Helper.ThreadRoutineArg p = new Helper.ThreadRoutineArg(); p.s = new Original::Semaphore(0, 1); p.o = null; p.wcb = (o => { manager.SetMethodInfo("BeginInvoke(Begin)"); manager.SyncVarAccess(asyncResult, MSyncVarOp.LOCK_ACQUIRE); manager.CommitSyncVarAccess(); // synchronous call of the actual method object result = null; try { result = @delegate.DynamicInvoke(delegateArguments); // make sure to copy values of out params List <object> byRefResults = new List <object>(); var delegateParameters = @delegate.GetType().GetMethod("Invoke").GetParameters(); for (int i = 0; i < delegateParameters.Length; i++) { if (delegateParameters[i].ParameterType.IsByRef) { byRefResults.Add(delegateArguments[i]); } } asyncResult.SetByRefResults(byRefResults.ToArray()); } catch (Exception e) { // From ECMA: // Note: the callee can throw exceptions. // Any unhandled exception propagates to the caller via the EndInvoke method. asyncResult.CalleeThrown = e; } // this is the actual return results of the method, which becomes the return value // of the EndInvoke asyncResult.Result = result; manager.SetMethodInfo("BeginInvoke(Completed)"); manager.SyncVarAccess(asyncResult, MSyncVarOp.LOCK_RELEASE); // we set completed before the callback is done because // the callback may call EndInvoke. asyncResult.Completed = true; asyncResult.MyHandle.Set(); manager.CommitSyncVarAccess(); // From ECMA: The VES shall call this delegate when the value [the callback] // is computed or an exception has been raised indicating that the result will // not be available. // TODO: but how is the callback supposed to "know" whether or not an exception was raised? if (callback != null) { callback(asyncResult); } }); // create the wrapper WaitCallback call = Helper.ThreadCreateWrapper(manager); // store it aware for later use asyncResult.MyDelegate = call; // TODO: how do we insure this one is done without instrumentation? IAsyncResult invokeResult = call.BeginInvoke(p, null, null); // we squirrel this way because we have to match the above BeginInvoke // with the EndInvoke later asyncResult.RealAsyncResult = invokeResult; // let the asynch task proceed ChessTask child = manager.TaskFork(); manager.RegisterTaskSemaphore(child, p.s, false); manager.TaskResume(child); return asyncResult; } else if (method.ShortName == "EndInvoke") { WrappedAsyncResult asyncResult = (WrappedAsyncResult)arguments[arguments.Length - 1]; // wait for the BeginInvoke to complete while (true) { manager.SetMethodInfo("EndInvoke(Begin)"); manager.SyncVarAccess(asyncResult, MSyncVarOp.LOCK_ACQUIRE); if (!asyncResult.Completed) { manager.LocalBacktrack(); continue; } manager.CommitSyncVarAccess(); break; } // for good measure, do the EndInvoke, to match the BeginInvoke asyncResult.MyDelegate.EndInvoke(asyncResult.RealAsyncResult); manager.SetMethodInfo("EndInvoke(Completed)"); manager.SyncVarAccess(asyncResult, MSyncVarOp.LOCK_RELEASE); manager.CommitSyncVarAccess(); if (asyncResult.CalleeThrown != null) { throw asyncResult.CalleeThrown; } else { // copy the results back! global::System.Array.Copy(asyncResult.GetByRefResults(), 0, arguments, 1, asyncResult.GetByRefResults().Length); } return asyncResult.Result; } else { throw new InvalidOperationException("unexpected method: " + method.FullName); } }, delegate() { // TODO: no instrumentation return null; })); }