Mercurial > pub > ImplabNet
view Implab.Fx/StaApartment.cs @ 212:a01d9df88d74 v2
Added class Trace<T> to manage channels for individual classes, if SomeClass
uses Trace<SomeClass> it sould be marked with TraceSourceAttribute
author | cin |
---|---|
date | Tue, 04 Apr 2017 12:04:05 +0300 |
parents | 3eb3255d8cc5 |
children | cbe10ac0731e |
line wrap: on
line source
using Implab.Components; using Implab.Diagnostics; using Implab.Parallels; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace Implab.Fx { public class StaApartment : RunnableComponent { readonly Thread m_worker; SynchronizationContext m_syncContext; SyncContextPromise m_enterPromise; readonly Promise m_threadStarted; readonly Promise m_threadTerminated; public StaApartment() : base(true) { m_threadStarted = new Promise(); m_threadTerminated = new Promise(); m_worker = new Thread(WorkerEntry); m_worker.SetApartmentState(ApartmentState.STA); m_worker.IsBackground = true; m_worker.Name = "STA managed aparment"; } public SynchronizationContext SyncContext { get { if (m_syncContext == null) throw new InvalidOperationException(); return m_syncContext; } } /// <summary> /// Returns the promise which will dispatch all handlers inside the apartment using it's <see cref="SynchronizationContext"/> /// </summary> /// <remarks> /// Current implementation is optimized and will lost aync operation stack /// </remarks> /// <returns>The promise</returns> public IPromise Enter() { if (m_enterPromise == null) throw new InvalidOperationException(); return m_enterPromise; } public IPromise Invoke(Action<ICancellationToken> action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; action(p); p.Resolve(); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } public IPromise<T> Invoke<T>(Func<ICancellationToken, T> action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise<T>(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; p.Resolve(action(p)); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } public IPromise Invoke(Action action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; action(); p.Resolve(); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } public IPromise<T> Invoke<T>(Func<T> action) { Safe.ArgumentNotNull(action, "action"); if (m_syncContext == null) throw new InvalidOperationException(); var p = new Promise<T>(); var lop = TraceContext.Instance.CurrentOperation; m_syncContext.Post(x => { TraceContext.Instance.EnterLogicalOperation(lop, false); try { if (p.CancelOperationIfRequested()) return; p.Resolve(action()); } catch (Exception e) { p.Reject(e); } finally { TraceContext.Instance.Leave(); } }, null); return p; } /// <summary> /// Starts the apartment thread /// </summary> /// <returns>Promise which will be fullfiled when the syncronization /// context will be ready to accept tasks.</returns> protected override IPromise OnStart() { m_worker.Start(); return m_threadStarted; } /// <summary> /// Posts quit message to the message loop of the apartment /// </summary> /// <returns>Promise</returns> protected override IPromise OnStop() { m_syncContext.Post(x => Application.ExitThread(), null); return m_threadTerminated; } void WorkerEntry() { m_syncContext = new WindowsFormsSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(m_syncContext); m_enterPromise = new SyncContextPromise(m_syncContext); m_threadStarted.Resolve(); m_enterPromise.Resolve(); Application.OleRequired(); Application.Run(); try { OnShutdown(); m_threadTerminated.Resolve(); } catch(Exception err) { m_threadTerminated.Reject(err); } } /// <summary> /// Called from the STA apartment after the message loop is terminated, override this /// method to handle apartment cleanup. /// </summary> protected virtual void OnShutdown() { } protected override void Dispose(bool disposing) { if (disposing) { if (!m_threadTerminated.IsResolved) m_syncContext.Post(x => Application.ExitThread(), null); } base.Dispose(disposing); } } }