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;
}
}
///
/// Returns the promise which will dispatch all handlers inside the apartment using it's
///
///
/// Current implementation is optimized and will lost aync operation stack
///
/// The promise
public IPromise Enter() {
if (m_enterPromise == null)
throw new InvalidOperationException();
return m_enterPromise;
}
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);
p.Resolve();
} catch (Exception e) {
p.Reject(e);
} finally {
TraceContext.Instance.Leave();
}
}, null);
return p;
}
public IPromise Invoke(Func 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;
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 Invoke(Func 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;
p.Resolve(action());
} catch (Exception e) {
p.Reject(e);
} finally {
TraceContext.Instance.Leave();
}
}, null);
return p;
}
///
/// Starts the apartment thread
///
/// Promise which will be fullfiled when the syncronization
/// context will be ready to accept tasks.
protected override IPromise OnStart() {
m_worker.Start();
return m_threadStarted;
}
///
/// Posts quit message to the message loop of the apartment
///
/// Promise
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);
}
}
///
/// Called from the STA apartment after the message loop is terminated, override this
/// method to handle apartment cleanup.
///
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);
}
}
}