/// <summary> /// Yields execution until next frame. /// </summary> /// <returns>Task.</returns> public ChannelMicroThreadAwaiter <int> NextFrame() { if (MicroThread.Current == null) { throw new Exception("NextFrame cannot be called out of the micro-thread context."); } return(FrameChannel.Receive()); }
internal Frame(IChannelOwner parent, string guid, FrameInitializer initializer) : base(parent, guid) { _channel = new FrameChannel(guid, parent.Connection, this); _initializer = initializer; Url = _initializer.Url; Name = _initializer.Name; ParentFrame = _initializer.ParentFrame; _loadStates = initializer.LoadStates; _channel.LoadState += (sender, e) => { lock (_loadStates) { if (e.Add.HasValue) { _loadStates.Add(e.Add.Value); LoadState?.Invoke(this, new LoadStateEventArgs { LifecycleEvent = e.Add.Value }); } if (e.Remove.HasValue) { _loadStates.Remove(e.Remove.Value); } } }; _channel.Navigated += (sender, e) => { Url = e.Url; Name = e.Name; Navigated?.Invoke(this, e); if (string.IsNullOrEmpty(e.Error)) { ((Page)Page)?.OnFrameNavigated(this); } }; }
internal Frame(ConnectionScope scope, string guid, FrameInitializer initializer) { _scope = scope; _channel = new FrameChannel(guid, scope, this); }
/// <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 while (true) { Action callback; MicroThread microThread; lock (scheduledMicroThreads) { if (scheduledMicroThreads.Count == 0) { break; } microThread = scheduledMicroThreads.Dequeue(); callback = microThread.Callback; microThread.Callback = null; } // 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)) { callback(); } } 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); } }
/// <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) { 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) { if (MicroThreadCallbackEnd != null) { MicroThreadCallbackEnd(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 != null) { MicroThreadStarted(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } if (MicroThreadCallbackStart != null) { MicroThreadCallbackStart(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } using (Profiler.Begin(MicroThread.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 { 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.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(); } } if (MicroThreadEnded != null) { MicroThreadEnded(this, new SchedulerThreadEventArgs(microThread, managedThreadId)); } } } runningMicroThread.Value = previousRunningMicrothread; if (previousRunningMicrothread != null) { if (MicroThreadCallbackStart != null) { MicroThreadCallbackStart(this, new SchedulerThreadEventArgs(previousRunningMicrothread, managedThreadId)); } } } } else { try { schedulerEntry.Action(); } catch (Exception e) { ActionException?.Invoke(this, schedulerEntry, e); } } } while (FrameChannel.Balance < 0) { FrameChannel.Send(0); } }