Skip to content

Commit c6f0489

Browse files
committed
[3.11] gh-95174: Add pthread stubs for WASI (GH-95234)
Co-authored-by: Brett Cannon <[email protected]>. (cherry picked from commit 0fe645d) Co-authored-by: Christian Heimes <[email protected]>
1 parent 06b5f78 commit c6f0489

17 files changed

+332
-41
lines changed

Doc/library/sys.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,8 @@ always available.
16581658
| | |
16591659
| | * ``'nt'``: Windows threads |
16601660
| | * ``'pthread'``: POSIX threads |
1661+
| | * ``'pthread-stubs'``: stub POSIX threads |
1662+
| | (on WebAssembly platforms without threading support) |
16611663
| | * ``'solaris'``: Solaris threads |
16621664
+------------------+---------------------------------------------------------+
16631665
| :const:`lock` | Name of the lock implementation: |

Include/cpython/pthread_stubs.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#ifndef Py_CPYTHON_PTRHEAD_STUBS_H
2+
#define Py_CPYTHON_PTRHEAD_STUBS_H
3+
4+
#if !defined(HAVE_PTHREAD_STUBS)
5+
# error "this header file requires stubbed pthreads."
6+
#endif
7+
8+
#ifndef _POSIX_THREADS
9+
# define _POSIX_THREADS 1
10+
#endif
11+
12+
/* Minimal pthread stubs for CPython.
13+
*
14+
* The stubs implement the minimum pthread API for CPython.
15+
* - pthread_create() fails.
16+
* - pthread_exit() calls exit(0).
17+
* - pthread_key_*() functions implement minimal TSS without destructor.
18+
* - all other functions do nothing and return 0.
19+
*/
20+
21+
#ifdef __wasi__
22+
// WASI's bits/alltypes.h provides type definitions when __NEED_ is set.
23+
// The header file can be included multiple times.
24+
# define __NEED_pthread_cond_t 1
25+
# define __NEED_pthread_condattr_t 1
26+
# define __NEED_pthread_mutex_t 1
27+
# define __NEED_pthread_mutexattr_t 1
28+
# define __NEED_pthread_key_t 1
29+
# define __NEED_pthread_t 1
30+
# define __NEED_pthread_attr_t 1
31+
# include <bits/alltypes.h>
32+
#else
33+
typedef struct { void *__x; } pthread_cond_t;
34+
typedef struct { unsigned __attr; } pthread_condattr_t;
35+
typedef struct { void *__x; } pthread_mutex_t;
36+
typedef struct { unsigned __attr; } pthread_mutexattr_t;
37+
typedef unsigned pthread_key_t;
38+
typedef unsigned pthread_t;
39+
typedef struct { unsigned __attr; } pthread_attr_t;
40+
#endif
41+
42+
// mutex
43+
PyAPI_FUNC(int) pthread_mutex_init(pthread_mutex_t *restrict mutex,
44+
const pthread_mutexattr_t *restrict attr);
45+
PyAPI_FUNC(int) pthread_mutex_destroy(pthread_mutex_t *mutex);
46+
PyAPI_FUNC(int) pthread_mutex_trylock(pthread_mutex_t *mutex);
47+
PyAPI_FUNC(int) pthread_mutex_lock(pthread_mutex_t *mutex);
48+
PyAPI_FUNC(int) pthread_mutex_unlock(pthread_mutex_t *mutex);
49+
50+
// condition
51+
PyAPI_FUNC(int) pthread_cond_init(pthread_cond_t *restrict cond,
52+
const pthread_condattr_t *restrict attr);
53+
PyAPI_FUNC(int) pthread_cond_destroy(pthread_cond_t *cond);
54+
PyAPI_FUNC(int) pthread_cond_wait(pthread_cond_t *restrict cond,
55+
pthread_mutex_t *restrict mutex);
56+
PyAPI_FUNC(int) pthread_cond_timedwait(pthread_cond_t *restrict cond,
57+
pthread_mutex_t *restrict mutex,
58+
const struct timespec *restrict abstime);
59+
PyAPI_FUNC(int) pthread_cond_signal(pthread_cond_t *cond);
60+
PyAPI_FUNC(int) pthread_condattr_init(pthread_condattr_t *attr);
61+
PyAPI_FUNC(int) pthread_condattr_setclock(
62+
pthread_condattr_t *attr, clockid_t clock_id);
63+
64+
// pthread
65+
PyAPI_FUNC(int) pthread_create(pthread_t *restrict thread,
66+
const pthread_attr_t *restrict attr,
67+
void *(*start_routine)(void *),
68+
void *restrict arg);
69+
PyAPI_FUNC(int) pthread_detach(pthread_t thread);
70+
PyAPI_FUNC(pthread_t) pthread_self(void);
71+
PyAPI_FUNC(int) pthread_exit(void *retval) __attribute__ ((__noreturn__));
72+
PyAPI_FUNC(int) pthread_attr_init(pthread_attr_t *attr);
73+
PyAPI_FUNC(int) pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
74+
PyAPI_FUNC(int) pthread_attr_destroy(pthread_attr_t *attr);
75+
76+
77+
// pthread_key
78+
#ifndef PTHREAD_KEYS_MAX
79+
# define PTHREAD_KEYS_MAX 128
80+
#endif
81+
82+
PyAPI_FUNC(int) pthread_key_create(pthread_key_t *key,
83+
void (*destr_function)(void *));
84+
PyAPI_FUNC(int) pthread_key_delete(pthread_key_t key);
85+
PyAPI_FUNC(void *) pthread_getspecific(pthread_key_t key);
86+
PyAPI_FUNC(int) pthread_setspecific(pthread_key_t key, const void *value);
87+
88+
#endif // Py_CPYTHON_PTRHEAD_STUBS_H

Include/cpython/pythread.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ PyAPI_FUNC(int) _PyThread_at_fork_reinit(PyThread_type_lock *lock);
2020
but hardcode the unsigned long to avoid errors for include directive.
2121
*/
2222
# define NATIVE_TSS_KEY_T unsigned long
23+
#elif defined(HAVE_PTHREAD_STUBS)
24+
# include "cpython/pthread_stubs.h"
25+
# define NATIVE_TSS_KEY_T pthread_key_t
2326
#else
2427
# error "Require native threads. See https://bugs.python.org/issue31370"
2528
#endif

Lib/test/pythoninfo.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,8 +588,8 @@ def collect_socket(info_add):
588588

589589
try:
590590
hostname = socket.gethostname()
591-
except OSError:
592-
# WASI SDK 15.0 does not have gethostname(2).
591+
except (OSError, AttributeError):
592+
# WASI SDK 16.0 does not have gethostname(2).
593593
if sys.platform != "wasi":
594594
raise
595595
else:

Lib/test/test_sys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ def test_attributes(self):
626626
def test_thread_info(self):
627627
info = sys.thread_info
628628
self.assertEqual(len(info), 3)
629-
self.assertIn(info.name, ('nt', 'pthread', 'solaris', None))
629+
self.assertIn(info.name, ('nt', 'pthread', 'pthread-stubs', 'solaris', None))
630630
self.assertIn(info.lock, ('semaphore', 'mutex+cond', None))
631631

632632
@unittest.skipUnless(support.is_emscripten, "only available on Emscripten")

Lib/test/test_threadsignals.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ def send_signals():
3636
os.kill(process_pid, signal.SIGUSR2)
3737
signalled_all.release()
3838

39+
3940
@threading_helper.requires_working_threading()
41+
@unittest.skipUnless(hasattr(signal, "alarm"), "test requires signal.alarm")
4042
class ThreadSignals(unittest.TestCase):
4143

4244
def test_signals(self):

Makefile.pre.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1573,6 +1573,7 @@ PYTHON_HEADERS= \
15731573
$(srcdir)/Include/cpython/objimpl.h \
15741574
$(srcdir)/Include/cpython/odictobject.h \
15751575
$(srcdir)/Include/cpython/picklebufobject.h \
1576+
$(srcdir)/Include/cpython/pthread_stubs.h \
15761577
$(srcdir)/Include/cpython/pyctype.h \
15771578
$(srcdir)/Include/cpython/pydebug.h \
15781579
$(srcdir)/Include/cpython/pyerrors.h \
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
wasm32-wasi builds no longer depend on WASIX's pthread stubs. Python now has
2+
its own stubbed pthread API.

Python/thread.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,15 @@ _PyThread_debug_deprecation(void)
9393
#endif
9494
}
9595

96-
#if defined(_POSIX_THREADS)
97-
# define PYTHREAD_NAME "pthread"
96+
#if defined(HAVE_PTHREAD_STUBS)
97+
# define PYTHREAD_NAME "pthread-stubs"
98+
# include "thread_pthread_stubs.h"
99+
#elif defined(_POSIX_THREADS)
100+
# if defined(__EMSCRIPTEN__) || !defined(__EMSCRIPTEN_PTHREADS__)
101+
# define PYTHREAD_NAME "pthread-stubs"
102+
# else
103+
# define PYTHREAD_NAME "pthread"
104+
# endif
98105
# include "thread_pthread.h"
99106
#elif defined(NT_THREADS)
100107
# define PYTHREAD_NAME "nt"
@@ -208,7 +215,9 @@ PyThread_GetInfo(void)
208215
}
209216
PyStructSequence_SET_ITEM(threadinfo, pos++, value);
210217

211-
#ifdef _POSIX_THREADS
218+
#ifdef HAVE_PTHREAD_STUBS
219+
value = Py_NewRef(Py_None);
220+
#elif defined(_POSIX_THREADS)
212221
#ifdef USE_SEMAPHORES
213222
value = PyUnicode_FromString("semaphore");
214223
#else
@@ -219,8 +228,7 @@ PyThread_GetInfo(void)
219228
return NULL;
220229
}
221230
#else
222-
Py_INCREF(Py_None);
223-
value = Py_None;
231+
value = Py_NewRef(Py_None);
224232
#endif
225233
PyStructSequence_SET_ITEM(threadinfo, pos++, value);
226234

Python/thread_pthread.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
#if defined(__APPLE__) || defined(HAVE_PTHREAD_DESTRUCTOR)
88
#define destructor xxdestructor
99
#endif
10-
#include <pthread.h>
10+
#ifndef HAVE_PTHREAD_STUBS
11+
# include <pthread.h>
12+
#endif
1113
#if defined(__APPLE__) || defined(HAVE_PTHREAD_DESTRUCTOR)
1214
#undef destructor
1315
#endif

0 commit comments

Comments
 (0)