1 // Copyright 2018 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #pragma once
8 
9 #include <assert.h>
10 
11 // The state of a thread has two axes. One is its arc from birth to
12 // death, expressed via its Lifecycle value. The other its current
13 // exception handler state, expressed via its Exception value.
14 
15 // This class exists to encapsulate all legal state transitions, so
16 // generally other assertions about the state of a thread prior to
17 // transitioning are not necessary.
18 
19 // Only RUNNING, SUSPENDED, or DYING threads may participate in
20 // exception handling.
21 
22 class ThreadState {
23 public:
24     // The only legal transition that isn't from top-to-bottom occurs
25     // when a thread is resumed after being suspended.
26     enum class Lifecycle {
27         // The ThreadDispatcher has been allocated, but not yet
28         // associated to a thread_t or an aspace.
29         INITIAL,
30 
31         // The ThreadDispatcher is now associated to its underlying
32         // thread_t and the containing process's address space, and is
33         // waiting to be run.
34         INITIALIZED,
35 
36         // The thread is running.
37         RUNNING,
38 
39         // The thread is currently suspended.
40         // Note that suspension is orthogonal to being "in an exception".
41         // A thread may be both suspended and in an exception, and the thread
42         // does not "resume" execution until it is resumed from both the
43         // suspension and the exception.
44         SUSPENDED,
45 
46         // The thread is going to die. It may still be interacting
47         // with exception handling state.
48         DYING,
49 
50         // The thread is being dissociated from all of its state, and
51         // no more interaction with userspace (including exception
52         // handlers) is possible.
53         DEAD,
54     };
55 
56     // IDLE threads become UNPROCESSED. UNPROCESSED threads are told
57     // to either RESUME or TRY_NEXT in a loop until they are killed
58     // (no more exception handlers) or resumed, in which case they
59     // become IDLE again.
60     enum class Exception {
61         // There's no pending exception.
62         IDLE,
63 
64         // The thread is waiting for the pending exception to be
65         // processed.
66         UNPROCESSED,
67 
68         // The exception has been processed, and the next exception
69         // handler should be queried.
70         TRY_NEXT,
71 
72         // The exception has been processed, and the thread should
73         // resume.
74         RESUME,
75     };
76 
lifecycle()77     Lifecycle lifecycle() const {
78         switch (value_) {
79         case Value::INITIAL_IDLE:
80             return Lifecycle::INITIAL;
81         case Value::INITIALIZED_IDLE:
82             return Lifecycle::INITIALIZED;
83         case Value::RUNNING_IDLE:
84         case Value::RUNNING_UNPROCESSED:
85         case Value::RUNNING_TRY_NEXT:
86         case Value::RUNNING_RESUME:
87             return Lifecycle::RUNNING;
88         case Value::SUSPENDED_IDLE:
89         case Value::SUSPENDED_UNPROCESSED:
90         case Value::SUSPENDED_TRY_NEXT:
91         case Value::SUSPENDED_RESUME:
92             return Lifecycle::SUSPENDED;
93         case Value::DYING_IDLE:
94         case Value::DYING_UNPROCESSED:
95         case Value::DYING_TRY_NEXT:
96         case Value::DYING_RESUME:
97             return Lifecycle::DYING;
98         case Value::DEAD_IDLE:
99             return Lifecycle::DEAD;
100         default:
101             ASSERT(false);
102         }
103     }
104 
105     // Only RUNNING, SUSPENDED, and DYING threads have meaningful
106     // exception state.
exception()107     Exception exception() const {
108         switch (value_) {
109         case Value::RUNNING_IDLE:
110         case Value::SUSPENDED_IDLE:
111         case Value::DYING_IDLE:
112             return Exception::IDLE;
113         case Value::RUNNING_UNPROCESSED:
114         case Value::SUSPENDED_UNPROCESSED:
115         case Value::DYING_UNPROCESSED:
116             return Exception::UNPROCESSED;
117         case Value::RUNNING_TRY_NEXT:
118         case Value::SUSPENDED_TRY_NEXT:
119         case Value::DYING_TRY_NEXT:
120             return Exception::TRY_NEXT;
121         case Value::RUNNING_RESUME:
122         case Value::SUSPENDED_RESUME:
123         case Value::DYING_RESUME:
124             return Exception::RESUME;
125         case Value::INITIALIZED_IDLE:
126         case Value::DEAD_IDLE:
127             // Someone could have, for example, requested zx_info_thread_t.
128             return Exception::IDLE;
129         default:
130             ASSERT(false);
131         }
132     }
133 
set(Lifecycle lifecycle)134     void set(Lifecycle lifecycle) {
135         switch (lifecycle) {
136         case Lifecycle::INITIAL:
137             DEBUG_ASSERT(false);
138             return;
139         case Lifecycle::INITIALIZED:
140             switch (value_) {
141             case Value::INITIAL_IDLE:
142                 value_ = Value::INITIALIZED_IDLE;
143                 return;
144             default:
145                 DEBUG_ASSERT(false);
146                 return;
147             }
148         case Lifecycle::RUNNING:
149             switch (value_) {
150             case Value::INITIALIZED_IDLE:
151             case Value::SUSPENDED_IDLE:
152                 value_ = Value::RUNNING_IDLE;
153                 return;
154             case Value::SUSPENDED_UNPROCESSED:
155                 value_ = Value::RUNNING_UNPROCESSED;
156                 return;
157             case Value::SUSPENDED_TRY_NEXT:
158                 value_ = Value::RUNNING_TRY_NEXT;
159                 return;
160             case Value::SUSPENDED_RESUME:
161                 value_ = Value::RUNNING_RESUME;
162                 return;
163             default:
164                 DEBUG_ASSERT(false);
165                 return;
166             }
167         case Lifecycle::SUSPENDED:
168             switch (value_) {
169             case Value::RUNNING_IDLE:
170                 value_ = Value::SUSPENDED_IDLE;
171                 return;
172             case Value::RUNNING_UNPROCESSED:
173                 value_ = Value::SUSPENDED_UNPROCESSED;
174                 return;
175             case Value::RUNNING_TRY_NEXT:
176                 value_ = Value::SUSPENDED_TRY_NEXT;
177                 return;
178             case Value::RUNNING_RESUME:
179                 value_ = Value::SUSPENDED_RESUME;
180                 return;
181             default:
182                 DEBUG_ASSERT(false);
183                 return;
184             }
185         case Lifecycle::DYING:
186             switch (value_) {
187             case Value::RUNNING_IDLE:
188             case Value::SUSPENDED_IDLE:
189             case Value::DYING_IDLE:
190                 value_ = Value::DYING_IDLE;
191                 return;
192             case Value::RUNNING_UNPROCESSED:
193             case Value::SUSPENDED_UNPROCESSED:
194             case Value::DYING_UNPROCESSED:
195                 value_ = Value::DYING_UNPROCESSED;
196                 return;
197             case Value::RUNNING_TRY_NEXT:
198             case Value::SUSPENDED_TRY_NEXT:
199             case Value::DYING_TRY_NEXT:
200                 value_ = Value::DYING_TRY_NEXT;
201                 return;
202             case Value::RUNNING_RESUME:
203             case Value::SUSPENDED_RESUME:
204             case Value::DYING_RESUME:
205                 value_ = Value::DYING_RESUME;
206                 return;
207             default:
208                 DEBUG_ASSERT(false);
209                 return;
210             }
211         case Lifecycle::DEAD:
212             switch (value_) {
213             case Value::DYING_IDLE:
214             case Value::DYING_UNPROCESSED:
215             case Value::DYING_TRY_NEXT:
216             case Value::DYING_RESUME:
217                 value_ = Value::DEAD_IDLE;
218                 return;
219             default:
220                 DEBUG_ASSERT(false);
221                 return;
222             }
223         }
224     }
225 
set(Exception exception)226     void set(Exception exception) {
227         switch (exception) {
228         case Exception::IDLE:
229             switch (value_) {
230             case Value::RUNNING_UNPROCESSED:
231             case Value::RUNNING_TRY_NEXT:
232             case Value::RUNNING_RESUME:
233                 value_ = Value::RUNNING_IDLE;
234                 return;
235             case Value::SUSPENDED_UNPROCESSED:
236             case Value::SUSPENDED_TRY_NEXT:
237             case Value::SUSPENDED_RESUME:
238                 value_ = Value::SUSPENDED_IDLE;
239                 return;
240             case Value::DYING_UNPROCESSED:
241             case Value::DYING_TRY_NEXT:
242             case Value::DYING_RESUME:
243                 value_ = Value::DYING_IDLE;
244                 return;
245             default:
246                 DEBUG_ASSERT(false);
247                 return;
248             }
249         case Exception::UNPROCESSED:
250             switch (value_) {
251             case Value::RUNNING_IDLE:
252                 value_ = Value::RUNNING_UNPROCESSED;
253                 return;
254             case Value::SUSPENDED_IDLE:
255                 value_ = Value::SUSPENDED_UNPROCESSED;
256                 return;
257             case Value::DYING_IDLE:
258                 value_ = Value::DYING_UNPROCESSED;
259                 return;
260             default:
261                 DEBUG_ASSERT(false);
262                 return;
263             }
264         case Exception::TRY_NEXT:
265             switch (value_) {
266             case Value::RUNNING_UNPROCESSED:
267                 value_ = Value::RUNNING_TRY_NEXT;
268                 return;
269             case Value::SUSPENDED_UNPROCESSED:
270                 value_ = Value::SUSPENDED_TRY_NEXT;
271                 return;
272             case Value::DYING_UNPROCESSED:
273                 value_ = Value::DYING_TRY_NEXT;
274                 return;
275             default:
276                 DEBUG_ASSERT(false);
277                 return;
278             }
279         case Exception::RESUME:
280             switch (value_) {
281             case Value::RUNNING_UNPROCESSED:
282                 value_ = Value::RUNNING_RESUME;
283                 return;
284             case Value::SUSPENDED_UNPROCESSED:
285                 value_ = Value::SUSPENDED_RESUME;
286                 return;
287             case Value::DYING_UNPROCESSED:
288                 value_ = Value::DYING_RESUME;
289                 return;
290             default:
291                 DEBUG_ASSERT(false);
292                 return;
293             }
294         }
295     }
296 
297 private:
298     enum class Value {
299         INITIAL_IDLE,
300 
301         INITIALIZED_IDLE,
302 
303         RUNNING_IDLE,
304         RUNNING_UNPROCESSED,
305         RUNNING_TRY_NEXT,
306         RUNNING_RESUME,
307 
308         SUSPENDED_IDLE,
309         SUSPENDED_UNPROCESSED,
310         SUSPENDED_TRY_NEXT,
311         SUSPENDED_RESUME,
312 
313         DYING_IDLE,
314         DYING_UNPROCESSED,
315         DYING_TRY_NEXT,
316         DYING_RESUME,
317 
318         DEAD_IDLE,
319     };
320 
321     Value value_ = Value::INITIAL_IDLE;
322 };
323 
324 const char* ThreadLifecycleToString(ThreadState::Lifecycle lifecycle);
325