Skip to content

Commit c4bdb57

Browse files
committed
Add improved support for signals (raise, kill, sigaction, etc)
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 b1c167c commit c4bdb57

File tree

16 files changed

+335
-93
lines changed

16 files changed

+335
-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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <signal.h>
2+
#include <stdio.h>
3+
#include <unistd.h>
4+
#include <errno.h>
5+
6+
int kill(pid_t pid, int sig) {
7+
if (pid == getpid()) {
8+
return raise(sig);
9+
}
10+
errno = EPERM;
11+
return -1;
12+
}

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: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#define _GNU_SOURCE // for sigorset/sigandset
2+
#include <stdbool.h>
3+
#include <threads.h>
4+
#include <signal.h>
5+
#include <errno.h>
6+
#include "libc.h"
7+
8+
#define SST_SIZE (_NSIG/8/sizeof(long))
9+
10+
static thread_local sigset_t __sig_mask;
11+
sigset_t __sig_pending;
12+
13+
static int siginvertset(sigset_t *dest, const sigset_t *src) {
14+
unsigned long i = 0, *d = (void*) dest, *s = (void*) src;
15+
for(; i < SST_SIZE; i++) d[i] = ~s[i];
16+
return 0;
17+
}
18+
19+
bool __sig_is_blocked(int sig) {
20+
return sigismember(&__sig_mask, sig);
21+
}
22+
23+
int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict old) {
24+
if (old) {
25+
*old = __sig_mask;
26+
}
27+
28+
switch (how) {
29+
case SIG_SETMASK:
30+
__sig_mask = *set;
31+
break;
32+
case SIG_BLOCK:
33+
sigorset(&__sig_mask, &__sig_mask, set);
34+
break;
35+
case SIG_UNBLOCK: {
36+
sigset_t tmp;
37+
siginvertset(&tmp, set);
38+
sigandset(&__sig_mask, &__sig_mask, &tmp);
39+
break;
40+
}
41+
default:
42+
return EINVAL;
43+
}
44+
45+
// These two signals can never be blocked.
46+
sigdelset(&__sig_mask, SIGKILL);
47+
sigdelset(&__sig_mask, SIGSTOP);
48+
49+
// Raise any pending signals that are now unblocked.
50+
for (int sig = 0; sig < _NSIG; sig++) {
51+
if (sigismember(&__sig_pending, sig) && !sigismember(&__sig_mask, sig)) {
52+
sigdelset(&__sig_pending, sig);
53+
raise(sig);
54+
}
55+
}
56+
57+
return 0;
58+
}
59+
60+
int sigpending(sigset_t *set) {
61+
*set = __sig_pending;
62+
return 0;
63+
}

system/lib/libc/raise.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#define _GNU_SOURCE // for sighandler_t
2+
#include <stdbool.h>
3+
#include <stddef.h>
4+
#include <signal.h>
5+
6+
extern struct sigaction __sig_actions[_NSIG];
7+
extern sigset_t __sig_pending;
8+
9+
bool __sig_is_blocked(int sig);
10+
11+
extern void __call_sighandler(sighandler_t handler, int sig);
12+
13+
int raise(int sig) {
14+
if (__sig_is_blocked(sig)) {
15+
sigaddset(&__sig_pending, sig);
16+
return 0;
17+
}
18+
if (__sig_actions[sig].sa_flags & SA_SIGINFO) {
19+
siginfo_t t = {0};
20+
__sig_actions[sig].sa_sigaction(sig, &t, NULL);
21+
} else {
22+
if (__sig_actions[sig].sa_handler == SIG_DFL || __sig_actions[sig].sa_handler == SIG_IGN) {
23+
24+
return 0;
25+
}
26+
// Avoid a direct call to the handler, and instead call via JS so we can
27+
// avoid strict signature checking.
28+
// https://github.com/emscripten-core/posixtestsuite/issues/6
29+
__call_sighandler(__sig_actions[sig].sa_handler, sig);
30+
}
31+
return 0;
32+
}

system/lib/libc/sigaction.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <stdio.h>
2+
#include <signal.h>
3+
#include <errno.h>
4+
#include "libc.h"
5+
6+
struct sigaction __sig_actions[_NSIG];
7+
8+
int __sigaction(int sig, const struct sigaction *restrict sa, struct sigaction *restrict old) {
9+
if (sig < 0 || sig >= _NSIG) {
10+
errno = EINVAL;
11+
return -1;
12+
}
13+
14+
if (old) {
15+
*old = __sig_actions[sig];
16+
}
17+
18+
__sig_actions[sig] = *sa;
19+
return 0;
20+
}
21+
22+
weak_alias(__sigaction, sigaction);

system/lib/libc/sigtimedwait.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <signal.h>
2+
#include <errno.h>
3+
#include "syscall.h"
4+
#include "libc.h"
5+
6+
extern sigset_t __sig_pending;
7+
8+
int sigtimedwait(const sigset_t *restrict mask, siginfo_t *restrict si, const struct timespec *restrict timeout) {
9+
for (int sig = 0; sig < _NSIG; sig++) {
10+
if (sigismember(mask, sig) && sigismember(&__sig_pending, sig)) {
11+
if (si) {
12+
siginfo_t t = {0};
13+
*si = t;
14+
}
15+
sigdelset(&__sig_pending, sig);
16+
return sig;
17+
}
18+
}
19+
20+
errno = EINVAL;
21+
return -1;
22+
}

tests/test_core.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5319,6 +5319,9 @@ def test_fs_64bit(self):
53195319
def test_sigalrm(self):
53205320
self.do_runf(test_file('test_sigalrm.c'), 'Received alarm!')
53215321

5322+
def test_signals(self):
5323+
self.do_core_test(test_file('test_signals.c'))
5324+
53225325
@no_windows('https://github.com/emscripten-core/emscripten/issues/8882')
53235326
def test_unistd_access(self):
53245327
self.uses_es6 = True

tests/test_posixtest.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ class posixtest(RunnerCore):
2929

3030
def filter_tests(all_tests):
3131
pthread_tests = [t for t in all_tests if t.startswith('pthread_')]
32-
# filter out some tests we don't support
33-
pthread_tests = [t for t in pthread_tests if not t.startswith('pthread_sigmask')]
3432
return pthread_tests
3533

3634

@@ -56,15 +54,15 @@ def get_pthread_tests():
5654
'test_pthread_atfork_3_2': 'fork() and multiple processes are not supported',
5755
'test_pthread_atfork_4_1': 'fork() and multiple processes are not supported',
5856
'test_pthread_kill_1_1': 'signals are not supported',
59-
'test_pthread_create_1_5': 'semaphores are not supported',
57+
'test_pthread_create_1_5': 'fork() and multiple processes are not supported',
6058
'test_pthread_exit_6_1': 'lacking necessary mmap() support',
6159
'test_pthread_spin_lock_1_1': 'signals are not supported',
6260
'test_pthread_mutex_lock_5_1': 'signals are not supported',
6361
'test_pthread_mutexattr_settype_2_1': 'interrupting pthread_mutex_lock wait via SIGALRM is not supported',
6462
'test_pthread_spin_lock_3_1': 'signals are not supported',
65-
'test_pthread_create_14_1': 'signals are not supported',
66-
'test_pthread_detach_4_3': 'signals are not supported',
67-
'test_pthread_join_6_3': 'signals are not supported',
63+
'test_pthread_create_14_1': 'creates too many threads',
64+
'test_pthread_detach_4_3': 'creates too many threads',
65+
'test_pthread_join_6_3': 'creates too many threads',
6866
'test_pthread_barrier_wait_3_2': 'signals are not supported',
6967
'test_pthread_cond_broadcast_1_2': 'tries to create 10,0000 threads, then depends on fork()',
7068
}

0 commit comments

Comments
 (0)