Skip to content

Commit f68fc1f

Browse files
committed
Put more thought into new signaling code
1 parent 6107eb3 commit f68fc1f

File tree

10 files changed

+127
-51
lines changed

10 files changed

+127
-51
lines changed

libc/calls/sig.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,9 @@ static textwindows int __sig_console_sig(uint32_t dwCtrlType) {
596596
}
597597

598598
__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
599+
// win32 launches a thread to deliver ctrl-c and ctrl-break when typed
600+
// it only happens when kNtEnableProcessedInput is in play on console.
601+
// otherwise we need to wait until read-nt.c discovers that keystroke.
599602
struct CosmoTib tls;
600603
__bootstrap_tls(&tls, __builtin_frame_address(0));
601604
__sig_generate(__sig_console_sig(dwCtrlType), SI_KERNEL);
@@ -616,17 +619,29 @@ textwindows int __sig_check(void) {
616619
}
617620
}
618621

619-
// delivers signals from other processes asynchronously
622+
// background thread for delivering inter-process signals asynchronously
623+
// this checks for undelivered process-wide signals, once per scheduling
624+
// quantum, which on windows should be every ~15ms or so, unless somehow
625+
// the process was tuned to have more fine-grained event timing. we want
626+
// signals to happen faster when possible; that happens when cancelation
627+
// points, e.g. read need to wait on i/o; they too check for new signals
620628
textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
621629
struct CosmoTib tls;
622630
__bootstrap_tls(&tls, __builtin_frame_address(0));
623631
char *sp = __builtin_frame_address(0);
624632
__maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ,
625633
STKSZ);
626634
for (;;) {
627-
int sig;
628-
if ((sig = __sig_getter(__sig.process, 0)))
635+
// dequeue all pending signals and fire them off. if there's no
636+
// thread that can handle them then __sig_generate will requeue
637+
// those signals back to __sig.process; hence the need for xchg
638+
unsigned long sigs =
639+
atomic_exchange_explicit(__sig.process, 0, memory_order_acq_rel);
640+
while (sigs) {
641+
int sig = bsfl(sigs) + 1;
642+
sigs &= ~(1ull << (sig - 1));
629643
__sig_generate(sig, SI_KERNEL);
644+
}
630645
Sleep(1);
631646
}
632647
return 0;

libc/calls/sig.internal.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
33
#include "libc/atomic.h"
44
#include "libc/calls/struct/sigset.h"
5+
#include "libc/nt/thunk/msabi.h"
56
#include "libc/thread/posixthread.internal.h"
67

78
#define SIG_HANDLED_NO_RESTART 1
@@ -28,8 +29,8 @@ void __sig_delete(int);
2829
void __sig_generate(int, int);
2930
void __sig_init(void);
3031

31-
char16_t *__sig_process_path(char16_t *, uint32_t);
32-
atomic_ulong *__sig_map_process(int, int);
32+
__msabi char16_t *__sig_process_path(char16_t *, uint32_t, int);
33+
__msabi atomic_ulong *__sig_map_process(int, int);
3334

3435
COSMOPOLITAN_C_END_
3536
#endif /* COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ */

libc/fmt/internal.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define COSMOPOLITAN_LIBC_FMT_STRTOL_H_
33
#include "libc/ctype.h"
44
#include "libc/errno.h"
5+
#include "libc/nt/thunk/msabi.h"
56
#include "libc/str/str.h"
67

78
#define CONSUME_SPACES(t, s, c) \
@@ -48,6 +49,6 @@
4849
int __vcscanf(int (*)(void *), int (*)(int, void *), void *, const char *,
4950
va_list);
5051
int __fmt(void *, void *, const char *, va_list, int *);
51-
char16_t *__itoa16(char16_t[21], uint64_t);
52+
__msabi char16_t *__itoa16(char16_t[21], uint64_t);
5253

5354
#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */

libc/intrin/itoa16.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/fmt/internal.h"
2020

21-
textwindows char16_t *__itoa16(char16_t p[21], uint64_t x) {
21+
__msabi textwindows char16_t *__itoa16(char16_t p[21], uint64_t x) {
2222
char t;
2323
size_t a, b, i = 0;
2424
do {

libc/intrin/sigproc.c

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "libc/fmt/internal.h"
2222
#include "libc/nt/createfile.h"
2323
#include "libc/nt/enum/accessmask.h"
24+
#include "libc/nt/enum/creationdisposition.h"
2425
#include "libc/nt/enum/fileflagandattributes.h"
2526
#include "libc/nt/enum/filemapflags.h"
2627
#include "libc/nt/enum/filemovemethod.h"
@@ -29,28 +30,60 @@
2930
#include "libc/nt/files.h"
3031
#include "libc/nt/memory.h"
3132
#include "libc/nt/runtime.h"
33+
#include "libc/nt/thunk/msabi.h"
3234
#ifdef __x86_64__
3335

34-
textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid) {
36+
// cut back on code size and avoid setting errno
37+
// this code is a mandatory dependency of winmain
38+
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
39+
__msabi extern typeof(CreateDirectory) *const __imp_CreateDirectoryW;
40+
__msabi extern typeof(CreateFile) *const __imp_CreateFileW;
41+
__msabi extern typeof(CreateFileMapping) *const __imp_CreateFileMappingW;
42+
__msabi extern typeof(MapViewOfFileEx) *const __imp_MapViewOfFileEx;
43+
__msabi extern typeof(SetEndOfFile) *const __imp_SetEndOfFile;
44+
__msabi extern typeof(SetFilePointer) *const __imp_SetFilePointer;
45+
46+
__msabi textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid,
47+
int create_directories) {
3548
char16_t *p = path;
36-
*p++ = 'C';
49+
*p++ = 'C'; // C:\ProgramData\cosmo\sig\x\y.pid
3750
*p++ = ':';
3851
*p++ = '\\';
39-
*p++ = 'v';
40-
*p++ = 'a';
52+
*p++ = 'P';
53+
*p++ = 'r';
54+
*p++ = 'o';
55+
*p++ = 'g';
4156
*p++ = 'r';
57+
*p++ = 'a';
58+
*p++ = 'm';
59+
*p++ = 'D';
60+
*p++ = 'a';
61+
*p++ = 't';
62+
*p++ = 'a';
63+
*p = 0;
64+
if (create_directories)
65+
__imp_CreateDirectoryW(path, 0);
66+
*p++ = '\\';
67+
*p++ = 'c';
68+
*p++ = 'o';
69+
*p++ = 's';
70+
*p++ = 'm';
71+
*p++ = 'o';
4272
*p = 0;
43-
CreateDirectory(path, 0);
73+
if (create_directories)
74+
__imp_CreateDirectoryW(path, 0);
4475
*p++ = '\\';
4576
*p++ = 's';
4677
*p++ = 'i';
4778
*p++ = 'g';
4879
*p = 0;
49-
CreateDirectory(path, 0);
80+
if (create_directories)
81+
__imp_CreateDirectoryW(path, 0);
5082
*p++ = '\\';
51-
p = __itoa16(p, (pid & 0x000fff00) >> 8);
83+
p = __itoa16(p, (pid & 0x000ff800) >> 11);
5284
*p = 0;
53-
CreateDirectory(path, 0);
85+
if (create_directories)
86+
__imp_CreateDirectoryW(path, 0);
5487
*p++ = '\\';
5588
p = __itoa16(p, pid);
5689
*p++ = '.';
@@ -61,33 +94,25 @@ textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid) {
6194
return path;
6295
}
6396

64-
textwindows static atomic_ulong *__sig_map_process_impl(int pid,
65-
int disposition) {
97+
__msabi textwindows atomic_ulong *__sig_map_process(int pid, int disposition) {
6698
char16_t path[128];
67-
intptr_t hand = CreateFile(__sig_process_path(path, pid),
68-
kNtGenericRead | kNtGenericWrite,
69-
kNtFileShareRead | kNtFileShareWrite, 0,
70-
disposition, kNtFileAttributeNormal, 0);
99+
__sig_process_path(path, pid, disposition == kNtOpenAlways);
100+
intptr_t hand = __imp_CreateFileW(path, kNtGenericRead | kNtGenericWrite,
101+
kNtFileShareRead | kNtFileShareWrite, 0,
102+
disposition, kNtFileAttributeNormal, 0);
71103
if (hand == -1)
72104
return 0;
73-
SetFilePointer(hand, 8, 0, kNtFileBegin);
74-
SetEndOfFile(hand);
75-
intptr_t map = CreateFileMapping(hand, 0, kNtPageReadwrite, 0, 8, 0);
105+
__imp_SetFilePointer(hand, 8, 0, kNtFileBegin);
106+
__imp_SetEndOfFile(hand);
107+
intptr_t map = __imp_CreateFileMappingW(hand, 0, kNtPageReadwrite, 0, 8, 0);
76108
if (!map) {
77-
CloseHandle(hand);
109+
__imp_CloseHandle(hand);
78110
return 0;
79111
}
80-
atomic_ulong *sigs = MapViewOfFileEx(map, kNtFileMapWrite, 0, 0, 8, 0);
81-
CloseHandle(map);
82-
CloseHandle(hand);
112+
atomic_ulong *sigs = __imp_MapViewOfFileEx(map, kNtFileMapWrite, 0, 0, 8, 0);
113+
__imp_CloseHandle(map);
114+
__imp_CloseHandle(hand);
83115
return sigs;
84116
}
85117

86-
textwindows atomic_ulong *__sig_map_process(int pid, int disposition) {
87-
int e = errno;
88-
atomic_ulong *res = __sig_map_process_impl(pid, disposition);
89-
errno = e;
90-
return res;
91-
}
92-
93118
#endif /* __x86_64__ */

libc/intrin/terminatethisprocess.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
#include "libc/runtime/internal.h"
2828
#ifdef __x86_64__
2929

30+
__msabi extern typeof(DeleteFile) *const __imp_DeleteFileW;
3031
__msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess;
32+
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
3133

3234
/**
3335
* Terminates the calling process and all of its threads.
@@ -40,8 +42,8 @@ textwindows dontinstrument void TerminateThisProcess(uint32_t dwWaitStatus) {
4042
atomic_ulong fake = 0;
4143
real = __sig.process;
4244
__sig.process = &fake;
43-
UnmapViewOfFile(real);
44-
DeleteFile(__sig_process_path(path, __pid));
45+
__imp_UnmapViewOfFile(real);
46+
__imp_DeleteFileW(__sig_process_path(path, __pid, false));
4547

4648
// "When a process terminates itself, TerminateProcess stops execution
4749
// of the calling thread and does not return." -Quoth MSDN

libc/proc/fork-nt.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,6 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
465465
__morph_tls();
466466
__tls_enabled = true;
467467
// the child's pending signals is initially empty
468-
atomic_store_explicit(__sig.process, 0, memory_order_relaxed);
469468
atomic_store_explicit(&tib->tib_sigpending, 0, memory_order_relaxed);
470469
// re-apply code morphing for function tracing
471470
if (ftrace_stackdigs)

libc/proc/kill-nt.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,22 @@ textwindows int sys_kill_nt(int pid, int sig) {
5959
if (pid <= 0 || pid == getpid()) {
6060
if (sig) {
6161
if (pid <= 0) {
62+
// if pid is 0 or -1 then kill the processes beneath us too.
63+
// this isn't entirely right but it's closer to being right.
64+
// having this behavior is helpful for servers like redbean.
6265
struct Dll *e;
6366
BLOCK_SIGNALS;
6467
__proc_lock();
65-
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e))
66-
TerminateProcess(PROC_CONTAINER(e)->handle, sig);
68+
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
69+
atomic_ulong *sigproc;
70+
struct Proc *pr = PROC_CONTAINER(e);
71+
if (sig != 9 && (sigproc = __sig_map_process(pid, kNtOpenExisting))) {
72+
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
73+
memory_order_release);
74+
} else {
75+
TerminateProcess(pr->handle, sig);
76+
}
77+
}
6778
__proc_unlock();
6879
ALLOW_SIGNALS;
6980
}
@@ -73,28 +84,38 @@ textwindows int sys_kill_nt(int pid, int sig) {
7384
}
7485
}
7586

76-
// attempt to signal via /var/sig shared memory file
87+
// find existing handle we own for process
88+
//
89+
// this step should come first to verify process existence. this is
90+
// because there's no guarantee that just because the shared memory
91+
// file exists, the process actually exists.
92+
int64_t handle, closeme = 0;
93+
if (!(handle = __proc_handle(pid))) {
94+
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
95+
closeme = handle;
96+
} else {
97+
goto OnError;
98+
}
99+
}
100+
101+
// attempt to signal via shared memory file
102+
//
103+
// now that we know the process exists, if it has a shared memory file
104+
// then we can be reasonably certain it's a cosmo process which should
105+
// be trusted to deliver its signal, unless it's a nine exterminations
77106
if (pid > 0 && sig != 9) {
78107
atomic_ulong *sigproc;
79108
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
80109
if (sig > 0)
81110
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
82111
memory_order_release);
83112
UnmapViewOfFile(sigproc);
113+
if (closeme)
114+
CloseHandle(closeme);
84115
return 0;
85116
}
86117
}
87118

88-
// find existing handle we own for process
89-
int64_t handle, closeme = 0;
90-
if (!(handle = __proc_handle(pid))) {
91-
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
92-
closeme = handle;
93-
} else {
94-
goto OnError;
95-
}
96-
}
97-
98119
// perform actual kill
99120
// process will report WIFSIGNALED with WTERMSIG(sig)
100121
bool32 ok = TerminateProcess(handle, sig);

libc/proc/kill.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@
2929
* The impact of this action can be terminating the process, or
3030
* interrupting it to request something happen.
3131
*
32+
* On Windows, signals are delivered between processes using shared
33+
* memory files stored in C:\ProgramData\cosmo\sig\x\y.pid which hold
34+
* the process signal mask. Any process that can access these files can
35+
* signal a cosmo process. The targeting process will then notice that a
36+
* signal has been added and delivers to any thread as soon as possible.
37+
*
38+
* On Windows, the concept of a process group isn't fully implemented.
39+
* Saying `kill(0, sig)` will deliver `sig` to all direct descendent
40+
* processes. Saying `kill(-pid, sig)` will be the same as saying
41+
* `kill(pid, sig)`.
42+
*
3243
* @param pid can be:
3344
* >0 signals one process by id
3445
* =0 signals all processes in current process group

libc/proc/proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ textwindows int64_t __proc_search(int pid) {
323323
int64_t handle = 0;
324324
BLOCK_SIGNALS;
325325
__proc_lock();
326+
// TODO(jart): we should increment a reference count when returning
326327
for (e = dll_first(__proc.list); e; e = dll_next(__proc.list, e)) {
327328
if (pid == PROC_CONTAINER(e)->pid) {
328329
handle = PROC_CONTAINER(e)->handle;

0 commit comments

Comments
 (0)