text: #ifndef KERN_SCHED_THREAD_H
  #define KERN_SCHED_THREAD_H
  
  #include <stddef.h>
  #include <stdint.h>
  
  #include <runtime/List.h>
  #include <runtime/Queue.h>
  #include <runtime/Vector.h>
  #include <runtime/SmartPointers.h>
  #include <handle/Manager.h>
  
  #include <arch/ThreadState.h>
  #include <arch/rwlock.h>
  
  #include "SchedulerData.h"
  
  namespace ipc {
  class IrqHandler;
  }
  
  namespace sched {
  class Blockable;
  class SignalFlag;
  struct Task;
  struct BlockWait;
  
  /**
   * Threads are the smallest units of execution in the kernel. They are the unit of work that the
   * scheduler concerns itself with.
   *
   * Each thread can either be ready to run, blocked, or paused. When the scheduler decides to run
   * the thread, its saved CPU state is loaded and the task executed. When the task returns to the
   * kernel (either through a syscall that blocks/context switches, or via its time quantum expiring)
   * its state is saved again.
   *
   * Depending on the nature of the thread's return to the kernel, it will be added back to the run
   * queue if it's ready to run again and not blocked. (This implies threads cannot change from
   * runnable to blocked if they're not currently executing.)
   */
  struct Thread: public rt::SharedFromThis<Thread> {
      friend class Scheduler;
      friend class Blockable;
      friend class IdleWorker;
      friend struct BlockWait;
  
      /// Length of thread names
      constexpr static const size_t kNameLength = 32;
  
      private:
          /// why a block returned
          enum class BlockState {
              None                        = 0,
              /// Currently blocking
              Blocking                    = 1,
              /// The blocking condition(s) were signalled
              Unblocked                   = 2,
              /// The block was timed, and the timeout has expired
              Timeout                     = 3,
              /// One of the blockables aborted the attempt to go to sleep
              Aborted                     = 4,
          };
  
      public:
          enum class State {
              /// Thread can become runnable at any time, but only via an explicit API call
              Paused                      = 0,
              /// Thread requests to be scheduled as soon as possible
              Runnable                    = 1,
              /// Thread is waiting on some event to occur
              Blocked                     = 2,
              /// Blocked on sleep (may end early)
              Sleeping                    = 3,
              /// Waiting for notification
              NotifyWait                  = 4,
              /// About to be destroyed; do not schedule or access.
              Zombie                      = 5,
          };
  
          /// return codes for blockOn
          enum class BlockOnReturn {
              /// Unknown error
              Error                       = -1,
              /// thread unblocked
              Unblocked                   = 0,
              /// block timed out
              Timeout                     = 1,
              /// block aborted
              Aborted                     = 2,
          };
  
          /**
           * Defines the types of faults that a thread may receive. These are synchronous-ish events
           * generated by the processor, indicating events such as an illegal opcode, or invalid math
           * operation.
           */
          enum class FaultType {
              /// General fault; this always terminates the task this thread is in
              General                     = 0,
              /// The instruction executed is invalid; context is pointer to the pc in exc frame
              InvalidInstruction          = 1,
              /// A page fault was unhandled (pc = faulting address; context = pc exception frame)
              UnhandledPagefault          = 2,
              /// Protection violation
              ProtectionViolation         = 3,
          };
  
      public:
          /// Global thread id
          uintptr_t tid = 0;
          /// task that owns us
          rt::SharedPtr<Task> task = nullptr;
          /// when set, we're attached to the given task
          bool attachedToTask = false;
  
          /// handle to the thread
          Handle handle;
  
          /// descriptive thread name, if desired
          char name[kNameLength] = {0};
  
          /// current thread state
          State state = State::Paused;
          /**
           * Threads marked as kernel mode are treated a bit specially by the scheduler, in that
           * only kernel threads may be placed in the highest priority run queues.
           */
          bool kernelMode = false;
  
          /**
           * Flag set when the scheduler has assigned the thread to a processor and it is executing;
           * it will be cleared immediately after the thread is switched out.
           *
           * This flag is the responsibility of the arch context switching code.
           */
          bool isActive = false;
          /// when set, this thread should kill itself when switched out
          bool needsToDie = false;
          /// Timestamp at which the thread was switched to
          uint64_t lastSwitchedTo = 0;
  
          /// number of the last syscall this thread performed
          uintptr_t lastSyscall = UINTPTR_MAX;
  
          /// scheduler data
          SchedulerThreadData sched;
  
          /**
           * Priority of the thread; this should be a value between -100 and 100, with negative
           * values having the lowest priority. The scheduler internally converts this to whatever
           * priority system it uses.
           */
          int16_t priority = 0;
  
          /**
           * Notification value and mask for the thread.
           *
           * Notifications are an asynchronous signalling mechanism that can be used to signal a
           * thread that a paritcular event occurred, without any additional auxiliary information;
           * this makes it ideal for things like interrupt handlers.
           *
           * Each thread defines a notification mask, which indicates on which bits (set) of the
           * notification set the thread is interested in; when the notification mask is updated, it
           * is compared against the mask, and if any bits are set, the thread can be unblocked.
           */
          uintptr_t notifications = 0;
          uintptr_t notificationMask = 0;
  
          /// Set once a thread has moved from NotifyWait to possibly runnable
          bool notified = false;
  
          /**
           * This counter is incremented any time the thread leaves the runnable state; it is used to
           * detect when a deadline (or other unblock event) takes place yet the subject thread has
           * changed state since then.
           *
           * In other words, when a deadline (or other object) fires, it should ensure the epoch
           * value is the same as what it was when it began blocking.
           */
          uintptr_t epoch = 0;
  
          /**
           * Objects this thread is currently blocking on
           */
          rt::List<rt::SharedPtr<Blockable>> blockingOn;
  
          /// Interrupt handlers owned by the thread; they're removed when we deallocate
          rt::List<rt::SharedPtr<ipc::IrqHandler>> irqHandlers;
  
          /**
           * The thread can be accessed read-only by multiple processes, but the scheduler will
           * always require write access, in order to be able to save processor state later.
           */
          DECLARE_RWLOCK(lock);
  
          /// notification flags to signal when terminating
          rt::List<rt::SharedPtr<SignalFlag>> terminateSignals;
  
          /// size of the thread's kernel stack (in bytes)
          size_t stackSize = 0;
          /// bottom of the kernel stack of this thread
          void *stack = nullptr;
  
          /// architecture-specific thread state
          arch::ThreadState regs __attribute__((aligned(16)));
  
      private:
          /// whether we're blocking and why the block finished
          BlockState blockState = BlockState::None;
  
      public:
          /// Allocates a new kernel thread
          static rt::SharedPtr<Thread> kernelThread(const rt::SharedPtr<Task> &task,
                  void (*entry)(uintptr_t), const uintptr_t param = 0);
          /// Allocates a new userspace thread
          static rt::SharedPtr<Thread> userThread(const rt::SharedPtr<Task> &task,
                  void (*entry)(uintptr_t), const uintptr_t param = 0);
  
          Thread(const rt::SharedPtr<Task> &task, const uintptr_t pc, const uintptr_t param,
                  const bool kernelThread = false);
          ~Thread();
  
          /// Context switches to this thread.
          void switchTo();
          /// Returns to user mode, with the specified program counter and stack.
          void returnToUser(const uintptr_t pc, const uintptr_t stack, const uintptr_t arg = 0) __attribute__((noreturn));
  
          /// Updates the notification mask; set bits are unmasked (i.e. will occur)
          inline void setNotificationMask(uintptr_t newMask) {
              __atomic_store(&this->notificationMask, &newMask, __ATOMIC_RELEASE);
          }
  
          /// Return thread handle
          constexpr auto getHandle() const {
              return this->handle;
          }
  
          /// Sets the thread's name.
          void setName(const char *name, const size_t length = 0);
          /// Sets the thread's scheduling priority
          inline void setPriority(int16_t priority) {
              __atomic_store(&this->priority, &priority, __ATOMIC_RELEASE);
          }
          /// Sets the thread's state.
          void setState(State newState) {
              if(this->state == State::Blocked && newState == State::Runnable) {
                  REQUIRE(this->blockingOn.empty(), "cannot be runnable while blocking");
              }
  
              __atomic_store(&this->state, &newState, __ATOMIC_RELEASE);
          }
          /// Atomically reads the current state.
          const State getState() const {
              State s;
              __atomic_load(&this->state, &s, __ATOMIC_RELAXED);
              return s;
          }
  
          /// Blocks the thread on the given object.
          BlockOnReturn blockOn(const rt::SharedPtr<Blockable> &b, const uint64_t until = 0);
          /// Unblocks the thread due to the given blockable
          void unblock(const rt::SharedPtr<Blockable> &b);
  
          /// Sets the given notification bits.
          void notify(const uintptr_t bits);
          /// Blocks the thread waiting to receive notifications, optionally with a mask.
          uintptr_t blockNotify(const uintptr_t mask = 0, const uintptr_t timeout = UINTPTR_MAX);
  
          /// Waits for the thread to be terminated.
          int waitOn(const uint64_t waitUntil = 0);
          /// Terminates this thread.
          void terminate(const bool release = true, const bool inIsr = false);
  
          /// Handles a fault of the given type. Called from fault handlers.
          void handleFault(const FaultType type, const uintptr_t pc, void *context, const void *arch);
  
          /// Returns a handle to the currently executing thread.
          static rt::SharedPtr<Thread> current();
          /// Blocks the current thread for the given number of nanoseconds.
          static void sleep(const uint64_t nanos);
          /// Give up the rest of this thread's CPU time
          static void yield();
          /// Terminates the calling thread
          static void die() __attribute__((noreturn));
  
      private:
          /// next thread id
          static uintptr_t nextTid;
  
          /// whether creation/deallocation of threads is logged
          static bool gLogLifecycle;
  
      private:
          /// Called when this thread is switching out
          void switchFrom();
  
          /// Called on context switch out to complete termination.
          void deferredTerminate();
          /// Invoke all termination handlers
          void callTerminators();
  
          /// scheduler is attempting to determine if thread should be runnable again
          void schedTestUnblock();
          /// any pending blocks should be cancelled
          void blockExpired();
          /// set thread state with optionally idsabling validation
          void setState(State newState, const bool validate) {
              if(validate) {
                  return this->setState(newState);
              }
  
              __atomic_store(&this->state, &newState, __ATOMIC_RELEASE);
          }
  };
  }
  
  #endif
  