From 1483bf0b6c3cafe3077e9c8da5561b8b7ba907bd Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Wed, 7 Jul 2021 13:14:34 -0400 Subject: [PATCH 01/14] [cmake] Support OpenBSD amd64 architecture name. x86_64 is spelled "amd64" on this platform. Return "amd64", for consistency with Swift and other projects. For Windows, return "x86_64". --- cmake/modules/SwiftSupport.cmake | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmake/modules/SwiftSupport.cmake b/cmake/modules/SwiftSupport.cmake index a42b61e40..c1e5c9d4c 100644 --- a/cmake/modules/SwiftSupport.cmake +++ b/cmake/modules/SwiftSupport.cmake @@ -27,8 +27,12 @@ function(get_swift_host_arch result_var_name) set("${result_var_name}" "armv7" PARENT_SCOPE) elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") set("${result_var_name}" "armv7" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") - set("${result_var_name}" "x86_64" PARENT_SCOPE) + elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "amd64|AMD64") + if(CMAKE_SYSTEM_NAME MATCHES "Windows") + set("${result_var_name}" "x86_64" PARENT_SCOPE) + else() + set("${result_var_name}" "amd64" PARENT_SCOPE) + endif() elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "IA64") set("${result_var_name}" "itanium" PARENT_SCOPE) elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86") From 99fce104939c82629ba15b4b46b2622d56911fc6 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Sat, 3 Oct 2020 00:27:45 -0400 Subject: [PATCH 02/14] HAVE_MACH is unnecessary here. The kevent backend requires _dispatch_bug_kevent_client unconditionally. We don't need to require this entire function require HAVE_MACH, just the inner few that is already present behind this conditional. --- src/init.c | 2 -- src/internal.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/init.c b/src/init.c index d54da4121..0fe0e4385 100644 --- a/src/init.c +++ b/src/init.c @@ -964,7 +964,6 @@ _dispatch_continuation_get_function_symbol(dispatch_continuation_t dc) return dc->dc_func; } -#if HAVE_MACH void _dispatch_bug_kevent_client(const char *msg, const char *filter, const char *operation, int err, uint64_t ident, uint64_t udata, @@ -1008,7 +1007,6 @@ _dispatch_bug_kevent_client(const char *msg, const char *filter, msg, strerror(err), err, udata, filter, ident, ident, func); } } -#endif // HAVE_MACH #if RDAR_49023449 diff --git a/src/internal.h b/src/internal.h index 4df1a2976..9f74f2c19 100644 --- a/src/internal.h +++ b/src/internal.h @@ -475,12 +475,10 @@ void _dispatch_bug_mach_client(const char *msg, mach_msg_return_t kr); struct dispatch_unote_class_s; -#if HAVE_MACH DISPATCH_NOINLINE DISPATCH_COLD void _dispatch_bug_kevent_client(const char *msg, const char *filter, const char *operation, int err, uint64_t ident, uint64_t udata, struct dispatch_unote_class_s *du); -#endif // HAVE_MACH DISPATCH_NOINLINE DISPATCH_COLD void _dispatch_bug_kevent_vanished(struct dispatch_unote_class_s *du); From d37d2fa18b4b9df99286982dedabdb71b5e30325 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Tue, 13 Oct 2020 19:59:28 -0400 Subject: [PATCH 03/14] fadvise is not present on OpenBSD. It is just a hint, after all, so it is not an error if it is unavailable. --- src/io.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/io.c b/src/io.c index e31e28c82..8f73916d1 100644 --- a/src/io.c +++ b/src/io.c @@ -2328,8 +2328,11 @@ _dispatch_operation_advise(dispatch_operation_t op, size_t chunk_size) case ESPIPE: break; // fd refers to a pipe or FIFO default: (void)dispatch_assume_zero(err); break; } +#elif defined(__OpenBSD__) + (void)err; #else #error "_dispatch_operation_advise not implemented on this platform" + (void)err; #endif // defined(F_RDADVISE) #endif // defined(_WIN32) } From fff494547998d5856880e232e0d17777215fe969 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Thu, 28 Jan 2021 20:26:41 -0500 Subject: [PATCH 04/14] Properly annotate fallthrough in kevent backend. Building dispatch from swift sets -Werror -Wimplicit-fallthrough. It is not enough to just comment the fallthrough; we have elsewhere defined DISPATCH_FALLTHROUGH, so we might as well use it. --- src/event/event_kevent.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/event/event_kevent.c b/src/event/event_kevent.c index 16b69b37d..fe5cfaa12 100644 --- a/src/event/event_kevent.c +++ b/src/event/event_kevent.c @@ -743,7 +743,7 @@ _dispatch_kq_poll(dispatch_wlh_t wlh, dispatch_kevent_t ke, int n, switch (err) { case ENOMEM: _dispatch_temporary_resource_shortage(); - /* FALLTHROUGH */ + DISPATCH_FALLTHROUGH; case EINTR: goto retry; case EBADF: @@ -754,7 +754,7 @@ _dispatch_kq_poll(dispatch_wlh_t wlh, dispatch_kevent_t ke, int n, (flags & KEVENT_FLAG_DYNAMIC_KQ_MUST_EXIST)) { return 0; } - /* FALLTHROUGH */ + DISPATCH_FALLTHROUGH; #endif // DISPATCH_USE_KEVENT_WORKLOOP default: DISPATCH_CLIENT_CRASH(err, "Unexpected error from kevent"); @@ -1410,7 +1410,7 @@ _dispatch_kq_fill_workloop_event(dispatch_kevent_t ke, int which, switch (which) { case DISPATCH_WORKLOOP_ASYNC_FROM_SYNC: fflags |= NOTE_WL_END_OWNERSHIP; - /* FALLTHROUGH */ + DISPATCH_FALLTHROUGH; case DISPATCH_WORKLOOP_ASYNC: case DISPATCH_WORKLOOP_ASYNC_DISCOVER_SYNC: case DISPATCH_WORKLOOP_ASYNC_QOS_UPDATE: @@ -1434,10 +1434,10 @@ _dispatch_kq_fill_workloop_event(dispatch_kevent_t ke, int which, case DISPATCH_WORKLOOP_ASYNC_LEAVE_FROM_SYNC: fflags |= NOTE_WL_END_OWNERSHIP; - /* FALLTHROUGH */ + DISPATCH_FALLTHROUGH; case DISPATCH_WORKLOOP_ASYNC_LEAVE_FROM_TRANSFER: fflags |= NOTE_WL_IGNORE_ESTALE; - /* FALLTHROUGH */ + DISPATCH_FALLTHROUGH; case DISPATCH_WORKLOOP_ASYNC_LEAVE: dispatch_assert(!_dq_state_is_enqueued_on_target(dq_state)); action = EV_ADD | EV_DELETE | EV_ENABLE; From 413ed2459a7f917e226b8a597170cf7f660d4ec6 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Wed, 7 Jul 2021 15:52:29 -0400 Subject: [PATCH 05/14] [tests] Make test harness portable. Prior to this change, on non-Linux, the test harness will already attempt to use dispatch functionality when testing dispatch itself. This does not seem intentional, and instead appears to be intended as the Mac OS case where the platform already has Dispatch and is using that when testing the just-built version. Instead, make the Linux case the general `__unix__` case. --- tests/bsdtestharness.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/bsdtestharness.c b/tests/bsdtestharness.c index 93ed8324b..4b66c8956 100644 --- a/tests/bsdtestharness.c +++ b/tests/bsdtestharness.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) @@ -144,7 +145,7 @@ main(int argc, char *argv[]) //fprintf(stderr, "pid = %d\n", pid); assert(pid > 0); -#if defined(__linux__) +#if defined(__unix__) int status; struct rusage usage; struct timeval tv_stop, tv_wall; @@ -159,9 +160,9 @@ main(int argc, char *argv[]) assert(res2 != -1); test_long("Process exited", (WIFEXITED(status) && WEXITSTATUS(status) && WEXITSTATUS(status) != 0xff) || WIFSIGNALED(status), 0); - printf("[PERF]\twall time: %ld.%06ld\n", tv_wall.tv_sec, tv_wall.tv_usec); - printf("[PERF]\tuser time: %ld.%06ld\n", usage.ru_utime.tv_sec, usage.ru_utime.tv_usec); - printf("[PERF]\tsystem time: %ld.%06ld\n", usage.ru_stime.tv_sec, usage.ru_stime.tv_usec); + printf("[PERF]\twall time: %" PRIdMAX ".%06" PRIdMAX "\n", (intmax_t)tv_wall.tv_sec, (intmax_t)tv_wall.tv_usec); + printf("[PERF]\tuser time: %" PRIdMAX ".%06" PRIdMAX"\n", (intmax_t)usage.ru_utime.tv_sec, (intmax_t)usage.ru_utime.tv_usec); + printf("[PERF]\tsystem time: %" PRIdMAX ".%06" PRIdMAX "\n", (intmax_t)usage.ru_stime.tv_sec, (intmax_t)usage.ru_stime.tv_usec); printf("[PERF]\tmax resident set size: %ld\n", usage.ru_maxrss); printf("[PERF]\tpage faults: %ld\n", usage.ru_majflt); printf("[PERF]\tswaps: %ld\n", usage.ru_nswap); From 3d7728e627225e89300c150bd9c62b2c002ef77d Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Wed, 7 Jul 2021 16:09:33 -0400 Subject: [PATCH 06/14] Lock implementation changes for OpenBSD. OpenBSD has futex, but not futex_pi. Segregate futex functionality from pi-futex functionality with a new preprocessor symbol. --- src/shims/lock.c | 29 ++++++++++++++++++++++++++--- src/shims/lock.h | 26 +++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/shims/lock.c b/src/shims/lock.c index 88fb8f8b6..8102c877c 100644 --- a/src/shims/lock.c +++ b/src/shims/lock.c @@ -56,6 +56,20 @@ _dispatch_thread_switch(dispatch_lock value, dispatch_lock_options_t flags, #endif #endif +#if defined(__unix__) +#if !HAVE_UL_UNFAIR_LOCK && !HAVE_FUTEX_PI +DISPATCH_ALWAYS_INLINE +static inline void +_dispatch_thread_switch(dispatch_lock value, dispatch_lock_options_t flags, + uint32_t timeout) +{ + (void)value; + (void)flags; + (void)timeout; +} +#endif +#endif + #pragma mark - semaphores #if USE_MACH_SEM @@ -395,8 +409,10 @@ _dispatch_unfair_lock_wake(uint32_t *uaddr, uint32_t flags) #include #ifdef __ANDROID__ #include -#else +#elif __linux__ #include +#else +#include #endif /* __ANDROID__ */ DISPATCH_ALWAYS_INLINE @@ -405,7 +421,12 @@ _dispatch_futex(uint32_t *uaddr, int op, uint32_t val, const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3, int opflags) { +#if __linux__ return (int)syscall(SYS_futex, uaddr, op | opflags, val, timeout, uaddr2, val3); +#else + (void)val3; + return futex(uaddr, op | opflags, (int)val, timeout, uaddr2); +#endif } // returns 0, ETIMEDOUT, EFAULT, EINTR, EWOULDBLOCK @@ -455,6 +476,7 @@ _dispatch_futex_wake(uint32_t *uaddr, int wake, int opflags) DISPATCH_INTERNAL_CRASH(errno, "_dlock_wake() failed"); } +#if HAVE_FUTEX_PI static void _dispatch_futex_lock_pi(uint32_t *uaddr, struct timespec *timeout, int detect, int opflags) @@ -472,6 +494,7 @@ _dispatch_futex_unlock_pi(uint32_t *uaddr, int opflags) if (rc == 0) return; DISPATCH_CLIENT_CRASH(errno, "futex_unlock_pi() failed"); } +#endif #endif #pragma mark - wait for address @@ -606,7 +629,7 @@ _dispatch_unfair_lock_lock_slow(dispatch_unfair_lock_t dul, } } } -#elif HAVE_FUTEX +#elif HAVE_FUTEX_PI void _dispatch_unfair_lock_lock_slow(dispatch_unfair_lock_t dul, dispatch_lock_options_t flags) @@ -643,7 +666,7 @@ _dispatch_unfair_lock_unlock_slow(dispatch_unfair_lock_t dul, dispatch_lock cur) if (_dispatch_lock_has_waiters(cur)) { _dispatch_unfair_lock_wake(&dul->dul_lock, 0); } -#elif HAVE_FUTEX +#elif HAVE_FUTEX_PI // futex_unlock_pi() handles both OWNER_DIED which we abuse & WAITERS _dispatch_futex_unlock_pi(&dul->dul_lock, FUTEX_PRIVATE_FLAG); #else diff --git a/src/shims/lock.h b/src/shims/lock.h index 6bf825aa7..0fcb65eb6 100644 --- a/src/shims/lock.h +++ b/src/shims/lock.h @@ -100,6 +100,25 @@ _dispatch_lock_owner(dispatch_lock lock_value) return lock_value & DLOCK_OWNER_MASK; } +#elif defined(__OpenBSD__) + +typedef uint32_t dispatch_tid; +typedef uint32_t dispatch_lock; + +#define DLOCK_OWNER_NULL ((dispatch_tid)0) +#define DLOCK_OWNER_MASK ((dispatch_lock)0xfffffffc) +#define DLOCK_WAITERS_BIT ((dispatch_lock)0x00000001) +#define DLOCK_FAILED_TRYLOCK_BIT ((dispatch_lock)0x00000002) + +#define _dispatch_tid_self() ((dispatch_tid)(_dispatch_get_tsd_base()->tid)) + +DISPATCH_ALWAYS_INLINE +static inline dispatch_tid +_dispatch_lock_owner(dispatch_lock lock_value) +{ + return lock_value & DLOCK_OWNER_MASK; +} + #else # error define _dispatch_lock encoding scheme for your platform here #endif @@ -167,10 +186,15 @@ _dispatch_lock_has_failed_trylock(dispatch_lock lock_value) #endif #ifndef HAVE_FUTEX -#ifdef __linux__ +#if defined(__linux__) +#define HAVE_FUTEX 1 +#define HAVE_FUTEX_PI 1 +#elif defined(__OpenBSD__) #define HAVE_FUTEX 1 +#define HAVE_FUTEX_PI 0 #else #define HAVE_FUTEX 0 +#define HAVE_FUTEX_PI 0 #endif #endif // HAVE_FUTEX From 05e05e9dd080bda511412217749d530f49098345 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Fri, 2 Oct 2020 23:36:39 -0400 Subject: [PATCH 07/14] Add a runloop implementation based on pipe2. The Linux implementation uses an eventfd to impement the runloop contract for CF. However, ordinary POSIX pipes are a widely available API and can be used to achieve the same result as eventfd on other systems that support it. Since POSIX pipes require a pair of file descriptors, we force file descriptors to 32-bit integers and pack these two into a single 64-bit integer. This keeps the management of the runloop handle simple rather than having to manage the handle storage externally, for the cost of limiting the range of the file descriptor type. --- private/private.h | 2 ++ src/queue.c | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/private/private.h b/private/private.h index b87f5dc2b..dc1777e49 100644 --- a/private/private.h +++ b/private/private.h @@ -191,6 +191,8 @@ void _dispatch_prohibit_transition_to_multithreaded(bool prohibit); typedef mach_port_t dispatch_runloop_handle_t; #elif defined(__linux__) || defined(__FreeBSD__) typedef int dispatch_runloop_handle_t; +#elif defined(__unix__) && !defined(__linux__) && !defined(__FreeBSD__) +typedef uint64_t dispatch_runloop_handle_t; #elif defined(_WIN32) typedef void *dispatch_runloop_handle_t; #else diff --git a/src/queue.c b/src/queue.c index 40f059c7a..4195bddfa 100644 --- a/src/queue.c +++ b/src/queue.c @@ -6472,7 +6472,7 @@ _dispatch_runloop_handle_is_valid(dispatch_runloop_handle_t handle) { #if TARGET_OS_MAC return MACH_PORT_VALID(handle); -#elif defined(__linux__) +#elif defined(__linux__) || defined(__unix__) return handle >= 0; #elif defined(_WIN32) return handle != NULL; @@ -6490,6 +6490,8 @@ _dispatch_runloop_queue_get_handle(dispatch_lane_t dq) #elif defined(__linux__) // decode: 0 is a valid fd, so offset by 1 to distinguish from NULL return ((dispatch_runloop_handle_t)(uintptr_t)dq->do_ctxt) - 1; +#elif defined(__unix__) && !defined(__linux__) + return ((dispatch_runloop_handle_t)(uintptr_t)dq->do_ctxt); #elif defined(_WIN32) return ((dispatch_runloop_handle_t)(uintptr_t)dq->do_ctxt); #else @@ -6507,6 +6509,8 @@ _dispatch_runloop_queue_set_handle(dispatch_lane_t dq, #elif defined(__linux__) // encode: 0 is a valid fd, so offset by 1 to distinguish from NULL dq->do_ctxt = (void *)(uintptr_t)(handle + 1); +#elif defined(__unix__) && !defined(__linux__) + dq->do_ctxt = (void *)(uintptr_t)handle; #elif defined(_WIN32) dq->do_ctxt = (void *)(uintptr_t)handle; #else @@ -6514,6 +6518,12 @@ _dispatch_runloop_queue_set_handle(dispatch_lane_t dq, #endif } +#if defined(__unix__) +#define DISPATCH_RUNLOOP_HANDLE_PACK(rfd, wfd) (((uint64_t)(rfd) << 32) | (wfd)) +#define DISPATCH_RUNLOOP_HANDLE_RFD(h) ((int)((h) >> 32)) +#define DISPATCH_RUNLOOP_HANDLE_WFD(h) ((int)((h) & 0xffffffff)) +#endif + static void _dispatch_runloop_queue_handle_init(void *ctxt) { @@ -6563,6 +6573,14 @@ _dispatch_runloop_queue_handle_init(void *ctxt) } } handle = fd; +#elif defined(__unix__) && !defined(__linux__) + int fds[2]; + int r = pipe2(fds, O_CLOEXEC | O_NONBLOCK); + if (r == -1) { + DISPATCH_CLIENT_CRASH(errno, "pipe2 failure"); + } + uint32_t rfd = (uint32_t)fds[0], wfd = (uint32_t)fds[1]; + handle = DISPATCH_RUNLOOP_HANDLE_PACK(rfd, wfd); #elif defined(_WIN32) HANDLE hEvent; hEvent = CreateEventW(NULL, /*bManualReset=*/FALSE, @@ -6597,6 +6615,11 @@ _dispatch_runloop_queue_handle_dispose(dispatch_lane_t dq) #elif defined(__linux__) int rc = close(handle); (void)dispatch_assume_zero(rc); +#elif defined(__unix__) && !defined(__linux__) + int rc = close(DISPATCH_RUNLOOP_HANDLE_WFD(handle)); + (void)dispatch_assume_zero(rc); + rc = close(DISPATCH_RUNLOOP_HANDLE_RFD(handle)); + (void)dispatch_assume_zero(rc); #elif defined(_WIN32) BOOL bSuccess; bSuccess = CloseHandle(handle); @@ -6633,6 +6656,13 @@ _dispatch_runloop_queue_class_poke(dispatch_lane_t dq) result = eventfd_write(handle, 1); } while (result == -1 && errno == EINTR); (void)dispatch_assume_zero(result); +#elif defined(__unix__) && !defined(__linux__) + int wfd = DISPATCH_RUNLOOP_HANDLE_WFD(handle); + ssize_t result; + do { + result = write(wfd, "x", 1); + } while (result == -1 && errno == EINTR); + (void)dispatch_assume_zero(result - 1); #elif defined(_WIN32) BOOL bSuccess; bSuccess = SetEvent(handle); @@ -7311,6 +7341,13 @@ _gettid(void) { return (pid_t)pthread_getthreadid_np(); } +#elif defined(__OpenBSD__) +DISPATCH_ALWAYS_INLINE +static inline pid_t +_gettid(void) +{ + return getthrid(); +} #elif defined(_WIN32) DISPATCH_ALWAYS_INLINE static inline DWORD From 18ffbd608b0b9afc6c25339d1067550095d7fa71 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Fri, 2 Oct 2020 22:53:00 -0400 Subject: [PATCH 08/14] Light cleanup of the kevent backend. Some functionality requires particular preprocessor feature flags to be set but errors are raised if they aren't, so the remedy is to put these behind relevant feature flags. * dispatch_kevent_t has a qos field only when DISPATCH_USE_KEVENT_QOS * _dispatch_kq_unote_update requires DISPATCH_HAVE_DIRECT_KNOTES as it is only called from functions behind this * _dispatch_workloop_actions and the associated enum are only used when DISPATCH_USE_KEVENT_WORKLOOP. Some unused variable void casts are removed (guard_ptr and pp), which do not refer to variables -- these may be upstream merge artefacts. A type cast is made to match types for _dispatch_bug_kevent_client. This function is incorrectly placed behind HAVE_MACH in src/init.c, but to keep the scope and impact of this commit narrow, we will defer that change. Since EVFILT_FS is not supported on all kevent implementations, put these behind an #ifdef. EVFILT_USER also requires similar handling, but since EVFILT_USER kevents are used intrinsically as part of the functionality, these will be handled in a separate commit. --- src/event/event_kevent.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/event/event_kevent.c b/src/event/event_kevent.c index fe5cfaa12..2664e0753 100644 --- a/src/event/event_kevent.c +++ b/src/event/event_kevent.c @@ -101,7 +101,9 @@ _evfiltstr(short filt) _evfilt2(EVFILT_MACHPORT); _evfilt2(DISPATCH_EVFILT_MACH_NOTIFICATION); #endif +#ifdef EVFILT_FS _evfilt2(EVFILT_FS); +#endif _evfilt2(EVFILT_USER); #ifdef EVFILT_SOCK _evfilt2(EVFILT_SOCK); @@ -388,8 +390,10 @@ _dispatch_kevent_print_error(dispatch_kevent_t ke) switch (ke->data) { case 0: return; +#if DISPATCH_USE_KEVENT_QOS case ERANGE: /* A broken QoS was passed to kevent_id() */ DISPATCH_INTERNAL_CRASH(ke->qos, "Invalid kevent priority"); +#endif default: // log the unexpected error _dispatch_bug_kevent_client("kevent", _evfiltstr(ke->filter), @@ -397,7 +401,7 @@ _dispatch_kevent_print_error(dispatch_kevent_t ke) ke->flags & EV_DELETE ? "delete" : ke->flags & EV_ADD ? "add" : ke->flags & EV_ENABLE ? "enable" : "monitor", - (int)ke->data, ke->ident, ke->udata, du); + (int)ke->data, ke->ident, (uint64_t)ke->udata, du); } } @@ -591,7 +595,6 @@ _dispatch_kq_create(intptr_t *fd_ptr) guardid_t guard = (uintptr_t)fd_ptr; kqfd = guarded_kqueue_np(&guard, GUARD_CLOSE | GUARD_DUP); #else - (void)guard_ptr; kqfd = kqueue(); #endif if (kqfd == -1) { @@ -860,7 +863,6 @@ _dispatch_kq_unote_set_kevent(dispatch_unote_t _du, dispatch_kevent_t dk, du->du_priority), #endif }; - (void)pp; // if DISPATCH_USE_KEVENT_QOS == 0 } DISPATCH_ALWAYS_INLINE @@ -985,6 +987,7 @@ _dispatch_sync_ipc_handoff_end(dispatch_wlh_t wlh, mach_port_t port) } #endif +#if DISPATCH_HAVE_DIRECT_KNOTES DISPATCH_NOINLINE static bool _dispatch_kq_unote_update(dispatch_wlh_t wlh, dispatch_unote_t _du, @@ -1055,6 +1058,7 @@ _dispatch_kq_unote_update(dispatch_wlh_t wlh, dispatch_unote_t _du, dispatch_assume_zero(r); return true; } +#endif #pragma mark dispatch_muxnote_t @@ -1283,6 +1287,7 @@ _dispatch_unote_unregister_direct(dispatch_unote_t du, uint32_t flags) #pragma mark - #pragma mark dispatch_event_loop +#if DISPATCH_USE_KEVENT_WORKLOOP enum { DISPATCH_WORKLOOP_ASYNC, DISPATCH_WORKLOOP_ASYNC_FROM_SYNC, @@ -1316,6 +1321,7 @@ static char const * const _dispatch_workloop_actions[] = { [DISPATCH_WORKLOOP_SYNC_WAKE] = "sync-wake", [DISPATCH_WORKLOOP_SYNC_END] = "sync-end", }; +#endif void _dispatch_event_loop_atfork_child(void) @@ -2445,6 +2451,7 @@ const dispatch_source_type_s _dispatch_source_type_vnode = { .dst_merge_evt = _dispatch_source_merge_evt, }; +#ifdef EVFILT_FS const dispatch_source_type_s _dispatch_source_type_vfs = { .dst_kind = "vfs", .dst_filter = EVFILT_FS, @@ -2477,6 +2484,7 @@ const dispatch_source_type_s _dispatch_source_type_vfs = { .dst_create = _dispatch_unote_create_without_handle, .dst_merge_evt = _dispatch_source_merge_evt, }; +#endif #ifdef EVFILT_SOCK const dispatch_source_type_s _dispatch_source_type_sock = { From c4814ec404e0bbb36080abf31c0eedd9f6fff238 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Sat, 3 Oct 2020 00:25:36 -0400 Subject: [PATCH 09/14] Support relative kqueue timers. Some kqueue timer implementations do not support absolute timers; these also only have millisecond resolution. --- src/event/event_kevent.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/event/event_kevent.c b/src/event/event_kevent.c index 2664e0753..a2c8c5f1d 100644 --- a/src/event/event_kevent.c +++ b/src/event/event_kevent.c @@ -2363,6 +2363,12 @@ _dispatch_event_loop_timer_arm(dispatch_timer_heap_t dth, uint32_t tidx, target += range.leeway; range.leeway = 0; } +#if !NOTE_ABSOLUTE + target = range.delay; +#if defined(__OpenBSD__) + target /= 1000000; +#endif +#endif _dispatch_event_loop_timer_program(dth, tidx, target, range.leeway, EV_ADD | EV_ENABLE); From ce4ef879e5531a407e8274629892bf49b7a27bd2 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Sat, 3 Oct 2020 00:20:01 -0400 Subject: [PATCH 10/14] Handle absence of EVFILT_USER with kevent backend. Some kqueue implementations do not have EVFILT_USER. We can work around this by creating a timer implementation with a very small timeout.[1] [1] Thanks to this post, which provided the idea for the workaround: https://lists.macosforge.org/pipermail/libdispatch-dev/2009-September/000010.html --- src/event/event_kevent.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/event/event_kevent.c b/src/event/event_kevent.c index a2c8c5f1d..d64636623 100644 --- a/src/event/event_kevent.c +++ b/src/event/event_kevent.c @@ -104,7 +104,9 @@ _evfiltstr(short filt) #ifdef EVFILT_FS _evfilt2(EVFILT_FS); #endif +#ifdef EVFILT_USER _evfilt2(EVFILT_USER); +#endif #ifdef EVFILT_SOCK _evfilt2(EVFILT_SOCK); #endif @@ -532,11 +534,22 @@ _dispatch_kevent_merge_muxed(dispatch_kevent_t ke) } } +/* + * If the kevent implementation doesn't support EVFILT_USER for + * signaling, then we use EVFILT_TIMER with EV_ONESHOT with this ident + * to make do. + */ +#define DISPATCH_KEVENT_ERSATZ_EVFILT_USER_IDENT (~0ull << 9) + DISPATCH_NOINLINE static void _dispatch_kevent_drain(dispatch_kevent_t ke) { +#ifdef EVFILT_USER if (ke->filter == EVFILT_USER) { +#else + if (ke->filter == EVFILT_TIMER && ke->ident == DISPATCH_KEVENT_ERSATZ_EVFILT_USER_IDENT) { +#endif _dispatch_kevent_mgr_debug("received", ke); return; } @@ -583,10 +596,17 @@ static void _dispatch_kq_create(intptr_t *fd_ptr) { static const dispatch_kevent_s kev = { +#ifdef EVFILT_USER .ident = 1, .filter = EVFILT_USER, .flags = EV_ADD|EV_CLEAR, .udata = (dispatch_kevent_udata_t)DISPATCH_WLH_MANAGER, +#else + .ident = DISPATCH_KEVENT_ERSATZ_EVFILT_USER_IDENT, + .filter = EVFILT_TIMER, + .flags = EV_ADD|EV_DISABLE|EV_ONESHOT, + .data = 1, +#endif }; int kqfd; @@ -789,9 +809,15 @@ _dispatch_kq_drain(dispatch_wlh_t wlh, dispatch_kevent_t ke, int n, #if DISPATCH_DEBUG for (r = 0; r < n; r++) { +#ifdef EVFILT_USER if (ke[r].filter != EVFILT_USER || DISPATCH_MGR_QUEUE_DEBUG) { _dispatch_kevent_debug_n(NULL, ke + r, r, n); } +#else + if (DISPATCH_MGR_QUEUE_DEBUG) { + _dispatch_kevent_debug_n(NULL, ke + r, r, n); + } +#endif } #endif @@ -923,9 +949,13 @@ _dispatch_kq_deferred_update(dispatch_wlh_t wlh, dispatch_kevent_t ke) ke->udata); dispatch_kevent_t dk = _dispatch_kq_deferred_reuse_slot(wlh, ddi, slot); *dk = *ke; +#ifdef EVFILT_USER if (ke->filter != EVFILT_USER) { _dispatch_kevent_mgr_debug("deferred", ke); } +#else + _dispatch_kevent_mgr_debug("deferred", ke); +#endif } else { _dispatch_kq_update_one(wlh, ke); } @@ -1887,10 +1917,17 @@ _dispatch_event_loop_poke(dispatch_wlh_t wlh, uint64_t dq_state, uint32_t flags) { if (wlh == DISPATCH_WLH_MANAGER) { dispatch_kevent_s ke = (dispatch_kevent_s){ +#ifdef EVFILT_USER .ident = 1, .filter = EVFILT_USER, .fflags = NOTE_TRIGGER, .udata = (dispatch_kevent_udata_t)DISPATCH_WLH_MANAGER, +#else + .ident = DISPATCH_KEVENT_ERSATZ_EVFILT_USER_IDENT, + .filter = EVFILT_TIMER, + .flags = EV_ADD|EV_ENABLE|EV_ONESHOT, + .data = 1 +#endif }; return _dispatch_kq_deferred_update(DISPATCH_WLH_ANON, &ke); } else if (wlh && wlh != DISPATCH_WLH_ANON) { From 937f30969c181ef399b755ce251d2028f1b6bbd7 Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Wed, 7 Jul 2021 13:01:27 -0400 Subject: [PATCH 11/14] Cast when printing dispatch_unote_ident_t. Different platforms may define dispatch_unote_ident_t differently and subsequently this type may have different bit widths. Therefore, when printing du_ident, cast explicitly to deal with format mismatch errors. The alternative is to specify explicit format string macros, but this is a little complex and casting is already being used in #584. One special case for consideration is `_dispatch_timer_unote_disarm`; this gets the `du_ident` and converts that into a `uint32_t` timer index. When `dispatch_unote_ident` is a `uint32_t` this is of course fine, but needs consideration for when it isn't. Here, we just cast down. This is somewhat reasonable since this is initialized from `_dispatch_timer_unote_idx` which returns an `unsigned int`. (Of course, we are not actually bounds checking that index, but that's outside the scope of this commit). --- src/event/event.c | 2 +- src/event/event_internal.h | 2 ++ src/event/event_kevent.c | 4 ++-- src/init.c | 4 ++-- src/source.c | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/event/event.c b/src/event/event.c index 6ace80ecb..33df9f171 100644 --- a/src/event/event.c +++ b/src/event/event.c @@ -792,7 +792,7 @@ static void _dispatch_timer_unote_disarm(dispatch_timer_source_refs_t dt, dispatch_timer_heap_t dth) { - uint32_t tidx = dt->du_ident; + uint32_t tidx = (uint32_t)dt->du_ident; dispatch_assert(_dispatch_unote_armed(dt)); _dispatch_timer_heap_remove(&dth[tidx], dt); diff --git a/src/event/event_internal.h b/src/event/event_internal.h index 14c485ee3..5b2c7fc80 100644 --- a/src/event/event_internal.h +++ b/src/event/event_internal.h @@ -125,6 +125,8 @@ _dispatch_timer_flags_from_clock(dispatch_clock_t clock) #if defined(_WIN32) typedef uintptr_t dispatch_unote_ident_t; +#elif defined(__OpenBSD__) +typedef uintptr_t dispatch_unote_ident_t; #else typedef uint32_t dispatch_unote_ident_t; #endif diff --git a/src/event/event_kevent.c b/src/event/event_kevent.c index d64636623..0d8db09f1 100644 --- a/src/event/event_kevent.c +++ b/src/event/event_kevent.c @@ -240,9 +240,9 @@ dispatch_kevent_debug(const char *verb, const dispatch_kevent_s *kev, #define _dispatch_du_debug(what, du) \ _dispatch_debug("kevent-source[%p]: %s kevent[%p] " \ - "{ filter = %s, ident = 0x%x }", \ + "{ filter = %s, ident = 0x%llx }", \ _dispatch_wref2ptr((du)->du_owner_wref), what, \ - (du), _evfiltstr((du)->du_filter), (du)->du_ident) + (du), _evfiltstr((du)->du_filter), (unsigned long long)(du)->du_ident) #if DISPATCH_MACHPORT_DEBUG #ifndef MACH_PORT_TYPE_SPREQUEST diff --git a/src/init.c b/src/init.c index 0fe0e4385..1640cbab8 100644 --- a/src/init.c +++ b/src/init.c @@ -1049,7 +1049,7 @@ _dispatch_bug_kevent_vanished(dispatch_unote_t du) "{ %p[%s], ident: %" PRIdPTR " / 0x%" PRIxPTR ", handler: %p }", dux_type(du._du)->dst_kind, dou._dq, dou._dq->dq_label ? dou._dq->dq_label : "", - du._du->du_ident, du._du->du_ident, func); + (intptr_t)du._du->du_ident, (uintptr_t)du._du->du_ident, func); } #endif // RDAR_49023449 @@ -1155,7 +1155,7 @@ _dispatch_logv_init(void *context DISPATCH_UNUSED) #else dprintf(dispatch_logfile, "=== log file opened for %s[%u] at " "%ld.%06u ===\n", getprogname() ?: "", getpid(), - tv.tv_sec, (int)tv.tv_usec); + (long)tv.tv_sec, (int)tv.tv_usec); #endif } } diff --git a/src/source.c b/src/source.c index d1581351d..69b731d92 100644 --- a/src/source.c +++ b/src/source.c @@ -1401,8 +1401,8 @@ _dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz) return dsnprintf(buf, bufsiz, "target = %s[%p], ident = 0x%llx, " "mask = 0x%x, pending_data = 0x%llx, registered = %d, " "armed = %d, %s%s%s", - target && target->dq_label ? target->dq_label : "", target, - (unsigned long long)dr->du_ident, dr->du_fflags, + target && target->dq_label ? target->dq_label : "", + target, (unsigned long long)dr->du_ident, dr->du_fflags, (unsigned long long)dr->ds_pending_data, _du_state_registered(du_state), _du_state_armed(du_state), (dqf & DSF_CANCELED) ? "cancelled, " : "", From 207ed9efaa7ef5e75d5f71d7de46e66867e73a6c Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Wed, 7 Jul 2021 16:17:30 -0400 Subject: [PATCH 12/14] Swift support and OpenBSD. Add the right `os(OpenBSD)` directives. Ensure `DISPATCH_COCOA_COMPAT` is defined and expose the runloop innards for `CFRunLoop` to use in Foundation down the line. --- private/private.h | 2 +- src/queue.c | 2 +- src/swift/Source.swift | 10 +++++----- src/swift/Wrapper.swift | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/private/private.h b/private/private.h index dc1777e49..b0ee07022 100644 --- a/private/private.h +++ b/private/private.h @@ -177,7 +177,7 @@ void _dispatch_prohibit_transition_to_multithreaded(bool prohibit); #if TARGET_OS_MAC #define DISPATCH_COCOA_COMPAT 1 -#elif defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32) +#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(_WIN32) #define DISPATCH_COCOA_COMPAT 1 #else #define DISPATCH_COCOA_COMPAT 0 diff --git a/src/queue.c b/src/queue.c index 4195bddfa..356fef19b 100644 --- a/src/queue.c +++ b/src/queue.c @@ -6950,7 +6950,7 @@ _dispatch_runloop_root_queue_wakeup_4CF(dispatch_queue_t dq) _dispatch_runloop_queue_wakeup(upcast(dq)._dl, 0, false); } -#if TARGET_OS_MAC || defined(_WIN32) +#if TARGET_OS_MAC || defined(_WIN32) || defined(__OpenBSD__) dispatch_runloop_handle_t _dispatch_runloop_root_queue_get_port_4CF(dispatch_queue_t dq) { diff --git a/src/swift/Source.swift b/src/swift/Source.swift index 57677c2c2..f9c24280d 100644 --- a/src/swift/Source.swift +++ b/src/swift/Source.swift @@ -116,7 +116,7 @@ extension DispatchSource { } #endif -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) public struct ProcessEvent : OptionSet, RawRepresentable { public let rawValue: UInt public init(rawValue: UInt) { self.rawValue = rawValue } @@ -174,7 +174,7 @@ extension DispatchSource { } #endif -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) public class func makeProcessSource(identifier: pid_t, eventMask: ProcessEvent, queue: DispatchQueue? = nil) -> DispatchSourceProcess { let source = dispatch_source_create(_swift_dispatch_source_type_PROC(), UInt(identifier), eventMask.rawValue, queue?.__wrapped) return DispatchSource(source: source) as DispatchSourceProcess @@ -224,7 +224,7 @@ extension DispatchSource { return DispatchSource(source: source) as DispatchSourceUserDataReplace } -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) public class func makeFileSystemObjectSource(fileDescriptor: Int32, eventMask: FileSystemEvent, queue: DispatchQueue? = nil) -> DispatchSourceFileSystemObject { let source = dispatch_source_create(_swift_dispatch_source_type_VNODE(), UInt(fileDescriptor), eventMask.rawValue, queue?.__wrapped) return DispatchSource(source: source) as DispatchSourceFileSystemObject @@ -290,7 +290,7 @@ extension DispatchSourceMemoryPressure { } #endif -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) extension DispatchSourceProcess { public var handle: pid_t { return pid_t(dispatch_source_get_handle(self as! DispatchSource)) @@ -646,7 +646,7 @@ extension DispatchSourceTimer { } } -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) extension DispatchSourceFileSystemObject { public var handle: Int32 { return Int32(dispatch_source_get_handle((self as! DispatchSource).__wrapped)) diff --git a/src/swift/Wrapper.swift b/src/swift/Wrapper.swift index 999c22d6c..63f76bcd9 100644 --- a/src/swift/Wrapper.swift +++ b/src/swift/Wrapper.swift @@ -182,7 +182,7 @@ extension DispatchSource : DispatchSourceMachSend, } #endif -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) extension DispatchSource : DispatchSourceProcess, DispatchSourceFileSystemObject { } @@ -273,7 +273,7 @@ public protocol DispatchSourceMemoryPressure : DispatchSourceProtocol { } #endif -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) public protocol DispatchSourceProcess : DispatchSourceProtocol { var handle: pid_t { get } @@ -303,7 +303,7 @@ public protocol DispatchSourceTimer : DispatchSourceProtocol { func scheduleRepeating(wallDeadline: DispatchWallTime, interval: Double, leeway: DispatchTimeInterval) } -#if !os(Linux) && !os(Android) && !os(Windows) +#if !os(Linux) && !os(Android) && !os(Windows) && !os(OpenBSD) public protocol DispatchSourceFileSystemObject : DispatchSourceProtocol { var handle: Int32 { get } From f0f7799d9d462aefa6c2c75570b1443de19ad47c Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Sun, 21 Nov 2021 20:56:12 -0500 Subject: [PATCH 13/14] [event] Workqueue monitoring for OpenBSD. Simply speaking, the global concurrent queue has a thread per cpu and schedules functions or blocks on one of these threads. If these threads are all busy, without workqueue monitoring, we cannot schedule a new function or block until one of those threads returns. If thread(s) have not returned however because they are asleep (say), then we could overcommit the global concurrent queue by scheduling more work. The Linux implementation checks each registered thread ID to see if the corresponding thread is in a runnable state according to procfs every second. On some platforms without procfs, this same information is obtainable essentially via the relevant sysctls. Libraries may exist to facilitate issuing the correct sysctls (c.f. `kvm_getprocs`) but these were not used here so to avoid complications with the right invocations when linking. A unit test is also added to test the overcommit functionality. In this test, we tie up all cpus to prevent the scheduled threads from returning and then attempt to run one more task. If the overcommit monitoring is working as intended, then Dispatch will detect all the threads are simply asleep and will attempt to overcommit. If not, the test will notice the function we attempted to overcommit did not run and fail the test. --- src/event/workqueue.c | 46 +++++++++++++++++++++ src/event/workqueue_internal.h | 2 +- tests/CMakeLists.txt | 6 +++ tests/dispatch_workqueue.c | 74 ++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 tests/dispatch_workqueue.c diff --git a/src/event/workqueue.c b/src/event/workqueue.c index 749b0a452..6bc65c3b6 100644 --- a/src/event/workqueue.c +++ b/src/event/workqueue.c @@ -247,6 +247,52 @@ _dispatch_workq_count_runnable_workers(dispatch_workq_monitor_t mon) _dispatch_unfair_lock_unlock(&mon->registered_tid_lock); } +#elif defined(__OpenBSD__) +#include +#include +#include + +static void +_dispatch_workq_count_runnable_workers(dispatch_workq_monitor_t mon) +{ + struct kinfo_proc kp[WORKQ_MAX_TRACKED_TIDS] = {0}; + size_t size, len; + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)getpid(), (int)sizeof(struct kinfo_proc), 0}; + if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0) { + _dispatch_debug("workq: Failed to sysctl1"); + return; + } + + size = size > sizeof(kp)? sizeof(kp): size; + len = size / sizeof(struct kinfo_proc); + mib[5] = (int)len; + if (sysctl(mib, 6, kp, &size, NULL, 0) < 0) { + _dispatch_debug("workq: Failed to sysctl2"); + return; + } + + int running_count = 0; + + _dispatch_unfair_lock_lock(&mon->registered_tid_lock); + + for (int i = 0; i < mon->num_registered_tids; i++) { + dispatch_tid tid = mon->registered_tids[i]; + for (size_t j = 0; j < len; j++) { + if ((dispatch_tid)kp[j].p_tid != tid) { + continue; + } + + if (kp[j].p_stat == SRUN || kp[j].p_stat == SIDL || kp[j].p_stat == SONPROC) { + running_count++; + break; + } + } + } + + mon->num_runnable = running_count; + + _dispatch_unfair_lock_unlock(&mon->registered_tid_lock); +} #else #error must define _dispatch_workq_count_runnable_workers #endif diff --git a/src/event/workqueue_internal.h b/src/event/workqueue_internal.h index 62028077a..118d42db5 100644 --- a/src/event/workqueue_internal.h +++ b/src/event/workqueue_internal.h @@ -30,7 +30,7 @@ void _dispatch_workq_worker_register(dispatch_queue_global_t root_q); void _dispatch_workq_worker_unregister(dispatch_queue_global_t root_q); -#if defined(__linux__) || defined(_WIN32) +#if defined(__linux__) || defined(_WIN32) || defined(__OpenBSD__) #define HAVE_DISPATCH_WORKQ_MONITORING 1 #else #define HAVE_DISPATCH_WORKQ_MONITORING 0 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0176a062b..9430c7c92 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -183,6 +183,12 @@ set_tests_properties(dispatch_io_pipe_close PROPERTIES TIMEOUT 5) add_unit_test(dispatch_c99 SOURCES dispatch_c99.c) add_unit_test(dispatch_plusplus SOURCES dispatch_plusplus.cpp) +if (DISPATCH_USE_INTERNAL_WORKQUEUE) + add_unit_test(dispatch_workqueue + SOURCES + dispatch_workqueue.c) +endif() + # test-specific link options if(WIN32) target_link_libraries(dispatch_io_muxed PRIVATE WS2_32) diff --git a/tests/dispatch_workqueue.c b/tests/dispatch_workqueue.c new file mode 100644 index 000000000..21e96e043 --- /dev/null +++ b/tests/dispatch_workqueue.c @@ -0,0 +1,74 @@ +#include +#include "dispatch_test.h" + +struct test_context { + uint32_t ncpu; + int flag; +}; + +static void +timeout(void *context) +{ + struct test_context *ctx = (struct test_context *)context; + sleep(2); // Give the monitor the best chance of firing. + test_int32_format(ctx->flag, 1, "flag"); + test_stop(); +} + +static void +raise_flag(void *context) +{ + struct test_context *ctx = (struct test_context *)context; + ctx->flag++; +} + +static void +spin(void *context) +{ + struct test_context *ctx = (struct test_context *)context; + sleep(ctx->ncpu * 2); +} + +static uint32_t +activecpu(void) +{ + uint32_t activecpu; +#if defined(__linux__) || defined(__OpenBSD__) + activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN); +#elif defined(_WIN32) + SYSTEM_INFO si; + GetSystemInfo(&si); + activecpu = si.dwNumberOfProcessors; +#else + size_t s = sizeof(activecpu); + sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0); +#endif + return activecpu; +} + +struct test_context ctx; + +int +main(void) +{ + uint32_t ncpu = activecpu(); + + dispatch_test_start("Dispatch workqueue"); + + dispatch_queue_t global = dispatch_get_global_queue(0, 0); + test_ptr_notnull("dispatch_get_global_queue", global); + + ctx.ncpu = ncpu; + dispatch_async_f(global, &ctx, timeout); + + for(int i = 0; i < (int)ncpu - 1; i++) { + dispatch_async_f(global, &ctx, spin); + } + + // All cpus are tied up at this point. Workqueue + // should execute this function by overcommit. + dispatch_async_f(global, &ctx, raise_flag); + + dispatch_main(); + return 0; +} From 04ab2a4b87f2640c395afb5766a299221b037d3d Mon Sep 17 00:00:00 2001 From: 3405691582 Date: Mon, 22 Aug 2022 22:31:03 -0400 Subject: [PATCH 14/14] Use some disposable heap arrays instead of stack. Using a 512K buffer in this test may mean that the test process may hit the stack size rlimit quickly and segfault. Instead of trying to reduce the buffer sizes and then subsequently try and unwind other weird behavior in this test, just use heap-allocated buffers. We don't actually seem to care about the contents in this test, so we can just throw them away immediately after we read into them. --- tests/dispatch_select.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/dispatch_select.c b/tests/dispatch_select.c index 6202711cb..352157a6e 100644 --- a/tests/dispatch_select.c +++ b/tests/dispatch_select.c @@ -67,8 +67,9 @@ stage1(int stage) dispatch_source_set_event_handler(source, ^{ size_t buffer_size = 500*1024; - char buffer[500*1024]; + char *buffer = malloc(buffer_size*sizeof(char)); ssize_t sz = dispatch_test_fd_read(fd, buffer, buffer_size); + free(buffer); test_sizet_less_than_or_equal("kevent read 1", sz, buffer_size+1); dispatch_source_cancel(source); }); @@ -129,12 +130,14 @@ stage2(void) dispatch_source_set_event_handler(source, ^{ size_t est = dispatch_source_get_data(source); test_sizet_less_than_or_equal("estimated", est, expected - actual); - char buffer[500*1024]; - ssize_t sz = dispatch_test_fd_read(fd, buffer, sizeof(buffer)); + size_t buffer_size = 500*1024*sizeof(char); + char *buffer = malloc(buffer_size); + ssize_t sz = dispatch_test_fd_read(fd, buffer, buffer_size); + free(buffer); actual += sz; - if (sz < (ssize_t)sizeof(buffer)) + if (sz < (ssize_t)(buffer_size)) { - sz = dispatch_test_fd_read(fd, buffer, sizeof(buffer)); + sz = dispatch_test_fd_read(fd, buffer, buffer_size); actual += sz; test_long("EOF", sz, 0); dispatch_source_cancel(source);