Skip to content

Commit c740175

Browse files
committed
Move signal tests from XcodeTools in this repo
1 parent b766487 commit c740175

File tree

6 files changed

+495
-0
lines changed

6 files changed

+495
-0
lines changed

Doc/signal-tests/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/signal-test-blocked
2+
/signal-test-sigaction
3+
/signal-tests-macos
4+
/signal-tests-linux

Doc/signal-tests/run-tests.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
set -uo pipefail
3+
4+
readonly LINUX_SWIFT_IMAGE="swift:5.3.3"
5+
6+
# This can only be run on macOS
7+
test "$(uname -s)" = "Darwin"
8+
9+
cd "$(dirname "$0")"
10+
11+
echo "*** RUNNING TEST SIGACTION (C) ON MACOS"
12+
make signal-test-sigaction && ./signal-test-sigaction
13+
rm signal-test-sigaction
14+
15+
echo
16+
echo
17+
echo "*** RUNNING TEST SIGACTION (C) ON LINUX"
18+
docker run --rm -it -v "$(pwd):/tmp/cwd" --workdir /tmp/cwd --security-opt=seccomp:unconfined --entrypoint bash "$LINUX_SWIFT_IMAGE" -c '
19+
make signal-test-sigaction && ./signal-test-sigaction; rm signal-test-sigaction
20+
'
21+
22+
echo
23+
echo
24+
echo "*** RUNNING TEST BLOCKED (C) ON MACOS"
25+
make signal-test-blocked && ./signal-test-blocked
26+
rm signal-test-blocked
27+
28+
echo
29+
echo
30+
echo "*** RUNNING TEST BLOCKED (C) ON LINUX"
31+
docker run --rm -it -v "$(pwd):/tmp/cwd" --workdir /tmp/cwd --security-opt=seccomp:unconfined --entrypoint bash "$LINUX_SWIFT_IMAGE" -c '
32+
CC=clang CFLAGS="-I/usr/lib/swift -fblocks" LDFLAGS="-L/usr/lib/swift/linux -lpthread -ldispatch -lBlocksRuntime" make signal-test-blocked && LD_LIBRARY_PATH="/usr/lib/swift/linux" ./signal-test-blocked
33+
rm signal-test-blocked
34+
'
35+
36+
echo
37+
echo
38+
echo "*** RUNNING GENERIC TESTS (SWIFT) ON MACOS"
39+
# We must compile, when run via swift as a script, some signal are handled by Swift itself
40+
swiftc ./signal-tests-macos.swift && ./signal-tests-macos
41+
rm signal-tests-macos
42+
43+
echo
44+
echo
45+
echo "*** RUNNING GENERIC TESTS (SWIFT) ON LINUX"
46+
docker run --rm -it -v "$(pwd):/tmp/cwd" --workdir /tmp/cwd --security-opt=seccomp:unconfined --entrypoint bash "$LINUX_SWIFT_IMAGE" -c '
47+
swiftc -o ./signal-tests-linux ./signal-tests-linux.swift && ./signal-tests-linux; rm signal-tests-linux
48+
'
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#include <dispatch/dispatch.h>
2+
#include <pthread.h>
3+
#include <signal.h>
4+
#include <stdio.h>
5+
6+
7+
8+
// CC=clang CFLAGS='-I/usr/lib/swift -fblocks' LDFLAGS='-L/usr/lib/swift/linux -lpthread -ldispatch -lBlocksRuntime' make signal-test-blocked
9+
// LD_LIBRARY_PATH=/usr/lib/swift/linux ./signal-test-blocked
10+
11+
#define SETUP_DISPATCH 0
12+
13+
static int s = SIGTERM;
14+
15+
typedef enum thread_action_e {
16+
INIT = 0,
17+
WAIT_INIT,
18+
NOP,
19+
UNBLOCK_SIGNAL
20+
} thread_action_t;
21+
22+
static thread_action_t thread_action = INIT;
23+
24+
static pthread_cond_t cond;
25+
static pthread_mutex_t mutex;
26+
27+
static void action(int signal) {
28+
const char *str = "🚦 Got signal in sigaction\n";
29+
write(2, str, strlen(str));
30+
}
31+
32+
static void *threadMain(void *info) {
33+
fprintf(stderr, "🧵 Thread starts!\n");
34+
35+
pthread_mutex_lock(&mutex);
36+
thread_action = WAIT_INIT;
37+
pthread_mutex_unlock(&mutex);
38+
pthread_cond_signal(&cond);
39+
40+
do {
41+
pthread_mutex_lock(&mutex);
42+
while (thread_action != UNBLOCK_SIGNAL)
43+
pthread_cond_wait(&cond, &mutex);
44+
pthread_mutex_unlock(&mutex);
45+
46+
sigset_t set;
47+
sigpending(&set);
48+
fprintf(stderr, "✊ Other thread pending: %d\n", sigismember(&set, s));
49+
50+
sigemptyset(&set);
51+
sigaddset(&set, s);
52+
pthread_sigmask(SIG_UNBLOCK, &set, NULL);
53+
54+
pthread_mutex_lock(&mutex);
55+
thread_action = NOP;
56+
pthread_mutex_unlock(&mutex);
57+
pthread_cond_signal(&cond);
58+
} while (1);
59+
60+
// fprintf(stderr, "🧵 Thread ends\n");
61+
return NULL;
62+
}
63+
64+
int main(int argc, const char * argv[]) {
65+
fprintf(stderr, "✊ Program starts!\n");
66+
67+
sigset_t set;
68+
sigemptyset(&set);
69+
sigaddset(&set, s);
70+
pthread_sigmask(SIG_BLOCK, &set, NULL);
71+
72+
pthread_cond_init(&cond, NULL);
73+
pthread_mutex_init(&mutex, NULL);
74+
75+
pthread_t thread;
76+
pthread_create(&thread, NULL, &threadMain, NULL);
77+
78+
pthread_mutex_lock(&mutex);
79+
while (thread_action != WAIT_INIT)
80+
pthread_cond_wait(&cond, &mutex);
81+
pthread_mutex_unlock(&mutex);
82+
83+
fprintf(stderr, "✊ Thread is inited\n");
84+
85+
#if SETUP_DISPATCH
86+
/* On Linux, eat signals */
87+
dispatch_queue_t signal_queue = dispatch_queue_create("signal-dispatch", NULL);
88+
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, s, 0, signal_queue);
89+
dispatch_source_set_event_handler(source, ^{
90+
fprintf(stderr, "🪡 Event from dispatch!\n");
91+
});
92+
dispatch_activate(source);
93+
#endif
94+
95+
struct sigaction act = {};
96+
act.sa_flags = 0;
97+
sigemptyset(&act.sa_mask);
98+
act.sa_handler = &action;
99+
sigaction(s, &act, NULL);
100+
101+
fprintf(stderr, "✊ Killing myself\n");
102+
kill(getpid(), s);
103+
104+
sigpending(&set);
105+
fprintf(stderr, "✊ Main thread pending: %d\n", sigismember(&set, s));
106+
107+
sleep(3);
108+
/* On macOS, when all threads block the signal, the system chooses one thread
109+
* and assigns the signal to it. Unblocking in another thread won’t move the
110+
* signal to it, and we won’t be able to access it.
111+
* On Linux, when a process-wide signal is pending, it is pending on all
112+
* the threads. If a thread unblocks the signal, it will handle it. */
113+
fprintf(stderr, "✊ Unblocking signal\n");
114+
pthread_mutex_lock(&mutex);
115+
thread_action = UNBLOCK_SIGNAL;
116+
pthread_mutex_unlock(&mutex);
117+
pthread_cond_signal(&cond);
118+
119+
sleep(1);
120+
sigpending(&set);
121+
fprintf(stderr, "✊ Main thread pending: %d\n", sigismember(&set, s));
122+
123+
#if SETUP_DISPATCH
124+
dispatch_source_cancel(source);
125+
dispatch_release(source);
126+
dispatch_release(signal_queue);
127+
#endif
128+
129+
return 0;
130+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* main.c
3+
* test
4+
*
5+
* Created by François Lamboley on 15/04/2021.
6+
*/
7+
8+
#include <signal.h>
9+
#include <stdio.h>
10+
11+
12+
13+
static void action(int signal) {
14+
fprintf(stderr, "Got %d\n", signal);
15+
}
16+
17+
18+
int main(void) {
19+
struct sigaction oldAction = {};
20+
21+
sigaction(15, NULL, &oldAction);
22+
fprintf(stderr, "%p\n", oldAction.sa_handler);
23+
24+
struct sigaction newAction = {};
25+
newAction.sa_flags = 0;
26+
sigemptyset(&newAction.sa_mask);
27+
/* We do not use sa_sigaction because it generates a warning on Linux w/
28+
* function signature `void action(int, struct __siginfo *, void *)` because
29+
* args are not exactly the same as on macOS, but would work too */
30+
newAction.sa_handler = &action;
31+
sigaction(15, &newAction, NULL);
32+
fprintf(stderr, "%p\n", newAction.sa_handler);
33+
34+
sigaction(15, NULL, &oldAction);
35+
fprintf(stderr, "%p\n", oldAction.sa_handler);
36+
37+
raise(15);
38+
39+
newAction.sa_handler = SIG_DFL;
40+
sigaction(15, &newAction, NULL);
41+
42+
sigaction(15, NULL, &oldAction);
43+
fprintf(stderr, "%p\n", oldAction.sa_handler);
44+
45+
return 0;
46+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import Foundation
2+
3+
4+
/* WORKS IN SWIFT LINUX DOCKER IMAGE */
5+
6+
7+
var doFirstTest = true
8+
var doSecondTest = true
9+
10+
var ignoreAction = sigaction()
11+
ignoreAction.sa_flags = 0
12+
sigemptyset(&ignoreAction.sa_mask)
13+
ignoreAction.__sigaction_handler.sa_handler = SIG_IGN
14+
15+
var defaultAction = sigaction()
16+
defaultAction.sa_flags = 0
17+
sigemptyset(&defaultAction.sa_mask)
18+
defaultAction.__sigaction_handler.sa_handler = SIG_DFL
19+
20+
21+
let signalSourceRetain: DispatchSourceSignal?
22+
if doFirstTest {
23+
var oldAction = sigaction()
24+
var oldActionHandlerPtr: OpaquePointer?
25+
let sigIgnOpaque = OpaquePointer(bitPattern: unsafeBitCast(SIG_IGN, to: Int.self))
26+
let sigDflOpaque = OpaquePointer(bitPattern: unsafeBitCast(SIG_DFL, to: Int.self))
27+
28+
sigaction(15, nil, &oldAction)
29+
oldActionHandlerPtr = OpaquePointer(bitPattern: unsafeBitCast(oldAction.__sigaction_handler.sa_handler, to: Int.self))
30+
print("before dispatch: ign: \(oldActionHandlerPtr == sigIgnOpaque)")
31+
print("before dispatch: dfl: \(oldActionHandlerPtr == sigDflOpaque)")
32+
print("before dispatch: mask: \(oldAction.sa_mask)")
33+
print("before dispatch: flags: \(oldAction.sa_flags)")
34+
35+
var newAction = sigaction()
36+
newAction.sa_flags = 0
37+
sigemptyset(&newAction.sa_mask)
38+
newAction.__sigaction_handler.sa_sigaction = { signal, siginfo, threadUserContext in
39+
print("got \(signal) from sigaction")
40+
}
41+
//newAction.__sigaction_handler.sa_handler = SIG_IGN
42+
let newActionHandlerPtr = OpaquePointer(bitPattern: unsafeBitCast(newAction.__sigaction_handler.sa_handler, to: Int.self))
43+
sigaction(15, &newAction, nil)
44+
45+
sigaction(15, nil, &oldAction)
46+
oldActionHandlerPtr = OpaquePointer(bitPattern: unsafeBitCast(oldAction.__sigaction_handler.sa_handler, to: Int.self))
47+
print("after sigaction: ign: \(oldActionHandlerPtr == sigIgnOpaque)")
48+
print("after sigaction: dfl: \(oldActionHandlerPtr == sigDflOpaque)")
49+
print("after sigaction: newAction: \(oldActionHandlerPtr == newActionHandlerPtr)")
50+
print("after sigaction: mask: \(oldAction.sa_mask)")
51+
print("after sigaction: flags: \(oldAction.sa_flags)")
52+
53+
let s = DispatchSource.makeSignalSource(signal: 15)
54+
s.setEventHandler{
55+
print("got \(s.data) signal 15 from dispatch")
56+
sigaction(15, nil, &oldAction)
57+
oldActionHandlerPtr = OpaquePointer(bitPattern: unsafeBitCast(oldAction.__sigaction_handler.sa_handler, to: Int.self))
58+
print("in dispatch: ign: \(oldActionHandlerPtr == sigIgnOpaque)")
59+
print("in dispatch: dfl: \(oldActionHandlerPtr == sigDflOpaque)")
60+
print("in dispatch: newAction: \(oldActionHandlerPtr == newActionHandlerPtr)")
61+
print("in dispatch: mask: \(oldAction.sa_mask)")
62+
print("in dispatch: flags: \(oldAction.sa_flags)")
63+
}
64+
s.resume()
65+
signalSourceRetain = s /* We want the source to stay on after end of if. */
66+
67+
sigaction(15, nil, &oldAction)
68+
oldActionHandlerPtr = OpaquePointer(bitPattern: unsafeBitCast(oldAction.__sigaction_handler.sa_handler, to: Int.self))
69+
print("after dispatch: ign: \(oldActionHandlerPtr == sigIgnOpaque)")
70+
print("after dispatch: dfl: \(oldActionHandlerPtr == sigDflOpaque)")
71+
print("after dispatch: newAction: \(oldActionHandlerPtr == newActionHandlerPtr)")
72+
print("after dispatch: mask: \(oldAction.sa_mask)")
73+
print("after dispatch: flags: \(oldAction.sa_flags)")
74+
75+
usleep(500)
76+
sigaction(15, nil, &oldAction)
77+
oldActionHandlerPtr = OpaquePointer(bitPattern: unsafeBitCast(oldAction.__sigaction_handler.sa_handler, to: Int.self))
78+
print("after dispatch and delay: ign: \(oldActionHandlerPtr == sigIgnOpaque)")
79+
print("after dispatch and delay: dfl: \(oldActionHandlerPtr == sigDflOpaque)")
80+
print("after dispatch and delay: newAction: \(oldActionHandlerPtr == newActionHandlerPtr)")
81+
print("after dispatch and delay: mask: \(oldAction.sa_mask)")
82+
print("after dispatch and delay: flags: \(oldAction.sa_flags)")
83+
/* raise and kill are not the same in a multi-threaded env */
84+
//raise(15)
85+
kill(getpid(), 15)
86+
sleep(1)
87+
88+
print("***** FIRST TEST DONE ******")
89+
} else {
90+
print("***** FIRST TEST SKIPPED ******")
91+
}
92+
93+
94+
var cond = pthread_cond_t()
95+
pthread_cond_init(&cond, nil)
96+
97+
if doSecondTest {
98+
sigaction(15, &ignoreAction, nil)
99+
100+
var threadAttr = pthread_attr_t()
101+
pthread_attr_init(&threadAttr)
102+
pthread_attr_setdetachstate(&threadAttr, Int32(PTHREAD_CREATE_DETACHED))
103+
// pthread_attr_set_qos_class_np(&threadAttr, QOS_CLASS_BACKGROUND, QOS_MIN_RELATIVE_PRIORITY)
104+
105+
var mutexAttr = pthread_mutexattr_t()
106+
pthread_mutexattr_init(&mutexAttr)
107+
pthread_mutexattr_settype(&mutexAttr, Int32(PTHREAD_MUTEX_NORMAL))
108+
109+
var mutex = pthread_mutex_t()
110+
pthread_mutex_init(&mutex, &mutexAttr)
111+
112+
var thread = pthread_t()
113+
pthread_create(&thread, &threadAttr, threadForSignal, nil)
114+
115+
pthread_mutex_lock(&mutex);
116+
pthread_cond_wait(&cond, &mutex)
117+
pthread_mutex_unlock(&mutex);
118+
119+
/* Thread is initialized and running */
120+
print("thread initialized and running")
121+
122+
sigaction(15, &defaultAction, nil)
123+
usleep(500)
124+
pthread_kill(thread, 15)
125+
// raise(15)
126+
// kill(getpid(), 15)
127+
128+
sleep(1)
129+
print("***** SECOND TEST DONE ******")
130+
} else {
131+
print("***** SECOND TEST SKIPPED ******")
132+
}
133+
134+
private func threadForSignal(_ arg: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
135+
pthread_cond_signal(&cond)
136+
repeat {
137+
print("pause send")
138+
pause()
139+
print("pause returned, setting sigaction back to ignore")
140+
sigaction(15, &ignoreAction, nil)
141+
} while true
142+
}

0 commit comments

Comments
 (0)