blob: ff0db2224008469971de639dfd55b6e78ba71a32 [file] [log] [blame]
// 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_