The Pedigree Project 0.1
wakeups.c
1#define _GNU_SOURCE
2#include <poll.h>
3#include <signal.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/syscall.h>
8
10 pthread_mutex_t mutex;
11 pthread_cond_t condition;
12 atomic_uint ready;
13 pid_t tid;
14 int released, destination, cleanup_ok;
15};
16static void condition_cleanup(void* argument) {
17 struct condition_waiter* waiter = argument;
18 waiter->cleanup_ok = sc_tls == 0x8291 && !sc_sample(waiter->destination);
19 pthread_mutex_unlock(&waiter->mutex);
20}
21static void* condition_entry(void* argument) {
22 struct condition_waiter* waiter = argument;
23 if (sc_pin(0, sc_cpus[0]))
24 return (void*)1;
25 sc_tls = 0x8291;
26 waiter->tid = (pid_t)syscall(SYS_gettid);
27 int result = 0;
28 pthread_mutex_lock(&waiter->mutex);
29 pthread_cleanup_push(condition_cleanup, waiter);
30 atomic_store_explicit(&waiter->ready, 1, memory_order_release);
31 while (!waiter->released && !result)
32 result = pthread_cond_wait(&waiter->condition, &waiter->mutex);
33 result |= sc_sample(waiter->destination) || sc_tls != 0x8291;
34 pthread_cleanup_pop(0);
35 pthread_mutex_unlock(&waiter->mutex);
36 return (void*)(intptr_t)result;
37}
38static int condition_wakeup(int cancel) {
39 int failed = 0, created = 0, locked = 0;
40 struct condition_waiter waiter = {.mutex = PTHREAD_MUTEX_INITIALIZER,
41 .condition = PTHREAD_COND_INITIALIZER,
42 .destination = sc_cpus[sc_count - 1]};
43 pthread_t thread;
44 CHECK(pthread_create(&thread, NULL, condition_entry, &waiter) == 0);
45 created = 1;
46 CHECK(sc_wait(&waiter.ready, 1) == 0);
47 CHECK(pthread_mutex_lock(&waiter.mutex) == 0);
48 locked = 1;
49 // Acquiring this mutex follows the peer's logical cond-wait enrollment.
50 CHECK(sc_pin(waiter.tid, waiter.destination) == 0);
51 CHECK(sc_mask(waiter.tid, waiter.destination) == 0);
52 if (cancel) {
53 CHECK(pthread_cancel(thread) == 0);
54 } else {
55 waiter.released = 1;
56 CHECK(pthread_cond_signal(&waiter.condition) == 0);
57 }
58 CHECK(pthread_mutex_unlock(&waiter.mutex) == 0);
59 locked = 0;
60 void* result;
61 CHECK(pthread_join(thread, &result) == 0);
62 created = 0;
63 CHECK(cancel ? result == PTHREAD_CANCELED && waiter.cleanup_ok : result == NULL);
64out:
65 if (locked)
66 pthread_mutex_unlock(&waiter.mutex);
67 if (created) {
68 pthread_cancel(thread);
69 pthread_join(thread, NULL);
70 }
71 pthread_cond_destroy(&waiter.condition);
72 pthread_mutex_destroy(&waiter.mutex);
73 return failed;
74}
75
76struct io_waiter {
77 atomic_uint ready, handled;
78 pid_t tid;
79 int fd, mode, destination;
80 unsigned handler_cpu;
81 int handler_ok;
82};
83static _Thread_local struct io_waiter* current_waiter;
84static void migration_signal(int number) {
85 struct io_waiter* waiter = current_waiter;
86 if (!waiter)
87 return;
88 int saved = errno;
89 unsigned cpu = UINT32_MAX, node = UINT32_MAX;
90 long result = syscall(SYS_getcpu, &cpu, &node, NULL);
91 waiter->handler_cpu = cpu;
92 waiter->handler_ok = number == SIGUSR1 && !result && !node && sc_tls == 0x3498;
93 atomic_store_explicit(&waiter->handled, 1, memory_order_release);
94 errno = saved;
95}
96static void* io_entry(void* argument) {
97 struct io_waiter* waiter = argument;
98 if (sc_pin(0, sc_cpus[0]))
99 return (void*)1;
100 sc_tls = 0x3498;
101 current_waiter = waiter;
102 waiter->tid = (pid_t)syscall(SYS_gettid);
103 sigset_t one;
104 sigemptyset(&one);
105 sigaddset(&one, SIGUSR1);
106 if (pthread_sigmask(SIG_UNBLOCK, &one, NULL))
107 return (void*)1;
108 atomic_store_explicit(&waiter->ready, 1, memory_order_release);
109 int error = 0;
110 if (waiter->mode) {
111 struct pollfd event = {.fd = waiter->fd, .events = POLLIN};
112 int result;
113 do
114 result = poll(&event, 1, 10000);
115 while (result < 0 && errno == EINTR);
116 error = result != 1 || !(event.revents & POLLIN);
117 }
118 char byte = 0;
119 ssize_t count;
120 do
121 count = read(waiter->fd, &byte, 1);
122 while (count < 0 && errno == EINTR);
123 error |= count != 1 || byte != 'W' || sc_sample(waiter->destination) || sc_tls != 0x3498;
124 current_waiter = NULL;
125 return (void*)(intptr_t)error;
126}
127static int io_wakeup(int mode) {
128 int failed = 0, created = 0, action_set = 0;
129 int pipefd[2] = {-1, -1};
130 struct sigaction action = {.sa_handler = migration_signal}, previous;
131 sigemptyset(&action.sa_mask);
132 struct io_waiter waiter = {.mode = mode, .destination = sc_cpus[sc_count - 1]};
133 pthread_t thread;
134 CHECK(sigaction(SIGUSR1, &action, &previous) == 0);
135 action_set = 1;
136 CHECK(pipe(pipefd) == 0);
137 waiter.fd = pipefd[0];
138 CHECK(pthread_create(&thread, NULL, io_entry, &waiter) == 0);
139 created = 1;
140 CHECK(sc_wait(&waiter.ready, 1) == 0);
141 CHECK(sc_pin(waiter.tid, waiter.destination) == 0);
142 CHECK(pthread_kill(thread, SIGUSR1) == 0);
143 CHECK(sc_wait(&waiter.handled, 1) == 0 && waiter.handler_ok &&
144 waiter.handler_cpu == (unsigned)waiter.destination);
145 CHECK(sc_send(pipefd[1], 'W') == 0);
146 void* result;
147 CHECK(pthread_join(thread, &result) == 0);
148 created = 0;
149 CHECK(result == NULL);
150out:
151 if (created) {
152 pthread_cancel(thread);
153 pthread_join(thread, NULL);
154 }
155 if (pipefd[0] >= 0)
156 close(pipefd[0]);
157 if (pipefd[1] >= 0)
158 close(pipefd[1]);
159 if (action_set)
160 sigaction(SIGUSR1, &previous, NULL);
161 return failed;
162}
163static void* signal_entry(void* argument) {
164 struct io_waiter* waiter = argument;
165 if (sc_pin(0, sc_cpus[0]))
166 return (void*)1;
167 sc_tls = 0x4289;
168 waiter->tid = (pid_t)syscall(SYS_gettid);
169 atomic_store_explicit(&waiter->ready, 1, memory_order_release);
170 sigset_t one;
171 sigemptyset(&one);
172 sigaddset(&one, SIGUSR2);
173 struct timespec timeout = {10, 0};
174 siginfo_t info;
175 int result = sigtimedwait(&one, &info, &timeout);
176 return (void*)(intptr_t)(result != SIGUSR2 || info.si_signo != SIGUSR2 ||
177 sc_sample(waiter->destination) || sc_tls != 0x4289);
178}
179static int signal_wakeup(void) {
180 int failed = 0, created = 0, masked = 0;
181 sigset_t one, old;
182 sigemptyset(&one);
183 sigaddset(&one, SIGUSR2);
184 struct io_waiter waiter = {.destination = sc_cpus[sc_count - 1]};
185 pthread_t thread;
186 CHECK(pthread_sigmask(SIG_BLOCK, &one, &old) == 0);
187 masked = 1;
188 CHECK(pthread_create(&thread, NULL, signal_entry, &waiter) == 0);
189 created = 1;
190 CHECK(sc_wait(&waiter.ready, 1) == 0 && sc_pin(waiter.tid, waiter.destination) == 0);
191 CHECK(pthread_kill(thread, SIGUSR2) == 0);
192 void* result;
193 CHECK(pthread_join(thread, &result) == 0);
194 created = 0;
195 CHECK(result == NULL);
196out:
197 if (created) {
198 pthread_cancel(thread);
199 pthread_join(thread, NULL);
200 }
201 if (masked)
202 pthread_sigmask(SIG_SETMASK, &old, NULL);
203 return failed;
204}
205int sc_wakeups(void) {
206 return condition_wakeup(0) || condition_wakeup(1) || io_wakeup(0) || io_wakeup(1) ||
207 signal_wakeup();
208}