1
+ using System ;
2
+ using System . Collections ;
3
+ using System . Collections . Generic ;
4
+ using System . Threading ;
5
+ using System . Threading . Tasks ;
6
+
7
+ namespace ROS2 . Executors
8
+ {
9
+ /// <summary>
10
+ /// Executor which wraps a <see cref="ManualExecutor"/> and automatically
11
+ /// executes the task created by <see cref="ManualExecutor.CreateSpinTask"/>.
12
+ /// </summary>
13
+ /// <remarks>
14
+ /// The spin task is automatically stopped when <see cref="Dispose"/>
15
+ /// is called or the context is shut down.
16
+ /// </remarks>
17
+ public sealed class TaskExecutor : IExecutor
18
+ {
19
+ /// <summary>
20
+ /// Task managed by this executor.
21
+ /// </summary>
22
+ public Task Task { get ; private set ; }
23
+
24
+ private readonly CancellationTokenSource CancellationSource = new CancellationTokenSource ( ) ;
25
+
26
+ private readonly ManualExecutor Executor ;
27
+
28
+ private readonly Context Context ;
29
+
30
+ /// <param name="context"> Context associated with this executor. </param>
31
+ /// <param name="timeout"> Maximum time to wait for work to become available. </param>
32
+ public TaskExecutor ( Context context , TimeSpan timeout )
33
+ {
34
+ this . Context = context ;
35
+ this . Executor = new ManualExecutor ( context ) ;
36
+ this . Task = this . Executor . CreateSpinTask ( timeout , this . CancellationSource . Token ) ;
37
+ try
38
+ {
39
+ context . OnShutdown += this . StopSpinTask ;
40
+ this . Task . Start ( ) ;
41
+ }
42
+ catch ( SystemException )
43
+ {
44
+ try
45
+ {
46
+ context . OnShutdown -= this . StopSpinTask ;
47
+ }
48
+ finally
49
+ {
50
+ this . Executor . Dispose ( ) ;
51
+ }
52
+ throw ;
53
+ }
54
+ }
55
+
56
+ /// <inheritdoc/>
57
+ public bool IsDisposed
58
+ {
59
+ get => this . Executor . IsDisposed ;
60
+ }
61
+
62
+ /// <inheritdoc/>
63
+ public int Count
64
+ {
65
+ get => this . Executor . Count ;
66
+ }
67
+
68
+ /// <inheritdoc/>
69
+ public bool IsReadOnly
70
+ {
71
+ get => this . Executor . IsReadOnly ;
72
+ }
73
+
74
+ /// <inheritdoc/>
75
+ public void Add ( INode node )
76
+ {
77
+ this . Executor . Add ( node ) ;
78
+ }
79
+
80
+ /// <inheritdoc/>
81
+ public void Clear ( )
82
+ {
83
+ this . Executor . Clear ( ) ;
84
+ }
85
+
86
+ /// <inheritdoc/>
87
+ public bool Contains ( INode node )
88
+ {
89
+ return this . Executor . Contains ( node ) ;
90
+ }
91
+
92
+ /// <inheritdoc/>
93
+ public void CopyTo ( INode [ ] array , int arrayIndex )
94
+ {
95
+ this . Executor . CopyTo ( array , arrayIndex ) ;
96
+ }
97
+
98
+ /// <inheritdoc/>
99
+ public bool Remove ( INode node )
100
+ {
101
+ return this . Executor . Remove ( node ) ;
102
+ }
103
+
104
+ /// <inheritdoc/>
105
+ public IEnumerator < INode > GetEnumerator ( )
106
+ {
107
+ return this . Executor . GetEnumerator ( ) ;
108
+ }
109
+
110
+ /// <inheritdoc/>
111
+ IEnumerator IEnumerable . GetEnumerator ( )
112
+ {
113
+ return this . GetEnumerator ( ) ;
114
+ }
115
+
116
+ /// <inheritdoc />
117
+ public void ScheduleRescan ( )
118
+ {
119
+ this . Executor . ScheduleRescan ( ) ;
120
+ }
121
+
122
+ /// <inheritdoc />
123
+ public bool TryScheduleRescan ( INode node )
124
+ {
125
+ return this . Executor . TryScheduleRescan ( node ) ;
126
+ }
127
+
128
+ /// <inheritdoc />
129
+ public void Wait ( )
130
+ {
131
+ this . Executor . Wait ( ) ;
132
+ }
133
+
134
+ /// <inheritdoc />
135
+ public bool TryWait ( TimeSpan timeout )
136
+ {
137
+ return this . Executor . TryWait ( timeout ) ;
138
+ }
139
+
140
+ /// <summary>
141
+ /// Stop the spin task and return after it has stopped.
142
+ /// </summary>
143
+ /// <remarks>
144
+ /// This function returns immediately if the spin task
145
+ /// has already been stopped.
146
+ /// </remarks>
147
+ private void StopSpinTask ( )
148
+ {
149
+ try
150
+ {
151
+ this . CancellationSource . Cancel ( ) ;
152
+ }
153
+ catch ( ObjectDisposedException )
154
+ {
155
+ // task has been canceled before
156
+ }
157
+ try
158
+ {
159
+ this . Task . Wait ( ) ;
160
+ }
161
+ catch ( AggregateException e )
162
+ {
163
+ e . Handle ( inner => inner is TaskCanceledException ) ;
164
+ }
165
+ catch ( ObjectDisposedException )
166
+ {
167
+ // task has already stopped
168
+ }
169
+ }
170
+
171
+ /// <inheritdoc />
172
+ /// <remarks>
173
+ /// The wrapper handles stopping the spin task.
174
+ /// </remarks>
175
+ public void Dispose ( )
176
+ {
177
+ try
178
+ {
179
+ this . StopSpinTask ( ) ;
180
+ }
181
+ catch ( AggregateException )
182
+ {
183
+ // prevent faulted task from preventing disposal
184
+ }
185
+ this . Context . OnShutdown -= this . StopSpinTask ;
186
+ this . Task . Dispose ( ) ;
187
+ this . Executor . Dispose ( ) ;
188
+ this . CancellationSource . Dispose ( ) ;
189
+ }
190
+ }
191
+ }
0 commit comments