StaApartment.cs
188 lines
| 6.0 KiB
| text/x-csharp
|
CSharpLexer
/ Implab.Fx / StaApartment.cs
|
|
r210 | 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); | ||||
| } | ||||
| } | ||||
| } | ||||
