StaApartment.cs
204 lines
| 6.7 KiB
| text/x-csharp
|
CSharpLexer
/ Implab.Fx / StaApartment.cs
cin
|
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; | ||||
cin
|
r211 | SyncContextPromise m_enterPromise; | ||
cin
|
r210 | 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; | ||||
} | ||||
} | ||||
cin
|
r211 | /// <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; | ||||
} | ||||
cin
|
r210 | 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); | ||||
cin
|
r211 | m_enterPromise = new SyncContextPromise(m_syncContext); | ||
cin
|
r210 | m_threadStarted.Resolve(); | ||
cin
|
r211 | m_enterPromise.Resolve(); | ||
cin
|
r210 | |||
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); | ||||
} | ||||
} | ||||
} | ||||