From 21a6f1f69fc2de9321eb8706d5db56a27e8baeb0 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 14 Oct 2024 14:40:31 -0700 Subject: [PATCH] Split the pthread library out of libc. NFC --- ChangeLog.md | 3 + embuilder.py | 4 + test/test_core.py | 6 +- test/test_other.py | 13 +-- tools/link.py | 1 - tools/system_libs.py | 224 ++++++++++++++++++++++++++----------------- 6 files changed, 151 insertions(+), 100 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index a57c2f4b2e2c6..4a74ebfbdeb05 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works. 3.1.70 (in development) ----------------------- +- The pthread part of libc 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) 3.1.69 - 10/12/24 ----------------- diff --git a/embuilder.py b/embuilder.py index 4b706c1d6df76..9dd4df2b972ce 100755 --- a/embuilder.py +++ b/embuilder.py @@ -71,6 +71,10 @@ 'libGL-webgl2-ofb-getprocaddr', 'libGL-ww-getprocaddr', 'libhtml5', + 'libpthread', + 'libpthread-stub', + 'libpthread-debug', + 'libpthread-debug-stub', 'libsockets', 'libsockets-ww', 'libstubs', diff --git a/test/test_core.py b/test/test_core.py index f6b746780beb3..2544db3ae19a6 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -4625,10 +4625,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 diff --git a/test/test_other.py b/test/test_other.py index 61e13c20c037b..1abca96276586 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -6001,9 +6001,9 @@ def test_bad_lookup(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], # 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')] @@ -6034,7 +6034,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')) @@ -12280,10 +12280,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): @@ -13177,12 +13177,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', '-lbulkmemory', '-lcompiler_rt', '-lmalloc'] + libs = ['-lc', '-lpthread', '-lbulkmemory', '-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 ', err) + self.assertContained(' -lpthread-debug ', err) self.assertContained(' -lcompiler_rt-mt ', err) def test_explicit_gl_linking(self): diff --git a/tools/link.py b/tools/link.py index 898188fce10ec..30148de893e88 100644 --- a/tools/link.py +++ b/tools/link.py @@ -2721,7 +2721,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++': [], diff --git a/tools/system_libs.py b/tools/system_libs.py index 13413638054d4..c980840042db8 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -974,6 +974,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, @@ -1088,7 +1220,7 @@ def get_files(self): ignore = [ 'ipc', 'passwd', 'signal', 'sched', 'time', 'linux', 'aio', 'exit', 'legacy', 'mq', 'setjmp', - 'ldso', 'malloc' + 'ldso', 'malloc', 'thread' ] # individual files @@ -1111,91 +1243,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' @@ -1307,10 +1354,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 @@ -2372,6 +2415,7 @@ def add_sanitizer_libs(): if settings.ALLOW_UNIMPLEMENTED_SYSCALLS: add_library('libstubs') if '-nolibc' not in args: + add_library('libpthread') if not settings.EXIT_RUNTIME: add_library('libnoexit') add_library('libc')