-
Notifications
You must be signed in to change notification settings - Fork 0
/
isolate.c
119 lines (99 loc) · 2.75 KB
/
isolate.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#define LIBISOLATE_C_
#include "isolate.h"
#include "libgotcha/libgotcha_api.h"
#include <assert.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#define MAGIC "isolate"
struct isolate {
libgotcha_group_t group;
char magic[sizeof MAGIC];
};
static thread_local struct isolate group;
void *isolate(void *(*fun)(void *), void *arg) {
return isolate_r(fun, arg, &group);
}
static void init(struct isolate *isolate, libgotcha_group_t group) {
assert(group);
assert(group != LIBGOTCHA_GROUP_ERROR);
isolate->group = group;
strcpy(isolate->magic, MAGIC);
}
void *isolate_r(void *(*fun)(void *), void *arg, struct isolate *ctx) {
assert(fun);
assert(sizeof *ctx == ISOLATE_OPAQUE_BYTES);
libgotcha_group_t group;
if(ctx && !strncmp(ctx->magic, MAGIC, sizeof MAGIC))
group = ctx->group;
else {
group = libgotcha_group_new();
if(ctx)
init(ctx, group);
}
libgotcha_group_thread_set(group);
void (*__ctype_init)(void) =
(void (*)(void)) (uintptr_t) libgotcha_group_symbol(group, "__ctype_init");
assert(__ctype_init);
__ctype_init();
void *res = fun(arg);
libgotcha_group_thread_set(LIBGOTCHA_GROUP_SHARED);
return res;
}
struct wrapper {
void *(*fun)(void *);
void *arg;
bool finished;
struct isolate group;
char *release;
};
static void release(void *release) {
struct wrapper *wrapper = release;
if(!wrapper->finished) {
libgotcha_group_thread_set(LIBGOTCHA_GROUP_SHARED);
libgotcha_group_renew(wrapper->group.group);
}
if(wrapper->release)
*wrapper->release = *MAGIC;
free(wrapper);
}
static void *wrapper(void *arg) {
struct wrapper *wrapper = arg;
pthread_cleanup_push(release, wrapper);
arg = isolate_r(wrapper->fun, wrapper->arg, &wrapper->group);
wrapper->finished = true;
pthread_cleanup_pop(true);
return arg;
}
static void disable(void) {
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
static void enable(void) {
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
}
#pragma weak libisolate_pthread_create = pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*fun)(void *), void *arg) {
static bool bootstrapped;
if(!bootstrapped) {
libgotcha_shared_prehook(disable);
libgotcha_shared_hook(enable);
if(strncmp(group.magic, MAGIC, sizeof MAGIC))
init(&group, libgotcha_group_new());
bootstrapped = true;
}
struct wrapper *args = malloc(sizeof *args);
char magic = *MAGIC;
args->fun = fun;
args->arg = arg;
args->finished = false;
args->release = NULL;
if(atomic_compare_exchange_strong(group.magic, &magic, '\0')) {
memcpy(&args->group, &group, sizeof args->group);
*args->group.magic = *MAGIC;
args->release = group.magic;
}
return pthread_create(thread, attr, wrapper, args);
}