diff --git a/examples/getcpucount.c b/examples/getcpucount.c index ec38eb478..7d757cc3c 100644 --- a/examples/getcpucount.c +++ b/examples/getcpucount.c @@ -7,19 +7,9 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/calls/calls.h" -#include "libc/fmt/itoa.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/sysconf.h" -#include "libc/stdio/stdio.h" -int main(int argc, char *argv[]) { - int c, n; - char a[22], *p; - if ((c = GetCpuCount())) { - p = FormatInt64(a, c); - *p++ = '\n'; - return write(1, a, p - a) == p - a ? 0 : 1; - } else { - return 1; - } +int main() { + kprintf("%d\n", GetCpuCount()); } diff --git a/examples/greenbean.c b/examples/greenbean.c index 941a32a96..7169a6180 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -8,7 +8,6 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/assert.h" -#include "libc/intrin/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigset.h" @@ -17,7 +16,9 @@ #include "libc/dce.h" #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/pthread.h" #include "libc/intrin/wait0.internal.h" #include "libc/limits.h" #include "libc/log/check.h" @@ -110,7 +111,7 @@ _Atomic(int) connections; _Atomic(int) closingtime; const char *volatile status; -int Worker(void *id, int tid) { +void *Worker(void *id) { int server, yes = 1; // load balance incoming connections for port 8080 across all threads @@ -276,8 +277,8 @@ void PrintStatus(void) { } int main(int argc, char *argv[]) { - int i; - struct spawn *th; + int i, rc; + pthread_t *th; uint32_t *hostips; // ShowCrashReports(); @@ -298,7 +299,7 @@ int main(int argc, char *argv[]) { threads = argc > 1 ? atoi(argv[1]) : GetCpuCount(); if (!(1 <= threads && threads <= 100000)) { - kprintf("error: invalid number of threads\n"); + kprintf("error: invalid number of threads: %d\n", threads); exit(1); } @@ -309,12 +310,12 @@ int main(int argc, char *argv[]) { pledge("stdio inet", 0); // spawn over 9,000 worker threads - th = calloc(threads, sizeof(*th)); + th = calloc(threads, sizeof(pthread_t)); for (i = 0; i < threads; ++i) { ++workers; - if (_spawn(Worker, (void *)(intptr_t)i, th + i) == -1) { + if ((rc = pthread_create(th + i, 0, Worker, (void *)(intptr_t)i))) { --workers; - kprintf("error: _spawn(%d) failed %m\n", i); + kprintf("error: pthread_create(%d) failed %s\n", i, strerror(rc)); } if (!(i % 500)) { PrintStatus(); @@ -332,7 +333,7 @@ int main(int argc, char *argv[]) { // join the workers for (i = 0; i < threads; ++i) { - _join(th + i); + pthread_join(th[i], 0); } // clean up memory diff --git a/libc/calls/getcpucount.c b/libc/calls/getcpucount.c index 3021ffd12..65b8e8d97 100644 --- a/libc/calls/getcpucount.c +++ b/libc/calls/getcpucount.c @@ -16,12 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/sched-sysv.internal.h" +#include "libc/calls/struct/cpuset.h" #include "libc/calls/weirdtypes.h" #include "libc/dce.h" -#include "libc/intrin/popcnt.h" #include "libc/macros.internal.h" #include "libc/nt/dll.h" #include "libc/nt/struct/systeminfo.h" @@ -35,13 +34,9 @@ #define ALL_PROCESSOR_GROUPS 0xffff static unsigned GetCpuCountLinux(void) { - uint64_t s[16]; - unsigned i, c, n; - if (!sys_sched_getaffinity(0, sizeof(s), s)) { - for (c = i = 0; i < ARRAYLEN(s); ++i) { - c += popcnt(s[i]); - } - return c; + cpu_set_t s = {0}; + if (sys_sched_getaffinity(0, sizeof(s), &s) != -1) { + return CPU_COUNT(&s); } else { return 0; } @@ -78,15 +73,7 @@ static textwindows unsigned GetCpuCountWindows(void) { } } -/** - * Returns number of CPUs in system. - * - * On Intel systems with HyperThreading this will return the number of - * cores multiplied by two. - * - * @return cpu count or 0 if it couldn't be determined - */ -unsigned GetCpuCount(void) { +static unsigned GetCpuCountImpl(void) { if (!IsWindows()) { if (!IsBsd()) { return GetCpuCountLinux(); @@ -97,3 +84,22 @@ unsigned GetCpuCount(void) { return GetCpuCountWindows(); } } + +static int g_cpucount; + +// precompute because process affinity on linux may change later +__attribute__((__constructor__)) static void init(void) { + g_cpucount = GetCpuCountImpl(); +} + +/** + * Returns number of CPUs in system. + * + * On Intel systems with HyperThreading this will return the number of + * cores multiplied by two. + * + * @return cpu count or 0 if it couldn't be determined + */ +unsigned GetCpuCount(void) { + return g_cpucount; +} diff --git a/libc/calls/sched_getaffinity.c b/libc/calls/sched_getaffinity.c index 8b6cdb3b2..e605d145a 100644 --- a/libc/calls/sched_getaffinity.c +++ b/libc/calls/sched_getaffinity.c @@ -41,7 +41,7 @@ static textwindows int sys_sched_getaffinity_nt(int tid, size_t size, } /** - * Gets CPU affinity for current thread. + * Gets CPU affinity for thread. * * While Windows allows us to change the thread affinity mask, it's only * possible to read the process affinity mask. Therefore this function diff --git a/libc/calls/sched_setaffinity.c b/libc/calls/sched_setaffinity.c index 80647cf2d..d57d9f813 100644 --- a/libc/calls/sched_setaffinity.c +++ b/libc/calls/sched_setaffinity.c @@ -66,7 +66,7 @@ static textwindows dontinline int sys_sched_setaffinity_nt(int pid, } /** - * Asks kernel to only schedule process on particular CPUs. + * Asks kernel to only schedule thread on particular CPUs. * * @param tid is the process or thread id (or 0 for caller) * @param bitsetsize is byte length of bitset, which should be 128 diff --git a/libc/intrin/intrin.h b/libc/intrin/intrin.h deleted file mode 100755 index e69de29bb..000000000 diff --git a/libc/intrin/phtread_attr_setschedpolicy.c b/libc/intrin/phtread_attr_setschedpolicy.c new file mode 100644 index 000000000..4de984b9d --- /dev/null +++ b/libc/intrin/phtread_attr_setschedpolicy.c @@ -0,0 +1,47 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" + +/** + * Sets thread scheduler policy attribute, e.g. + * + * pthread_t id; + * pthread_attr_t attr; + * pthread_attr_init(&attr); + * struct sched_param pri = {sched_get_priority_min(SCHED_OTHER)}; + * pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + * pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + * pthread_attr_setschedparam(&attr, &pri); + * pthread_create(&id, &attr, func, 0); + * pthread_attr_destroy(&attr); + * pthread_join(id, 0); + * + * @param policy may be one of: + * - `SCHED_OTHER` the default policy + * - `SCHED_FIFO` for real-time scheduling (usually needs root) + * - `SCHED_RR` for round-robin scheduling (usually needs root) + * - `SCHED_IDLE` for lowest effort (Linux and FreeBSD only) + * - `SCHED_BATCH` for "batch" style execution of processes if + * supported (Linux), otherwise it's treated as `SCHED_OTHER` + * @see sched_setscheduler() + */ +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) { + attr->schedpolicy = policy; + return 0; +} diff --git a/libc/intrin/pthread.h b/libc/intrin/pthread.h index d0b81ba1d..576beb427 100644 --- a/libc/intrin/pthread.h +++ b/libc/intrin/pthread.h @@ -21,6 +21,9 @@ #define PTHREAD_CREATE_JOINABLE 0 #define PTHREAD_CREATE_DETACHED 1 +#define PTHREAD_INHERIT_SCHED 0 +#define PTHREAD_EXPLICIT_SCHED 1 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -83,12 +86,13 @@ typedef struct pthread_rwlock_s { typedef struct pthread_attr_s { char detachstate; - size_t stacksize; - size_t guardsize; - char *stackaddr; - int scope; + char inheritsched; + int schedparam; int schedpolicy; - int inheritsched; + int scope; + unsigned guardsize; + unsigned stacksize; + char *stackaddr; } pthread_attr_t; int pthread_yield(void); diff --git a/libc/intrin/pthread2.h b/libc/intrin/pthread2.h index 5d3c18332..0dffe6892 100644 --- a/libc/intrin/pthread2.h +++ b/libc/intrin/pthread2.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_INTRIN_PTHREAD2_H_ #define COSMOPOLITAN_LIBC_INTRIN_PTHREAD2_H_ +#include "libc/calls/struct/cpuset.h" #include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/timespec.h" #include "libc/intrin/pthread.h" @@ -11,6 +12,10 @@ int pthread_attr_getschedparam(const pthread_attr_t *, struct sched_param *); int pthread_attr_setschedparam(pthread_attr_t *, const struct sched_param *); int pthread_cond_timedwait(pthread_cond_t *, pthread_mutex_t *, const struct timespec *); +int pthread_setaffinity_np(pthread_t, size_t, const cpu_set_t *); +int pthread_getaffinity_np(pthread_t, size_t, cpu_set_t *); +int pthread_setschedparam(pthread_t, int, const struct sched_param *); +int pthread_getschedparam(pthread_t, int *, struct sched_param *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/pthread_attr_getinheritsched.c b/libc/intrin/pthread_attr_getinheritsched.c new file mode 100644 index 000000000..1ec8dfaa4 --- /dev/null +++ b/libc/intrin/pthread_attr_getinheritsched.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" + +/** + * Returns thread inherit schedule attribute. + */ +int pthread_attr_getinheritsched(const pthread_attr_t *attr, + int *inheritsched) { + *inheritsched = attr->inheritsched; + return 0; +} diff --git a/libc/intrin/pthread_attr_getschedparam.c b/libc/intrin/pthread_attr_getschedparam.c new file mode 100644 index 000000000..b6170a6da --- /dev/null +++ b/libc/intrin/pthread_attr_getschedparam.c @@ -0,0 +1,28 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread2.h" + +/** + * Gets thread scheduler parameter attribute. + */ +int pthread_attr_getschedparam(const pthread_attr_t *attr, + struct sched_param *param) { + *param = (struct sched_param){attr->schedparam}; + return 0; +} diff --git a/libc/intrin/pthread_attr_getschedpolicy.c b/libc/intrin/pthread_attr_getschedpolicy.c new file mode 100644 index 000000000..a19325f93 --- /dev/null +++ b/libc/intrin/pthread_attr_getschedpolicy.c @@ -0,0 +1,27 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread.h" + +/** + * Gets thread scheduler policy attribute + */ +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) { + *policy = attr->schedpolicy; + return 0; +} diff --git a/libc/intrin/pthread_attr_init.c b/libc/intrin/pthread_attr_init.c index ee00ed36f..ac6f31166 100644 --- a/libc/intrin/pthread_attr_init.c +++ b/libc/intrin/pthread_attr_init.c @@ -26,7 +26,6 @@ */ int pthread_attr_init(pthread_attr_t *attr) { *attr = (pthread_attr_t){ - .detachstate = PTHREAD_CREATE_JOINABLE, .stacksize = GetStackSize(), .guardsize = PAGESIZE, }; diff --git a/libc/intrin/pthread_attr_setinheritsched.c b/libc/intrin/pthread_attr_setinheritsched.c new file mode 100644 index 000000000..74d6fc352 --- /dev/null +++ b/libc/intrin/pthread_attr_setinheritsched.c @@ -0,0 +1,53 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/errno.h" +#include "libc/intrin/pthread.h" + +/** + * Sets thread scheduler inheritance attribute, e.g. + * + * pthread_t id; + * pthread_attr_t attr; + * pthread_attr_init(&attr); + * struct sched_param pri = {sched_get_priority_min(SCHED_OTHER)}; + * pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + * pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + * pthread_attr_setschedparam(&attr, &pri); + * pthread_create(&id, &attr, func, 0); + * pthread_attr_destroy(&attr); + * pthread_join(id, 0); + * + * @param inheritsched may be one of: + * - `PTHREAD_INHERIT_SCHED` the default + * - `PTHREAD_EXPLICIT_SCHED` to enable rescheduling + * @return 0 on success, or errno on error + * @raise EINVAL on bad value + */ +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) { + switch (inheritsched) { + case PTHREAD_INHERIT_SCHED: + case PTHREAD_EXPLICIT_SCHED: + attr->inheritsched = inheritsched; + return 0; + default: + assert(!"badval"); + return EINVAL; + } +} diff --git a/libc/intrin/pthread_attr_setschedparam.c b/libc/intrin/pthread_attr_setschedparam.c new file mode 100644 index 000000000..70a348d58 --- /dev/null +++ b/libc/intrin/pthread_attr_setschedparam.c @@ -0,0 +1,48 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/sched_param.h" +#include "libc/errno.h" +#include "libc/intrin/pthread2.h" + +/** + * Sets thread scheduler parameter attribute, e.g. + * + * pthread_t id; + * pthread_attr_t attr; + * pthread_attr_init(&attr); + * struct sched_param pri = {sched_get_priority_min(SCHED_OTHER)}; + * pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + * pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + * pthread_attr_setschedparam(&attr, &pri); + * pthread_create(&id, &attr, func, 0); + * pthread_attr_destroy(&attr); + * pthread_join(id, 0); + * + * @param param specifies priority on scheduling policies that need it + * @see pthread_attr_setschedpolicy() + * @see sched_get_priority_min() + * @see sched_get_priority_max() + * @see sched_setparam() + */ +int pthread_attr_setschedparam(pthread_attr_t *attr, + const struct sched_param *param) { + if (!param) return EINVAL; + attr->schedparam = param->sched_priority; + return 0; +} diff --git a/libc/intrin/pthread_barrier_wait.c b/libc/intrin/pthread_barrier_wait.c index 95a6c424a..d1931b4e9 100644 --- a/libc/intrin/pthread_barrier_wait.c +++ b/libc/intrin/pthread_barrier_wait.c @@ -16,12 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/dce.h" #include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" -#include "libc/intrin/intrin.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/pthread.h" #include "libc/limits.h" @@ -39,13 +35,9 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) { if (atomic_fetch_add(&barrier->waits, 1) + 1 == barrier->count) { if (atomic_fetch_add(&barrier->waits, 1) + 1 < barrier->count * 2) { - atomic_store(&barrier->popped, 1); + atomic_store_explicit(&barrier->popped, 1, memory_order_relaxed); do { - if (IsLinux() || IsOpenbsd()) { - _futex_wake(&barrier->popped, INT_MAX, barrier->pshared); - } else { - pthread_yield(); - } + _futex_wake(&barrier->popped, INT_MAX, barrier->pshared); } while (atomic_load_explicit(&barrier->waits, memory_order_relaxed) < barrier->count * 2); atomic_store_explicit(&barrier->popped, 0, memory_order_relaxed); @@ -54,11 +46,7 @@ int pthread_barrier_wait(pthread_barrier_t *barrier) { return PTHREAD_BARRIER_SERIAL_THREAD; } do { - if (IsLinux() || IsOpenbsd()) { - _futex_wait(&barrier->popped, 0, barrier->pshared, 0); - } else { - pthread_yield(); - } + _futex_wait(&barrier->popped, 0, barrier->pshared, 0); } while (atomic_load_explicit(&barrier->waits, memory_order_relaxed) < barrier->count); atomic_fetch_add(&barrier->waits, 1); diff --git a/libc/intrin/pthread_mutex_lock.c b/libc/intrin/pthread_mutex_lock.c index 24f2de035..374f97a2d 100644 --- a/libc/intrin/pthread_mutex_lock.c +++ b/libc/intrin/pthread_mutex_lock.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" -#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/asmflag.h" #include "libc/intrin/atomic.h" @@ -36,12 +35,10 @@ static int pthread_mutex_lock_spin(pthread_mutex_t *mutex, int expect, for (i = 0; i != 1 << tries; i++) { } tries++; - } else if (IsLinux() || IsOpenbsd()) { + } else { atomic_fetch_add(&mutex->waits, 1); _futex_wait(&mutex->lock, expect, mutex->pshared, &(struct timespec){1}); atomic_fetch_sub(&mutex->waits, 1); - } else { - pthread_yield(); } return tries; } diff --git a/libc/intrin/pthread_rwlock_rdlock.c b/libc/intrin/pthread_rwlock_rdlock.c index 23a9c7941..e0502ab7e 100644 --- a/libc/intrin/pthread_rwlock_rdlock.c +++ b/libc/intrin/pthread_rwlock_rdlock.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" #include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" #include "libc/intrin/pthread.h" @@ -28,12 +27,10 @@ static int pthread_rwlock_rdlock_spin(pthread_rwlock_t *rwlock, int expect, for (i = 0; i != 1 << tries; i++) { } tries++; - } else if (IsLinux() || IsOpenbsd()) { + } else { atomic_fetch_add(&rwlock->waits, 1); _futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1}); atomic_fetch_sub(&rwlock->waits, 1); - } else { - pthread_yield(); } return tries; } diff --git a/libc/intrin/pthread_rwlock_unlock.c b/libc/intrin/pthread_rwlock_unlock.c index 168fc644d..c90f9ecbd 100644 --- a/libc/intrin/pthread_rwlock_unlock.c +++ b/libc/intrin/pthread_rwlock_unlock.c @@ -21,7 +21,6 @@ #include "libc/errno.h" #include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" -#include "libc/intrin/intrin.h" #include "libc/intrin/pthread.h" /** diff --git a/libc/intrin/pthread_rwlock_wrlock.c b/libc/intrin/pthread_rwlock_wrlock.c index 4adbc2d1b..f50ebc966 100644 --- a/libc/intrin/pthread_rwlock_wrlock.c +++ b/libc/intrin/pthread_rwlock_wrlock.c @@ -16,7 +16,6 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" #include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" #include "libc/intrin/pthread.h" @@ -28,12 +27,10 @@ static int pthread_rwlock_wrlock_spin(pthread_rwlock_t *rwlock, int expect, for (i = 0; i != 1 << tries; i++) { } tries++; - } else if (IsLinux() || IsOpenbsd()) { + } else { atomic_fetch_add(&rwlock->waits, 1); _futex_wait(&rwlock->lock, expect, rwlock->pshared, &(struct timespec){1}); atomic_fetch_sub(&rwlock->waits, 1); - } else { - pthread_yield(); } return tries; } diff --git a/libc/intrin/wait0.c b/libc/intrin/wait0.c index e850e4249..0bfa2bf12 100644 --- a/libc/intrin/wait0.c +++ b/libc/intrin/wait0.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/errno.h" #include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" #include "libc/intrin/pthread.h" @@ -37,10 +36,8 @@ void _wait0(const int *ctid) { for (;;) { if (!(x = atomic_load_explicit(ctid, memory_order_relaxed))) { break; - } else if (IsLinux() || IsOpenbsd()) { - _futex_wait(ctid, x, PTHREAD_PROCESS_SHARED, &(struct timespec){2}); } else { - pthread_yield(); + _futex_wait(ctid, x, PTHREAD_PROCESS_SHARED, &(struct timespec){2}); } } if (IsOpenbsd()) { diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index cb11bead7..e548083d7 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1336,7 +1336,7 @@ syscon sched SCHED_OTHER 0 127 2 127 0 127 # standard round-robin syscon sched SCHED_FIFO 1 127 1 127 1 127 # [real-time] first-in, first-out policy syscon sched SCHED_RR 2 127 3 127 2 127 # [real-time] round-robin policy syscon sched SCHED_BATCH 3 127 2 127 0 127 # for "batch" style execution of processes; polyfilled as SCHED_OTHER on non-Linux -syscon sched SCHED_IDLE 5 127 2 127 0 127 # for running very low priority background jobs; polyfilled as SCHED_OTHER on non-Linux +𝔰𝔶𝔰𝔠𝔬𝔫 sched SCHED_IDLE 5 127 2 127 0 127 # for running very low priority background jobs; polyfilled as SCHED_OTHER on non-Linux syscon sched SCHED_DEADLINE 6 127 127 127 127 127 # can only be set by sched_setattr() syscon sched SCHED_RESET_ON_FORK 0x40000000 0 0 0 0 0 # Can be ORed in to make sure the process is reverted back to SCHED_NORMAL on fork(); no-op on non-Linux diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 69e5abcf3..7a51695dc 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ #define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_ +#include "libc/calls/struct/sched_param.h" #include "libc/intrin/pthread.h" #include "libc/runtime/runtime.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) @@ -68,9 +69,11 @@ struct PosixThread { void _pthread_free(struct PosixThread *) hidden; void _pthread_wait(struct PosixThread *) hidden; +int _pthread_reschedule(struct PosixThread *) hidden; void _pthread_zombies_add(struct PosixThread *) hidden; void _pthread_zombies_decimate(void) hidden; void _pthread_zombies_harvest(void) hidden; +int _pthread_setschedparam_freebsd(int, int, const struct sched_param *); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/thread/pthread_cond_timedwait.c b/libc/thread/pthread_cond_timedwait.c index b10684053..1245fb77c 100644 --- a/libc/thread/pthread_cond_timedwait.c +++ b/libc/thread/pthread_cond_timedwait.c @@ -19,7 +19,6 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/timespec.h" -#include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/atomic.h" #include "libc/intrin/futex.internal.h" @@ -77,11 +76,7 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, rel = _timespec_sub(*abstime, now); tsp = &rel; } - if (IsLinux() || IsOpenbsd()) { - _futex_wait(&cond->seq, seq, cond->pshared, tsp); - } else { - sched_yield(); - } + _futex_wait(&cond->seq, seq, cond->pshared, tsp); } while (seq == atomic_load_explicit(&cond->seq, memory_order_relaxed)); atomic_fetch_sub(&cond->waits, 1); diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index a248e18c8..a08471e7e 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/sched-sysv.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -37,6 +38,7 @@ #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" +#include "libc/sysv/errfuns.h" #include "libc/thread/internal.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/spawn.h" @@ -62,6 +64,9 @@ void _pthread_free(struct PosixThread *pt) { static int PosixThread(void *arg, int tid) { struct PosixThread *pt = arg; enum PosixThreadStatus status; + if (pt->attr.inheritsched == PTHREAD_EXPLICIT_SCHED) { + _pthread_reschedule(pt); + } if (!setjmp(pt->exiter)) { ((cthread_t)__get_tls())->pthread = (pthread_t)pt; pt->rc = pt->start_routine(pt->arg); diff --git a/libc/thread/pthread_getaffinity_np.c b/libc/thread/pthread_getaffinity_np.c new file mode 100644 index 000000000..35064bbf5 --- /dev/null +++ b/libc/thread/pthread_getaffinity_np.c @@ -0,0 +1,46 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/cpuset.h" +#include "libc/errno.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +/** + * Gets CPU affinity for thread. + * + * While Windows allows us to change the thread affinity mask, it's only + * possible to read the process affinity mask. Therefore this function + * won't reflect the changes made by psched_setaffinity_np() on Windows. + * + * @param bitsetsize is byte length of bitset, which should be 128 + * @return 0 on success, or errno on error + * @raise ENOSYS if not Linux or Windows + */ +int pthread_getaffinity_np(pthread_t thread, size_t bitsetsize, + cpu_set_t *bitset) { + int rc, e = errno; + struct PosixThread *pt = (struct PosixThread *)thread; + if (!sched_getaffinity(pt->tid, bitsetsize, bitset)) { + return 0; + } else { + rc = errno; + errno = e; + return rc; + } +} diff --git a/libc/thread/pthread_getschedparam.c b/libc/thread/pthread_getschedparam.c new file mode 100644 index 000000000..b98ef8873 --- /dev/null +++ b/libc/thread/pthread_getschedparam.c @@ -0,0 +1,31 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/pthread2.h" +#include "libc/thread/posixthread.internal.h" + +/** + * Gets most recently set scheduling of thread. + */ +int pthread_getschedparam(pthread_t thread, int *policy, + struct sched_param *param) { + struct PosixThread *pt = (struct PosixThread *)thread; + *policy = pt->attr.schedpolicy; + *param = (struct sched_param){pt->attr.schedparam}; + return 0; +} diff --git a/libc/thread/pthread_reschedule.c b/libc/thread/pthread_reschedule.c new file mode 100644 index 000000000..2b7cc3664 --- /dev/null +++ b/libc/thread/pthread_reschedule.c @@ -0,0 +1,42 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/sched-sysv.internal.h" +#include "libc/calls/struct/sched_param.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/sysv/errfuns.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +int _pthread_reschedule(struct PosixThread *pt) { + int rc, e = errno; + struct sched_param param = {pt->attr.schedparam}; + if (IsNetbsd()) { + rc = sys_sched_setparam_netbsd(0, pt->tid, pt->attr.schedpolicy, ¶m); + } else if (IsLinux()) { + rc = sys_sched_setscheduler(pt->tid, pt->attr.schedpolicy, ¶m); + } else if (IsFreebsd()) { + rc = _pthread_setschedparam_freebsd(pt->tid, pt->attr.schedpolicy, ¶m); + } else { + rc = enosys(); + } + rc = rc != -1 ? 0 : errno; + errno = e; + return rc; +} diff --git a/libc/thread/pthread_setaffinity_np.c b/libc/thread/pthread_setaffinity_np.c new file mode 100644 index 000000000..28a67ceab --- /dev/null +++ b/libc/thread/pthread_setaffinity_np.c @@ -0,0 +1,42 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/cpuset.h" +#include "libc/errno.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +/** + * Asks kernel to only schedule thread on particular CPUs. + * + * @param bitsetsize is byte length of bitset, which should be 128 + * @return 0 on success, or errno on error + * @raise ENOSYS if not Linux or Windows + */ +int pthread_setaffinity_np(pthread_t thread, size_t bitsetsize, + const cpu_set_t *bitset) { + int rc, e = errno; + struct PosixThread *pt = (struct PosixThread *)thread; + if (!sched_setaffinity(pt->tid, bitsetsize, bitset)) { + return 0; + } else { + rc = errno; + errno = e; + return rc; + } +} diff --git a/libc/thread/pthread_setschedparam.c b/libc/thread/pthread_setschedparam.c new file mode 100644 index 000000000..4d44b2093 --- /dev/null +++ b/libc/thread/pthread_setschedparam.c @@ -0,0 +1,51 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/intrin/pthread2.h" +#include "libc/thread/posixthread.internal.h" + +/** + * Changes scheduling of thread, e.g. + * + * struct sched_param p = {sched_get_priority_min(SCHED_OTHER)}; + * pthread_setschedparam(thread, SCHED_OTHER, &p); + * + * @param policy may be one of: + * - `SCHED_OTHER` the default policy + * - `SCHED_FIFO` for real-time scheduling (usually needs root) + * - `SCHED_RR` for round-robin scheduling (usually needs root) + * - `SCHED_IDLE` for lowest effort (Linux and FreeBSD only) + * - `SCHED_BATCH` for "batch" style execution of processes if + * supported (Linux), otherwise it's treated as `SCHED_OTHER` + * @raise ENOSYS on XNU, Windows, OpenBSD + * @raise EPERM if not authorized to use scheduler in question (e.g. + * trying to use a real-time scheduler as non-root on Linux) or + * possibly because pledge() was used and isn't allowing this + * @see sched_get_priority_min() + * @see sched_get_priority_max() + * @see sched_setscheduler() + */ +int pthread_setschedparam(pthread_t thread, int policy, + const struct sched_param *param) { + struct PosixThread *pt = (struct PosixThread *)thread; + if (!param) return EINVAL; + pt->attr.schedpolicy = policy; + pt->attr.schedparam = param->sched_priority; + return _pthread_reschedule(pt); +} diff --git a/libc/thread/pthread_setschedparam_freebsd.c b/libc/thread/pthread_setschedparam_freebsd.c new file mode 100644 index 000000000..2a54cceb4 --- /dev/null +++ b/libc/thread/pthread_setschedparam_freebsd.c @@ -0,0 +1,48 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/sched_param.h" +#include "libc/sysv/consts/sched.h" +#include "libc/thread/freebsd.internal.h" +#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" + +#define RTP_SET_FREEBSD 1 +#define PRI_REALTIME_FREEBSD 2 +#define RTP_PRIO_MAX_FREEBSD 31 +#define PRI_FIFO_BIT_FREEBSD 8 +#define PRI_FIFO_FREEBSD (PRI_REALTIME_FREEBSD | PRI_FIFO_BIT_FREEBSD) +#define PRI_TIMESHARE_FREEBSD 3 + +int rtprio_thread(int fun, int tid, struct rtprio *inout_rtp); + +int _pthread_setschedparam_freebsd(int tid, int policy, + const struct sched_param *param) { + struct rtprio rtp; + if (policy == SCHED_RR) { + rtp.type = PRI_REALTIME_FREEBSD; + rtp.prio = RTP_PRIO_MAX_FREEBSD - param->sched_priority; + } else if (policy == SCHED_FIFO) { + rtp.type = PRI_FIFO_FREEBSD; + rtp.prio = RTP_PRIO_MAX_FREEBSD - param->sched_priority; + } else { + rtp.type = PRI_TIMESHARE_FREEBSD; + rtp.prio = 0; + } + return rtprio_thread(RTP_SET_FREEBSD, tid, &rtp); +} diff --git a/test/libc/thread/pthread_create_test.c b/test/libc/thread/pthread_create_test.c index 15e6259e4..421761208 100644 --- a/test/libc/thread/pthread_create_test.c +++ b/test/libc/thread/pthread_create_test.c @@ -17,16 +17,20 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sigaction.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/pthread.h" +#include "libc/intrin/pthread2.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sched.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/subprocess.h" @@ -89,6 +93,33 @@ TEST(pthread_detach, testDetachUponCreation) { ASSERT_EQ(0, pthread_attr_destroy(&attr)); } +static void *CheckSchedule(void *arg) { + int rc, policy; + struct sched_param prio; + ASSERT_EQ(0, pthread_getschedparam(pthread_self(), &policy, &prio)); + ASSERT_EQ(SCHED_OTHER, policy); + ASSERT_EQ(sched_get_priority_min(SCHED_OTHER), prio.sched_priority); + if (IsWindows() || IsXnu() || IsOpenbsd()) { + ASSERT_EQ(ENOSYS, pthread_setschedparam(pthread_self(), policy, &prio)); + } else { + ASSERT_EQ(0, pthread_setschedparam(pthread_self(), policy, &prio)); + } + return 0; +} + +TEST(pthread_detach, scheduling) { + pthread_t id; + pthread_attr_t attr; + struct sched_param pri = {sched_get_priority_min(SCHED_OTHER)}; + ASSERT_EQ(0, pthread_attr_init(&attr)); + ASSERT_EQ(0, pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)); + ASSERT_EQ(0, pthread_attr_setschedpolicy(&attr, SCHED_OTHER)); + ASSERT_EQ(0, pthread_attr_setschedparam(&attr, &pri)); + ASSERT_EQ(0, pthread_create(&id, &attr, CheckSchedule, 0)); + ASSERT_EQ(0, pthread_attr_destroy(&attr)); + ASSERT_EQ(0, pthread_join(id, 0)); +} + static void *CheckStack(void *arg) { char buf[1024 * 1024]; TriggerSignal();