Mercurial > pub > ImplabNet
annotate Implab.Fx/StaApartment.cs @ 238:bdfdba6b645b v2
fixed unpredictable Safe.Dispose behaviour
author | cin |
---|---|
date | Fri, 01 Dec 2017 01:28:56 +0300 |
parents | 3eb3255d8cc5 |
children | cbe10ac0731e |
rev | line source |
---|---|
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; | |
211
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
16 SyncContextPromise m_enterPromise; |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
17 |
210 | 18 readonly Promise m_threadStarted; |
19 readonly Promise m_threadTerminated; | |
20 | |
21 public StaApartment() : base(true) { | |
22 m_threadStarted = new Promise(); | |
23 m_threadTerminated = new Promise(); | |
24 | |
25 m_worker = new Thread(WorkerEntry); | |
26 m_worker.SetApartmentState(ApartmentState.STA); | |
27 m_worker.IsBackground = true; | |
28 m_worker.Name = "STA managed aparment"; | |
29 } | |
30 | |
31 public SynchronizationContext SyncContext { | |
32 get { | |
33 if (m_syncContext == null) | |
34 throw new InvalidOperationException(); | |
35 return m_syncContext; | |
36 } | |
37 } | |
38 | |
211
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
39 /// <summary> |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
40 /// Returns the promise which will dispatch all handlers inside the apartment using it's <see cref="SynchronizationContext"/> |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
41 /// </summary> |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
42 /// <remarks> |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
43 /// Current implementation is optimized and will lost aync operation stack |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
44 /// </remarks> |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
45 /// <returns>The promise</returns> |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
46 public IPromise Enter() { |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
47 if (m_enterPromise == null) |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
48 throw new InvalidOperationException(); |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
49 return m_enterPromise; |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
50 } |
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
51 |
210 | 52 public IPromise Invoke(Action<ICancellationToken> action) { |
53 Safe.ArgumentNotNull(action, "action"); | |
54 | |
55 if (m_syncContext == null) | |
56 throw new InvalidOperationException(); | |
57 var p = new Promise(); | |
58 var lop = TraceContext.Instance.CurrentOperation; | |
59 | |
60 m_syncContext.Post(x => { | |
61 TraceContext.Instance.EnterLogicalOperation(lop, false); | |
62 try { | |
63 if (p.CancelOperationIfRequested()) | |
64 return; | |
65 | |
66 action(p); | |
67 p.Resolve(); | |
68 } catch (Exception e) { | |
69 p.Reject(e); | |
70 } finally { | |
71 TraceContext.Instance.Leave(); | |
72 } | |
73 }, null); | |
74 | |
75 return p; | |
76 } | |
77 | |
78 public IPromise<T> Invoke<T>(Func<ICancellationToken, T> action) { | |
79 Safe.ArgumentNotNull(action, "action"); | |
80 | |
81 if (m_syncContext == null) | |
82 throw new InvalidOperationException(); | |
83 var p = new Promise<T>(); | |
84 var lop = TraceContext.Instance.CurrentOperation; | |
85 | |
86 m_syncContext.Post(x => { | |
87 TraceContext.Instance.EnterLogicalOperation(lop, false); | |
88 try { | |
89 if (p.CancelOperationIfRequested()) | |
90 return; | |
91 p.Resolve(action(p)); | |
92 } catch (Exception e) { | |
93 p.Reject(e); | |
94 } finally { | |
95 TraceContext.Instance.Leave(); | |
96 } | |
97 }, null); | |
98 | |
99 return p; | |
100 } | |
101 | |
102 public IPromise Invoke(Action action) { | |
103 Safe.ArgumentNotNull(action, "action"); | |
104 | |
105 if (m_syncContext == null) | |
106 throw new InvalidOperationException(); | |
107 var p = new Promise(); | |
108 var lop = TraceContext.Instance.CurrentOperation; | |
109 | |
110 m_syncContext.Post(x => { | |
111 TraceContext.Instance.EnterLogicalOperation(lop, false); | |
112 try { | |
113 if (p.CancelOperationIfRequested()) | |
114 return; | |
115 action(); | |
116 p.Resolve(); | |
117 } catch (Exception e) { | |
118 p.Reject(e); | |
119 } finally { | |
120 TraceContext.Instance.Leave(); | |
121 } | |
122 }, null); | |
123 | |
124 return p; | |
125 } | |
126 | |
127 public IPromise<T> Invoke<T>(Func<T> action) { | |
128 Safe.ArgumentNotNull(action, "action"); | |
129 | |
130 if (m_syncContext == null) | |
131 throw new InvalidOperationException(); | |
132 var p = new Promise<T>(); | |
133 var lop = TraceContext.Instance.CurrentOperation; | |
134 | |
135 m_syncContext.Post(x => { | |
136 TraceContext.Instance.EnterLogicalOperation(lop, false); | |
137 try { | |
138 if (p.CancelOperationIfRequested()) | |
139 return; | |
140 p.Resolve(action()); | |
141 } catch (Exception e) { | |
142 p.Reject(e); | |
143 } finally { | |
144 TraceContext.Instance.Leave(); | |
145 } | |
146 }, null); | |
147 | |
148 return p; | |
149 } | |
150 | |
151 | |
152 /// <summary> | |
153 /// Starts the apartment thread | |
154 /// </summary> | |
155 /// <returns>Promise which will be fullfiled when the syncronization | |
156 /// context will be ready to accept tasks.</returns> | |
157 protected override IPromise OnStart() { | |
158 m_worker.Start(); | |
159 return m_threadStarted; | |
160 } | |
161 | |
162 /// <summary> | |
163 /// Posts quit message to the message loop of the apartment | |
164 /// </summary> | |
165 /// <returns>Promise</returns> | |
166 protected override IPromise OnStop() { | |
167 m_syncContext.Post(x => Application.ExitThread(), null); | |
168 return m_threadTerminated; | |
169 } | |
170 | |
171 void WorkerEntry() { | |
172 m_syncContext = new WindowsFormsSynchronizationContext(); | |
173 SynchronizationContext.SetSynchronizationContext(m_syncContext); | |
211
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
174 m_enterPromise = new SyncContextPromise(m_syncContext); |
210 | 175 m_threadStarted.Resolve(); |
211
3eb3255d8cc5
Code review, added a non generic version of SyncContextPromise
cin
parents:
210
diff
changeset
|
176 m_enterPromise.Resolve(); |
210 | 177 |
178 Application.OleRequired(); | |
179 Application.Run(); | |
180 | |
181 try { | |
182 OnShutdown(); | |
183 m_threadTerminated.Resolve(); | |
184 } catch(Exception err) { | |
185 m_threadTerminated.Reject(err); | |
186 } | |
187 } | |
188 | |
189 /// <summary> | |
190 /// Called from the STA apartment after the message loop is terminated, override this | |
191 /// method to handle apartment cleanup. | |
192 /// </summary> | |
193 protected virtual void OnShutdown() { | |
194 } | |
195 | |
196 protected override void Dispose(bool disposing) { | |
197 if (disposing) { | |
198 if (!m_threadTerminated.IsResolved) | |
199 m_syncContext.Post(x => Application.ExitThread(), null); | |
200 } | |
201 base.Dispose(disposing); | |
202 } | |
203 } | |
204 } |