Mercurial > pub > ImplabNet
comparison Implab.Fx/StaApartment.cs @ 210:5dc21f6a3222 v2
Code review for RunnableComponent
Added StaApartment class based on System.Windows.Forms.Application message loop
author | cin |
---|---|
date | Mon, 20 Mar 2017 17:44:18 +0300 |
parents | |
children | 3eb3255d8cc5 |
comparison
equal
deleted
inserted
replaced
209:a867536c68fc | 210:5dc21f6a3222 |
---|---|
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 } |