feat: extend sched with currentThreadSetEnv and currentThreadUnsetEnv
This commit is contained in:
1 parent
7703e1a45d
commit
48b66a86f7
1 file changed
+173
+173
@@ -837,6 +837,179 @@ const char* currentThreadGetEnv(const char* name)
|
|||||||
return value + 1;
|
return value + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool currentThreadSetEnv(const char* name, const char* value)
|
||||||
|
{
|
||||||
|
Thread* self = &threads[currentIndex];
|
||||||
|
uint64 nameLen = strlen(name);
|
||||||
|
uint64 valueLen = strlen(value);
|
||||||
|
if (nameLen + 1 + valueLen + 1 > envEntryMax)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int slot = -1;
|
||||||
|
for (int i = 0; i < self->envCount; i++) {
|
||||||
|
if (envNameMatches(self->env[i], name)) {
|
||||||
|
slot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (slot < 0) {
|
||||||
|
if (self->envCount >= envMaxVars)
|
||||||
|
return false;
|
||||||
|
slot = self->envCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(self->env[slot], name, nameLen);
|
||||||
|
self->env[slot][nameLen] = '=';
|
||||||
|
memcpy(self->env[slot] + nameLen + 1, value, valueLen);
|
||||||
|
self->env[slot][nameLen + 1 + valueLen] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool currentThreadUnsetEnv(const char* name)
|
||||||
|
{
|
||||||
|
Thread* self = &threads[currentIndex];
|
||||||
|
int slot = -1;
|
||||||
|
for (int i = 0; i < self->envCount; i++) {
|
||||||
|
if (envNameMatches(self->env[i], name)) {
|
||||||
|
slot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (slot < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int last = self->envCount - 1;
|
||||||
|
if (slot != last)
|
||||||
|
memmove(self->env[slot], self->env[slot + 1], (uint64)(last - slot) * envEntryMax);
|
||||||
|
self->envCount--;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentThreadEnvCount(void)
|
||||||
|
{
|
||||||
|
return threads[currentIndex].envCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* currentThreadEnvEntry(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= threads[currentIndex].envCount)
|
||||||
|
return nullptr;
|
||||||
|
return threads[currentIndex].env[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool threadStop(uint32 id)
|
||||||
|
{
|
||||||
|
if (id == 0 || id >= maxThreads)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ThreadState state = threads[id].state;
|
||||||
|
if (state == threadDead || state == threadUnused)
|
||||||
|
return false;
|
||||||
|
if (state == threadStopped) {
|
||||||
|
/* Idempotent, matching real kill(pid, SIGSTOP) sent to an already-
|
||||||
|
stopped process: the state transition already happened, so there
|
||||||
|
is no new event for the parent's SIGCHLD to report -- raising
|
||||||
|
again here would be a second notification for a single stop. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
threads[id].state = threadStopped;
|
||||||
|
kprintf("[sched] pid=%u '%s' stopped\n", id, threads[id].name);
|
||||||
|
|
||||||
|
/* Real job-control SIGCHLD-on-stop, the same asynchronous notification
|
||||||
|
threadExit/threadKill above already raise on death (Thread.parentId,
|
||||||
|
signal.h's sigRaiseInfo) -- a shell watching a background job's
|
||||||
|
parentId finds out the instant it stops, not only when it eventually
|
||||||
|
dies or exits. Only reached on the actual ...->Stopped transition
|
||||||
|
above, never on the already-Stopped no-op return just above.
|
||||||
|
siCodeChldStopped/id/sigStop: real Unix's WIFSTOPPED(status) +
|
||||||
|
WSTOPSIG(status) would report whichever signal actually caused the
|
||||||
|
stop -- this function is called from exactly two places (signal.c's
|
||||||
|
sigRaise sigStop branch, a real SIGSTOP; isr.c's sigTstp-default-
|
||||||
|
disposition branch, a real SIGTSTP) and does not itself know which
|
||||||
|
one, so si_status always reports sigStop here regardless. A known,
|
||||||
|
documented imprecision for the SIGTSTP path (which also has no live
|
||||||
|
ring-3 demo of its own yet -- see signal.h's file comment) -- not a
|
||||||
|
fabricated distinction either path never had. */
|
||||||
|
sigRaiseInfo(threads[id].parentId, sigChld, siCodeChldStopped, (int)id, sigStop);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool threadContinue(uint32 id)
|
||||||
|
{
|
||||||
|
if (id == 0 || id >= maxThreads)
|
||||||
|
return false;
|
||||||
|
if (threads[id].state != threadStopped)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
threads[id].state = threadRunnable;
|
||||||
|
kprintf("[sched] pid=%u '%s' continued\n", id, threads[id].name);
|
||||||
|
|
||||||
|
/* Same job-control SIGCHLD contract as threadStop above, mirrored for
|
||||||
|
SIGCONT: real job-control shells rely on this to notice a
|
||||||
|
backgrounded job resumed without polling its state. Only reached
|
||||||
|
past the "was it actually Stopped" guard above, so this never fires
|
||||||
|
for a SIGCONT sent to a target that was never stopped in the first
|
||||||
|
place, matching real Unix (nothing to report). siCodeChldContinued/
|
||||||
|
id/sigCont: real Unix's WIFCONTINUED(status), si_status unused for
|
||||||
|
this one (POSIX defines none for CLD_CONTINUED either) -- sigCont
|
||||||
|
here is honest, not a placeholder, unlike threadStop's own sigStop
|
||||||
|
above: threadContinue really is only ever reached via a genuine
|
||||||
|
SIGCONT (signal.c's sigRaise sigCont branch is its only caller with
|
||||||
|
a live path to a Stopped target). */
|
||||||
|
sigRaiseInfo(threads[id].parentId, sigChld, siCodeChldContinued, (int)id, sigCont);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool threadWakeForSignal(uint32 id)
|
||||||
|
{
|
||||||
|
if (id == 0 || id >= maxThreads)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ThreadState state = threads[id].state;
|
||||||
|
if (state != threadSleeping && state != threadBlocked)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
threads[id].state = threadRunnable;
|
||||||
|
kprintf("[sig] pid=%u '%s' woken early for a signal\n", id, threads[id].name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 threadCapacity(void)
|
||||||
|
{
|
||||||
|
return maxThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gives a dead/discarded user thread's whole elfSlotSize slot back to the
|
||||||
|
pmm. Verified, not just invoked blindly: a byte inside the slot's stack
|
||||||
|
region (always mapped for any user thread, whatever its image looks
|
||||||
|
like -- see loadImageByPath/spawnUser) is checked present BEFORE the
|
||||||
|
unmap (proving there was really something to reclaim, not a no-op on an
|
||||||
|
already-empty slot) and absent AFTER (proving the unmap actually took
|
||||||
|
the frame back), logged so the boot gate can hold the wiring itself
|
||||||
|
accountable rather than trusting that calling pagingUnmapUserRange was
|
||||||
|
enough. Kernel threads (userSlotBase 0) are silently skipped. */
|
||||||
|
static void reclaimUserSlot(uint32 id, const Thread* thread)
|
||||||
|
{
|
||||||
|
if (thread->userSlotBase == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint64 slotBase = thread->userSlotBase;
|
||||||
|
uint64 stackProbe = slotBase + elfSlotStackOffset - 1;
|
||||||
|
bool wasMapped = pagingResolve(stackProbe) != 0;
|
||||||
|
|
||||||
|
pagingUnmapUserRange(slotBase, elfSlotSize);
|
||||||
|
|
||||||
|
bool nowFreed = pagingResolve(stackProbe) == 0;
|
||||||
|
kprintf("[sched] reap pid=%u freed user slot %p (was-mapped=%s now-freed=%s) -> %s\n",
|
||||||
|
id, (void*)slotBase, wasMapped ? "yes" : "no", nowFreed ? "yes" : "no",
|
||||||
|
(wasMapped && nowFreed) ? "user-mem-reclaimed-ok" : "user-mem-reclaimed-FAIL");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Every open fd a thread still holds when it dies represents a live
|
||||||
}
|
}
|
||||||
|
|
||||||
void threadSleep(uint64 milliseconds)
|
void threadSleep(uint64 milliseconds)
|
||||||
|
|||||||
Reference in new issue
Block a user