210
|
1 using Implab.Components;
|
|
2 using Implab.Diagnostics;
|
|
3 using Implab.Parallels;
|
|
4 using System;
|
|
5 using System.Collections.Generic;
|
|
6 using System.Linq;
|
|
7 using System.Text;
|
|
8 using System.Threading;
|
|
9 using System.Threading.Tasks;
|
|
10 using System.Windows.Forms;
|
|
11
|
|
12 namespace Implab.Fx {
|
|
13 public class StaApartment : RunnableComponent {
|
|
14 readonly Thread m_worker;
|
|
15 SynchronizationContext m_syncContext;
|
|
16 readonly Promise m_threadStarted;
|
|
17 readonly Promise m_threadTerminated;
|
|
18
|
|
19 public StaApartment() : base(true) {
|
|
20 m_threadStarted = new Promise();
|
|
21 m_threadTerminated = new Promise();
|
|
22
|
|
23 m_worker = new Thread(WorkerEntry);
|
|
24 m_worker.SetApartmentState(ApartmentState.STA);
|
|
25 m_worker.IsBackground = true;
|
|
26 m_worker.Name = "STA managed aparment";
|
|
27 }
|
|
28
|
|
29 public SynchronizationContext SyncContext {
|
|
30 get {
|
|
31 if (m_syncContext == null)
|
|
32 throw new InvalidOperationException();
|
|
33 return m_syncContext;
|
|
34 }
|
|
35 }
|
|
36
|
|
37 public IPromise Invoke(Action<ICancellationToken> action) {
|
|
38 Safe.ArgumentNotNull(action, "action");
|
|
39
|
|
40 if (m_syncContext == null)
|
|
41 throw new InvalidOperationException();
|
|
42 var p = new Promise();
|
|
43 var lop = TraceContext.Instance.CurrentOperation;
|
|
44
|
|
45 m_syncContext.Post(x => {
|
|
46 TraceContext.Instance.EnterLogicalOperation(lop, false);
|
|
47 try {
|
|
48 if (p.CancelOperationIfRequested())
|
|
49 return;
|
|
50
|
|
51 action(p);
|
|
52 p.Resolve();
|
|
53 } catch (Exception e) {
|
|
54 p.Reject(e);
|
|
55 } finally {
|
|
56 TraceContext.Instance.Leave();
|
|
57 }
|
|
58 }, null);
|
|
59
|
|
60 return p;
|
|
61 }
|
|
62
|
|
63 public IPromise<T> Invoke<T>(Func<ICancellationToken, T> action) {
|
|
64 Safe.ArgumentNotNull(action, "action");
|
|
65
|
|
66 if (m_syncContext == null)
|
|
67 throw new InvalidOperationException();
|
|
68 var p = new Promise<T>();
|
|
69 var lop = TraceContext.Instance.CurrentOperation;
|
|
70
|
|
71 m_syncContext.Post(x => {
|
|
72 TraceContext.Instance.EnterLogicalOperation(lop, false);
|
|
73 try {
|
|
74 if (p.CancelOperationIfRequested())
|
|
75 return;
|
|
76 p.Resolve(action(p));
|
|
77 } catch (Exception e) {
|
|
78 p.Reject(e);
|
|
79 } finally {
|
|
80 TraceContext.Instance.Leave();
|
|
81 }
|
|
82 }, null);
|
|
83
|
|
84 return p;
|
|
85 }
|
|
86
|
|
87 public IPromise Invoke(Action action) {
|
|
88 Safe.ArgumentNotNull(action, "action");
|
|
89
|
|
90 if (m_syncContext == null)
|
|
91 throw new InvalidOperationException();
|
|
92 var p = new Promise();
|
|
93 var lop = TraceContext.Instance.CurrentOperation;
|
|
94
|
|
95 m_syncContext.Post(x => {
|
|
96 TraceContext.Instance.EnterLogicalOperation(lop, false);
|
|
97 try {
|
|
98 if (p.CancelOperationIfRequested())
|
|
99 return;
|
|
100 action();
|
|
101 p.Resolve();
|
|
102 } catch (Exception e) {
|
|
103 p.Reject(e);
|
|
104 } finally {
|
|
105 TraceContext.Instance.Leave();
|
|
106 }
|
|
107 }, null);
|
|
108
|
|
109 return p;
|
|
110 }
|
|
111
|
|
112 public IPromise<T> Invoke<T>(Func<T> action) {
|
|
113 Safe.ArgumentNotNull(action, "action");
|
|
114
|
|
115 if (m_syncContext == null)
|
|
116 throw new InvalidOperationException();
|
|
117 var p = new Promise<T>();
|
|
118 var lop = TraceContext.Instance.CurrentOperation;
|
|
119
|
|
120 m_syncContext.Post(x => {
|
|
121 TraceContext.Instance.EnterLogicalOperation(lop, false);
|
|
122 try {
|
|
123 if (p.CancelOperationIfRequested())
|
|
124 return;
|
|
125 p.Resolve(action());
|
|
126 } catch (Exception e) {
|
|
127 p.Reject(e);
|
|
128 } finally {
|
|
129 TraceContext.Instance.Leave();
|
|
130 }
|
|
131 }, null);
|
|
132
|
|
133 return p;
|
|
134 }
|
|
135
|
|
136
|
|
137 /// <summary>
|
|
138 /// Starts the apartment thread
|
|
139 /// </summary>
|
|
140 /// <returns>Promise which will be fullfiled when the syncronization
|
|
141 /// context will be ready to accept tasks.</returns>
|
|
142 protected override IPromise OnStart() {
|
|
143 m_worker.Start();
|
|
144 return m_threadStarted;
|
|
145 }
|
|
146
|
|
147 /// <summary>
|
|
148 /// Posts quit message to the message loop of the apartment
|
|
149 /// </summary>
|
|
150 /// <returns>Promise</returns>
|
|
151 protected override IPromise OnStop() {
|
|
152 m_syncContext.Post(x => Application.ExitThread(), null);
|
|
153 return m_threadTerminated;
|
|
154 }
|
|
155
|
|
156 void WorkerEntry() {
|
|
157 m_syncContext = new WindowsFormsSynchronizationContext();
|
|
158 SynchronizationContext.SetSynchronizationContext(m_syncContext);
|
|
159
|
|
160 m_threadStarted.Resolve();
|
|
161
|
|
162 Application.OleRequired();
|
|
163 Application.Run();
|
|
164
|
|
165 try {
|
|
166 OnShutdown();
|
|
167 m_threadTerminated.Resolve();
|
|
168 } catch(Exception err) {
|
|
169 m_threadTerminated.Reject(err);
|
|
170 }
|
|
171 }
|
|
172
|
|
173 /// <summary>
|
|
174 /// Called from the STA apartment after the message loop is terminated, override this
|
|
175 /// method to handle apartment cleanup.
|
|
176 /// </summary>
|
|
177 protected virtual void OnShutdown() {
|
|
178 }
|
|
179
|
|
180 protected override void Dispose(bool disposing) {
|
|
181 if (disposing) {
|
|
182 if (!m_threadTerminated.IsResolved)
|
|
183 m_syncContext.Post(x => Application.ExitThread(), null);
|
|
184 }
|
|
185 base.Dispose(disposing);
|
|
186 }
|
|
187 }
|
|
188 }
|