Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ See docs/process.md for more on how version tagging works.
on by default, but now it cannot be disabled. This setting was originally
added in 2019 as a temporary measure while engines and bundlers learned to
deal with `import.meta`. (#23171)
- The pthread functions now live in a separate libpthread (or libpthread-stub).
This library is automatically included so this change will only effect those
building with `-nostdlib` or `-nolibc`. (#22735)

4.0.3 - 02/07/25
----------------
Expand Down
4 changes: 4 additions & 0 deletions embuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@
'libGL-webgl2-ofb-getprocaddr',
'libGL-ww-getprocaddr',
'libhtml5',
'libpthread',
'libpthread-stub',
'libpthread-debug',
'libpthread-debug-stub',
'libsockets',
'libsockets-ww',
'libstubs',
Expand Down
6 changes: 3 additions & 3 deletions test/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4614,10 +4614,10 @@ def test_dylink_postsets_chunking(self):

@with_dylink_reversed
@parameterized({
'libcxx': ('libc,libc++,libmalloc,libc++abi',),
'libcxx': ('libc,libpthread,libc++,libmalloc,libc++abi',),
'all': ('1',),
'missing': ('libc,libmalloc,libc++abi', False, False, False),
'missing_assertions': ('libc,libmalloc,libc++abi', False, False, True),
'missing': ('libc,libpthread,libmalloc,libc++abi', False, False, False),
'missing_assertions': ('libc,libpthread,libmalloc,libc++abi', False, False, True),
})
def test_dylink_syslibs(self, syslibs, expect_pass=True, with_reversed=True, assertions=True):
# one module uses libcxx, need to force its inclusion when it isn't the main
Expand Down
13 changes: 7 additions & 6 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -6293,9 +6293,9 @@ def test_fs_dev_random(self):
# partial list, but ok since we grab them as needed
'parial': [{'EMCC_FORCE_STDLIBS': 'libc++'}, False],
# fail! not enough stdlibs
'partial_only': [{'EMCC_FORCE_STDLIBS': 'libc++,libc,libc++abi', 'EMCC_ONLY_FORCED_STDLIBS': '1'}, True],
'partial_only': [{'EMCC_FORCE_STDLIBS': 'libc++,libc,libpthread,libc++abi', 'EMCC_ONLY_FORCED_STDLIBS': '1'}, True],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth a changelog mention because this is observable by some users.

# force all the needed stdlibs, so this works even though we ignore the input file
'full_only': [{'EMCC_FORCE_STDLIBS': 'libc,libc++abi,libc++,libmalloc', 'EMCC_ONLY_FORCED_STDLIBS': '1'}, False],
'full_only': [{'EMCC_FORCE_STDLIBS': 'libc,libpthread,libc++abi,libc++,libmalloc', 'EMCC_ONLY_FORCED_STDLIBS': '1'}, False],
})
def test_only_force_stdlibs(self, env, fail):
cmd = [EMXX, test_file('hello_libcxx.cpp')]
Expand Down Expand Up @@ -6326,7 +6326,7 @@ def test_only_force_stdlibs_2(self):
}
}
''')
with env_modify({'EMCC_FORCE_STDLIBS': 'libc,libc++abi,libc++,libmalloc', 'EMCC_ONLY_FORCED_STDLIBS': '1'}):
with env_modify({'EMCC_FORCE_STDLIBS': 'libc,libpthread,libc++abi,libc++,libmalloc', 'EMCC_ONLY_FORCED_STDLIBS': '1'}):
self.run_process([EMXX, 'src.cpp', '-sDISABLE_EXCEPTION_CATCHING=0'])
self.assertContained('Caught exception: std::exception', self.run_js('a.out.js'))

Expand Down Expand Up @@ -12758,10 +12758,10 @@ def test_nostdlib(self):
self.assertContained(err, self.expect_fail([EMCC, test_file('unistd/close.c'), '-nodefaultlibs']))

# Build again but with explit system libraries
libs = ['-lc', '-lcompiler_rt']
libs = ['-lc', '-lpthread', '-lcompiler_rt']
self.run_process([EMCC, test_file('unistd/close.c'), '-nostdlib'] + libs)
self.run_process([EMCC, test_file('unistd/close.c'), '-nodefaultlibs'] + libs)
self.run_process([EMCC, test_file('unistd/close.c'), '-nolibc', '-lc'])
self.run_process([EMCC, test_file('unistd/close.c'), '-nolibc', '-lc', '-lpthread'])
self.run_process([EMCC, test_file('unistd/close.c'), '-nostartfiles'])

def test_argument_match(self):
Expand Down Expand Up @@ -13619,12 +13619,13 @@ def test_offset_convertor_plus_wasm2js(self):

def test_standard_library_mapping(self):
# Test the `-l` flags on the command line get mapped the correct libraries variant
libs = ['-lc', '-lcompiler_rt', '-lmalloc']
libs = ['-lc', '-lpthread', '-lcompiler_rt', '-lmalloc']
err = self.run_process([EMCC, test_file('hello_world.c'), '-pthread', '-nodefaultlibs', '-v'] + libs, stderr=PIPE).stderr

# Check that the linker was run with `-mt` variants because `-pthread` was passed.
self.assertContained(' -lc-mt-debug ', err)
self.assertContained(' -ldlmalloc-mt-debug ', err)
self.assertContained(' -lpthread-debug ', err)
self.assertContained(' -lcompiler_rt-mt ', err)

def test_explicit_gl_linking(self):
Expand Down
1 change: 0 additions & 1 deletion tools/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -2752,7 +2752,6 @@ def map_to_js_libs(library_name):
'dl': [],
'm': [],
'rt': [],
'pthread': [],
# This is the name of GNU's C++ standard library. We ignore it here
# for compatibility with GNU toolchains.
'stdc++': [],
Expand Down
224 changes: 134 additions & 90 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,138 @@ class libnoexit(Library):
src_files = ['atexit_dummy.c']


class libpthread(MuslInternalLibrary,
DebugLibrary,
AsanInstrumentedLibrary):
name = 'libpthread'
# See libc
cflags = ['-Os', '-fno-inline-functions', '-fno-builtin']
cflags += ['-Wno-unused-but-set-variable',
'-Wno-unused-variable',
'-Wno-shift-op-parentheses',
'-Wno-unused-label',
'-Wno-logical-op-parentheses',
'-Wno-bitwise-op-parentheses']

def __init__(self, **kwargs):
self.is_stub = kwargs.pop('stub')
super().__init__(**kwargs)

def get_cflags(self):
cflags = super().get_cflags()
if not self.is_stub:
cflags += ['-pthread']
return cflags

def get_base_name(self):
name = super().get_base_name()
if self.is_stub:
name += '-stub'
return name

@classmethod
def vary_on(cls):
return super().vary_on() + ['stub']

@classmethod
def get_default_variation(cls, **kwargs):
return super().get_default_variation(stub=not settings.PTHREADS, **kwargs)

def get_files(self):
files = files_in_path(
path='system/lib/pthread',
filenames=['thread_profiler.c'])

if self.is_stub:
# Include just a subset of the thread directory
files += files_in_path(
path='system/lib/libc/musl/src/thread',
filenames=[
'pthread_self.c',
'pthread_cleanup_push.c',
'pthread_attr_init.c',
'pthread_attr_destroy.c',
'pthread_attr_get.c',
'pthread_attr_setdetachstate.c',
'pthread_attr_setguardsize.c',
'pthread_attr_setinheritsched.c',
'pthread_attr_setschedparam.c',
'pthread_attr_setschedpolicy.c',
'pthread_attr_setscope.c',
'pthread_attr_setstack.c',
'pthread_attr_setstacksize.c',
'pthread_getconcurrency.c',
'pthread_getcpuclockid.c',
'pthread_getschedparam.c',
'pthread_setschedprio.c',
'pthread_setconcurrency.c',
'default_attr.c',
# C11 thread library functions
'call_once.c',
'tss_create.c',
'tss_delete.c',
'tss_set.c',
'cnd_broadcast.c',
'cnd_destroy.c',
'cnd_init.c',
'cnd_signal.c',
'cnd_timedwait.c',
'cnd_wait.c',
'mtx_destroy.c',
'mtx_init.c',
'mtx_lock.c',
'mtx_timedlock.c',
'mtx_trylock.c',
'mtx_unlock.c',
'thrd_create.c',
'thrd_exit.c',
'thrd_join.c',
'thrd_sleep.c',
'thrd_yield.c',
])
files += files_in_path(
path='system/lib/pthread',
filenames=[
'library_pthread_stub.c',
'pthread_self_stub.c',
'proxying_stub.c',
])
else:
files += glob_in_path(
path='system/lib/libc/musl/src/thread',
glob_pattern='*.c',
excludes=[
'clone.c',
'pthread_create.c',
'pthread_kill.c', 'pthread_sigmask.c',
'__set_thread_area.c', 'synccall.c',
'__syscall_cp.c', '__tls_get_addr.c',
'__unmapself.c',
# Empty files, simply ignore them.
'syscall_cp.c', 'tls.c',
# TODO: Support these. See #12216.
'pthread_setname_np.c',
'pthread_getname_np.c',
])
files += files_in_path(
path='system/lib/pthread',
filenames=[
'library_pthread.c',
'em_task_queue.c',
'proxying.c',
'proxying_legacy.c',
'thread_mailbox.c',
'pthread_create.c',
'pthread_kill.c',
'emscripten_thread_init.c',
'emscripten_thread_state.S',
'emscripten_futex_wait.c',
'emscripten_futex_wake.c',
'emscripten_yield.c',
])
return files


class libc(MuslInternalLibrary,
DebugLibrary,
AsanInstrumentedLibrary,
Expand Down Expand Up @@ -1098,7 +1230,7 @@ def get_files(self):
ignore = [
'ipc', 'passwd', 'signal', 'sched', 'time', 'linux',
'aio', 'exit', 'legacy', 'mq', 'setjmp',
'ldso', 'malloc'
'ldso', 'malloc', 'thread'
]

# individual files
Expand All @@ -1120,91 +1252,6 @@ def get_files(self):

ignore += LIBC_SOCKETS

if self.is_mt:
ignore += [
'clone.c',
'pthread_create.c',
'pthread_kill.c', 'pthread_sigmask.c',
'__set_thread_area.c', 'synccall.c',
'__syscall_cp.c', '__tls_get_addr.c',
'__unmapself.c',
# Empty files, simply ignore them.
'syscall_cp.c', 'tls.c',
# TODO: Support these. See #12216.
'pthread_setname_np.c',
'pthread_getname_np.c',
]
libc_files += files_in_path(
path='system/lib/pthread',
filenames=[
'library_pthread.c',
'em_task_queue.c',
'proxying.c',
'proxying_legacy.c',
'thread_mailbox.c',
'pthread_create.c',
'pthread_kill.c',
'emscripten_thread_init.c',
'emscripten_thread_state.S',
'emscripten_futex_wait.c',
'emscripten_futex_wake.c',
'emscripten_yield.c',
])
else:
ignore += ['thread']
libc_files += files_in_path(
path='system/lib/libc/musl/src/thread',
filenames=[
'pthread_self.c',
'pthread_cleanup_push.c',
'pthread_attr_init.c',
'pthread_attr_destroy.c',
'pthread_attr_get.c',
'pthread_attr_setdetachstate.c',
'pthread_attr_setguardsize.c',
'pthread_attr_setinheritsched.c',
'pthread_attr_setschedparam.c',
'pthread_attr_setschedpolicy.c',
'pthread_attr_setscope.c',
'pthread_attr_setstack.c',
'pthread_attr_setstacksize.c',
'pthread_getconcurrency.c',
'pthread_getcpuclockid.c',
'pthread_getschedparam.c',
'pthread_setschedprio.c',
'pthread_setconcurrency.c',
'default_attr.c',
# C11 thread library functions
'call_once.c',
'tss_create.c',
'tss_delete.c',
'tss_set.c',
'cnd_broadcast.c',
'cnd_destroy.c',
'cnd_init.c',
'cnd_signal.c',
'cnd_timedwait.c',
'cnd_wait.c',
'mtx_destroy.c',
'mtx_init.c',
'mtx_lock.c',
'mtx_timedlock.c',
'mtx_trylock.c',
'mtx_unlock.c',
'thrd_create.c',
'thrd_exit.c',
'thrd_join.c',
'thrd_sleep.c',
'thrd_yield.c',
])
libc_files += files_in_path(
path='system/lib/pthread',
filenames=[
'library_pthread_stub.c',
'pthread_self_stub.c',
'proxying_stub.c',
])

# These files are in libc directories, but only built in libc_optz.
ignore += [
'pow_small.c', 'log_small.c', 'log2_small.c'
Expand Down Expand Up @@ -1322,10 +1369,6 @@ def get_files(self):
if settings.RELOCATABLE:
libc_files += files_in_path(path='system/lib/libc', filenames=['dynlink.c'])

libc_files += files_in_path(
path='system/lib/pthread',
filenames=['thread_profiler.c'])

libc_files += glob_in_path('system/lib/libc/compat', '*.c')

# Check for missing file in non_lto_files list. Do this here
Expand Down Expand Up @@ -2378,6 +2421,7 @@ def add_sanitizer_libs():
if settings.ALLOW_UNIMPLEMENTED_SYSCALLS:
add_library('libstubs')
if not options.nolibc:
add_library('libpthread')
if not settings.EXIT_RUNTIME:
add_library('libnoexit')
add_library('libc')
Expand Down
Loading