145 lines
6.5 KiB
C
145 lines
6.5 KiB
C
#ifndef FROSTBYTE_SCHED_H
|
|
#define FROSTBYTE_SCHED_H
|
|
|
|
#include "types.h"
|
|
#include "vfs.h"
|
|
#include "prioq.h"
|
|
#include "isr.h"
|
|
#include "signalnum.h"
|
|
#include "mmap.h"
|
|
|
|
/* Default scheduling weight (xv6/CFS-style "nice" niceWeight -- higher runs
|
|
more often, not longer). threadSetPriority scales relative to this. */
|
|
#define schedDefaultWeight 1024u
|
|
|
|
/* Returned by threadCreate/threadCreateUser when the thread table is full
|
|
(see threadCapacity) -- never a valid thread id, since id 0 is always the
|
|
permanent idle thread. Callers must check for this before using the
|
|
result to index into anything thread-related. */
|
|
#define threadInvalidId 0xFFFFFFFFu
|
|
|
|
/* Capacity of Thread.name, including the terminator. Large enough for every
|
|
name this kernel currently produces (kernel thread literals, "/bin/..."
|
|
paths passed to threadCreateUser by spawnUser/doSpawn) with room to
|
|
spare; longer names are truncated, never overflowed. */
|
|
#define threadNameMax 32
|
|
|
|
/* Capacity of Thread.env below: envMaxVars is how many "NAME=VALUE" slots
|
|
a process may hold at once, envEntryMax is the max size in bytes of a
|
|
single slot INCLUDING both the '=' separator and the nul terminator
|
|
(so "NAME=VALUE\0" must fit in envEntryMax bytes). Generous enough for
|
|
anything this kernel's own shell/demos actually set (see user/envprint.c)
|
|
with room to spare, the same "small fixed table, not a growable heap
|
|
allocation" choice Thread.cwd already made for a single path. */
|
|
#define envMaxVars 8
|
|
#define envEntryMax 64
|
|
|
|
/* One bit per signal number in Thread.sigPending/sigBlocked, and the size
|
|
of Thread.sigHandlers[] below -- see signal.h for the actual signal
|
|
subsystem this supports. Defined here, not signal.h, purely to break the
|
|
include cycle (signal.h includes sched.h for the Thread type). */
|
|
#define sigMaxSignals 32
|
|
|
|
/* Depth of the per-thread nested-handler context stack (Thread.sigSavedRegs/
|
|
sigSavedBlocked below) -- how many signal handlers may genuinely interrupt
|
|
each other (handler A running, a second signal arrives and is not blocked
|
|
by A's own sa_mask, so handler B runs nested inside A, then A resumes once
|
|
B returns) before sigConsumePending (signal.c) refuses to deliver a new
|
|
one and simply leaves it pending. A real kernel bounds this too (rlimit
|
|
on the signal-frame stack it pushes into user memory); this one has no
|
|
user-memory signal frame to overflow at all (see signal.h's file comment
|
|
-- the saved context lives in the Thread struct itself), so the bound
|
|
exists purely to keep that struct's size fixed and to fail a runaway
|
|
signal-storm safely (further deliveries just wait) instead of unboundedly
|
|
growing kernel memory per thread. 4 is generous headroom for anything
|
|
this kernel's own handlers actually do (none nest more than one deep
|
|
today -- see user/nesttarget.c) without being large enough to make
|
|
Thread noticeably bigger than sigMaxSignals-worth of handlers already
|
|
makes it. */
|
|
#define sigMaxNestDepth 4
|
|
|
|
typedef enum {
|
|
threadUnused = 0,
|
|
threadRunnable = 1,
|
|
threadRunning = 2,
|
|
threadSleeping = 3,
|
|
threadDead = 4,
|
|
threadBlocked = 5,
|
|
/* Job-control stop (signal.h's SIGSTOP/SIGTSTP): excluded from
|
|
schedule()'s ready-queue rebuild exactly like Sleeping/Blocked
|
|
already are (only threadRunnable is ever inserted), so a stopped
|
|
thread simply never gets picked until threadContinue moves it back
|
|
to threadRunnable. Distinct from threadBlocked because it is NOT
|
|
waiting on a wait-channel condition -- schedWakeChannel must never
|
|
treat it as a match (see schedWakeChannel), only threadContinue
|
|
(an explicit SIGCONT, not "the resource became available") ever
|
|
moves a thread out of this state. */
|
|
threadStopped = 6
|
|
} ThreadState;
|
|
|
|
typedef void (*ThreadEntry)(void);
|
|
|
|
typedef struct Thread {
|
|
uint64 rsp;
|
|
uint64 stackBase;
|
|
uint64 kernelStackTop;
|
|
uint64 sleepUntil;
|
|
/* Absolute pitTicks() deadline for a pending alarm(2)-style SIGALRM
|
|
(0 == no alarm pending), set by threadSetAlarm and consulted once
|
|
per timer tick by schedule() -- the exact same shape as sleepUntil
|
|
above, checked in the exact same loop, except expiry raises a real
|
|
signal (sigRaise, signal.h) against the owning thread instead of
|
|
just flipping its own ThreadState back to Runnable. Deliberately a
|
|
plain deadline, not a wait-channel/ThreadState change: unlike
|
|
threadSleep, a thread with a pending alarm keeps running (or
|
|
sleeping, or blocked on something unrelated) exactly as it already
|
|
was right up until the deadline, exactly like real alarm()'s timer
|
|
ticking down in the background regardless of what the process is
|
|
doing. */
|
|
uint64 alarmAt;
|
|
/* poll.h's sysPoll (syscall.c) uses the same restart-the-syscall park
|
|
pattern sysRead/sysWrite/sysWait already use, but a poll call can be
|
|
woken by any one of several unrelated fds, not one -- so instead of
|
|
parking on a single fd's own wait channel (vfsBlockingChannel),
|
|
sysPoll blocks every waiting thread on ONE shared channel
|
|
(schedPollChannel below) and simply re-checks its whole fd list from
|
|
scratch on every wake, spurious or not, exactly like the retry loop
|
|
every OTHER restartable syscall already has. pollActive marks "this
|
|
thread is mid a not-yet-satisfied sysPoll call" (so a retry entry
|
|
can tell itself apart from a genuinely fresh call); pollDeadline is
|
|
the absolute pitTicks() deadline schedule()'s own expiry scan forces
|
|
a wake at if no fd ever becomes ready first (0 == wait forever,
|
|
meaningful only while pollActive is true -- the same "0 reserved"
|
|
convention alarmAt above already uses, but here 0 is a real,
|
|
legal, requested state (a timeout of -1) rather than "nothing
|
|
pending"). */
|
|
bool pollActive;
|
|
uint64 pollDeadline;
|
|
uint64 brk;
|
|
uint64 vruntime;
|
|
uint64 exitCode;
|
|
uint32 weight;
|
|
uint32 id;
|
|
ThreadState state;
|
|
const char* name;
|
|
FileDescriptor fds[maxOpenFiles];
|
|
} Thread;
|
|
|
|
void schedInit(void);
|
|
uint32 threadCreate(const char* name, ThreadEntry entry);
|
|
uint32 threadCreateUser(const char* name, uint64 entry, uint64 userStackTop);
|
|
uint64 schedule(uint64 currentRsp);
|
|
void schedStart(void);
|
|
|
|
void threadExit(uint64 code);
|
|
void threadSleep(uint64 milliseconds);
|
|
void threadSetBrk(uint32 id, uint64 brk);
|
|
uint64 currentThreadSbrk(int64 increment);
|
|
|
|
uint32 threadCount(void);
|
|
Thread* threadAt(uint32 index);
|
|
Thread* currentThread(void);
|
|
uint32 currentThreadId(void);
|
|
|
|
#endif
|