Mercurial > pub > ImplabNet
diff Implab.Fx/StaApartment.cs @ 210:5dc21f6a3222 v2
Code review for RunnableComponent
Added StaApartment class based on System.Windows.Forms.Application message loop
author | cin |
---|---|
date | Mon, 20 Mar 2017 17:44:18 +0300 |
parents | |
children | 3eb3255d8cc5 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.Fx/StaApartment.cs Mon Mar 20 17:44:18 2017 +0300 @@ -0,0 +1,188 @@ +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; + 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; + } + } + + 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_threadStarted.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); + } + } +}