| // Copyright 2018 The Fuchsia Authors | |
| // | |
| // Use of this source code is governed by a MIT-style | |
| // license that can be found in the LICENSE file or at | |
| // https://opensource.org/licenses/MIT | |
| #ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_THREAD_STATE_H_ | |
| #define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_THREAD_STATE_H_ | |
| #include <assert.h> | |
| // The state of a thread has two axes. One is its arc from birth to | |
| // death, expressed via its Lifecycle value. The other its current | |
| // exception handler state, expressed via its Exception value. | |
| // This class exists to encapsulate all legal state transitions, so | |
| // generally other assertions about the state of a thread prior to | |
| // transitioning are not necessary. | |
| // Only RUNNING, SUSPENDED, or DYING threads may participate in | |
| // exception handling. | |
| class ThreadState { | |
| public: | |
| // The only legal transition that isn't from top-to-bottom occurs | |
| // when a thread is resumed after being suspended. | |
| enum class Lifecycle { | |
| // The ThreadDispatcher has been allocated, but not yet | |
| // associated to a Thread or an aspace. | |
| INITIAL, | |
| // The ThreadDispatcher is now associated to its underlying | |
| // Thread and the containing process's address space, and is | |
| // waiting to be run. | |
| INITIALIZED, | |
| // The thread is running. | |
| RUNNING, | |
| // The thread is currently suspended. | |
| // Note that suspension is orthogonal to being "in an exception". | |
| // A thread may be both suspended and in an exception, and the thread | |
| // does not "resume" execution until it is resumed from both the | |
| // suspension and the exception. | |
| SUSPENDED, | |
| // The thread is going to die. It may still be interacting | |
| // with exception handling state. | |
| DYING, | |
| // The thread is being dissociated from all of its state, and | |
| // no more interaction with userspace (including exception | |
| // handlers) is possible. | |
| DEAD, | |
| }; | |
| // IDLE threads become UNPROCESSED. UNPROCESSED threads are told | |
| // to either RESUME or TRY_NEXT in a loop until they are killed | |
| // (no more exception handlers) or resumed, in which case they | |
| // become IDLE again. | |
| enum class Exception { | |
| // There's no pending exception. | |
| IDLE, | |
| // The thread is waiting for the pending exception to be | |
| // processed. | |
| UNPROCESSED, | |
| // The exception has been processed, and the next exception | |
| // handler should be queried. | |
| TRY_NEXT, | |
| // The exception has been processed, and the thread should | |
| // resume. | |
| RESUME, | |
| }; | |
| Lifecycle lifecycle() const { | |
| switch (value_) { | |
| case Value::INITIAL_IDLE: | |
| return Lifecycle::INITIAL; | |
| case Value::INITIALIZED_IDLE: | |
| return Lifecycle::INITIALIZED; | |
| case Value::RUNNING_IDLE: | |
| case Value::RUNNING_UNPROCESSED: | |
| case Value::RUNNING_TRY_NEXT: | |
| case Value::RUNNING_RESUME: | |
| return Lifecycle::RUNNING; | |
| case Value::SUSPENDED_IDLE: | |
| case Value::SUSPENDED_UNPROCESSED: | |
| case Value::SUSPENDED_TRY_NEXT: | |
| case Value::SUSPENDED_RESUME: | |
| return Lifecycle::SUSPENDED; | |
| case Value::DYING_IDLE: | |
| case Value::DYING_UNPROCESSED: | |
| case Value::DYING_TRY_NEXT: | |
| case Value::DYING_RESUME: | |
| return Lifecycle::DYING; | |
| case Value::DEAD_IDLE: | |
| return Lifecycle::DEAD; | |
| default: | |
| ASSERT(false); | |
| } | |
| } | |
| // Only RUNNING, SUSPENDED, and DYING threads have meaningful | |
| // exception state. | |
| Exception exception() const { | |
| switch (value_) { | |
| case Value::RUNNING_IDLE: | |
| case Value::SUSPENDED_IDLE: | |
| case Value::DYING_IDLE: | |
| return Exception::IDLE; | |
| case Value::RUNNING_UNPROCESSED: | |
| case Value::SUSPENDED_UNPROCESSED: | |
| case Value::DYING_UNPROCESSED: | |
| return Exception::UNPROCESSED; | |
| case Value::RUNNING_TRY_NEXT: | |
| case Value::SUSPENDED_TRY_NEXT: | |
| case Value::DYING_TRY_NEXT: | |
| return Exception::TRY_NEXT; | |
| case Value::RUNNING_RESUME: | |
| case Value::SUSPENDED_RESUME: | |
| case Value::DYING_RESUME: | |
| return Exception::RESUME; | |
| case Value::INITIALIZED_IDLE: | |
| case Value::DEAD_IDLE: | |
| // Someone could have, for example, requested zx_info_thread_t. | |
| return Exception::IDLE; | |
| default: | |
| ASSERT(false); | |
| } | |
| } | |
| void set(Lifecycle lifecycle) { | |
| switch (lifecycle) { | |
| case Lifecycle::INITIAL: | |
| DEBUG_ASSERT(false); | |
| return; | |
| case Lifecycle::INITIALIZED: | |
| switch (value_) { | |
| case Value::INITIAL_IDLE: | |
| value_ = Value::INITIALIZED_IDLE; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| case Lifecycle::RUNNING: | |
| switch (value_) { | |
| case Value::INITIALIZED_IDLE: | |
| case Value::SUSPENDED_IDLE: | |
| value_ = Value::RUNNING_IDLE; | |
| return; | |
| case Value::SUSPENDED_UNPROCESSED: | |
| value_ = Value::RUNNING_UNPROCESSED; | |
| return; | |
| case Value::SUSPENDED_TRY_NEXT: | |
| value_ = Value::RUNNING_TRY_NEXT; | |
| return; | |
| case Value::SUSPENDED_RESUME: | |
| value_ = Value::RUNNING_RESUME; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| case Lifecycle::SUSPENDED: | |
| switch (value_) { | |
| case Value::RUNNING_IDLE: | |
| value_ = Value::SUSPENDED_IDLE; | |
| return; | |
| case Value::RUNNING_UNPROCESSED: | |
| value_ = Value::SUSPENDED_UNPROCESSED; | |
| return; | |
| case Value::RUNNING_TRY_NEXT: | |
| value_ = Value::SUSPENDED_TRY_NEXT; | |
| return; | |
| case Value::RUNNING_RESUME: | |
| value_ = Value::SUSPENDED_RESUME; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| case Lifecycle::DYING: | |
| switch (value_) { | |
| case Value::RUNNING_IDLE: | |
| case Value::SUSPENDED_IDLE: | |
| case Value::DYING_IDLE: | |
| value_ = Value::DYING_IDLE; | |
| return; | |
| case Value::RUNNING_UNPROCESSED: | |
| case Value::SUSPENDED_UNPROCESSED: | |
| case Value::DYING_UNPROCESSED: | |
| value_ = Value::DYING_UNPROCESSED; | |
| return; | |
| case Value::RUNNING_TRY_NEXT: | |
| case Value::SUSPENDED_TRY_NEXT: | |
| case Value::DYING_TRY_NEXT: | |
| value_ = Value::DYING_TRY_NEXT; | |
| return; | |
| case Value::RUNNING_RESUME: | |
| case Value::SUSPENDED_RESUME: | |
| case Value::DYING_RESUME: | |
| value_ = Value::DYING_RESUME; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| case Lifecycle::DEAD: | |
| switch (value_) { | |
| case Value::DYING_IDLE: | |
| case Value::DYING_UNPROCESSED: | |
| case Value::DYING_TRY_NEXT: | |
| case Value::DYING_RESUME: | |
| value_ = Value::DEAD_IDLE; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| } | |
| } | |
| void set(Exception exception) { | |
| switch (exception) { | |
| case Exception::IDLE: | |
| switch (value_) { | |
| case Value::RUNNING_UNPROCESSED: | |
| case Value::RUNNING_TRY_NEXT: | |
| case Value::RUNNING_RESUME: | |
| value_ = Value::RUNNING_IDLE; | |
| return; | |
| case Value::SUSPENDED_UNPROCESSED: | |
| case Value::SUSPENDED_TRY_NEXT: | |
| case Value::SUSPENDED_RESUME: | |
| value_ = Value::SUSPENDED_IDLE; | |
| return; | |
| case Value::DYING_UNPROCESSED: | |
| case Value::DYING_TRY_NEXT: | |
| case Value::DYING_RESUME: | |
| value_ = Value::DYING_IDLE; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| case Exception::UNPROCESSED: | |
| switch (value_) { | |
| case Value::RUNNING_IDLE: | |
| value_ = Value::RUNNING_UNPROCESSED; | |
| return; | |
| case Value::SUSPENDED_IDLE: | |
| value_ = Value::SUSPENDED_UNPROCESSED; | |
| return; | |
| case Value::DYING_IDLE: | |
| value_ = Value::DYING_UNPROCESSED; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| case Exception::TRY_NEXT: | |
| switch (value_) { | |
| case Value::RUNNING_UNPROCESSED: | |
| value_ = Value::RUNNING_TRY_NEXT; | |
| return; | |
| case Value::SUSPENDED_UNPROCESSED: | |
| value_ = Value::SUSPENDED_TRY_NEXT; | |
| return; | |
| case Value::DYING_UNPROCESSED: | |
| value_ = Value::DYING_TRY_NEXT; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| case Exception::RESUME: | |
| switch (value_) { | |
| case Value::RUNNING_UNPROCESSED: | |
| value_ = Value::RUNNING_RESUME; | |
| return; | |
| case Value::SUSPENDED_UNPROCESSED: | |
| value_ = Value::SUSPENDED_RESUME; | |
| return; | |
| case Value::DYING_UNPROCESSED: | |
| value_ = Value::DYING_RESUME; | |
| return; | |
| default: | |
| DEBUG_ASSERT(false); | |
| return; | |
| } | |
| } | |
| } | |
| private: | |
| enum class Value { | |
| INITIAL_IDLE, | |
| INITIALIZED_IDLE, | |
| RUNNING_IDLE, | |
| RUNNING_UNPROCESSED, | |
| RUNNING_TRY_NEXT, | |
| RUNNING_RESUME, | |
| SUSPENDED_IDLE, | |
| SUSPENDED_UNPROCESSED, | |
| SUSPENDED_TRY_NEXT, | |
| SUSPENDED_RESUME, | |
| DYING_IDLE, | |
| DYING_UNPROCESSED, | |
| DYING_TRY_NEXT, | |
| DYING_RESUME, | |
| DEAD_IDLE, | |
| }; | |
| Value value_ = Value::INITIAL_IDLE; | |
| }; | |
| const char* ThreadLifecycleToString(ThreadState::Lifecycle lifecycle); | |
| #endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_THREAD_STATE_H_ |