mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Put more thought into new signaling code
This commit is contained in:
parent
6107eb38f9
commit
f68fc1f815
10 changed files with 127 additions and 51 deletions
|
@ -596,6 +596,9 @@ static textwindows int __sig_console_sig(uint32_t dwCtrlType) {
|
|||
}
|
||||
|
||||
__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
|
||||
// win32 launches a thread to deliver ctrl-c and ctrl-break when typed
|
||||
// it only happens when kNtEnableProcessedInput is in play on console.
|
||||
// otherwise we need to wait until read-nt.c discovers that keystroke.
|
||||
struct CosmoTib tls;
|
||||
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||
__sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL);
|
||||
|
@ -616,7 +619,12 @@ textwindows int __sig_check(void) {
|
|||
}
|
||||
}
|
||||
|
||||
// delivers signals from other processes asynchronously
|
||||
// background thread for delivering inter-process signals asynchronously
|
||||
// this checks for undelivered process-wide signals, once per scheduling
|
||||
// quantum, which on windows should be every ~15ms or so, unless somehow
|
||||
// the process was tuned to have more fine-grained event timing. we want
|
||||
// signals to happen faster when possible; that happens when cancelation
|
||||
// points, e.g. read need to wait on i/o; they too check for new signals
|
||||
textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
|
||||
struct CosmoTib tls;
|
||||
__bootstrap_tls(&tls, __builtin_frame_address(0));
|
||||
|
@ -624,9 +632,16 @@ textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
|
|||
__maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ,
|
||||
STKSZ);
|
||||
for (;;) {
|
||||
int sig;
|
||||
if ((sig = __sig_getter(__sig.process, 0)))
|
||||
// dequeue all pending signals and fire them off. if there's no
|
||||
// thread that can handle them then __sig_generate will requeue
|
||||
// those signals back to __sig.process; hence the need for xchg
|
||||
unsigned long sigs =
|
||||
atomic_exchange_explicit(__sig.process, 0, memory_order_acq_rel);
|
||||
while (sigs) {
|
||||
int sig = bsfl(sigs) + 1;
|
||||
sigs &= ~(1ull << (sig - 1));
|
||||
__sig_generate(sig, SI_KERNEL);
|
||||
}
|
||||
Sleep(1);
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
#define SIG_HANDLED_NO_RESTART 1
|
||||
|
@ -28,8 +29,8 @@ void __sig_delete(int);
|
|||
void __sig_generate(int, int);
|
||||
void __sig_init(void);
|
||||
|
||||
char16_t *__sig_process_path(char16_t *, uint32_t);
|
||||
atomic_ulong *__sig_map_process(int, int);
|
||||
__msabi char16_t *__sig_process_path(char16_t *, uint32_t, int);
|
||||
__msabi atomic_ulong *__sig_map_process(int, int);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ */
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define COSMOPOLITAN_LIBC_FMT_STRTOL_H_
|
||||
#include "libc/ctype.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#define CONSUME_SPACES(t, s, c) \
|
||||
|
@ -48,6 +49,6 @@
|
|||
int __vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *,
|
||||
va_list);
|
||||
int __fmt(void *, void *, const char *, va_list, int *);
|
||||
char16_t *__itoa16(char16_t[21], uint64_t);
|
||||
__msabi char16_t *__itoa16(char16_t[21], uint64_t);
|
||||
|
||||
#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/internal.h"
|
||||
|
||||
textwindows char16_t *__itoa16(char16_t p[21], uint64_t x) {
|
||||
__msabi textwindows char16_t *__itoa16(char16_t p[21], uint64_t x) {
|
||||
char t;
|
||||
size_t a, b, i = 0;
|
||||
do {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/fmt/internal.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/filemovemethod.h"
|
||||
|
@ -29,28 +30,60 @@
|
|||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid) {
|
||||
// cut back on code size and avoid setting errno
|
||||
// this code is a mandatory dependency of winmain
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(CreateDirectory) *const __imp_CreateDirectoryW;
|
||||
__msabi extern typeof(CreateFile) *const __imp_CreateFileW;
|
||||
__msabi extern typeof(CreateFileMapping) *const __imp_CreateFileMappingW;
|
||||
__msabi extern typeof(MapViewOfFileEx) *const __imp_MapViewOfFileEx;
|
||||
__msabi extern typeof(SetEndOfFile) *const __imp_SetEndOfFile;
|
||||
__msabi extern typeof(SetFilePointer) *const __imp_SetFilePointer;
|
||||
|
||||
__msabi textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid,
|
||||
int create_directories) {
|
||||
char16_t *p = path;
|
||||
*p++ = 'C';
|
||||
*p++ = 'C'; // C:\ProgramData\cosmo\sig\x\y.pid
|
||||
*p++ = ':';
|
||||
*p++ = '\\';
|
||||
*p++ = 'v';
|
||||
*p++ = 'a';
|
||||
*p++ = 'P';
|
||||
*p++ = 'r';
|
||||
*p++ = 'o';
|
||||
*p++ = 'g';
|
||||
*p++ = 'r';
|
||||
*p++ = 'a';
|
||||
*p++ = 'm';
|
||||
*p++ = 'D';
|
||||
*p++ = 'a';
|
||||
*p++ = 't';
|
||||
*p++ = 'a';
|
||||
*p = 0;
|
||||
CreateDirectory(path, 0);
|
||||
if (create_directories)
|
||||
__imp_CreateDirectoryW(path, 0);
|
||||
*p++ = '\\';
|
||||
*p++ = 'c';
|
||||
*p++ = 'o';
|
||||
*p++ = 's';
|
||||
*p++ = 'm';
|
||||
*p++ = 'o';
|
||||
*p = 0;
|
||||
if (create_directories)
|
||||
__imp_CreateDirectoryW(path, 0);
|
||||
*p++ = '\\';
|
||||
*p++ = 's';
|
||||
*p++ = 'i';
|
||||
*p++ = 'g';
|
||||
*p = 0;
|
||||
CreateDirectory(path, 0);
|
||||
if (create_directories)
|
||||
__imp_CreateDirectoryW(path, 0);
|
||||
*p++ = '\\';
|
||||
p = __itoa16(p, (pid & 0x000fff00) >> 8);
|
||||
p = __itoa16(p, (pid & 0x000ff800) >> 11);
|
||||
*p = 0;
|
||||
CreateDirectory(path, 0);
|
||||
if (create_directories)
|
||||
__imp_CreateDirectoryW(path, 0);
|
||||
*p++ = '\\';
|
||||
p = __itoa16(p, pid);
|
||||
*p++ = '.';
|
||||
|
@ -61,33 +94,25 @@ textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid) {
|
|||
return path;
|
||||
}
|
||||
|
||||
textwindows static atomic_ulong *__sig_map_process_impl(int pid,
|
||||
int disposition) {
|
||||
__msabi textwindows atomic_ulong *__sig_map_process(int pid, int disposition) {
|
||||
char16_t path[128];
|
||||
intptr_t hand = CreateFile(__sig_process_path(path, pid),
|
||||
kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareRead | kNtFileShareWrite, 0,
|
||||
disposition, kNtFileAttributeNormal, 0);
|
||||
__sig_process_path(path, pid, disposition == kNtOpenAlways);
|
||||
intptr_t hand = __imp_CreateFileW(path, kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareRead | kNtFileShareWrite, 0,
|
||||
disposition, kNtFileAttributeNormal, 0);
|
||||
if (hand == -1)
|
||||
return 0;
|
||||
SetFilePointer(hand, 8, 0, kNtFileBegin);
|
||||
SetEndOfFile(hand);
|
||||
intptr_t map = CreateFileMapping(hand, 0, kNtPageReadwrite, 0, 8, 0);
|
||||
__imp_SetFilePointer(hand, 8, 0, kNtFileBegin);
|
||||
__imp_SetEndOfFile(hand);
|
||||
intptr_t map = __imp_CreateFileMappingW(hand, 0, kNtPageReadwrite, 0, 8, 0);
|
||||
if (!map) {
|
||||
CloseHandle(hand);
|
||||
__imp_CloseHandle(hand);
|
||||
return 0;
|
||||
}
|
||||
atomic_ulong *sigs = MapViewOfFileEx(map, kNtFileMapWrite, 0, 0, 8, 0);
|
||||
CloseHandle(map);
|
||||
CloseHandle(hand);
|
||||
atomic_ulong *sigs = __imp_MapViewOfFileEx(map, kNtFileMapWrite, 0, 0, 8, 0);
|
||||
__imp_CloseHandle(map);
|
||||
__imp_CloseHandle(hand);
|
||||
return sigs;
|
||||
}
|
||||
|
||||
textwindows atomic_ulong *__sig_map_process(int pid, int disposition) {
|
||||
int e = errno;
|
||||
atomic_ulong *res = __sig_map_process_impl(pid, disposition);
|
||||
errno = e;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -27,7 +27,9 @@
|
|||
#include "libc/runtime/internal.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
__msabi extern typeof(DeleteFile) *const __imp_DeleteFileW;
|
||||
__msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess;
|
||||
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
|
||||
|
||||
/**
|
||||
* Terminates the calling process and all of its threads.
|
||||
|
@ -40,8 +42,8 @@ textwindows dontinstrument void TerminateThisProcess(uint32_t dwWaitStatus) {
|
|||
atomic_ulong fake = 0;
|
||||
real = __sig.process;
|
||||
__sig.process = &fake;
|
||||
UnmapViewOfFile(real);
|
||||
DeleteFile(__sig_process_path(path, __pid));
|
||||
__imp_UnmapViewOfFile(real);
|
||||
__imp_DeleteFileW(__sig_process_path(path, __pid, false));
|
||||
|
||||
// "When a process terminates itself, TerminateProcess stops execution
|
||||
// of the calling thread and does not return." -Quoth MSDN
|
||||
|
|
|
@ -465,7 +465,6 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
|||
__morph_tls();
|
||||
__tls_enabled = true;
|
||||
// the child's pending signals is initially empty
|
||||
atomic_store_explicit(__sig.process, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&tib->tib_sigpending, 0, memory_order_relaxed);
|
||||
// re-apply code morphing for function tracing
|
||||
if (ftrace_stackdigs)
|
||||
|
|
|
@ -59,11 +59,22 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
if (pid <= 0 || pid == getpid()) {
|
||||
if (sig) {
|
||||
if (pid <= 0) {
|
||||
// if pid is 0 or -1 then kill the processes beneath us too.
|
||||
// this isn't entirely right but it's closer to being right.
|
||||
// having this behavior is helpful for servers like redbean.
|
||||
struct Dll *e;
|
||||
BLOCK_SIGNALS;
|
||||
__proc_lock();
|
||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e))
|
||||
TerminateProcess(PROC_CONTAINER(e)->handle, sig);
|
||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
|
||||
atomic_ulong *sigproc;
|
||||
struct Proc *pr = PROC_CONTAINER(e);
|
||||
if (sig != 9 && (sigproc = __sig_map_process(pid, kNtOpenExisting))) {
|
||||
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
|
||||
memory_order_release);
|
||||
} else {
|
||||
TerminateProcess(pr->handle, sig);
|
||||
}
|
||||
}
|
||||
__proc_unlock();
|
||||
ALLOW_SIGNALS;
|
||||
}
|
||||
|
@ -73,7 +84,25 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
}
|
||||
}
|
||||
|
||||
// attempt to signal via /var/sig shared memory file
|
||||
// find existing handle we own for process
|
||||
//
|
||||
// this step should come first to verify process existence. this is
|
||||
// because there's no guarantee that just because the shared memory
|
||||
// file exists, the process actually exists.
|
||||
int64_t handle, closeme = 0;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
|
||||
closeme = handle;
|
||||
} else {
|
||||
goto OnError;
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to signal via shared memory file
|
||||
//
|
||||
// now that we know the process exists, if it has a shared memory file
|
||||
// then we can be reasonably certain it's a cosmo process which should
|
||||
// be trusted to deliver its signal, unless it's a nine exterminations
|
||||
if (pid > 0 && sig != 9) {
|
||||
atomic_ulong *sigproc;
|
||||
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
|
||||
|
@ -81,20 +110,12 @@ textwindows int sys_kill_nt(int pid, int sig) {
|
|||
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
|
||||
memory_order_release);
|
||||
UnmapViewOfFile(sigproc);
|
||||
if (closeme)
|
||||
CloseHandle(closeme);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// find existing handle we own for process
|
||||
int64_t handle, closeme = 0;
|
||||
if (!(handle = __proc_handle(pid))) {
|
||||
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
|
||||
closeme = handle;
|
||||
} else {
|
||||
goto OnError;
|
||||
}
|
||||
}
|
||||
|
||||
// perform actual kill
|
||||
// process will report WIFSIGNALED with WTERMSIG(sig)
|
||||
bool32 ok = TerminateProcess(handle, sig);
|
||||
|
|
|
@ -29,6 +29,17 @@
|
|||
* The impact of this action can be terminating the process, or
|
||||
* interrupting it to request something happen.
|
||||
*
|
||||
* On Windows, signals are delivered between processes using shared
|
||||
* memory files stored in C:\ProgramData\cosmo\sig\x\y.pid which hold
|
||||
* the process signal mask. Any process that can access these files can
|
||||
* signal a cosmo process. The targeting process will then notice that a
|
||||
* signal has been added and delivers to any thread as soon as possible.
|
||||
*
|
||||
* On Windows, the concept of a process group isn't fully implemented.
|
||||
* Saying `kill(0, sig)` will deliver `sig` to all direct descendent
|
||||
* processes. Saying `kill(-pid, sig)` will be the same as saying
|
||||
* `kill(pid, sig)`.
|
||||
*
|
||||
* @param pid can be:
|
||||
* >0 signals one process by id
|
||||
* =0 signals all processes in current process group
|
||||
|
|
|
@ -323,6 +323,7 @@ textwindows int64_t __proc_search(int pid) {
|
|||
int64_t handle = 0;
|
||||
BLOCK_SIGNALS;
|
||||
__proc_lock();
|
||||
// TODO(jart): we should increment a reference count when returning
|
||||
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
|
||||
if (pid == PROC_CONTAINER(e)->pid) {
|
||||
handle = PROC_CONTAINER(e)->handle;
|
||||
|
|
Loading…
Reference in a new issue