Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/wapython
Path: blob/main/core/posix-wasm/src/threads.c
1067 views
1
/*
2
The goal of this is a very minimal version of enough of the pthread api
3
to start Python, without it actually using any real threading capabilities.
4
This is necessary because there is no option to build modern Python without
5
pthreads (that used to be possible long ago).
6
7
Inspired by https://github.com/mpdn/unthread
8
9
NOTE: I had initially carried over a minimal implementation of
10
pthread_mutex_init, but that actually breaks in Python/ceval_gil.h in the
11
function create_gil, where the line MUTEX_INIT(gil->switch_mutex) somehow makes
12
it so the gil never gets set. That's fine - this is not meant to work
13
like actual pthreads yet!
14
15
16
NOTE: Right after I spent like two days writing this something very
17
similar popped up in Python itself! https://github.com/python/cpython/pull/95234
18
ARGH, that's so annoying, but obviously also good. Their implementation is more
19
minimal, but maybe that is better. That said, these don't build due to
20
incompatibility with libc-wasm-zig, so we're sticking with our own pthreads.
21
*/
22
23
#include <stdio.h>
24
#include "threads.h"
25
26
//#define debug printf
27
28
#define debug
29
30
#define DEFAULT_ATTR_INITIALIZER \
31
{ \
32
{ \
33
.initialized = true, .detach_state = PTHREAD_CREATE_JOINABLE, \
34
.guard_size = 0, .inherit_sched = PTHREAD_INHERIT_SCHED, \
35
.scope = PTHREAD_SCOPE_PROCESS, .sched_param = (struct sched_param){}, \
36
.sched_policy = SCHED_OTHER, .stack_addr = NULL, .stack_size = 1 << 20, \
37
} \
38
}
39
40
static const union pthread_attr_t default_attr = DEFAULT_ATTR_INITIALIZER;
41
42
#define MAIN_ID 0
43
44
static struct pthread_fiber main_thread = {
45
.id = MAIN_ID,
46
.state = RUNNING,
47
.attr = DEFAULT_ATTR_INITIALIZER,
48
.cancel_state = PTHREAD_CANCEL_ENABLE,
49
.cancel_type = PTHREAD_CANCEL_DEFERRED,
50
.sched_policy = SCHED_OTHER,
51
.list_index = {-1, -1},
52
};
53
54
// Current running thread
55
static pthread_t current = &main_thread;
56
57
static const pthread_condattr_t pthread_condattr_default = {
58
.pshared = PTHREAD_PROCESS_PRIVATE,
59
.initialized = true,
60
.clock_id = CLOCK_MONOTONIC,
61
};
62
63
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) {
64
debug("pthread_cond_init - c implementation\n");
65
return 0;
66
}
67
68
int pthread_cond_destroy(pthread_cond_t *cond) {
69
debug("pthread_cond_destroy - c implementation\n");
70
return 0;
71
}
72
73
// The pthread_cond_signal() call unblocks at least one of the threads that are
74
// blocked on the specified condition variable cond (if any threads are blocked
75
// on cond).
76
int pthread_cond_signal(pthread_cond_t *cond) {
77
debug("pthread_cond_signal - c implementation\n");
78
// just do nothing - since we never block
79
return 0;
80
}
81
82
int pthread_condattr_init(pthread_condattr_t *attr) {
83
debug("pthread_condattr_init - c implementation\n");
84
*attr = pthread_condattr_default;
85
return 0;
86
}
87
88
int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) {
89
debug("pthread_condattr_setclock - c implementation\n");
90
attr->clock_id = clock_id;
91
return 0;
92
}
93
94
#define SIZE 1000
95
static void *values[SIZE];
96
static pthread_key_t keys[SIZE];
97
static int nkeys = 0;
98
99
void *pthread_getspecific(pthread_key_t key) {
100
debug("pthread_getspecific - key=%d\n", key.id);
101
for (int i = 0; i < nkeys; i++) {
102
if (keys[i].id == key.id) {
103
debug("pthread_getspecific - return value=%p\n", values[i]);
104
return values[i];
105
}
106
}
107
return 0;
108
}
109
110
int pthread_setspecific(pthread_key_t key, const void *value) {
111
debug("pthread_setspecific - c implementation, key=%d, value=%p\n", key.id,
112
value);
113
for (int i = 0; i < nkeys; i++) {
114
if (keys[i].id == key.id) {
115
debug("pthread_setspecific - changing key number %d\n", i);
116
values[i] = value;
117
return 0;
118
}
119
}
120
nkeys += 1;
121
debug("pthread_setspecific; added a new key so now there are %d\n", nkeys);
122
if (nkeys >= SIZE) {
123
printf("BOOM! ran out of pthread keys!");
124
}
125
keys[nkeys - 1] = key;
126
values[nkeys - 1] = value;
127
return 0;
128
}
129
130
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) {
131
debug("pthread_key_create - c implementation\n");
132
size_t id;
133
134
static size_t next_key_id = 1;
135
136
id = next_key_id++;
137
*key = (pthread_key_t){
138
.id = id,
139
.destructor = destructor,
140
.initialized = true,
141
};
142
143
return 0;
144
}
145
146
int pthread_key_delete(pthread_key_t key) {
147
debug("pthread_key_delete - c implementation\n");
148
// we don't store anything, so nothing to delete...
149
return 0;
150
}
151
152
// The pthread_mutex_init() function initialises the mutex referenced by mutex
153
// with attributes specified by attr. If attr is NULL, the default mutex
154
// attributes are used; the effect is the same as passing the address of a
155
// default mutex attributes object. Upon successful initialisation, the state of
156
// the mutex becomes initialised and unlocked.
157
int pthread_mutex_init(pthread_mutex_t *mutex,
158
const pthread_mutexattr_t *attr) {
159
debug("pthread_mutex_init - c implementation\n");
160
return 0;
161
}
162
163
int pthread_mutex_destroy(pthread_mutex_t *mutex) {
164
debug("pthread_mutex_init - c implementation\n");
165
return 0;
166
}
167
168
// The mutex object referenced by mutex is locked by calling
169
// pthread_mutex_lock(). If the mutex is already locked, the calling thread
170
// blocks until the mutex becomes available. This operation returns with the
171
// mutex object referenced by mutex in the locked state with the calling thread
172
// as its owner.
173
int pthread_mutex_lock(pthread_mutex_t *mutex) {
174
// debug("pthread_mutex_lock - c implementation\n");
175
return 0;
176
}
177
178
int pthread_mutex_unlock(pthread_mutex_t *mutex) {
179
// debug("pthread_mutex_unlock - c implementation\n");
180
return 0;
181
}
182
183
int pthread_mutex_trylock(pthread_mutex_t *mutex) {
184
// debug("pthread_mutex_unlock - c implementation\n");
185
return 0;
186
}
187
188
// The pthread_self() function returns the thread ID of the calling thread.
189
pthread_t pthread_self() {
190
debug("pthread_self - c implementation\n");
191
return current;
192
}
193
194
int pthread_attr_init(union pthread_attr_t *attr) {
195
debug("pthread_attr_init\n");
196
return 0;
197
}
198
199
int pthread_attr_destroy(union pthread_attr_t *attr) {
200
debug("pthread_attr_destroy\n");
201
return 0;
202
}
203
204
int pthread_attr_setstacksize(union pthread_attr_t *attr, size_t stacksize) {
205
debug("pthread_attr_setstacksize\n");
206
attr->data.stack_size = stacksize;
207
return 0;
208
}
209
210
int pthread_attr_getstacksize(const union pthread_attr_t *attr,
211
size_t *stacksize) {
212
debug("pthread_attr_getstacksize\n");
213
*stacksize = attr->data.stack_size;
214
return 0;
215
}
216
217
int pthread_create(pthread_t *thread, const union pthread_attr_t *attr,
218
void *(*start_routine)(void *), void *arg) {
219
fprintf(stderr,
220
"pthread_create: creation of threads is not yet implemented.\n");
221
return -1;
222
}
223
224
int pthread_detach(pthread_t thread) {
225
debug("pthread_detach\n");
226
return 0;
227
}
228
229
void pthread_exit(void *retval) { debug("pthread_exit\n"); }
230
231
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
232
const struct timespec *abstime) {
233
// locking is trivial when there can be only one thread.
234
return 0;
235
}
236
237
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) {
238
// locking is trivial when there can be only one thread.
239
return 0;
240
}
241
242
int pthread_kill(pthread_t thread, int sig) { return 0; }
243
244
int pthread_getcpuclockid(pthread_t thread, clockid_t *clockid) {
245
*clockid = (clockid_t) CLOCK_THREAD_CPUTIME_ID;
246
return 0;
247
}
248
249