Skip to content

Commit

Permalink
Introduce interprocess signaling on Windows
Browse files Browse the repository at this point in the history
This change gets rsync working without any warning or errors. On Windows
we now create a bunch of C:\var\sig\x\y.pid shared memory files, so sigs
can be delivered between processes. WinMain() creates this file when the
process starts. If the program links signaling system calls then we make
a thread at startup too, which allows asynchronous delivery each quantum
and cancelation points can spot these signals potentially faster on wait

See #1240
  • Loading branch information
jart committed Sep 19, 2024
1 parent 8527462 commit 0d74673
Show file tree
Hide file tree
Showing 22 changed files with 302 additions and 62 deletions.
22 changes: 3 additions & 19 deletions libc/calls/createpipename.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,20 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/atomic.h"
#include "libc/fmt/internal.h"
#include "libc/intrin/atomic.h"
#include "libc/runtime/internal.h"

static textwindows char16_t *itoa16(char16_t p[21], uint64_t x) {
char t;
size_t a, b, i = 0;
do {
p[i++] = x % 10 + '0';
x = x / 10;
} while (x > 0);
if (i) {
for (a = 0, b = i - 1; a < b; ++a, --b) {
t = p[a];
p[a] = p[b];
p[b] = t;
}
}
return p + i;
}

// This function is called very early by WinMain().
textwindows char16_t *__create_pipe_name(char16_t *a) {
char16_t *p = a;
const char *q = "\\\\?\\pipe\\cosmo\\";
static atomic_uint x;
while (*q)
*p++ = *q++;
p = itoa16(p, __pid);
p = __itoa16(p, __pid);
*p++ = '-';
p = itoa16(p, atomic_fetch_add(&x, 1));
p = __itoa16(p, atomic_fetch_add(&x, 1));
*p = 0;
return a;
}
32 changes: 27 additions & 5 deletions libc/calls/sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@
#include "libc/intrin/bsf.h"
#include "libc/intrin/describebacktrace.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/maps.h"
#include "libc/intrin/strace.h"
#include "libc/intrin/weaken.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/context.h"
#include "libc/nt/enum/exceptionhandleractions.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/nt/enum/signal.h"
#include "libc/nt/enum/status.h"
#include "libc/nt/events.h"
Expand All @@ -46,6 +47,7 @@
#include "libc/nt/struct/ntexceptionpointers.h"
#include "libc/nt/synchronization.h"
#include "libc/nt/thread.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sa.h"
Expand All @@ -58,6 +60,8 @@
* @fileoverview Cosmopolitan Signals for Windows.
*/

#define STKSZ 65536

struct SignalFrame {
unsigned rva;
unsigned flags;
Expand All @@ -80,7 +84,7 @@ textwindows bool __sig_ignored(int sig) {

textwindows void __sig_delete(int sig) {
struct Dll *e;
atomic_fetch_and_explicit(&__sig.pending, ~(1ull << (sig - 1)),
atomic_fetch_and_explicit(__sig.process, ~(1ull << (sig - 1)),
memory_order_relaxed);
_pthread_lock();
for (e = dll_last(_pthread_list); e; e = dll_prev(_pthread_list, e))
Expand Down Expand Up @@ -108,7 +112,7 @@ static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) {
textwindows int __sig_get(sigset_t masked) {
int sig;
if (!(sig = __sig_getter(&__get_tls()->tib_sigpending, masked)))
sig = __sig_getter(&__sig.pending, masked);
sig = __sig_getter(__sig.process, masked);
return sig;
}

Expand Down Expand Up @@ -179,6 +183,7 @@ textwindows int __sig_raise(volatile int sig, int sic) {
unsigned rva, flags;
struct PosixThread *pt = _pthread_self();
if (__sig_start(pt, sig, &rva, &flags)) {

if (flags & SA_RESETHAND) {
STRACE("resetting %G handler", sig);
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
Expand Down Expand Up @@ -410,7 +415,7 @@ textwindows void __sig_generate(int sig, int sic) {
STRACE("terminating on %G due to no handler", sig);
__sig_terminate(sig);
}
if (atomic_load_explicit(&__sig.pending, memory_order_acquire) &
if (atomic_load_explicit(__sig.process, memory_order_acquire) &
(1ull << (sig - 1))) {
return;
}
Expand Down Expand Up @@ -448,7 +453,7 @@ textwindows void __sig_generate(int sig, int sic) {
__sig_killer(mark, sig, sic);
_pthread_unref(mark);
} else {
atomic_fetch_or_explicit(&__sig.pending, 1ull << (sig - 1),
atomic_fetch_or_explicit(__sig.process, 1ull << (sig - 1),
memory_order_relaxed);
}
ALLOW_SIGNALS;
Expand Down Expand Up @@ -611,11 +616,28 @@ textwindows int __sig_check(void) {
}
}

// delivers signals from other processes asynchronously
textwindows dontinstrument static uint32_t __sig_worker(void *arg) {
struct CosmoTib tls;
__bootstrap_tls(&tls, __builtin_frame_address(0));
char *sp = __builtin_frame_address(0);
__maps_track((char *)(((uintptr_t)sp + __pagesize - 1) & -__pagesize) - STKSZ,
STKSZ);
for (;;) {
int sig;
if ((sig = __sig_getter(__sig.process, 0)))
__sig_generate(sig, SI_KERNEL);
Sleep(1);
}
return 0;
}

__attribute__((__constructor__(10))) textstartup void __sig_init(void) {
if (!IsWindows())
return;
AddVectoredExceptionHandler(true, (void *)__sig_crash);
SetConsoleCtrlHandler((void *)__sig_console, true);
CreateThread(0, STKSZ, __sig_worker, 0, kNtStackSizeParamIsAReservation, 0);
}

#endif /* __x86_64__ */
8 changes: 6 additions & 2 deletions libc/calls/sig.internal.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_
#include "libc/atomic.h"
#include "libc/calls/struct/sigset.h"
#include "libc/thread/posixthread.internal.h"

Expand All @@ -9,8 +10,8 @@
COSMOPOLITAN_C_START_

struct Signals {
_Atomic(uint64_t) pending;
_Atomic(uint64_t) count;
atomic_ulong *process;
atomic_ulong count;
};

extern struct Signals __sig;
Expand All @@ -27,5 +28,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);

COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_LIBC_CALLS_SIGNALS_INTERNAL_H_ */
2 changes: 1 addition & 1 deletion libc/calls/sigpending.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ int sigpending(sigset_t *pending) {
}
rc = 0;
} else if (IsWindows()) {
*pending = atomic_load_explicit(&__sig.pending, memory_order_acquire) |
*pending = atomic_load_explicit(__sig.process, memory_order_acquire) |
atomic_load_explicit(&__get_tls()->tib_sigpending,
memory_order_acquire);
rc = 0;
Expand Down
1 change: 1 addition & 0 deletions libc/fmt/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,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);

#endif /* COSMOPOLITAN_LIBC_FMT_STRTOL_H_ */
36 changes: 36 additions & 0 deletions libc/intrin/itoa16.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2024 Justine Alexandra Roberts Tunney │
│ │
│ Permission to use, copy, modify, and/or distribute this software for │
│ any purpose with or without fee is hereby granted, provided that the │
│ above copyright notice and this permission notice appear in all copies. │
│ │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/fmt/internal.h"

textwindows char16_t *__itoa16(char16_t p[21], uint64_t x) {
char t;
size_t a, b, i = 0;
do {
p[i++] = x % 10 + '0';
x = x / 10;
} while (x > 0);
if (i) {
for (a = 0, b = i - 1; a < b; ++a, --b) {
t = p[a];
p[a] = p[b];
p[b] = t;
}
}
return p + i;
}
76 changes: 76 additions & 0 deletions libc/intrin/sig.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,25 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/sysv/consts/sig.h"
#include "libc/atomic.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/dce.h"
#include "libc/fmt/internal.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.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"
#include "libc/nt/enum/filesharemode.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/files.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/thread/tls.h"

struct Signals __sig;
Expand Down Expand Up @@ -52,3 +66,65 @@ void __sig_unblock(sigset_t m) {
sys_sigprocmask(SIG_SETMASK, &m, 0);
}
}

#ifdef __x86_64__

textwindows char16_t *__sig_process_path(char16_t *path, uint32_t pid) {
char16_t *p = path;
*p++ = 'C';
*p++ = ':';
*p++ = '\\';
*p++ = 'v';
*p++ = 'a';
*p++ = 'r';
*p = 0;
CreateDirectory(path, 0);
*p++ = '\\';
*p++ = 's';
*p++ = 'i';
*p++ = 'g';
*p = 0;
CreateDirectory(path, 0);
*p++ = '\\';
p = __itoa16(p, (pid & 0x000fff00) >> 8);
*p = 0;
CreateDirectory(path, 0);
*p++ = '\\';
p = __itoa16(p, pid);
*p++ = '.';
*p++ = 'p';
*p++ = 'i';
*p++ = 'd';
*p = 0;
return path;
}

textwindows static atomic_ulong *__sig_map_process_impl(int pid) {
char16_t path[128];
intptr_t hand = CreateFile(__sig_process_path(path, pid),
kNtGenericRead | kNtGenericWrite,
kNtFileShareRead | kNtFileShareWrite, 0,
kNtOpenAlways, kNtFileAttributeNormal, 0);
if (hand == -1)
return 0;
SetFilePointer(hand, 8, 0, kNtFileBegin);
SetEndOfFile(hand);
intptr_t map = CreateFileMapping(hand, 0, kNtPageReadwrite, 0, 8, 0);
if (!map) {
CloseHandle(hand);
return 0;
}
atomic_ulong *sigs = MapViewOfFileEx(map, kNtFileMapWrite, 0, 0, 8, 0);
CloseHandle(map);
CloseHandle(hand);
return sigs;
}

textwindows atomic_ulong *__sig_map_process(int pid) {
int e = errno;
atomic_ulong *res = __sig_map_process_impl(pid);
errno = e;
return res;
}

#endif /* __x86_64__ */
3 changes: 2 additions & 1 deletion libc/intrin/sigprocmask-nt.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/intrin/atomic.h"
Expand All @@ -34,7 +35,7 @@ textwindows int __sig_mask(int how, const sigset_t *neu, sigset_t *old) {
}

// get address of sigset to modify
_Atomic(uint64_t) *mask = &__get_tls()->tib_sigmask;
atomic_ulong *mask = &__get_tls()->tib_sigmask;

// handle read-only case
sigset_t oldmask;
Expand Down
20 changes: 20 additions & 0 deletions libc/intrin/terminatethisprocess.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,37 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/atomic.h"
#include "libc/calls/sig.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/limits.h"
#include "libc/nt/files.h"
#include "libc/nt/memory.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/internal.h"
#ifdef __x86_64__

__msabi extern typeof(TerminateProcess) *const __imp_TerminateProcess;

/**
* Terminates the calling process and all of its threads.
*/
textwindows dontinstrument void TerminateThisProcess(uint32_t dwWaitStatus) {

// delete sig file
char16_t path[128];
atomic_ulong *real;
atomic_ulong fake = 0;
real = __sig.process;
__sig.process = &fake;
UnmapViewOfFile(real);
DeleteFile(__sig_process_path(path, __pid));

// "When a process terminates itself, TerminateProcess stops execution
// of the calling thread and does not return." -Quoth MSDN
__imp_TerminateProcess(-1, dwWaitStatus);
__builtin_unreachable();
}

#endif /* __x86_64__ */
4 changes: 4 additions & 0 deletions libc/nt/files.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ bool32 GetVolumeInformationByHandle(int64_t hFile,
char16_t *opt_out_lpFileSystemNameBuffer,
uint32_t nFileSystemNameSize);

uint32_t SetFilePointer(intptr_t hFile, int32_t lDistanceToMove,
long *opt_inout_lpDistanceToMoveHigh,
uint32_t dwMoveMethod);

#if ShouldUseMsabiAttribute()
#include "libc/nt/thunk/files.inc"
#endif /* ShouldUseMsabiAttribute() */
Expand Down
Loading

0 comments on commit 0d74673

Please sign in to comment.