Skip to content

Commit a6b51aa

Browse files
authored
Add improved support for signals (raise, kill, sigaction, etc) (#14883)
Although we don't have any support for external or async signal a lot of this functionality works fine within a single process (e.g. using raise the syncronously run signal handlers in your own process). I'm adding this so that as a followup we can implement `pthread_kill` to send signal between threads. See: #14872
1 parent 3c61cc2 commit a6b51aa

File tree

16 files changed

+370
-93
lines changed

16 files changed

+370
-93
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ See docs/process.md for more on how version tagging works.
2020

2121
2.0.27
2222
------
23+
- Added some support for signal handling libc functions (raise, kill,
24+
sigaction, sigpending, etc). We still don't have a way to deliver signals from
25+
the outside but these at least now work for sending signals to the current
26+
thread (JS context) (#14883).
2327
- Added `EM_ASYNC_JS` macro - similar to `EM_JS`, but allows using `await`
2428
inside the JS block and automatically integrates with Asyncify without
2529
the need for listing the declared function in `ASYNCIFY_IMPORTS` (#9709).

src/library.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,6 +2619,20 @@ LibraryManager.library = {
26192619
return 0;
26202620
},
26212621

2622+
// http://pubs.opengroup.org/onlinepubs/000095399/functions/alarm.html
2623+
alarm__deps: ['raise'],
2624+
alarm: function(seconds) {
2625+
setTimeout(function() {
2626+
_raise({{{ cDefine('SIGALRM') }}});
2627+
}, seconds*1000);
2628+
},
2629+
2630+
// Helper for raise() to avoid signature mismatch failures:
2631+
// https://github.com/emscripten-core/posixtestsuite/issues/6
2632+
__call_sighandler: function(fp, sig) {
2633+
{{{ makeDynCall('vi', 'fp') }}}(sig);
2634+
},
2635+
26222636
// ==========================================================================
26232637
// emscripten.h
26242638
// ==========================================================================
@@ -3681,6 +3695,12 @@ LibraryManager.library = {
36813695
},
36823696
#endif
36833697

3698+
$safeSetTimeout__deps: ['$callUserCallback',
3699+
#if !MINIMAL_RUNTIME
3700+
'$runtimeKeepalivePush',
3701+
'$runtimeKeepalivePop',
3702+
#endif
3703+
],
36843704
$safeSetTimeout: function(func, timeout) {
36853705
{{{ runtimeKeepalivePush() }}}
36863706
return setTimeout(function() {

src/library_signals.js

Lines changed: 0 additions & 85 deletions
This file was deleted.

src/modules.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ var LibraryManager = {
6161
'library_formatString.js',
6262
'library_math.js',
6363
'library_path.js',
64-
'library_signals.js',
6564
'library_syscall.js',
6665
'library_html5.js',
6766
'library_stack_trace.js',

system/lib/libc/kill.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2021 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <signal.h>
9+
#include <stdio.h>
10+
#include <unistd.h>
11+
#include <errno.h>
12+
13+
int kill(pid_t pid, int sig) {
14+
if (pid == getpid()) {
15+
return raise(sig);
16+
}
17+
errno = EPERM;
18+
return -1;
19+
}

system/lib/libc/musl/include/signal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ int sigandset(sigset_t *, const sigset_t *, const sigset_t *);
259259

260260
#define SIG_ERR ((void (*)(int))-1)
261261
#define SIG_DFL ((void (*)(int)) 0)
262-
#define SIG_IGN ((void (*)(int)) 1)
262+
#define SIG_IGN ((void (*)(int))-2) /* XXX EMSCRIPTEN: use -2 since 1 is a valid function address */
263263

264264
typedef int sig_atomic_t;
265265

system/lib/libc/pthread_sigmask.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2021 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#define _GNU_SOURCE // for sigorset/sigandset
9+
#include <stdbool.h>
10+
#include <threads.h>
11+
#include <signal.h>
12+
#include <errno.h>
13+
#include "libc.h"
14+
15+
#define SST_SIZE (_NSIG/8/sizeof(long))
16+
17+
static thread_local sigset_t __sig_mask;
18+
sigset_t __sig_pending;
19+
20+
static int siginvertset(sigset_t *dest, const sigset_t *src) {
21+
unsigned long i = 0, *d = (void*) dest, *s = (void*) src;
22+
for(; i < SST_SIZE; i++) d[i] = ~s[i];
23+
return 0;
24+
}
25+
26+
bool __sig_is_blocked(int sig) {
27+
return sigismember(&__sig_mask, sig);
28+
}
29+
30+
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict old) {
31+
if (old) {
32+
*old = __sig_mask;
33+
}
34+
35+
switch (how) {
36+
case SIG_SETMASK:
37+
__sig_mask = *set;
38+
break;
39+
case SIG_BLOCK:
40+
sigorset(&__sig_mask, &__sig_mask, set);
41+
break;
42+
case SIG_UNBLOCK: {
43+
sigset_t tmp;
44+
siginvertset(&tmp, set);
45+
sigandset(&__sig_mask, &__sig_mask, &tmp);
46+
break;
47+
}
48+
default:
49+
return EINVAL;
50+
}
51+
52+
// These two signals can never be blocked.
53+
sigdelset(&__sig_mask, SIGKILL);
54+
sigdelset(&__sig_mask, SIGSTOP);
55+
56+
// Raise any pending signals that are now unblocked.
57+
for (int sig = 0; sig < _NSIG; sig++) {
58+
if (sigismember(&__sig_pending, sig) && !sigismember(&__sig_mask, sig)) {
59+
sigdelset(&__sig_pending, sig);
60+
raise(sig);
61+
}
62+
}
63+
64+
return 0;
65+
}
66+
67+
int sigpending(sigset_t *set) {
68+
*set = __sig_pending;
69+
return 0;
70+
}

system/lib/libc/raise.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2021 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#define _GNU_SOURCE // for sighandler_t
9+
#include <stdbool.h>
10+
#include <stddef.h>
11+
#include <signal.h>
12+
13+
extern struct sigaction __sig_actions[_NSIG];
14+
extern sigset_t __sig_pending;
15+
16+
bool __sig_is_blocked(int sig);
17+
18+
extern void __call_sighandler(sighandler_t handler, int sig);
19+
20+
int raise(int sig) {
21+
if (__sig_is_blocked(sig)) {
22+
sigaddset(&__sig_pending, sig);
23+
return 0;
24+
}
25+
if (__sig_actions[sig].sa_flags & SA_SIGINFO) {
26+
siginfo_t t = {0};
27+
__sig_actions[sig].sa_sigaction(sig, &t, NULL);
28+
} else {
29+
if (__sig_actions[sig].sa_handler == SIG_DFL || __sig_actions[sig].sa_handler == SIG_IGN) {
30+
31+
return 0;
32+
}
33+
// Avoid a direct call to the handler, and instead call via JS so we can
34+
// avoid strict signature checking.
35+
// https://github.com/emscripten-core/posixtestsuite/issues/6
36+
__call_sighandler(__sig_actions[sig].sa_handler, sig);
37+
}
38+
return 0;
39+
}

system/lib/libc/sigaction.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2021 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <stdio.h>
9+
#include <signal.h>
10+
#include <errno.h>
11+
#include "libc.h"
12+
13+
struct sigaction __sig_actions[_NSIG];
14+
15+
int __sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old) {
16+
if (sig < 0 || sig >= _NSIG) {
17+
errno = EINVAL;
18+
return -1;
19+
}
20+
21+
if (old) {
22+
*old = __sig_actions[sig];
23+
}
24+
25+
__sig_actions[sig] = *sa;
26+
return 0;
27+
}
28+
29+
weak_alias(__sigaction, sigaction);

system/lib/libc/sigtimedwait.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2021 The Emscripten Authors. All rights reserved.
3+
* Emscripten is available under two separate licenses, the MIT license and the
4+
* University of Illinois/NCSA Open Source License. Both these licenses can be
5+
* found in the LICENSE file.
6+
*/
7+
8+
#include <signal.h>
9+
#include <errno.h>
10+
#include "syscall.h"
11+
#include "libc.h"
12+
13+
extern sigset_t __sig_pending;
14+
15+
int sigtimedwait(const sigset_t *restrict mask, siginfo_t *restrict si, const struct timespec *restrict timeout) {
16+
for (int sig = 0; sig < _NSIG; sig++) {
17+
if (sigismember(mask, sig) && sigismember(&__sig_pending, sig)) {
18+
if (si) {
19+
siginfo_t t = {0};
20+
*si = t;
21+
}
22+
sigdelset(&__sig_pending, sig);
23+
return sig;
24+
}
25+
}
26+
27+
errno = EINVAL;
28+
return -1;
29+
}

0 commit comments

Comments
 (0)