Goto sanos source index
//
// thread.c
//
// Thread routines
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the project nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
#include <os.h>
#include <string.h>
#include <atomic.h>
#include <inifile.h>
#include <os/syscall.h>
#include <os/seg.h>
#include <os/tss.h>
#include <os/syspage.h>
#include "heap.h"
// environ.c
char **initenv(struct section *sect);
char **copyenv(char **env);
void freeenv(char **env);
struct critsect proc_lock;
int nextprocid = 1;
void init_threads(hmodule_t hmod, struct term *initterm) {
struct tib *tib = gettib();
struct peb *peb = getpeb();
struct process *proc;
mkcs(&proc_lock);
proc = malloc(sizeof(struct process));
if (!proc) panic("unable to allocate initial process");
memset(proc, 0, sizeof(struct process));
proc->id = nextprocid++;
proc->threadcnt = 1;
proc->hndl = dup(self());
proc->iob[0] = 0;
proc->iob[1] = 1;
proc->iob[2] = 2;
proc->term = initterm;
proc->hmod = hmod;
proc->cmdline = NULL;
proc->env = initenv(find_section(osconfig(), "env"));
proc->facility = LOG_DAEMON;
proc->ident = strdup("os");
tib->proc = proc;
peb->firstproc = peb->lastproc = proc;
}
handle_t mkthread(void (__stdcall *startaddr)(struct tib *), unsigned long stacksize, char *name, struct tib **ptib) {
return syscall(SYSCALL_MKTHREAD, &startaddr);
}
void __stdcall threadstart(struct tib *tib) {
// Call thread routine
if (tib->flags & CREATE_POSIX) {
endthread((int) ((void *(*)(void *)) (tib->startaddr))(tib->startarg));
} else {
((void (__stdcall *)(void *)) (tib->startaddr))(tib->startarg);
endthread(0);
}
}
static struct process *mkproc(struct process *parent, int hndl, int flags) {
struct peb *peb = getpeb();
struct process *proc;
int i;
proc = malloc(sizeof(struct process));
if (!proc) return NULL;
memset(proc, 0, sizeof(struct process));
if (flags & CREATE_DETACHED) {
for (i = 0; i < 3; i++) proc->iob[i] = NOHANDLE;
} else {
for (i = 0; i < 3; i++) proc->iob[i] = dup(parent->iob[i]);
proc->term = parent->term;
}
proc->hndl = dup(hndl);
proc->facility = LOG_USER;
if ((flags & CREATE_NO_ENV) == 0) proc->env = copyenv(parent->env);
enter(&proc_lock);
proc->id = nextprocid++;
if (flags & CREATE_CHILD) proc->parent = parent;
proc->prevproc = peb->lastproc;
if (!peb->firstproc) peb->firstproc = proc;
if (peb->lastproc) peb->lastproc->nextproc = proc;
peb->lastproc = proc;
leave(&proc_lock);
return proc;
}
handle_t beginthread(void (__stdcall *startaddr)(void *), unsigned int stacksize, void *arg, int flags, char *name, struct tib **ptib) {
struct process *parent = gettib()->proc;
struct tib *tib;
struct process *proc;
handle_t h;
h = mkthread(threadstart, stacksize, name, &tib);
if (h < 0) return h;
tib->startaddr = (void *) startaddr;
tib->startarg = arg;
tib->flags = flags;
if (flags & CREATE_NEW_PROCESS) {
proc = mkproc(parent, h, flags);
if (!proc) {
errno = ENOMEM;
return -1;
}
} else {
proc = parent;
}
atomic_add(&proc->threadcnt, 1);
tib->proc = proc;
tib->pid = proc->id;
if (!(flags & CREATE_SUSPENDED)) resume(h);
if (ptib) *ptib = tib;
return h;
}
handle_t self() {
return gettib()->hndl;
}
struct tib *getthreadblock(handle_t thread) {
int rc;
rc = syscall(SYSCALL_GETTHREADBLOCK, &thread);
if (rc < 0) return NULL;
return (struct tib *) rc;
}
int suspend(handle_t thread) {
return syscall(SYSCALL_SUSPEND, &thread);
}
int resume(handle_t thread) {
return syscall(SYSCALL_RESUME, &thread);
}
void endproc(struct process *proc, int status) {
struct peb *peb = getpeb();
struct process *parent;
struct process *p;
struct zombie *z;
int i;
int ppid = -1;
enter(&proc_lock);
// Add zombie to parent process
parent = proc->parent;
if (parent) {
z = (struct zombie *) malloc(sizeof(struct zombie));
if (z) {
z->pid = proc->id;
z->status = status;
z->next = parent->zombies;
parent->zombies = z;
ppid = parent->id;
}
}
// Free zombies for process
while (proc->zombies) {
z = proc->zombies->next;
free(proc->zombies);
proc->zombies = z;
}
// Remove process from process list
if (proc->nextproc) proc->nextproc->prevproc = proc->prevproc;
if (proc->prevproc) proc->prevproc->nextproc = proc->nextproc;
if (proc == peb->firstproc) peb->firstproc = proc->nextproc;
if (proc == peb->lastproc) peb->lastproc = proc->prevproc;
// Orphan child processes
for (p = peb->firstproc; p; p = p->nextproc) if (p->parent == proc) p->parent = NULL;
leave(&proc_lock);
// Close standard handles
for (i = 0; i < 3; i++) if (proc->iob[i] >= 0) close(proc->iob[i]);
// Cleanup process resources
if (proc->hmod) dlclose(proc->hmod);
if (proc->cmdline) free(proc->cmdline);
if (proc->ident) free(proc->ident);
if (proc->env) freeenv(proc->env);
if (proc->heap) heap_destroy(proc->heap);
close(proc->hndl);
free(proc);
// Notify parent about child termination
if (ppid != -1) kill(ppid, SIGCHLD);
}
void endthread(int status) {
struct process *proc;
proc = gettib()->proc;
if (atomic_add(&proc->threadcnt, -1) == 0) endproc(proc, status);
syscall(SYSCALL_ENDTHREAD, &status);
}
tid_t gettid() {
return gettib()->tid;
}
pid_t getpid() {
return gettib()->pid;
}
pid_t getppid() {
struct tib *tib = gettib();
int id;
enter(&proc_lock);
if (tib && tib->proc && tib->proc->parent) {
id = tib->proc->parent->id;
} else {
id = -1;
errno = ESRCH;
}
leave(&proc_lock);
return id;
}
int getchildstat(pid_t pid, int *status) {
struct process *proc = gettib()->proc;
struct zombie *z;
int rc;
enter(&proc_lock);
z = proc->zombies;
if (pid == -1) {
if (z) proc->zombies = z->next;
} else {
struct zombie *pz = NULL;
while (z) {
if (z->pid == pid) {
if (pz) {
pz->next = z->next;
} else {
proc->zombies = z->next;
}
break;
}
pz = z;
z = z->next;
}
}
if (z) {
rc = z->pid;
if (status) *status = z->status;
free(z);
} else {
rc = -1;
}
leave(&proc_lock);
return rc;
}
int setchildstat(pid_t pid, int status) {
struct process *proc = gettib()->proc;
struct zombie *z;
enter(&proc_lock);
z = (struct zombie *) malloc(sizeof(struct zombie));
z->pid = pid;
z->status = status;
z->next = proc->zombies;
proc->zombies = z;
leave(&proc_lock);
return 0;
}
handle_t getprochandle(pid_t pid) {
handle_t h = NOHANDLE;
struct process *p;
enter(&proc_lock);
for (p = getpeb()->firstproc; p; p = p->nextproc) {
if (p->id == pid) {
h = dup(p->hndl);
break;
}
}
leave(&proc_lock);
if (h == NOHANDLE) errno = ESRCH;
return h;
}
int getcontext(handle_t thread, void *context) {
return syscall(SYSCALL_GETCONTEXT, &thread);
}
int setcontext(handle_t thread, void *context) {
return syscall(SYSCALL_SETCONTEXT, &thread);
}
int getprio(handle_t thread) {
return syscall(SYSCALL_GETPRIO, &thread);
}
int setprio(handle_t thread, int priority) {
return syscall(SYSCALL_SETPRIO, &thread);
}
clock_t threadtimes(handle_t thread, struct tms *tms) {
return syscall(SYSCALL_THREADTIMES, (void *) &thread);
}
clock_t times(struct tms *tms) {
return threadtimes(self(), tms);
}
int msleep(int millisecs) {
return syscall(SYSCALL_MSLEEP, &millisecs);
}
struct tib *gettib() {
struct tib *tib;
__asm {
mov eax, fs:[TIB_SELF_OFFSET]
mov [tib], eax
}
return tib;
}
void exit(int status) {
struct process *proc = gettib()->proc;
if (proc->atexit) proc->atexit(status, 0);
endthread(status);
}
void _exit(int status) {
struct process *proc = gettib()->proc;
if (proc->atexit) proc->atexit(status, 1);
endthread(status);
}
static void __stdcall spawn_program(void *args) {
struct process *proc = gettib()->proc;
int rc;
rc = exec(proc->hmod, proc->cmdline, proc->env);
exit(rc);
}
static char *procname(const char *name) {
char *procname;
char *p = (char *) name;
char *start = p;
char *end = NULL;
while (*p) {
if (*p == PS1 || *p == PS2) start = p + 1;
if (*p == '.') end = p;
p++;
}
if (!end || end < start) end = p;
procname = malloc(end - start + 1);
if (!procname) return NULL;
memcpy(procname, start, end - start);
procname[end - start] = 0;
return procname;
}
int spawn(int mode, const char *pgm, const char *cmdline, char *env[], struct tib **tibptr) {
hmodule_t hmod;
handle_t hthread;
struct tib *tib;
struct process *proc;
int flags;
int rc;
char pgmbuf[MAXPATH];
char *name;
if (!pgm) {
const char *p = cmdline;
char *q = pgmbuf;
if (!cmdline) {
errno = EINVAL;
return -1;
}
while (*p != 0 && *p != ' ') {
if (q - pgmbuf == MAXPATH - 1) break;
*q++ = *p++;
}
*q++ = 0;
pgm = pgmbuf;
}
hmod = dlopen(pgm, RTLD_EXE | RTLD_SCRIPT);
if (!hmod) return -1;
flags = CREATE_SUSPENDED | CREATE_NEW_PROCESS;
if (mode & P_DETACH) flags |= CREATE_DETACHED;
if (mode & P_CHILD) flags |= CREATE_CHILD;
if (env) flags |= CREATE_NO_ENV;
name = procname(pgm);
hthread = beginthread(spawn_program, 0, NULL, flags, name, &tib);
if (hthread < 0) {
free(name);
dlclose(hmod);
return -1;
}
proc = tib->proc;
proc->hmod = hmod;
proc->cmdline = strdup(cmdline);
proc->ident = name;
if (env) proc->env = copyenv(env);
if (mode & (P_NOWAIT | P_SUSPEND)) {
if (tibptr) *tibptr = tib;
if ((mode & P_SUSPEND) == 0) resume(hthread);
return hthread;
} else {
rc = resume(hthread);
if (rc >= 0) {
while (1) {
rc = waitone(hthread, INFINITE);
if (rc >= 0 || errno != EINTR) break;
}
close(hthread);
}
return rc;
}
}
int spawnve(int mode, const char *pgm, char *argv[], char *env[], struct tib **tibptr) {
char *cmdline;
char *cmd;
int cmdlen;
char **args;
int rc;
// Compute command line size
cmdlen = 0;
for (args = argv; *args; args++) {
int escape = 0;
char *p = *args;
if (!*p) {
escape = 1;
} else {
while (*p) {
cmdlen++;
if (*p == ' ') escape = 1;
if (*p == '"') {
cmdlen++;
escape = 1;
}
p++;
}
cmdlen++;
}
if (escape) cmdlen += 2;
}
// Build command line
cmd = cmdline = malloc(cmdlen);
for (args = argv; *args; args++) {
int escape = 0;
char *p = *args;
if (!*p) {
escape = 1;
} else {
while (*p) {
if (*p == ' ' || *p == '"') escape = 1;
p++;
}
}
if (*args != argv[0]) *cmd++ = ' ';
if (escape) *cmd++ = '"';
p = *args;
while (*p) {
if (*p == '"') *cmd++ = '"';
*cmd++ = *p++;
}
if (escape) *cmd++ = '"';
}
*cmd = 0;
// Spawn program
rc = spawn(mode, pgm, cmdline, env, tibptr);
free(cmdline);
return rc;
}
int spawnv(int mode, const char *pgm, char *argv[]) {
return spawnve(mode, pgm, argv, NULL, NULL);
}
int spawnl(int mode, const char *pgm, char *arg0, ...) {
return spawnve(mode, pgm, &arg0, NULL, NULL);
}