/// <summary> /// Runs until no runnable tasklets left. /// This function is reentrant. /// </summary> public void Run() { #if SILICONSTUDIO_PLATFORM_UWP int managedThreadId = 0; #else int managedThreadId = Thread.CurrentThread.ManagedThreadId; #endif MicroThreadCallbackList callbacks = default(MicroThreadCallbackList); while (true) { SchedulerEntry schedulerEntry; MicroThread microThread; lock (scheduledEntries) { // Reclaim callbacks of previous microthread MicroThreadCallbackNode callback; while (callbacks.TakeFirst(out callback)) { callback.Clear(); callbackNodePool.Add(callback); } if (scheduledEntries.Count == 0) { break; } schedulerEntry = scheduledEntries.Dequeue(); microThread = schedulerEntry.MicroThread; if (microThread != null) { callbacks = microThread.Callbacks; microThread.Callbacks = default(MicroThreadCallbackList); } } // Since it can be reentrant, it should be restored after running the callback. var previousRunningMicrothread = runningMicroThread.Value; if (previousRunningMicrothread != null) { MicroThreadCallbackEnd?.Invoke(this, new SchedulerThreadEventArgs(previousRunningMicrothread, managedThreadId)); } runningMicroThread.Value = microThread; if (microThread != null) { var previousSyncContext = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(microThread.SynchronizationContext); // TODO: Do we still need to try/catch here? Everything should be caught in the continuation wrapper and put into MicroThread.Exception try { if (microThread.State == MicroThreadState.Starting) { MicroThreadStarted?.Invoke(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } MicroThreadCallbackStart?.Invoke(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); using (Profiler.Begin(MicroThreadProfilingKeys.ProfilingKey, microThread.ScriptId)) { var callback = callbacks.First; while (callback != null) { callback.Invoke(); callback = callback.Next; } } } catch (Exception e) { Log.Error("Unexpected exception while executing a micro-thread", e); microThread.SetException(e); } finally { MicroThreadCallbackEnd?.Invoke(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); SynchronizationContext.SetSynchronizationContext(previousSyncContext); if (microThread.IsOver) { lock (microThread.AllLinkedListNode) { if (microThread.CompletionTask != null) { if (microThread.State == MicroThreadState.Failed || microThread.State == MicroThreadState.Canceled) { microThread.CompletionTask.TrySetException(microThread.Exception); } else { microThread.CompletionTask.TrySetResult(1); } } else if (microThread.State == MicroThreadState.Failed && microThread.Exception != null) { // Nothing was listening on the micro thread and it crashed // Let's treat it as unhandled exception and propagate it // Use ExceptionDispatchInfo.Capture to not overwrite callstack if (PropagateExceptions && (microThread.Flags & MicroThreadFlags.IgnoreExceptions) != MicroThreadFlags.IgnoreExceptions) { ExceptionDispatchInfo.Capture(microThread.Exception).Throw(); } } MicroThreadEnded?.Invoke(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } } runningMicroThread.Value = previousRunningMicrothread; if (previousRunningMicrothread != null) { MicroThreadCallbackStart?.Invoke(this, new SchedulerThreadEventArgs(previousRunningMicrothread, managedThreadId)); } } } else { try { schedulerEntry.Action(); } catch (Exception e) { ActionException?.Invoke(this, schedulerEntry, e); } } } while (FrameChannel.Balance < 0) { FrameChannel.Send(0); } }
/// <summary> /// Runs until no runnable tasklets left. /// This function is reentrant. /// </summary> public void Run() { #if SILICONSTUDIO_PLATFORM_WINDOWS_RUNTIME int managedThreadId = 0; #else int managedThreadId = Thread.CurrentThread.ManagedThreadId; #endif MicroThreadCallbackList callbacks = default(MicroThreadCallbackList); while (true) { MicroThread microThread; lock (scheduledMicroThreads) { // Reclaim callbacks of previous microthread MicroThreadCallbackNode callback; while (callbacks.TakeFirst(out callback)) { callback.Clear(); callbackNodePool.Add(callback); } if (scheduledMicroThreads.Count == 0) { break; } microThread = scheduledMicroThreads.Dequeue(); callbacks = microThread.Callbacks; microThread.Callbacks = default(MicroThreadCallbackList); } // Since it can be reentrant, it should be restored after running the callback. var previousRunningMicrothread = runningMicroThread.Value; if (previousRunningMicrothread != null) { if (MicroThreadCallbackEnd != null) { MicroThreadCallbackEnd(this, new SchedulerThreadEventArgs(previousRunningMicrothread, managedThreadId)); } } runningMicroThread.Value = microThread; var previousSyncContext = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(microThread.SynchronizationContext); try { if (microThread.State == MicroThreadState.Starting && MicroThreadStarted != null) { MicroThreadStarted(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } if (MicroThreadCallbackStart != null) { MicroThreadCallbackStart(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } using (Profiler.Begin(microThread.ProfilingKey)) { var callback = callbacks.First; while (callback != null) { microThread.ThrowIfExceptionRequest(); callback.Invoke(); callback = callback.Next; } microThread.ThrowIfExceptionRequest(); } } catch (Exception e) { Log.Error("Unexpected exception while executing a micro-thread", e); microThread.SetException(e); } finally { if (MicroThreadCallbackEnd != null) { MicroThreadCallbackEnd(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } SynchronizationContext.SetSynchronizationContext(previousSyncContext); if (microThread.IsOver) { lock (microThread.AllLinkedListNode) { if (microThread.CompletionTask != null) { if (microThread.State == MicroThreadState.Failed || microThread.State == MicroThreadState.Cancelled) { microThread.CompletionTask.TrySetException(microThread.Exception); } else { microThread.CompletionTask.TrySetResult(1); } } else if (microThread.State == MicroThreadState.Failed && microThread.Exception != null) { // Nothing was listening on the micro thread and it crashed // Let's treat it as unhandled exception and propagate it // Use ExceptionDispatchInfo.Capture to not overwrite callstack if ((microThread.Flags & MicroThreadFlags.IgnoreExceptions) != MicroThreadFlags.IgnoreExceptions) { ExceptionDispatchInfo.Capture(microThread.Exception).Throw(); } } if (MicroThreadEnded != null) { MicroThreadEnded(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } } } runningMicroThread.Value = previousRunningMicrothread; if (previousRunningMicrothread != null) { if (MicroThreadCallbackStart != null) { MicroThreadCallbackStart(this, new SchedulerThreadEventArgs(previousRunningMicrothread, managedThreadId)); } } } } while (FrameChannel.Balance < 0) { FrameChannel.Send(0); } }