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 }