diff --git a/.gitignore b/.gitignore index aa26514c9..1bf54ca69 100644 --- a/.gitignore +++ b/.gitignore @@ -29,5 +29,3 @@ config configure libtool .dirstamp -/dispatch/module.modulemap -/private/module.modulemap diff --git a/CMakeLists.txt b/CMakeLists.txt index 75c419c13..51f1f1b71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,29 +1,89 @@ - -cmake_minimum_required(VERSION 3.15.1) +cmake_minimum_required(VERSION 3.26...3.29) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) -# NOTE(compnerd) enable CMP0091 - select MSVC runtime based on -# CMAKE_MSVC_RUNTIME_LIBRARY. Requires CMake 3.15 or newer. -if(POLICY CMP0091) - cmake_policy(SET CMP0091 NEW) +if(POLICY CMP0157 AND CMAKE_Swift_COMPILER_USE_OLD_DRIVER) + cmake_policy(SET CMP0157 OLD) +endif() + +if(POLICY CMP0181) + cmake_policy(SET CMP0181 NEW) endif() project(dispatch VERSION 1.3 LANGUAGES C CXX) -if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") - include(ClangClCompileRules) -endif() +set(CMAKE_POSITION_INDEPENDENT_CODE YES) + +if(WIN32) + include(CheckCSourceCompiles) + include(CheckSymbolExists) + + check_c_source_compiles([=[ +#include +int main(int argc, char *argv[]) { + switch ((LOGICAL_PROCESSOR_RELATIONSHIP)0) { + case RelationProcessorDie: + case RelationNumaNodeEx: + return 0; + } + return 0; +} +]=] DISPATCH_HAVE_EXTENDED_SLPI_20348) + if(DISPATCH_HAVE_EXTENDED_SLPI_20348) + add_compile_definitions(DISPATCH_HAVE_EXTENDED_SLPI_20348) + endif() + + check_c_source_compiles([=[ +#include +int main(int argc, char *argv[]) { + switch ((LOGICAL_PROCESSOR_RELATIONSHIP)0) { + case RelationProcessorModule: + return 0; + } + return 0; +} +]=] DISPATCH_HAVE_EXTENDED_SLPI_22000) + if(DISPATCH_HAVE_EXTENDED_SLPI_22000) + add_compile_definitions(DISPATCH_HAVE_EXTENDED_SLPI_22000) + endif() + + check_c_source_compiles([=[ +#include +#include +int main(int argc, char *argv[]) { + FILE_PIPE_LOCAL_INFORMATION fpli; +} +]=] HAVE_FILE_PIPE_LOCAL_INFORMATION) + if(HAVE_FILE_PIPE_LOCAL_INFORMATION) + add_compile_definitions(HAVE_FILE_PIPE_LOCAL_INFORMATION) + endif() + + check_symbol_exists(mkstemp "stdlib.h" HAVE_MKSTEMP) + if(HAVE_MKSTEMP) + add_compile_definitions(HAVE_MKSTEMP) + endif() + + check_c_source_compiles([=[ +#include +int main(int argc, char *argv[]) { + mode_t mode; +} +]=] HAVE_MODE_T) + if(HAVE_MODE_T) + add_compile_definitions(HAVE_MODE_T) + endif() -if(CMAKE_SYSTEM_NAME STREQUAL Windows) - include(DispatchWindowsSupport) - dispatch_windows_arch_spelling(${CMAKE_SYSTEM_PROCESSOR} DISPATCH_MSVC_ARCH) - dispatch_windows_include_for_arch(${DISPATCH_MSVC_ARCH} DISPATCH_INCLUDES) - include_directories(BEFORE SYSTEM ${DISPATCH_INCLUDES}) - dispatch_windows_lib_for_arch(${CMAKE_SYSTEM_PROCESSOR} DISPATCH_LIBDIR) - link_directories(${DISPATCH_LIBDIR}) + check_c_source_compiles([=[ +#include +int main(int argc, char *argv[]) { + pid_t mode; +} +]=] HAVE_PID_T) + if(HAVE_PID_T) + add_compile_definitions(HAVE_PID_T) + endif() endif() set(CMAKE_C_STANDARD 11) @@ -42,6 +102,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_THREAD_PREFER_PTHREAD TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE) +if(ANDROID) + set(CMAKE_HAVE_LIBC_PTHREAD YES) +endif() find_package(Threads REQUIRED) include(CheckCCompilerFlag) @@ -49,6 +112,7 @@ include(CheckCSourceCompiles) include(CheckFunctionExists) include(CheckIncludeFiles) include(CheckLibraryExists) +include(CheckLinkerFlag) include(CheckSymbolExists) include(GNUInstallDirs) include(CTest) @@ -57,7 +121,8 @@ include(DispatchAppleOptions) include(DispatchSanitization) include(DispatchCompilerWarnings) include(DTrace) -include(SwiftSupport) + +include(EnableFramePointers) # NOTE(abdulras) this is the CMake supported way to control whether we generate # shared or static libraries. This impacts the behaviour of `add_library` in @@ -71,7 +136,7 @@ set(USE_LIBDISPATCH_INIT_CONSTRUCTOR ${ENABLE_DISPATCH_INIT_CONSTRUCTOR}) option(ENABLE_DTRACE "enable dtrace support" "") -if(CMAKE_SYSTEM_NAME STREQUAL Darwin OR CMAKE_SYSTEM_NAME STREQUAL FreeBSD) +if(APPLE OR BSD) set(ENABLE_INTERNAL_PTHREAD_WORKQUEUES_DEFAULT OFF) else() set(ENABLE_INTERNAL_PTHREAD_WORKQUEUES_DEFAULT ON) @@ -102,6 +167,8 @@ option(ENABLE_THREAD_LOCAL_STORAGE "enable usage of thread local storage via _Th set(DISPATCH_USE_THREAD_LOCAL_STORAGE ${ENABLE_THREAD_LOCAL_STORAGE}) +check_linker_flag(C "LINKER:--build-id=sha1" LINKER_SUPPORTS_BUILD_ID) + check_symbol_exists(__GNU_LIBRARY__ "features.h" _GNU_SOURCE) if(_GNU_SOURCE) set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} -D_GNU_SOURCE) @@ -113,7 +180,9 @@ if(__BUILTIN_TRAP) set(HAVE_NORETURN_BUILTIN_TRAP 1) endif() -find_package(LibRT) +if(NOT ANDROID) + find_package(LibRT) +endif() check_function_exists(_pthread_workqueue_init HAVE__PTHREAD_WORKQUEUE_INIT) check_function_exists(getprogname HAVE_GETPROGNAME) @@ -128,6 +197,7 @@ check_function_exists(pthread_attr_setcpupercent_np HAVE_PTHREAD_ATTR_SETCPUPERC check_function_exists(pthread_yield_np HAVE_PTHREAD_YIELD_NP) check_function_exists(pthread_main_np HAVE_PTHREAD_MAIN_NP) check_function_exists(pthread_workqueue_setdispatch_np HAVE_PTHREAD_WORKQUEUE_SETDISPATCH_NP) +check_function_exists(pthread_setname_np HAVE_PTHREAD_SETNAME_NP) check_function_exists(strlcpy HAVE_STRLCPY) check_function_exists(sysconf HAVE_SYSCONF) check_function_exists(arc4random HAVE_ARC4RANDOM) @@ -166,12 +236,12 @@ if(HAVE_MACH) else() set(USE_MACH_SEM 0) endif() -if(CMAKE_SYSTEM_NAME STREQUAL Windows) - add_compile_definitions($<$,$>:USE_WIN32_SEM>) +if(WIN32) + add_compile_definitions($<$:USE_WIN32_SEM>) endif() check_library_exists(pthread sem_init "" USE_POSIX_SEM) # NOTE: android has not always provided a libpthread, but uses the pthreads API -if(CMAKE_SYSTEM_NAME STREQUAL Android) +if(ANDROID) set(USE_POSIX_SEM 1) endif() @@ -197,16 +267,17 @@ check_symbol_exists(VQ_FREE_SPACE_CHANGE "sys/mount.h" HAVE_DECL_VQ_FREE_SPACE_C check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY) check_symbol_exists(program_invocation_name "errno.h" HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) if (HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) - add_compile_definitions($<$,$>:_GNU_SOURCE=1>) + add_compile_definitions($<$:_GNU_SOURCE=1>) endif() check_symbol_exists(__printflike "bsd/sys/cdefs.h" HAVE_PRINTFLIKE) -if(CMAKE_SYSTEM_NAME STREQUAL Android) +if(ANDROID) set(ENABLE_DTRACE_DEFAULT OFF) + add_link_options("LINKER:-z,max-page-size=16384") endif() -if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) - add_compile_definitions($<$,$>:_WITH_DPRINTF>) +if(BSD) + add_compile_definitions($<$:_WITH_DPRINTF>) endif() if(ENABLE_DTRACE) @@ -217,9 +288,9 @@ if(ENABLE_DTRACE) endif() if(dtrace_EXECUTABLE) - add_compile_definitions($<$,$>:DISPATCH_USE_DTRACE=1>) + add_compile_definitions($<$:DISPATCH_USE_DTRACE=1>) else() - add_compile_definitions($<$,$>:DISPATCH_USE_DTRACE=0>) + add_compile_definitions($<$:DISPATCH_USE_DTRACE=0>) endif() find_program(leaks_EXECUTABLE leaks) @@ -228,35 +299,24 @@ if(leaks_EXECUTABLE) endif() -if(CMAKE_SYSTEM_NAME STREQUAL Darwin) - add_custom_command(OUTPUT - "${PROJECT_SOURCE_DIR}/dispatch/module.modulemap" - "${PROJECT_SOURCE_DIR}/private/module.modulemap" - COMMAND - ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/dispatch/darwin/module.modulemap" "${PROJECT_SOURCE_DIR}/dispatch/module.modulemap" - COMMAND - ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/private/darwin/module.modulemap" "${PROJECT_SOURCE_DIR}/private/module.modulemap") +if(APPLE) + add_compile_options($<:$:-fmodule-map-file=${PROJECT_SOURCE_DIR}/dispatch/darwin/module.modulemap> + $<:$:-fmodule-map-file=${PROJECT_SOURCE_DIR}/private/darwin/module.modulemap>) else() - add_custom_command(OUTPUT - "${PROJECT_SOURCE_DIR}/dispatch/module.modulemap" - "${PROJECT_SOURCE_DIR}/private/module.modulemap" - COMMAND - ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/dispatch/generic/module.modulemap" "${PROJECT_SOURCE_DIR}/dispatch/module.modulemap" - COMMAND - ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/private/generic/module.modulemap" "${PROJECT_SOURCE_DIR}/private/module.modulemap") + add_compile_options($<$:-fmodule-map-file=${PROJECT_SOURCE_DIR}/dispatch/generic/module.modulemap> + $<$:-fmodule-map-file=${PROJECT_SOURCE_DIR}/private/generic/module.modulemap>) endif() -add_custom_target(module-maps ALL - DEPENDS - "${PROJECT_SOURCE_DIR}/dispatch/module.modulemap" - "${PROJECT_SOURCE_DIR}/private/module.modulemap") configure_file("${PROJECT_SOURCE_DIR}/cmake/config.h.in" "${PROJECT_BINARY_DIR}/config/config_ac.h") -add_compile_definitions($<$,$>:HAVE_CONFIG_H>) +add_compile_definitions($<$:HAVE_CONFIG_H>) if(ENABLE_SWIFT) - set(INSTALL_TARGET_DIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/$" CACHE PATH "Path where the libraries will be installed") + include(SwiftSupport) + option(dispatch_INSTALL_ARCH_SUBDIR "Install libraries under an architecture subdirectory" NO) + + set(INSTALL_TARGET_DIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/${dispatch_PLATFORM}$<$:/${dispatch_ARCH}>" CACHE PATH "Path where the libraries will be installed") set(INSTALL_DISPATCH_HEADERS_DIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/dispatch" CACHE PATH "Path where the headers will be installed for libdispatch") set(INSTALL_BLOCK_HEADERS_DIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/Block" CACHE PATH "Path where the headers will be installed for the blocks runtime") set(INSTALL_OS_HEADERS_DIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/os" CACHE PATH "Path where the os/ headers will be installed") @@ -272,6 +332,9 @@ add_subdirectory(dispatch) add_subdirectory(man) add_subdirectory(os) add_subdirectory(private) +if(NOT APPLE) + add_subdirectory(src/BlocksRuntime) +endif() add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index e36bf969f..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,10 +0,0 @@ -By submitting a pull request, you represent that you have the right to license -your contribution to Apple and the community, and agree by submitting the patch -that your contributions are licensed under the [Swift -license](https://swift.org/LICENSE.txt). - ---- - -Before submitting the pull request, please make sure you have tested your -changes and that they follow the Swift project [guidelines for contributing -code](https://swift.org/contributing/#contributing-code). diff --git a/cmake/config.h.in b/cmake/config.h.in index 2896a2083..fc630ea67 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -208,6 +208,9 @@ /* Define to 1 if you have the `_pthread_workqueue_init' function. */ #cmakedefine HAVE__PTHREAD_WORKQUEUE_INIT +/* Define to 1 if you have the `pthread_setname_np' function. */ +#cmakedefine01 HAVE_PTHREAD_SETNAME_NP + /* Define to use non-portable pthread TSD optimizations for Mac OS X) */ #cmakedefine USE_APPLE_TSD_OPTIMIZATIONS @@ -257,3 +260,6 @@ /* Define if using Darwin $NOCANCEL */ #cmakedefine __DARWIN_NON_CANCELABLE + +/* Define to 1 if you have the `strlcpy` function. */ +#cmakedefine01 HAVE_STRLCPY diff --git a/cmake/modules/ClangClCompileRules.cmake b/cmake/modules/ClangClCompileRules.cmake deleted file mode 100644 index 0265d5ea7..000000000 --- a/cmake/modules/ClangClCompileRules.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -# clang-cl interprets paths starting with /U as macro undefines, so we need to -# put a -- before the input file path to force it to be treated as a path. -string(REPLACE "-c " "-c -- " CMAKE_C_COMPILE_OBJECT "${CMAKE_C_COMPILE_OBJECT}") -string(REPLACE "-c " "-c -- " CMAKE_CXX_COMPILE_OBJECT "${CMAKE_CXX_COMPILE_OBJECT}") diff --git a/cmake/modules/DispatchCompilerWarnings.cmake b/cmake/modules/DispatchCompilerWarnings.cmake index 6ef9d3164..b10644410 100644 --- a/cmake/modules/DispatchCompilerWarnings.cmake +++ b/cmake/modules/DispatchCompilerWarnings.cmake @@ -1,75 +1,84 @@ if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") # TODO: someone needs to provide the msvc equivalent warning flags +elseif(WIN32) + # Tareting Windows but using a non-MSVC compiler. Set -fms-extensions + # so that we can use __popcnt64 + add_compile_options($<$:-fms-extensions>) else() - add_compile_options($<$,$>:-Werror>) - add_compile_options($<$,$>:-Wall>) - add_compile_options($<$,$>:-Wextra>) + add_compile_options($<$:-Werror>) + add_compile_options($<$:-Wall>) + add_compile_options($<$:-Wextra>) - add_compile_options($<$,$>:-Warray-bounds-pointer-arithmetic>) - add_compile_options($<$,$>:-Wassign-enum>) - add_compile_options($<$,$>:-Watomic-properties>) - add_compile_options($<$,$>:-Wcomma>) - add_compile_options($<$,$>:-Wconditional-uninitialized>) - add_compile_options($<$,$>:-Wconversion>) - add_compile_options($<$,$>:-Wcovered-switch-default>) - add_compile_options($<$,$>:-Wdate-time>) - add_compile_options($<$,$>:-Wdeprecated>) - add_compile_options($<$,$>:-Wdocumentation>) - add_compile_options($<$,$>:-Wdouble-promotion>) - add_compile_options($<$,$>:-Wduplicate-enum>) - add_compile_options($<$,$>:-Wexpansion-to-defined>) - add_compile_options($<$,$>:-Wfloat-equal>) - add_compile_options($<$,$>:-Widiomatic-parentheses>) - add_compile_options($<$,$>:-Winfinite-recursion>) - add_compile_options($<$,$>:-Wmissing-prototypes>) - add_compile_options($<$,$>:-Wnewline-eof>) - add_compile_options($<$,$>:-Wnullable-to-nonnull-conversion>) - add_compile_options($<$,$>:-Wobjc-interface-ivars>) - add_compile_options($<$,$>:-Wover-aligned>) - add_compile_options($<$,$>:-Wpacked>) - add_compile_options($<$,$>:-Wpointer-arith>) - add_compile_options($<$,$>:-Wselector>) - add_compile_options($<$,$>:-Wshadow>) - add_compile_options($<$,$>:-Wshorten-64-to-32>) - add_compile_options($<$,$>:-Wsign-conversion>) - add_compile_options($<$,$>:-Wstatic-in-inline>) - add_compile_options($<$,$>:-Wsuper-class-method-mismatch>) - add_compile_options($<$,$>:-Wswitch>) - add_compile_options($<$,$>:-Wunguarded-availability>) - add_compile_options($<$,$>:-Wunreachable-code>) - add_compile_options($<$,$>:-Wunused>) + add_compile_options($<$:-Warray-bounds-pointer-arithmetic>) + add_compile_options($<$:-Wassign-enum>) + add_compile_options($<$:-Watomic-properties>) + add_compile_options($<$:-Wcomma>) + add_compile_options($<$:-Wconditional-uninitialized>) + add_compile_options($<$:-Wconversion>) + add_compile_options($<$:-Wcovered-switch-default>) + add_compile_options($<$:-Wdate-time>) + add_compile_options($<$:-Wdeprecated>) + add_compile_options($<$:-Wdocumentation>) + add_compile_options($<$:-Wdouble-promotion>) + add_compile_options($<$:-Wduplicate-enum>) + add_compile_options($<$:-Wexpansion-to-defined>) + add_compile_options($<$:-Wfloat-equal>) + add_compile_options($<$:-Widiomatic-parentheses>) + add_compile_options($<$:-Winfinite-recursion>) + add_compile_options($<$:-Wmissing-prototypes>) + add_compile_options($<$:-Wnewline-eof>) + add_compile_options($<$:-Wnullable-to-nonnull-conversion>) + add_compile_options($<$:-Wobjc-interface-ivars>) + add_compile_options($<$:-Wover-aligned>) + add_compile_options($<$:-Wpacked>) + add_compile_options($<$:-Wpointer-arith>) + add_compile_options($<$:-Wselector>) + add_compile_options($<$:-Wshadow>) + add_compile_options($<$:-Wshorten-64-to-32>) + add_compile_options($<$:-Wsign-conversion>) + add_compile_options($<$:-Wstatic-in-inline>) + add_compile_options($<$:-Wsuper-class-method-mismatch>) + add_compile_options($<$:-Wswitch>) + add_compile_options($<$:-Wunguarded-availability>) + add_compile_options($<$:-Wunreachable-code>) + add_compile_options($<$:-Wunused>) - add_compile_options($<$,$>:-Wno-unknown-warning-option>) - add_compile_options($<$,$>:-Wno-trigraphs>) - add_compile_options($<$,$>:-Wno-four-char-constants>) - add_compile_options($<$,$>:-Wno-disabled-macro-expansion>) - add_compile_options($<$,$>:-Wno-pedantic>) - add_compile_options($<$,$>:-Wno-bad-function-cast>) - add_compile_options($<$,$>:-Wno-c++-compat>) - add_compile_options($<$,$>:-Wno-c++98-compat>) - add_compile_options($<$,$>:-Wno-c++98-compat-pedantic>) - add_compile_options($<$,$>:-Wno-cast-align>) - add_compile_options($<$,$>:-Wno-cast-qual>) - add_compile_options($<$,$>:-Wno-documentation-unknown-command>) - add_compile_options($<$,$>:-Wno-format-nonliteral>) - add_compile_options($<$,$>:-Wno-missing-variable-declarations>) - add_compile_options($<$,$>:-Wno-old-style-cast>) - add_compile_options($<$,$>:-Wno-padded>) - add_compile_options($<$,$>:-Wno-reserved-id-macro>) - add_compile_options($<$,$>:-Wno-shift-sign-overflow>) - add_compile_options($<$,$>:-Wno-undef>) - add_compile_options($<$,$>:-Wno-unreachable-code-aggressive>) - add_compile_options($<$,$>:-Wno-unused-macros>) - add_compile_options($<$,$>:-Wno-used-but-marked-unused>) - add_compile_options($<$,$>:-Wno-vla>) + add_compile_options($<$:-Wno-unknown-warning-option>) + add_compile_options($<$:-Wno-trigraphs>) + add_compile_options($<$:-Wno-four-char-constants>) + add_compile_options($<$:-Wno-disabled-macro-expansion>) + add_compile_options($<$:-Wno-pedantic>) + add_compile_options($<$:-Wno-bad-function-cast>) + add_compile_options($<$:-Wno-c++-compat>) + add_compile_options($<$:-Wno-c++98-compat>) + add_compile_options($<$:-Wno-c++98-compat-pedantic>) + add_compile_options($<$:-Wno-cast-align>) + add_compile_options($<$:-Wno-cast-qual>) + add_compile_options($<$:-Wno-documentation-unknown-command>) + add_compile_options($<$:-Wno-format-nonliteral>) + add_compile_options($<$:-Wno-missing-variable-declarations>) + add_compile_options($<$:-Wno-old-style-cast>) + add_compile_options($<$:-Wno-padded>) + add_compile_options($<$:-Wno-reserved-id-macro>) + add_compile_options($<$:-Wno-shift-sign-overflow>) + add_compile_options($<$:-Wno-undef>) + add_compile_options($<$:-Wno-unreachable-code-aggressive>) + add_compile_options($<$:-Wno-unused-macros>) + add_compile_options($<$:-Wno-used-but-marked-unused>) + add_compile_options($<$:-Wno-void-pointer-to-int-cast>) + add_compile_options($<$:-Wno-vla>) - if(CMAKE_SYSTEM_NAME STREQUAL Android) - add_compile_options($<$,$>:-Wno-incompatible-function-pointer-types>) - add_compile_options($<$,$>:-Wno-implicit-function-declaration>) - add_compile_options($<$,$>:-Wno-conversion>) - add_compile_options($<$,$>:-Wno-int-conversion>) - add_compile_options($<$,$>:-Wno-shorten-64-to-32>) + if(ANDROID) + add_compile_options($<$:-Wno-incompatible-function-pointer-types>) + add_compile_options($<$:-Wno-implicit-function-declaration>) + add_compile_options($<$:-Wno-conversion>) + add_compile_options($<$:-Wno-int-conversion>) + add_compile_options($<$:-Wno-shorten-64-to-32>) endif() - add_compile_options($<$,$>:-Wno-error=assign-enum>) + add_compile_options($<$:-Wno-error=assign-enum>) + + # Should re-enable after rdar://133498289 is fixed (ie. fixing the one mismatched cast in apply.c) + add_compile_options($<$:-Wno-cast-function-type-mismatch>) + add_compile_options($<$:-Wno-error=unknown-warning-option>) endif() diff --git a/cmake/modules/DispatchSanitization.cmake b/cmake/modules/DispatchSanitization.cmake index a0641f524..b5c6ee64f 100644 --- a/cmake/modules/DispatchSanitization.cmake +++ b/cmake/modules/DispatchSanitization.cmake @@ -2,7 +2,7 @@ set(DISPATCH_USE_SANITIZER "" CACHE STRING "Define the sanitizer used to build binaries and tests.") -if(CMAKE_SYSTEM_NAME STREQUAL Darwin AND DISPATCH_USE_SANITIZER) +if(APPLE AND DISPATCH_USE_SANITIZER) message(FATAL_ERROR "building libdispatch with sanitization is not supported on Darwin") endif() diff --git a/cmake/modules/DispatchWindowsSupport.cmake b/cmake/modules/DispatchWindowsSupport.cmake deleted file mode 100644 index 87675a78a..000000000 --- a/cmake/modules/DispatchWindowsSupport.cmake +++ /dev/null @@ -1,74 +0,0 @@ - -function(dispatch_windows_arch_spelling arch var) - if(${arch} STREQUAL i686) - set(${var} x86 PARENT_SCOPE) - elseif(${arch} STREQUAL x86_64 OR ${arch} STREQUAL AMD64) - set(${var} x64 PARENT_SCOPE) - elseif(${arch} STREQUAL armv7) - set(${var} arm PARENT_SCOPE) - elseif(${arch} STREQUAL aarch64) - set(${var} arm64 PARENT_SCOPE) - else() - message(FATAL_ERROR "do not know MSVC spelling for ARCH: `${arch}`") - endif() -endfunction() - -function(dispatch_verify_windows_environment_variables) - set(VCToolsInstallDir $ENV{VCToolsInstallDir}) - set(UniversalCRTSdkDir $ENV{UniversalCRTSdkDir}) - set(UCRTVersion $ENV{UCRTVersion}) - - if("${VCToolsInstallDir}" STREQUAL "") - message(SEND_ERROR "VCToolsInstallDir environment variable must be set") - endif() - if("${UniversalCRTSdkDir}" STREQUAL "") - message(SEND_ERROR "UniversalCRTSdkDir environment variable must be set") - endif() - if("${UCRTVersion}" STREQUAL "") - message(SEND_ERROR "UCRTVersion environment variable must be set") - endif() -endfunction() - -function(dispatch_windows_include_for_arch arch var) - dispatch_verify_windows_environment_variables() - - set(paths - "$ENV{VCToolsInstallDir}/include" - "$ENV{UniversalCRTSdkDir}/Include/$ENV{UCRTVersion}/ucrt" - "$ENV{UniversalCRTSdkDir}/Include/$ENV{UCRTVersion}/shared" - "$ENV{UniversalCRTSdkDir}/Include/$ENV{UCRTVersion}/um") - set(${var} ${paths} PARENT_SCOPE) -endfunction() - -function(dispatch_windows_lib_for_arch arch var) - dispatch_verify_windows_environment_variables() - dispatch_windows_arch_spelling(${arch} ARCH) - - set(paths) - if(${ARCH} STREQUAL x86) - list(APPEND paths "$ENV{VCToolsInstallDir}/Lib") - else() - list(APPEND paths "$ENV{VCToolsInstallDir}/Lib/${ARCH}") - endif() - list(APPEND paths - "$ENV{UniversalCRTSdkDir}/Lib/$ENV{UCRTVersion}/ucrt/${ARCH}" - "$ENV{UniversalCRTSdkDir}/Lib/$ENV{UCRTVersion}/um/${ARCH}") - set(${var} ${paths} PARENT_SCOPE) -endfunction() - -function(dispatch_windows_generate_sdk_vfs_overlay flags) - dispatch_verify_windows_environment_variables() - - get_filename_component(VCToolsInstallDir $ENV{VCToolsInstallDir} ABSOLUTE) - get_filename_component(UniversalCRTSdkDir $ENV{UniversalCRTSdkDir} ABSOLUTE) - set(UCRTVersion $ENV{UCRTVersion}) - - # TODO(compnerd) use a target to avoid re-creating this file all the time - configure_file("${PROJECT_SOURCE_DIR}/utils/WindowsSDKVFSOverlay.yaml.in" - "${PROJECT_BINARY_DIR}/windows-sdk-vfs-overlay.yaml" - @ONLY) - - set(${flags} - -ivfsoverlay;"${PROJECT_BINARY_DIR}/windows-sdk-vfs-overlay.yaml" - PARENT_SCOPE) -endfunction() diff --git a/cmake/modules/EnableFramePointers.cmake b/cmake/modules/EnableFramePointers.cmake new file mode 100644 index 000000000..8573d8dc4 --- /dev/null +++ b/cmake/modules/EnableFramePointers.cmake @@ -0,0 +1,13 @@ +# +# Including this file enables frame pointers, if we know how. +# + +include(CheckCompilerFlag) + +# Check if the compiler supports -fno-omit-frame-pointer +check_compiler_flag(C -fno-omit-frame-pointer SUPPORTS_NO_OMIT_FP) + +# If it does, use it +if (SUPPORTS_NO_OMIT_FP) + add_compile_options($<$:-fno-omit-frame-pointer>) +endif() diff --git a/cmake/modules/SwiftSupport.cmake b/cmake/modules/SwiftSupport.cmake index 4310b54c9..697d98516 100644 --- a/cmake/modules/SwiftSupport.cmake +++ b/cmake/modules/SwiftSupport.cmake @@ -1,37 +1,67 @@ +include_guard() -# Returns the current achitecture name in a variable -# -# Usage: -# get_swift_host_arch(result_var_name) -# -# If the current architecture is supported by Swift, sets ${result_var_name} -# with the sanitized host architecture name derived from CMAKE_SYSTEM_PROCESSOR. -function(get_swift_host_arch result_var_name) - if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") - set("${result_var_name}" "x86_64" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64") - set("${result_var_name}" "aarch64" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64") - set("${result_var_name}" "powerpc64" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ppc64le") - set("${result_var_name}" "powerpc64le" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "s390x") - set("${result_var_name}" "s390x" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv6l") - set("${result_var_name}" "armv6" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7-a") - 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}" STREQUAL "IA64") - set("${result_var_name}" "itanium" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86") - set("${result_var_name}" "i686" PARENT_SCOPE) - elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") - set("${result_var_name}" "i686" PARENT_SCOPE) +if(NOT dispatch_MODULE_TRIPLE OR NOT dispatch_ARCH OR NOT dispatch_PLATFORM) + # Get the target information from the Swift compiler. + set(module_triple_command "${CMAKE_Swift_COMPILER}" -print-target-info) + if(CMAKE_Swift_COMPILER_TARGET) + list(APPEND module_triple_command -target ${CMAKE_Swift_COMPILER_TARGET}) + endif() + execute_process(COMMAND ${module_triple_command} OUTPUT_VARIABLE target_info_json) +endif() + +if(NOT dispatch_MODULE_TRIPLE) + string(JSON module_triple GET "${target_info_json}" "target" "moduleTriple") + set(dispatch_MODULE_TRIPLE "${module_triple}" CACHE STRING "Triple used to install swiftmodule files") + mark_as_advanced(dispatch_MODULE_TRIPLE) + message(CONFIGURE_LOG "Swift module triple: ${module_triple}") +endif() + +if(NOT dispatch_ARCH) + if(CMAKE_Swift_COMPILER_VERSION VERSION_EQUAL 0.0.0 OR CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 6.2) + # For newer compilers, we can use the -print-target-info command to get the architecture. + string(JSON module_arch GET "${target_info_json}" "target" "arch") + else() + # For older compilers, extract the value from `dispatch_MODULE_TRIPLE`. + string(REGEX MATCH "^[^-]+" module_arch "${dispatch_MODULE_TRIPLE}") + endif() + + set(dispatch_ARCH "${module_arch}" CACHE STRING "Arch folder name used to install libraries") + mark_as_advanced(dispatch_ARCH) + message(CONFIGURE_LOG "Swift arch: ${dispatch_ARCH}") +endif() + +if(NOT dispatch_PLATFORM) + if(CMAKE_Swift_COMPILER_VERSION VERSION_EQUAL 0.0.0 OR CMAKE_Swift_COMPILER_VERSION VERSION_GREATER_EQUAL 6.2) + # For newer compilers, we can use the -print-target-info command to get the platform. + string(JSON swift_platform GET "${target_info_json}" "target" "platform") else() - message(FATAL_ERROR "Unrecognized architecture on host system: ${CMAKE_SYSTEM_PROCESSOR}") + # For older compilers, compile the value from `CMAKE_SYSTEM_NAME`. + if(APPLE) + set(swift_platform macosx) + else() + set(swift_platform "$") + endif() + endif() + + set(dispatch_PLATFORM "${swift_platform}" CACHE STRING "Platform folder name used to install libraries") + mark_as_advanced(dispatch_PLATFORM) + message(CONFIGURE_LOG "Swift platform: ${dispatch_PLATFORM}") +endif() + +function(install_swift_module target) + get_target_property(module ${target} Swift_MODULE_NAME) + if(NOT module) + set(module ${target}) endif() + + set(INSTALL_SWIFT_MODULE_DIR "${CMAKE_INSTALL_LIBDIR}/swift$<$>:_static>/${dispatch_PLATFORM}" CACHE PATH "Path where the swift modules will be installed") + + install( + FILES $/${module}.swiftdoc + DESTINATION ${INSTALL_SWIFT_MODULE_DIR}/${module}.swiftmodule + RENAME ${dispatch_MODULE_TRIPLE}.swiftdoc) + install( + FILES $/${module}.swiftmodule + DESTINATION ${INSTALL_SWIFT_MODULE_DIR}/${module}.swiftmodule + RENAME ${dispatch_MODULE_TRIPLE}.swiftmodule) endfunction() diff --git a/dispatch/CMakeLists.txt b/dispatch/CMakeLists.txt index 8b8be8cfb..478ee8fba 100644 --- a/dispatch/CMakeLists.txt +++ b/dispatch/CMakeLists.txt @@ -1,4 +1,13 @@ +if(APPLE) + set(DISPATCH_MODULE_MAP ${PROJECT_SOURCE_DIR}/dispatch/darwin/module.modulemap) +else() + set(DISPATCH_MODULE_MAP ${PROJECT_SOURCE_DIR}/dispatch/generic/module.modulemap) +endif() +configure_file(dispatch-vfs.yaml.in + ${CMAKE_BINARY_DIR}/dispatch-vfs-overlay.yaml + @ONLY) + install(FILES base.h block.h @@ -16,9 +25,8 @@ install(FILES DESTINATION "${INSTALL_DISPATCH_HEADERS_DIR}") if(ENABLE_SWIFT) - get_filename_component(MODULE_MAP module.modulemap REALPATH) install(FILES - ${MODULE_MAP} + ${DISPATCH_MODULE_MAP} DESTINATION "${INSTALL_DISPATCH_HEADERS_DIR}") endif() diff --git a/dispatch/base.h b/dispatch/base.h index 7123f83ad..00c723b36 100644 --- a/dispatch/base.h +++ b/dispatch/base.h @@ -178,24 +178,32 @@ #endif #endif -#if defined(_WIN32) -#if defined(__DISPATCH_BUILDING_DISPATCH__) -#if defined(__cplusplus) -#define DISPATCH_EXPORT extern "C" __declspec(dllexport) -#else -#define DISPATCH_EXPORT extern __declspec(dllexport) -#endif -#else -#if defined(__cplusplus) -#define DISPATCH_EXPORT extern "C" __declspec(dllimport) -#else -#define DISPATCH_EXPORT extern __declspec(dllimport) -#endif +#ifndef DISPATCH_EXTERN +# if defined(__cplusplus) +# define DISPATCH_EXTERN extern "C" +# else +# define DISPATCH_EXTERN extern +# endif #endif -#elif __GNUC__ -#define DISPATCH_EXPORT extern __attribute__((visibility("default"))) + +#if defined(dispatch_STATIC) +# if __GNUC__ +# define DISPATCH_EXPORT DISPATCH_EXTERN __attribute__((__visibility__("hidden"))) +# else +# define DISPATCH_EXPORT DISPATCH_EXTERN +# endif #else -#define DISPATCH_EXPORT extern +# if defined(_WIN32) +# if defined(dispatch_EXPORTS) +# define DISPATCH_EXPORT DISPATCH_EXTERN __declspec(dllexport) +# else +# define DISPATCH_EXPORT DISPATCH_EXTERN __declspec(dllimport) +# endif +# elif __GNUC__ +# define DISPATCH_EXPORT DISPATCH_EXTERN __attribute__((__visibility__("default"))) +# else +# define DISPATCH_EXPORT DISPATCH_EXTERN +# endif #endif #if __GNUC__ @@ -323,4 +331,4 @@ typedef void (*dispatch_function_t)(void *_Nullable); -#endif +#endif /* __DISPATCH_BASE__ */ diff --git a/dispatch/dispatch-vfs.yaml.in b/dispatch/dispatch-vfs.yaml.in new file mode 100644 index 000000000..9416dda81 --- /dev/null +++ b/dispatch/dispatch-vfs.yaml.in @@ -0,0 +1,11 @@ +--- +version: 0 +case-sensitive: false +use-external-names: false +roots: + - name: "@CMAKE_CURRENT_SOURCE_DIR@" + type: directory + contents: + - name: module.modulemap + type: file + external-contents: "@DISPATCH_MODULE_MAP@" diff --git a/dispatch/dispatch.h b/dispatch/dispatch.h index 0c7bdd43a..ef65e38c2 100644 --- a/dispatch/dispatch.h +++ b/dispatch/dispatch.h @@ -77,4 +77,4 @@ #undef __DISPATCH_INDIRECT__ #endif /* !__DISPATCH_BUILDING_DISPATCH__ */ -#endif +#endif /* __DISPATCH_PUBLIC__ */ diff --git a/dispatch/group.h b/dispatch/group.h index 6b30b26c6..b98b47c16 100644 --- a/dispatch/group.h +++ b/dispatch/group.h @@ -276,4 +276,4 @@ __END_DECLS DISPATCH_ASSUME_NONNULL_END -#endif +#endif /* __DISPATCH_GROUP__ */ diff --git a/dispatch/introspection.h b/dispatch/introspection.h index ea7dcd8f5..d216f462a 100644 --- a/dispatch/introspection.h +++ b/dispatch/introspection.h @@ -185,4 +185,4 @@ __END_DECLS DISPATCH_ASSUME_NONNULL_END -#endif +#endif /* __DISPATCH_INTROSPECTION__ */ diff --git a/dispatch/object.h b/dispatch/object.h index 02815f3f2..3f3cf3279 100644 --- a/dispatch/object.h +++ b/dispatch/object.h @@ -547,4 +547,4 @@ __END_DECLS DISPATCH_ASSUME_NONNULL_END -#endif +#endif /* __DISPATCH_OBJECT__ */ diff --git a/dispatch/once.h b/dispatch/once.h index fbce4b111..19af4f9a6 100644 --- a/dispatch/once.h +++ b/dispatch/once.h @@ -122,4 +122,4 @@ __END_DECLS DISPATCH_ASSUME_NONNULL_END -#endif +#endif /* __DISPATCH_ONCE__ */ diff --git a/dispatch/queue.h b/dispatch/queue.h index 969dc880a..ff68d2308 100644 --- a/dispatch/queue.h +++ b/dispatch/queue.h @@ -173,12 +173,12 @@ DISPATCH_DECL_SUBCLASS(dispatch_queue_main, dispatch_queue_serial); * * @discussion * Dispatch concurrent queues are lightweight objects to which regular and - * barrier workitems may be submited. Barrier workitems are invoked in + * barrier workitems may be submitted. Barrier workitems are invoked in * exclusion of any other kind of workitem in FIFO order. * * Regular workitems can be invoked concurrently for the same concurrent queue, * in any order. However, regular workitems will not be invoked before any - * barrier workitem submited ahead of them has been invoked. + * barrier workitem submitted ahead of them has been invoked. * * In other words, if a serial queue is equivalent to a mutex in the Dispatch * world, a concurrent queue is equivalent to a reader-writer lock, where @@ -1542,4 +1542,4 @@ __END_DECLS DISPATCH_ASSUME_NONNULL_END -#endif +#endif /* __DISPATCH_QUEUE__ */ diff --git a/dispatch/source.h b/dispatch/source.h index 597d23a4f..9d605a9df 100644 --- a/dispatch/source.h +++ b/dispatch/source.h @@ -32,7 +32,7 @@ #endif #if !defined(_WIN32) -#include +#include #endif DISPATCH_ASSUME_NONNULL_BEGIN @@ -54,7 +54,7 @@ DISPATCH_ASSUME_NONNULL_BEGIN * Dispatch sources are used to automatically submit event handler blocks to * dispatch queues in response to external events. */ -DISPATCH_SOURCE_DECL(dispatch_source); +DISPATCH_SOURCE_DECL(dispatch_source) __BEGIN_DECLS @@ -770,4 +770,4 @@ __END_DECLS DISPATCH_ASSUME_NONNULL_END -#endif +#endif /* __DISPATCH_SOURCE__ */ diff --git a/dispatch/time.h b/dispatch/time.h index 02dd27f6e..9f66c195f 100644 --- a/dispatch/time.h +++ b/dispatch/time.h @@ -132,4 +132,4 @@ __END_DECLS DISPATCH_ASSUME_NONNULL_END -#endif +#endif /* __DISPATCH_TIME__ */ diff --git a/os/CMakeLists.txt b/os/CMakeLists.txt index 282af25f7..cccf97ba6 100644 --- a/os/CMakeLists.txt +++ b/os/CMakeLists.txt @@ -3,9 +3,10 @@ # voucher_private.h are included in the source tarball install(FILES - object.h + generic_base.h generic_unix_base.h generic_win_base.h + object.h DESTINATION "${INSTALL_OS_HEADERS_DIR}") diff --git a/os/generic_base.h b/os/generic_base.h new file mode 100644 index 000000000..e85257843 --- /dev/null +++ b/os/generic_base.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011-2014 Apple Inc. All rights reserved. + * + * @APPLE_APACHE_LICENSE_HEADER_START@ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @APPLE_APACHE_LICENSE_HEADER_END@ + */ + +#ifndef __OS_GENERIC_BASE__ +#define __OS_GENERIC_BASE__ + +#if !defined(__BEGIN_DECLS) && !defined(__END_DECLS) +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif +#endif + +#endif /* __OS_GENERIC_BASE__ */ diff --git a/os/generic_unix_base.h b/os/generic_unix_base.h index aaf6f8504..b77d2adaa 100644 --- a/os/generic_unix_base.h +++ b/os/generic_unix_base.h @@ -13,6 +13,8 @@ #ifndef __OS_GENERIC_UNIX_BASE__ #define __OS_GENERIC_UNIX_BASE__ +#include + #if __has_include() #include #endif diff --git a/os/generic_win_base.h b/os/generic_win_base.h index afc5f4265..5f1ea74c2 100644 --- a/os/generic_win_base.h +++ b/os/generic_win_base.h @@ -13,6 +13,8 @@ #ifndef __OS_GENERIC_WIN_BASE__ #define __OS_GENERIC_WIN_BASE__ +#include + // Unices provide `roundup` via sys/param.h #define roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) // Unices provide `MAX` via sys/param.h @@ -22,16 +24,10 @@ // Unices provide `howmany` via sys/param.h #define howmany(x, y) (((x) + ((y) - 1)) / (y)) +#ifndef HAVE_MODE_T typedef int mode_t; -typedef void pthread_attr_t; - -#if defined(__cplusplus) -#define __BEGIN_DECLS extern "C" { -#define __END_DECLS } -#else -#define __BEGIN_DECLS -#define __END_DECLS #endif +typedef void pthread_attr_t; #ifndef API_AVAILABLE #define API_AVAILABLE(...) diff --git a/private/mach_private.h b/private/mach_private.h index e311aee16..d6b96c740 100644 --- a/private/mach_private.h +++ b/private/mach_private.h @@ -189,7 +189,7 @@ DISPATCH_DECL(dispatch_mach_msg); * @const DISPATCH_MACH_MSG_DESTRUCTOR_FREE * Message buffer will be deallocated with free(3). * - * @const DISPATCH_MACH_MSG_DESTRUCTOR_FREE + * @const DISPATCH_MACH_MSG_DESTRUCTOR_VM_DEALLOCATE * Message buffer will be deallocated with vm_deallocate. */ DISPATCH_ENUM(dispatch_mach_msg_destructor, unsigned int, diff --git a/private/private.h b/private/private.h index b87f5dc2b..8b1e19670 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 @@ -189,8 +189,10 @@ void _dispatch_prohibit_transition_to_multithreaded(bool prohibit); #if TARGET_OS_MAC typedef mach_port_t dispatch_runloop_handle_t; -#elif defined(__linux__) || defined(__FreeBSD__) +#elif defined(__linux__) typedef int dispatch_runloop_handle_t; +#elif defined(__unix__) +typedef uint64_t dispatch_runloop_handle_t; #elif defined(_WIN32) typedef void *dispatch_runloop_handle_t; #else diff --git a/src/BlocksRuntime/CMakeLists.txt b/src/BlocksRuntime/CMakeLists.txt index 1bed20210..3732b0aec 100644 --- a/src/BlocksRuntime/CMakeLists.txt +++ b/src/BlocksRuntime/CMakeLists.txt @@ -2,13 +2,16 @@ add_library(BlocksRuntime data.c runtime.c) -if(CMAKE_SYSTEM_NAME STREQUAL Windows) +if(WIN32) target_sources(BlocksRuntime PRIVATE BlocksRuntime.def) if(NOT BUILD_SHARED_LIBS) - target_compile_definitions(BlocksRuntime PRIVATE + target_compile_definitions(BlocksRuntime PUBLIC BlocksRuntime_STATIC) + target_compile_options(BlocksRuntime PUBLIC + "$<$:SHELL:$<$:-Xclang >-static-libclosure>" + $<$:SHELL:-Xcc -static-libclosure>) endif() endif() @@ -19,8 +22,9 @@ if(HAVE_OBJC AND CMAKE_DL_LIBS) ${CMAKE_DL_LIBS}) endif() -set_target_properties(BlocksRuntime PROPERTIES - POSITION_INDEPENDENT_CODE TRUE) +if(LINKER_SUPPORTS_BUILD_ID) + target_link_options(BlocksRuntime PRIVATE "LINKER:--build-id=sha1") +endif() add_library(BlocksRuntime::BlocksRuntime ALIAS BlocksRuntime) @@ -35,4 +39,4 @@ install(TARGETS BlocksRuntime EXPORT dispatchExports ARCHIVE DESTINATION ${INSTALL_TARGET_DIR} LIBRARY DESTINATION ${INSTALL_TARGET_DIR} - RUNTIME DESTINATION bin) + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/BlocksRuntime/runtime.c b/src/BlocksRuntime/runtime.c index bfec1a0bf..4b7d4bfa2 100644 --- a/src/BlocksRuntime/runtime.c +++ b/src/BlocksRuntime/runtime.c @@ -468,18 +468,16 @@ static void _Block_byref_assign_copy(void *dest, const void *arg, const int flag // Old compiler SPI static void _Block_byref_release(const void *arg) { struct Block_byref *byref = (struct Block_byref *)arg; - int32_t refcount; // dereference the forwarding pointer since the compiler isn't doing this anymore (ever?) byref = byref->forwarding; - + // To support C++ destructors under GC we arrange for there to be a finalizer for this // by using an isa that directs the code to a finalizer that calls the byref_destroy method. if ((byref->flags & BLOCK_BYREF_NEEDS_FREE) == 0) { return; // stack or GC or global } - refcount = byref->flags & BLOCK_REFCOUNT_MASK; - os_assert(refcount); + os_assert(byref->flags & BLOCK_REFCOUNT_MASK); if (latching_decr_int_should_deallocate(&byref->flags)) { if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) { struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c88b430de..c10583054 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,8 +1,4 @@ -if(NOT CMAKE_SYSTEM_NAME STREQUAL Darwin) - add_subdirectory(BlocksRuntime) -endif() - add_library(dispatch allocator.c apply.c @@ -57,7 +53,7 @@ add_library(dispatch shims/yield.c shims/yield.h) -if(CMAKE_SYSTEM_NAME STREQUAL Windows) +if(WIN32) target_sources(dispatch PRIVATE shims/generic_sys_queue.h shims/generic_win_stubs.c @@ -82,19 +78,12 @@ if(ENABLE_DTRACE) endif() if(HAVE_OBJC) - # TODO(compnerd) split DispatchStubs.cc into a separate component for the ObjC - # registration and a separate component for the swift compiler's emission of a - # call to the ObjC autorelease elision entry point. target_sources(dispatch PRIVATE data.m object.m - swift/DispatchStubs.cc) + swift/DispatchStubs.m) endif() - -set_target_properties(dispatch PROPERTIES - POSITION_INDEPENDENT_CODE YES) - target_include_directories(dispatch PUBLIC ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} @@ -103,11 +92,17 @@ target_include_directories(dispatch PUBLIC target_include_directories(dispatch PRIVATE ${PROJECT_SOURCE_DIR}/private) -if(CMAKE_SYSTEM_NAME STREQUAL Windows) +if(NOT BUILD_SHARED_LIBS) + target_compile_definitions(dispatch PUBLIC + dispatch_STATIC) + target_compile_options(dispatch PUBLIC + "$<$:SHELL:-Xcc -Ddispatch_STATIC>") +endif() +if(WIN32) target_compile_definitions(dispatch PRIVATE _CRT_NONSTDC_NO_WARNINGS _CRT_SECURE_NO_WARNINGS) -elseif(CMAKE_SYSTEM_NAME STREQUAL Android) +elseif(ANDROID) target_compile_options(dispatch PRIVATE -U_GNU_SOURCE) endif() @@ -116,7 +111,7 @@ if(DISPATCH_ENABLE_ASSERTS) DISPATCH_DEBUG=1) endif() -if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") +if("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC") target_compile_options(dispatch PRIVATE /EHs-c-) target_compile_options(dispatch PRIVATE /W3) else() @@ -124,6 +119,24 @@ else() target_compile_options(dispatch PRIVATE -Wall) endif() +# Work around a release-mode miscompile on windows arm64 +# Disable /Os and /Ot in /O1 and /O2 on queue.c +if(("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") AND + (("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "ARM64") OR + ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64"))) + string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) + set(FLAGS_BUILD_TYPE "${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}") + string(REGEX MATCHALL "/[Oo][12]" FLAGS "${CMAKE_C_FLAGS} ${FLAGS_BUILD_TYPE}") + if (FLAGS) + if (FLAGS MATCHES "1$") + set(FLAGS "/Od;/Og;/Oy;/Ob2;/GF;/Gy") + elseif (FLAGS MATCHES "2$") + set(FLAGS "/Od;/Og;/Oi;/Oy;/Ob2;/GF;/Gy") + endif() + set_source_files_properties(queue.c PROPERTIES COMPILE_OPTIONS "${FLAGS}") + endif() +endif() + # FIXME(compnerd) add check for -fblocks? target_compile_options(dispatch PRIVATE -fblocks) @@ -139,15 +152,16 @@ target_link_libraries(dispatch PRIVATE Threads::Threads) target_link_libraries(dispatch PUBLIC BlocksRuntime::BlocksRuntime) -if(CMAKE_SYSTEM_NAME STREQUAL Windows) +if(WIN32) target_link_libraries(dispatch PRIVATE + AdvAPI32 ShLwApi WS2_32 WinMM synchronization) endif() -if(CMAKE_SYSTEM_NAME STREQUAL Darwin) +if(APPLE) set_property(TARGET dispatch APPEND_STRING PROPERTY LINK_FLAGS "-Xlinker -compatibility_version -Xlinker 1" "-Xlinker -current_version -Xlinker ${VERSION}" @@ -155,6 +169,14 @@ if(CMAKE_SYSTEM_NAME STREQUAL Darwin) "-Xlinker -alias_list -Xlinker ${PROJECT_SOURCE_DIR}/xcodeconfig/libdispatch.aliases") endif() +if(NOT APPLE AND NOT WIN32) + set_target_properties(dispatch PROPERTIES INSTALL_RPATH "$ORIGIN") +endif() + +if(LINKER_SUPPORTS_BUILD_ID) + target_link_options(dispatch PRIVATE "LINKER:--build-id=sha1") +endif() + if(ENABLE_SWIFT) add_subdirectory(swift) endif() @@ -164,4 +186,4 @@ install(TARGETS dispatch EXPORT dispatchExports ARCHIVE DESTINATION ${INSTALL_TARGET_DIR} LIBRARY DESTINATION ${INSTALL_TARGET_DIR} - RUNTIME DESTINATION bin) + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/event/event.c b/src/event/event.c index 98dd87171..33df9f171 100644 --- a/src/event/event.c +++ b/src/event/event.c @@ -766,9 +766,9 @@ _dispatch_timer_heap_update(dispatch_timer_heap_t dth, #pragma mark timer unote #define _dispatch_timer_du_debug(what, du) \ - _dispatch_debug("kevent-source[%p]: %s kevent[%p] { ident = 0x%x }", \ + _dispatch_debug("kevent-source[%p]: %s kevent[%p] { ident = 0x%llx }", \ _dispatch_wref2ptr((du)->du_owner_wref), what, \ - (du), (du)->du_ident) + (du), (unsigned long long)((du)->du_ident)) DISPATCH_ALWAYS_INLINE static inline unsigned int @@ -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_config.h b/src/event/event_config.h index 4f4b6e5a3..fac801256 100644 --- a/src/event/event_config.h +++ b/src/event/event_config.h @@ -118,7 +118,11 @@ // FreeBSD's kevent does not support those # ifndef NOTE_ABSOLUTE -# define NOTE_ABSOLUTE 0 +# ifdef NOTE_ABSTIME +# define NOTE_ABSOLUTE NOTE_ABSTIME +# else +# define NOTE_ABSOLUTE 0 +# endif # endif # ifndef NOTE_EXITSTATUS # define NOTE_EXITSTATUS 0 diff --git a/src/event/event_epoll.c b/src/event/event_epoll.c index f31d13ee0..b7fe8379b 100644 --- a/src/event/event_epoll.c +++ b/src/event/event_epoll.c @@ -89,7 +89,9 @@ DISPATCH_ALWAYS_INLINE static inline uint32_t _dispatch_muxnote_armed_events(dispatch_muxnote_t dmn) { - return dmn->dmn_events & ~dmn->dmn_disarmed_events; + uint32_t events = dmn->dmn_events; + uint16_t disarmed_events = dmn->dmn_disarmed_events; + return events & ~(uint32_t)disarmed_events; } DISPATCH_ALWAYS_INLINE 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 16b69b37d..eb80082c8 100644 --- a/src/event/event_kevent.c +++ b/src/event/event_kevent.c @@ -50,6 +50,10 @@ DISPATCH_STATIC_GLOBAL(struct dispatch_muxnote_bucket_s _dispatch_sources[DSL_HA #define DISPATCH_NOTE_CLOCK_WALL NOTE_NSECONDS | NOTE_MACH_CONTINUOUS_TIME #define DISPATCH_NOTE_CLOCK_MONOTONIC NOTE_MACHTIME | NOTE_MACH_CONTINUOUS_TIME #define DISPATCH_NOTE_CLOCK_UPTIME NOTE_MACHTIME +#elif __FreeBSD__ +#define DISPATCH_NOTE_CLOCK_WALL NOTE_NSECONDS +#define DISPATCH_NOTE_CLOCK_MONOTONIC NOTE_NSECONDS +#define DISPATCH_NOTE_CLOCK_UPTIME NOTE_NSECONDS #else #define DISPATCH_NOTE_CLOCK_WALL 0 #define DISPATCH_NOTE_CLOCK_MONOTONIC 0 @@ -101,8 +105,12 @@ _evfiltstr(short filt) _evfilt2(EVFILT_MACHPORT); _evfilt2(DISPATCH_EVFILT_MACH_NOTIFICATION); #endif +#ifdef EVFILT_FS _evfilt2(EVFILT_FS); +#endif +#ifdef EVFILT_USER _evfilt2(EVFILT_USER); +#endif #ifdef EVFILT_SOCK _evfilt2(EVFILT_SOCK); #endif @@ -236,9 +244,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 @@ -388,8 +396,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 +407,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); } } @@ -528,11 +538,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; } @@ -579,10 +600,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; @@ -591,7 +619,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) { @@ -743,7 +770,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 +781,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"); @@ -786,9 +813,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 @@ -860,7 +893,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 @@ -921,9 +953,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); } @@ -985,6 +1021,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 +1092,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 +1321,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 +1355,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) @@ -1410,7 +1450,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 +1474,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; @@ -1881,10 +1921,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) { @@ -2357,6 +2404,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); @@ -2445,6 +2498,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 +2531,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 = { diff --git a/src/event/event_windows.c b/src/event/event_windows.c index ce322258a..af67dfd4c 100644 --- a/src/event/event_windows.c +++ b/src/event/event_windows.c @@ -260,8 +260,16 @@ _dispatch_muxnote_retain(dispatch_muxnote_t dmn) static void _dispatch_muxnote_release(dispatch_muxnote_t dmn) { - uintptr_t refcount = os_atomic_dec(&dmn->dmn_refcount, relaxed); + // We perform a minor optimization here - perform the decrement with + // release semantics. In the case that we are going to dispose of the + // value, we perform the acquire fence. This reduces the cost on the + // normal path by avoiding the acquire fence. This should be more + // beneficial on ARM64, as X64 being TSO'ed doesn't gain much. However, + // `mfence` being isolated should hopefully be a bit more efficient than + // the repeated `lock` if there is contention. + uintptr_t refcount = os_atomic_dec(&dmn->dmn_refcount, release); if (refcount == 0) { + os_atomic_thread_fence(acquire); _dispatch_muxnote_dispose(dmn); } else if (refcount == UINTPTR_MAX) { DISPATCH_INTERNAL_CRASH(0, "muxnote refcount underflow"); @@ -359,12 +367,11 @@ _dispatch_socket_callback(PTP_CALLBACK_INSTANCE inst, void *context, DWORD dwBytesAvailable = 1; if (lNetworkEvents & FD_CLOSE) { dwBytesAvailable = 0; - // Post to all registered read and write handlers - lNetworkEvents |= FD_READ | FD_WRITE; } else if (lNetworkEvents & FD_READ) { ioctlsocket(sock, FIONREAD, &dwBytesAvailable); } - if (lNetworkEvents & FD_READ) { + if ((lNetworkEvents & FD_CLOSE) || + ((lNetworkEvents & FD_READ) && (dwBytesAvailable > 0))) { _dispatch_muxnote_retain(dmn); if (!PostQueuedCompletionStatus(hPort, dwBytesAvailable, (ULONG_PTR)DISPATCH_PORT_SOCKET_READ, (LPOVERLAPPED)dmn)) { @@ -372,9 +379,10 @@ _dispatch_socket_callback(PTP_CALLBACK_INSTANCE inst, void *context, "PostQueuedCompletionStatus"); } } - if (lNetworkEvents & FD_WRITE) { + if ((lNetworkEvents & FD_CLOSE) || (lNetworkEvents & FD_WRITE)) { _dispatch_muxnote_retain(dmn); - if (!PostQueuedCompletionStatus(hPort, dwBytesAvailable, + if (!PostQueuedCompletionStatus(hPort, + lNetworkEvents & FD_CLOSE ? 0 : 1, (ULONG_PTR)DISPATCH_PORT_SOCKET_WRITE, (LPOVERLAPPED)dmn)) { DISPATCH_INTERNAL_CRASH(GetLastError(), "PostQueuedCompletionStatus"); diff --git a/src/event/workqueue.c b/src/event/workqueue.c index 28f167517..07cc17bc5 100644 --- a/src/event/workqueue.c +++ b/src/event/workqueue.c @@ -22,6 +22,15 @@ #if DISPATCH_USE_INTERNAL_WORKQUEUE +#if defined(_WIN32) +#include + +#if !defined(WCT_MAX_NODE_COUNT) +#define WCT_MAX_NODE_COUNT 16 +#endif + +#endif + /* * dispatch_workq monitors the thread pool that is * executing the work enqueued on libdispatch's pthread @@ -180,6 +189,156 @@ _dispatch_workq_count_runnable_workers(dispatch_workq_monitor_t mon) _dispatch_unfair_lock_unlock(&mon->registered_tid_lock); } +#elif defined(_WIN32) +static void +_dispatch_workq_init_wct(void *hWCTSession) +{ + // TODO(compnerd) this should have an associated CloseThreadWaitChainSession + *(HWCT **)hWCTSession = OpenThreadWaitChainSession(0, NULL); +} + +static void +_dispatch_workq_count_runnable_workers(dispatch_workq_monitor_t mon) +{ + static dispatch_once_t _wct_init_pred; + static HWCT hWCTSession; + static WAITCHAIN_NODE_INFO wait_chain[WCT_MAX_NODE_COUNT]; + + dispatch_once_f(&_wct_init_pred, &hWCTSession, &_dispatch_workq_init_wct); + + int running_count = 0; + + _dispatch_unfair_lock_lock(&mon->registered_tid_lock); + + for (int i = 0; i < mon->num_registered_tids; ++i) { + /* See _dispatch_tid_self() */ + dispatch_tid tid = mon->registered_tids[i] >> 2; + + DWORD count = WCT_MAX_NODE_COUNT; + BOOL cycle = FALSE; + if (GetThreadWaitChain(hWCTSession, 0, 0, tid, &count, wait_chain, &cycle)) { + // Check the deepest entry to see what the thread is waiting on. + DWORD index = MIN(count, WCT_MAX_NODE_COUNT) - 1; + if (wait_chain[index].ObjectType == WctThreadType) { + if (wait_chain[index].ObjectStatus != WctStatusRunning) { + continue; + } + } + } + + // Ensure that the thread is not waiting on IO + // XXX(compnerd) is this needed? The wait chain reports SMB + // and Socket IO, but it is unclear if that includes normal IO. + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, tid); + if (hThread == NULL) { + _dispatch_debug("workq: unable to open thread %lu: %lu", tid, GetLastError()); + continue; + } + + BOOL IOPending = TRUE; + if (GetThreadIOPendingFlag(hThread, &IOPending)) + if (!IOPending) + ++running_count; + + CloseHandle(hThread); + } + + mon->num_runnable = running_count; + + _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); +} +#elif defined(__FreeBSD__) +#include +#include +#include + +static void +_dispatch_workq_count_runnable_workers(dispatch_workq_monitor_t mon) +{ + struct kinfo_proc kp[WORKQ_MAX_TRACKED_TIDS]; + size_t size; + int count, runners = 0; + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, (int)getpid()}; + + // get size we need + if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0) { + _dispatch_debug("workq: failed to get size for kinfo_proc[] from sysctll"); + return; + } + + // only care about up to WORKQ_MAX_TRACKED_TIDS threads + size = MIN(sizeof(kp), size); + + if (sysctl(mib, 4, kp, &size, NULL, 0) < 0) { + _dispatch_debug("workq: failed to get kinfo_proc[] from sysctl"); + return; + } + + count = (int)(size / sizeof(struct kinfo_proc)); + + _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 (int j = 0; i < count; ++i) { + if ((dispatch_tid)kp[j].ki_tid != tid) { continue; } + if (kp[j].ki_stat == SRUN || kp[j].ki_stat == SIDL) { + ++runners; + break; + } + } + } + + mon->num_runnable = runners; + + _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 b6ca6df2f..cc9474e80 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__) +#if defined(__linux__) || defined(_WIN32) || defined(__OpenBSD__) || defined(__FreeBSD__) #define HAVE_DISPATCH_WORKQ_MONITORING 1 #else #define HAVE_DISPATCH_WORKQ_MONITORING 0 diff --git a/src/init.c b/src/init.c index f4c3bae4c..b7364fea3 100644 --- a/src/init.c +++ b/src/init.c @@ -24,6 +24,9 @@ // NOTE: this file must not contain any atomic operations #include "internal.h" +#if __linux__ +#include // for PATH_MAX +#endif #if HAVE_MACH #include "protocolServer.h" @@ -380,9 +383,9 @@ dispatch_get_global_queue(intptr_t priority, uintptr_t flags) } dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority); #if !HAVE_PTHREAD_WORKQUEUE_QOS - if (qos == QOS_CLASS_MAINTENANCE) { + if (qos == DISPATCH_QOS_MAINTENANCE) { qos = DISPATCH_QOS_BACKGROUND; - } else if (qos == QOS_CLASS_USER_INTERACTIVE) { + } else if (qos == DISPATCH_QOS_USER_INTERACTIVE) { qos = DISPATCH_QOS_USER_INITIATED; } #endif @@ -928,14 +931,14 @@ _dispatch_fault(const char *reason, const char *fmt, ...) }) void -_dispatch_bug(size_t line, long val) +_dispatch_bug(size_t line, uintptr_t val) { dispatch_once_f(&_dispatch_build_pred, NULL, _dispatch_build_init); if (_dispatch_bug_log_is_repeated()) return; - _dispatch_log("BUG in libdispatch: %s - %lu - 0x%lx", - _dispatch_build, (unsigned long)line, val); + _dispatch_log("BUG in libdispatch: %s - %lu - 0x%llx", + _dispatch_build, (unsigned long)line, (unsigned long long)val); } #if HAVE_MACH @@ -961,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, @@ -1005,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 @@ -1048,7 +1049,11 @@ _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); +#if defined(__FreeBSD__) + (intptr_t)du._du->du_ident, (intptr_t)du._du->du_ident, func); +#else + (intptr_t)du._du->du_ident, (uintptr_t)du._du->du_ident, func); +#endif } #endif // RDAR_49023449 @@ -1063,7 +1068,7 @@ _dispatch_bug_deprecated(const char *msg) } void -_dispatch_abort(size_t line, long val) +_dispatch_abort(size_t line, uintptr_t val) { _dispatch_bug(line, val); abort(); @@ -1147,14 +1152,14 @@ _dispatch_logv_init(void *context DISPATCH_UNUSED) szProgramName, GetCurrentProcessId(), tv.tv_sec, (int)tv.tv_usec); if (len > 0) { - len = MIN(len, sizeof(szMessage) - 1); + len = MIN(len, (int)sizeof(szMessage) - 1); _write(dispatch_logfile, szMessage, len); _write(dispatch_logfile, "\n", 1); } #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/internal.h b/src/internal.h index cf4ccfe84..a67a771dc 100644 --- a/src/internal.h +++ b/src/internal.h @@ -277,6 +277,9 @@ upcast(dispatch_object_t dou) #include #include #endif +#if defined(__FreeBSD__) +#include +#endif // __FreeBSD__ #include #include #include @@ -466,7 +469,7 @@ DISPATCH_EXPORT DISPATCH_NOTHROW void dispatch_atfork_child(void); extern uint8_t _dispatch_mode; DISPATCH_EXPORT DISPATCH_NOINLINE DISPATCH_COLD -void _dispatch_bug(size_t line, long val); +void _dispatch_bug(size_t line, uintptr_t val); #if HAVE_MACH DISPATCH_NOINLINE DISPATCH_COLD @@ -475,12 +478,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); @@ -489,7 +490,7 @@ DISPATCH_NOINLINE DISPATCH_COLD void _dispatch_bug_deprecated(const char *msg); DISPATCH_NOINLINE DISPATCH_NORETURN DISPATCH_COLD -void _dispatch_abort(size_t line, long val); +void _dispatch_abort(size_t line, uintptr_t val); #if !defined(DISPATCH_USE_OS_DEBUG_LOG) && DISPATCH_DEBUG #if __has_include() @@ -549,11 +550,11 @@ void _dispatch_log(const char *msg, ...); */ DISPATCH_ALWAYS_INLINE static inline void -_dispatch_assert(long e, size_t line) DISPATCH_STATIC_ASSERT_IF(!e) +_dispatch_assert(uintptr_t e, size_t line) DISPATCH_STATIC_ASSERT_IF(!e) { if (unlikely(DISPATCH_DEBUG && !e)) _dispatch_abort(line, e); } -#define dispatch_assert(e) _dispatch_assert((long)(e), __LINE__) +#define dispatch_assert(e) _dispatch_assert((uintptr_t)(e), __LINE__) /* * A lot of API return zero upon success and not-zero on fail. Let's capture @@ -561,11 +562,11 @@ _dispatch_assert(long e, size_t line) DISPATCH_STATIC_ASSERT_IF(!e) */ DISPATCH_ALWAYS_INLINE static inline void -_dispatch_assert_zero(long e, size_t line) DISPATCH_STATIC_ASSERT_IF(e) +_dispatch_assert_zero(uintptr_t e, size_t line) DISPATCH_STATIC_ASSERT_IF(e) { if (unlikely(DISPATCH_DEBUG && e)) _dispatch_abort(line, e); } -#define dispatch_assert_zero(e) _dispatch_assert_zero((long)(e), __LINE__) +#define dispatch_assert_zero(e) _dispatch_assert_zero((uintptr_t)(e), __LINE__) /* * For reporting bugs or impedance mismatches between libdispatch and external @@ -575,12 +576,12 @@ _dispatch_assert_zero(long e, size_t line) DISPATCH_STATIC_ASSERT_IF(e) */ DISPATCH_ALWAYS_INLINE static inline void -_dispatch_assume(long e, size_t line) DISPATCH_STATIC_ASSERT_IF(!e) +_dispatch_assume(uintptr_t e, size_t line) DISPATCH_STATIC_ASSERT_IF(!e) { if (unlikely(!e)) _dispatch_bug(line, e); } #define dispatch_assume(e) \ - ({ __typeof__(e) _e = (e); _dispatch_assume((long)_e, __LINE__); _e; }) + ({ __typeof__(e) _e = (e); _dispatch_assume((uintptr_t)_e, __LINE__); _e; }) /* * A lot of API return zero upon success and not-zero on fail. Let's capture @@ -588,12 +589,12 @@ _dispatch_assume(long e, size_t line) DISPATCH_STATIC_ASSERT_IF(!e) */ DISPATCH_ALWAYS_INLINE static inline void -_dispatch_assume_zero(long e, size_t line) DISPATCH_STATIC_ASSERT_IF(e) +_dispatch_assume_zero(uintptr_t e, size_t line) DISPATCH_STATIC_ASSERT_IF(e) { if (unlikely(e)) _dispatch_bug(line, e); } #define dispatch_assume_zero(e) \ - ({ __typeof__(e) _e = (e); _dispatch_assume_zero((long)_e, __LINE__); _e; }) + ({ __typeof__(e) _e = (e); _dispatch_assume_zero((uintptr_t)_e, __LINE__); _e; }) /* Make sure the debug statments don't get too stale */ #define _dispatch_debug(x, args...) do { \ diff --git a/src/io.c b/src/io.c index cbea654cb..7ae1e0efe 100644 --- a/src/io.c +++ b/src/io.c @@ -22,7 +22,6 @@ #if defined(__FreeBSD__) #include -#define F_RDADVISE F_RDAHEAD #endif #ifndef DISPATCH_IO_DEBUG @@ -407,9 +406,13 @@ dispatch_io_create_f(dispatch_io_type_t type, dispatch_fd_t fd, } #if defined(_WIN32) -#define _is_separator(ch) ((ch) == '/' || (ch) == '\\') +#define _is_separator(ch) ((ch) == L'/' || (ch) == L'\\') +#define _dispatch_stat _wstati64 +typedef struct _stati64 _dispatch_stat_t; #else #define _is_separator(ch) ((ch) == '/') +#define _dispatch_stat stat +typedef struct stat _dispatch_stat_t; #endif dispatch_io_t @@ -424,16 +427,36 @@ dispatch_io_create_with_path(dispatch_io_type_t type, const char *path, if (PathIsRelativeA(path)) { return DISPATCH_BAD_INPUT; } + int cchLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, NULL, 0); + if (!cchLength) { + dispatch_assert(GetLastError() == ERROR_NO_UNICODE_TRANSLATION); + return DISPATCH_BAD_INPUT; + } + dispatch_io_path_data_t path_data = malloc(sizeof(*path_data) + sizeof(WCHAR) * cchLength); + if (!path_data) { + return DISPATCH_OUT_OF_MEMORY; + } + path_data->pathlen = cchLength - 1; // Don't include terminating null character + cchLength = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path, -1, path_data->path, cchLength); + if (!cchLength) { + free(path_data); + // We already checked the input when measuring buffer length. + // Any error at this point seems fatal. + DISPATCH_INTERNAL_CRASH(GetLastError(), "MultiByteToWideChar"); + return DISPATCH_BAD_INPUT; + } #else if (!_is_separator(*path)) { return DISPATCH_BAD_INPUT; } -#endif size_t pathlen = strlen(path); dispatch_io_path_data_t path_data = malloc(sizeof(*path_data) + pathlen+1); if (!path_data) { return DISPATCH_OUT_OF_MEMORY; } + path_data->pathlen = pathlen; + memcpy(path_data->path, path, pathlen + 1); +#endif dispatch_io_t channel = _dispatch_io_create(type); channel->fd = -1; _dispatch_channel_debug("create with path %s", channel, path); @@ -441,16 +464,14 @@ dispatch_io_create_with_path(dispatch_io_type_t type, const char *path, path_data->channel = channel; path_data->oflag = oflag; path_data->mode = mode; - path_data->pathlen = pathlen; - memcpy(path_data->path, path, pathlen + 1); _dispatch_retain(queue); _dispatch_retain(channel); dispatch_async(channel->queue, ^{ int err = 0; - struct stat st; + _dispatch_stat_t st; _dispatch_io_syscall_switch_noerr(err, #if defined(_WIN32) - stat(path_data->path, &st), + _dispatch_stat(path_data->path, &st), #else (path_data->oflag & O_NOFOLLOW) == O_NOFOLLOW #if __APPLE__ @@ -465,7 +486,7 @@ dispatch_io_create_with_path(dispatch_io_type_t type, const char *path, if ((path_data->oflag & O_CREAT) && !_is_separator(*(path_data->path + path_data->pathlen - 1))) { // Check parent directory - char *c = NULL; + dispatch_io_path_char_t *c = NULL; for (ssize_t i = (ssize_t)path_data->pathlen - 1; i >= 0; i--) { if (_is_separator(path_data->path[i])) { c = &path_data->path[i]; @@ -476,7 +497,7 @@ dispatch_io_create_with_path(dispatch_io_type_t type, const char *path, *c = 0; int perr; _dispatch_io_syscall_switch_noerr(perr, - stat(path_data->path, &st), + _dispatch_stat(path_data->path, &st), case 0: // Since the parent directory exists, open() will // create a regular file after the fd_entry has @@ -486,7 +507,7 @@ dispatch_io_create_with_path(dispatch_io_type_t type, const char *path, break; ); #if defined(_WIN32) - *c = '\\'; + *c = L'\\'; #else *c = '/'; #endif @@ -602,7 +623,11 @@ dispatch_io_create_with_io(dispatch_io_type_t type, dispatch_io_t in_channel, mode_t mode = in_channel->fd_entry->stat.mode; dev_t dev = in_channel->fd_entry->stat.dev; size_t path_data_len = sizeof(struct dispatch_io_path_data_s) + +#if defined(_WIN32) + sizeof(WCHAR) * (in_channel->fd_entry->path_data->pathlen + 1); +#else in_channel->fd_entry->path_data->pathlen + 1; +#endif dispatch_io_path_data_t path_data = malloc(path_data_len); memcpy(path_data, in_channel->fd_entry->path_data, path_data_len); @@ -1292,7 +1317,7 @@ _dispatch_fd_entry_unguard(dispatch_fd_entry_t fd_entry) { (void)fd_entry; } #endif // DISPATCH_USE_GUARDED_FD static inline dispatch_fd_t -_dispatch_fd_entry_guarded_open(dispatch_fd_entry_t fd_entry, const char *path, +_dispatch_fd_entry_guarded_open(dispatch_fd_entry_t fd_entry, const dispatch_io_path_char_t *path, int oflag, mode_t mode) { #if DISPATCH_USE_GUARDED_FD guardid_t guard = (uintptr_t)fd_entry; @@ -1333,7 +1358,7 @@ _dispatch_fd_entry_guarded_open(dispatch_fd_entry_t fd_entry, const char *path, } else if (oflag & _O_TRUNC) { dwCreationDisposition = TRUNCATE_EXISTING; } - return (dispatch_fd_t)CreateFile(path, dwDesiredAccess, + return (dispatch_fd_t)CreateFileW(path, dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, dwCreationDisposition, 0, NULL); #else @@ -1432,9 +1457,30 @@ _dispatch_fd_entry_create_with_fd(dispatch_fd_t fd, uintptr_t hash) #if defined(_WIN32) DWORD dwType = GetFileType((HANDLE)fd); if (dwType == FILE_TYPE_PIPE) { - unsigned long value = 1; - int result = ioctlsocket((SOCKET)fd, (long)FIONBIO, &value); - (void)dispatch_assume_zero(result); + if (_dispatch_handle_is_socket((HANDLE)fd)) { + unsigned long value = 1; + int result = ioctlsocket((SOCKET)fd, (long)FIONBIO, &value); + (void)dispatch_assume_zero(result); + } else { + // Try to make writing nonblocking, although pipes not coming + // from Foundation.Pipe may not have FILE_WRITE_ATTRIBUTES. + DWORD dwPipeMode = 0; + if (GetNamedPipeHandleState((HANDLE)fd, &dwPipeMode, NULL, + NULL, NULL, NULL, 0) && !(dwPipeMode & PIPE_NOWAIT)) { + dwPipeMode |= PIPE_NOWAIT; + if (!SetNamedPipeHandleState((HANDLE)fd, &dwPipeMode, + NULL, NULL)) { + // We may end up blocking on subsequent writes, but we + // don't have a good alternative. + // The WriteQuotaAvailable from NtQueryInformationFile + // erroneously returns 0 when there is a blocking read + // on the other end of the pipe. + _dispatch_fd_entry_debug("failed to set PIPE_NOWAIT", + fd_entry); + } + } + } + _dispatch_stream_init(fd_entry, _dispatch_get_default_queue(false)); } else { @@ -1576,7 +1622,11 @@ _dispatch_fd_entry_create_with_path(dispatch_io_path_data_t path_data, // On devs lock queue dispatch_fd_entry_t fd_entry = _dispatch_fd_entry_create( path_data->channel->queue); +#if defined(_WIN32) + _dispatch_fd_entry_debug("create: path %S", fd_entry, path_data->path); +#else _dispatch_fd_entry_debug("create: path %s", fd_entry, path_data->path); +#endif if (S_ISREG(mode)) { #if defined(_WIN32) _dispatch_disk_init(fd_entry, 0); @@ -2307,8 +2357,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) } @@ -2489,24 +2542,23 @@ _dispatch_operation_perform(dispatch_operation_t op) } bSuccess = TRUE; } else if (GetFileType(hFile) == FILE_TYPE_PIPE) { - // Unfortunately there isn't a good way to achieve O_NONBLOCK - // semantics when writing to a pipe. SetNamedPipeHandleState() - // can allow pipes to be switched into a "no wait" mode, but - // that doesn't work on most pipe handles because Windows - // doesn't consistently create pipes with FILE_WRITE_ATTRIBUTES - // access. The best we can do is to try to query the write quota - // and then write as much as we can. + // WriteFile with more bytes than are available in the + // buffer of a NOWAIT pipe will immediately return 0, + // so clamp our requested write length to make progress. IO_STATUS_BLOCK iosb; FILE_PIPE_LOCAL_INFORMATION fpli; - NTSTATUS status = _dispatch_NtQueryInformationFile(hFile, &iosb, - &fpli, sizeof(fpli), FilePipeLocalInformation); + NTSTATUS status = _dispatch_NtQueryInformationFile(hFile, + &iosb, &fpli, sizeof(fpli), FilePipeLocalInformation); if (NT_SUCCESS(status)) { - if (fpli.WriteQuotaAvailable == 0) { - err = EAGAIN; - goto error; + // WriteQuotaAvailable is unreliable in the presence + // of a blocking reader, when it can return zero, so only + // account for it otherwise + if (fpli.WriteQuotaAvailable > 0) { + len = MIN(len, fpli.WriteQuotaAvailable); } - len = MIN(len, fpli.WriteQuotaAvailable); + len = MIN(len, fpli.OutboundQuota); } + OVERLAPPED ovlOverlapped = {}; bSuccess = WriteFile(hFile, buf, (DWORD)len, (LPDWORD)&processed, &ovlOverlapped); @@ -2523,6 +2575,10 @@ _dispatch_operation_perform(dispatch_operation_t op) processed = 0; } } + if (bSuccess && processed == 0) { + err = EAGAIN; + goto error; + } } else { bSuccess = WriteFile(hFile, buf, (DWORD)len, (LPDWORD)&processed, NULL); diff --git a/src/io_internal.h b/src/io_internal.h index c076cfc69..f71a76b51 100644 --- a/src/io_internal.h +++ b/src/io_internal.h @@ -79,12 +79,18 @@ struct dispatch_stream_s { typedef struct dispatch_stream_s *dispatch_stream_t; +#if defined(_WIN32) +typedef WCHAR dispatch_io_path_char_t; +#else +typedef char dispatch_io_path_char_t; +#endif + struct dispatch_io_path_data_s { dispatch_io_t channel; int oflag; mode_t mode; size_t pathlen; - char path[]; + dispatch_io_path_char_t path[]; }; typedef struct dispatch_io_path_data_s *dispatch_io_path_data_t; diff --git a/src/object.c b/src/object.c index 261e1996d..91841c007 100644 --- a/src/object.c +++ b/src/object.c @@ -202,9 +202,11 @@ _dispatch_xref_dispose(dispatch_object_t dou) _dispatch_mach_xref_dispose(dou._dm); break; #endif +#if DISPATCH_COCOA_COMPAT case DISPATCH_QUEUE_RUNLOOP_TYPE: _dispatch_runloop_queue_xref_dispose(dou._dl); break; +#endif } return _dispatch_release_tailcall(dou._os_obj); } diff --git a/src/queue.c b/src/queue.c index b07a8a659..fe8344c15 100644 --- a/src/queue.c +++ b/src/queue.c @@ -23,6 +23,33 @@ #include "protocol.h" // _dispatch_send_wakeup_runloop_thread #endif +#if defined(__linux__) +#include +#include +#endif + +#if defined(_WIN32) +// Wrapper around SetThreadDescription for UTF-8 strings +void _dispatch_win32_set_thread_description(HANDLE hThread, const char *description) { + int wcsize = MultiByteToWideChar(CP_UTF8, 0, description, -1, NULL, 0); + if (wcsize == 0) { + return; + } + + wchar_t* wcstr = (wchar_t*)malloc(wcsize * sizeof(wchar_t)); + if (wcstr == NULL) { + return; + } + + int result = MultiByteToWideChar(CP_UTF8, 0, description, -1, wcstr, wcsize); + if (result != 0) { + SetThreadDescription(hThread, wcstr); + } + + free(wcstr); +} +#endif + static inline void _dispatch_root_queues_init(void); static void _dispatch_lane_barrier_complete(dispatch_lane_class_t dqu, dispatch_qos_t qos, dispatch_wakeup_flags_t flags); @@ -773,7 +800,7 @@ _dispatch_async_redirect_invoke(dispatch_continuation_t dc, { dispatch_thread_frame_s dtf; struct dispatch_continuation_s *other_dc = dc->dc_other; - dispatch_invoke_flags_t ctxt_flags = (dispatch_invoke_flags_t)dc->dc_ctxt; + dispatch_invoke_flags_t ctxt_flags = (dispatch_invoke_flags_t)(uintptr_t)dc->dc_ctxt; // if we went through _dispatch_root_queue_push_override, // the "right" root queue was stuffed into dc_func dispatch_queue_global_t assumed_rq = (dispatch_queue_global_t)dc->dc_func; @@ -6216,10 +6243,69 @@ _dispatch_worker_thread(void *context) _dispatch_sigmask(); #endif _dispatch_introspection_thread_add(); + dispatch_priority_t pri = dq->dq_priority; + pthread_priority_t pp = _dispatch_get_priority(); + +#if defined(__linux__) + // The Linux kernel does not have a direct analogue to the QoS-based + // thread policy engine found in XNU. + // + // We cannot use 'pthread_setschedprio', because all threads with default + // scheduling policy (SCHED_OTHER) have the same pthread 'priority'. + // For both CFS, which was introduced in Linux 2.6.23, and its successor + // EEVDF (since 6.6) 'sched_get_priority_max' and 'sched_get_priority_min' + // will just return 0. + // + // However, as outlined in "man 2 setpriority", the nice value is a + // per‐thread attribute: different threads in the same process can have + // different nice values. We can thus setup the thread's initial priority + // by converting the QoS class and relative priority to a 'nice' value. + pp = _dispatch_priority_to_pp_strip_flags(pri); + int nice = _dispatch_pp_to_nice(pp); + + #if HAVE_PTHREAD_SETNAME_NP + // pthread thread names are restricted to just 16 characters + // including NUL. Truncate the label name from the beginning as it tends + // to be more unique at the end. + size_t label_length = strlen(dq->dq_label); + const char * thread_name = dq->dq_label; + if (label_length > 0) { + const size_t max_thread_name_length = 16 - 1; // minus the NUL byte; + thread_name = thread_name + (label_length - max_thread_name_length); + } else { + thread_name = "DispatchWorker"; + } + pthread_setname_np(pthread_self(), thread_name); + #endif // HAVE_PTHREAD_SETNAME_NP + + errno = 0; + int rc = setpriority(PRIO_PROCESS, 0, nice); + if (rc != -1 || errno == 0) { + _dispatch_thread_setspecific(dispatch_priority_key, (void *)(uintptr_t)pp); + } else { + _dispatch_log("Failed to set thread priority for worker thread: pqc=%p errno=%d\n", pqc, errno); + } +#elif defined(_WIN32) + pp = _dispatch_priority_to_pp_strip_flags(pri); + int win_priority = _dispatch_pp_to_win32_priority(pp); + + HANDLE current = GetCurrentThread(); + + // Set thread description to the label of the root queue + if (dq->dq_label) { + _dispatch_win32_set_thread_description(current, dq->dq_label); + } + + int rc = SetThreadPriority(current, win_priority); + if (rc) { + _dispatch_thread_setspecific(dispatch_priority_key, (void *)(uintptr_t)pp); + } else { + DWORD dwError = GetLastError(); + _dispatch_log("Failed to set thread priority for worker thread: pqc=%p win_priority=%d dwError=%lu\n", pqc, win_priority, dwError); + } +#endif const int64_t timeout = 5ull * NSEC_PER_SEC; - pthread_priority_t pp = _dispatch_get_priority(); - dispatch_priority_t pri = dq->dq_priority; // If the queue is neither // - the manager @@ -6258,14 +6344,27 @@ _dispatch_worker_thread(void *context) (void)os_atomic_inc2o(dq, dgq_thread_pool_size, release); _dispatch_root_queue_poke(dq, 1, 0); _dispatch_release(dq); // retained in _dispatch_root_queue_poke_slow + +#if defined(_WIN32) + // Make sure to properly end the background processing mode + if (win_priority == THREAD_MODE_BACKGROUND_BEGIN) { + SetThreadPriority(current, THREAD_MODE_BACKGROUND_END); + } +#endif + return NULL; } #if defined(_WIN32) static unsigned WINAPI _dispatch_worker_thread_thunk(LPVOID lpParameter) { - _dispatch_worker_thread(lpParameter); - return 0; + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + if (FAILED(hr)) { + _dispatch_client_assert_fail("Error %ld initializing Windows Runtime", hr); + } + _dispatch_worker_thread(lpParameter); + CoUninitialize(); + return 0; } #endif // defined(_WIN32) #endif // DISPATCH_USE_PTHREAD_POOL @@ -6467,7 +6566,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; @@ -6485,6 +6584,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__) + return ((dispatch_runloop_handle_t)(uintptr_t)dq->do_ctxt); #elif defined(_WIN32) return ((dispatch_runloop_handle_t)(uintptr_t)dq->do_ctxt); #else @@ -6502,6 +6603,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__) + dq->do_ctxt = (void *)(uintptr_t)handle; #elif defined(_WIN32) dq->do_ctxt = (void *)(uintptr_t)handle; #else @@ -6509,6 +6612,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) { @@ -6558,9 +6667,18 @@ _dispatch_runloop_queue_handle_init(void *ctxt) } } handle = fd; +#elif defined(__unix__) && !defined(__linux__) + // swift-corelib-foundation PR #3004 implemented a pipe based queue handle + 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=*/TRUE, + hEvent = CreateEventW(NULL, /*bManualReset=*/FALSE, /*bInitialState=*/FALSE, NULL); if (hEvent == NULL) { DISPATCH_INTERNAL_CRASH(GetLastError(), "CreateEventW"); @@ -6592,6 +6710,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); @@ -6628,6 +6751,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); @@ -6915,7 +7045,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) { @@ -7013,6 +7143,7 @@ _dispatch_sig_thread(void *ctxt DISPATCH_UNUSED) _dispatch_clear_stack(0); #if defined(_WIN32) Sleep(INFINITE); + __builtin_unreachable(); #else _dispatch_sigsuspend(); #endif @@ -7305,6 +7436,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 diff --git a/src/shims.h b/src/shims.h index e14697a9f..a65052dd0 100644 --- a/src/shims.h +++ b/src/shims.h @@ -58,9 +58,6 @@ #define DISPATCH_WORKQ_MAX_PTHREAD_COUNT 255 #endif -#include "shims/hw_config.h" -#include "shims/priority.h" - #if HAVE_PTHREAD_NP_H #include #endif @@ -69,6 +66,9 @@ #include #endif +#include "shims/hw_config.h" +#include "shims/priority.h" + #if !HAVE_DECL_FD_COPY #define FD_COPY(f, t) (void)(*(t) = *(f)) #endif diff --git a/src/shims/atomic.h b/src/shims/atomic.h index c002e726a..bc232f1fb 100644 --- a/src/shims/atomic.h +++ b/src/shims/atomic.h @@ -35,7 +35,11 @@ #if defined(__cplusplus) #define _Bool bool #endif +#if defined(__ANDROID__) && __NDK_MAJOR__ >= 23 +#include +#else #include +#endif #define memory_order_ordered memory_order_seq_cst #define memory_order_dependency memory_order_acquire diff --git a/src/shims/generic_win_stubs.h b/src/shims/generic_win_stubs.h index 985bbe30b..af32ed201 100644 --- a/src/shims/generic_win_stubs.h +++ b/src/shims/generic_win_stubs.h @@ -49,6 +49,7 @@ bool _dispatch_handle_is_socket(HANDLE hFile); void _dispatch_QueryInterruptTimePrecise(PULONGLONG lpInterruptTimePrecise); void _dispatch_QueryUnbiasedInterruptTimePrecise(PULONGLONG lpUnbiasedInterruptTimePrecise); +#ifndef HAVE_FILE_PIPE_LOCAL_INFORMATION enum { FilePipeLocalInformation = 24, }; @@ -65,6 +66,7 @@ typedef struct _FILE_PIPE_LOCAL_INFORMATION { ULONG NamedPipeState; ULONG NamedPipeEnd; } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; +#endif NTSTATUS _dispatch_NtQueryInformationFile(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, diff --git a/src/shims/hw_config.h b/src/shims/hw_config.h index 8ab79f3ed..4e6f7c3c9 100644 --- a/src/shims/hw_config.h +++ b/src/shims/hw_config.h @@ -102,14 +102,14 @@ static inline uint32_t _dispatch_hw_get_config(_dispatch_hw_config_t c) { uint32_t val = 1; -#if defined(__linux__) && HAVE_SYSCONF +#if defined(__FreeBSD__) || (defined(__linux__) && HAVE_SYSCONF) switch (c) { case _dispatch_hw_config_logical_cpus: case _dispatch_hw_config_physical_cpus: return (uint32_t)sysconf(_SC_NPROCESSORS_CONF); case _dispatch_hw_config_active_cpus: { -#ifdef __USE_GNU +#if defined(__FreeBSD__) || __USE_GNU // Prefer pthread_getaffinity_np because it considers // scheduler cpu affinity. This matters if the program // is restricted to a subset of the online cpus (eg via numactl). @@ -156,10 +156,19 @@ _dispatch_hw_get_config(_dispatch_hw_config_t c) ++dwProcessorPhysicalCount; dwProcessorLogicalCount += __popcnt64(slpiCurrent->ProcessorMask); break; +#if defined(DISPATCH_HAVE_EXTENDED_SLPI_20348) + case RelationProcessorDie: +#endif case RelationProcessorPackage: case RelationNumaNode: +#if defined(DISPATCH_HAVE_EXTENDED_SLPI_20348) + case RelationNumaNodeEx: +#endif case RelationCache: case RelationGroup: +#if defined(DISPATCH_HAVE_EXTENDED_SLPI_22000) + case RelationProcessorModule: +#endif case RelationAll: break; } @@ -187,12 +196,16 @@ _dispatch_hw_get_config(_dispatch_hw_config_t c) name = "hw.activecpu"; break; } #elif defined(__FreeBSD__) - (void)c; name = "kern.smp.cpus"; + (void)c; name = "kern.smp.cpus"; +#elif defined(__OpenBSD__) + (void)c; #endif if (name) { size_t valsz = sizeof(val); +#if !defined(__OpenBSD__) r = sysctlbyname(name, &val, &valsz, NULL, 0); (void)dispatch_assume_zero(r); +#endif dispatch_assert(valsz == sizeof(uint32_t)); } else { #if HAVE_SYSCONF && defined(_SC_NPROCESSORS_ONLN) diff --git a/src/shims/lock.c b/src/shims/lock.c index d4d63134a..85e44544c 100644 --- a/src/shims/lock.c +++ b/src/shims/lock.c @@ -54,6 +54,31 @@ _dispatch_thread_switch(dispatch_lock value, dispatch_lock_options_t flags, SwitchToThread(); } #endif +#elif defined(__FreeBSD__) +#if !HAVE_UL_UNFAIR_LOCK +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; + sched_yield(); +} +#endif // HAVE_UL_UNFAIR_LOCK +#elif 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 @@ -206,10 +231,10 @@ _dispatch_sema4_timedwait(_dispatch_sema4_t *sema, dispatch_time_t timeout) struct timespec _timeout; int ret; + uint64_t nsec = _dispatch_time_nanoseconds_since_epoch(timeout); + _timeout.tv_sec = (__typeof__(_timeout.tv_sec))(nsec / NSEC_PER_SEC); + _timeout.tv_nsec = (__typeof__(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); do { - uint64_t nsec = _dispatch_time_nanoseconds_since_epoch(timeout); - _timeout.tv_sec = (__typeof__(_timeout.tv_sec))(nsec / NSEC_PER_SEC); - _timeout.tv_nsec = (__typeof__(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); ret = sem_timedwait(sema, &_timeout); } while (unlikely(ret == -1 && errno == EINTR)); @@ -266,6 +291,7 @@ void _dispatch_sema4_init(_dispatch_sema4_t *sema, int policy DISPATCH_UNUSED) // lazily allocate the semaphore port + os_atomic_cmpxchg(sema, *sema, 0, relaxed); while (!dispatch_assume(tmp = CreateSemaphore(NULL, 0, LONG_MAX, NULL))) { _dispatch_temporary_resource_shortage(); } @@ -394,8 +420,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 @@ -404,7 +432,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 @@ -454,6 +487,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) @@ -471,6 +505,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 @@ -508,7 +543,23 @@ _dispatch_wait_on_address(uint32_t volatile *_address, uint32_t value, } return _dispatch_futex_wait(address, value, NULL, FUTEX_PRIVATE_FLAG); #elif defined(_WIN32) - return WaitOnAddress(address, &value, sizeof(value), INFINITE) == TRUE; + // Round up to the nearest ms as `WaitOnAddress` takes a timeout in ms. + // Integral division will truncate, so make sure that we do the roundup. + DWORD dwMilliseconds = + nsecs == DISPATCH_TIME_FOREVER + ? INFINITE : ((nsecs + 1000000) / 1000000); + if (dwMilliseconds == 0) return ETIMEDOUT; + return WaitOnAddress(address, &value, sizeof(value), dwMilliseconds) == TRUE; +#elif defined(__FreeBSD__) + (void)flags; + if (nsecs != DISPATCH_TIME_FOREVER) { + struct timespec ts = { + .tv_sec = (__typeof__(ts.tv_sec))(nsecs / NSEC_PER_SEC), + .tv_nsec = (__typeof__(ts.tv_nsec))(nsecs % NSEC_PER_SEC), + }; + return _umtx_op((void*)address, UMTX_OP_WAIT_UINT, value, (void*)(uintptr_t)sizeof(struct timespec), (void*)&ts); + } + return _umtx_op((void*)address, UMTX_OP_WAIT_UINT, value, 0, 0); #else #error _dispatch_wait_on_address unimplemented for this platform #endif @@ -523,6 +574,8 @@ _dispatch_wake_by_address(uint32_t volatile *address) _dispatch_futex_wake((uint32_t *)address, INT_MAX, FUTEX_PRIVATE_FLAG); #elif defined(_WIN32) WakeByAddressAll((uint32_t *)address); +#elif defined(__FreeBSD__) + _umtx_op((void*)address, UMTX_OP_WAKE, INT_MAX, 0, 0); #else (void)address; #endif @@ -599,7 +652,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) @@ -636,7 +689,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 @@ -682,7 +735,7 @@ _dispatch_once_wait(dispatch_once_gate_t dgo) _dispatch_futex_wait(lock, (dispatch_lock)new_v, NULL, FUTEX_PRIVATE_FLAG); #else - _dispatch_thread_switch(new_v, 0, timeout++); + _dispatch_thread_switch((dispatch_lock)new_v, 0, timeout++); #endif (void)timeout; } diff --git a/src/shims/lock.h b/src/shims/lock.h index ca450d503..36ccc9920 100644 --- a/src/shims/lock.h +++ b/src/shims/lock.h @@ -79,6 +79,28 @@ _dispatch_lock_owner(dispatch_lock lock_value) return lock_value & DLOCK_OWNER_MASK; } +#elif defined(__FreeBSD__) + +#include +#include +#include + +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 << 2)) + +DISPATCH_ALWAYS_INLINE +static inline dispatch_tid +_dispatch_lock_owner(dispatch_lock lock_value) +{ + return lock_value & DLOCK_OWNER_MASK; +} + #elif defined(_WIN32) #include @@ -100,6 +122,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 +208,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 @@ -564,7 +610,7 @@ typedef struct dispatch_once_gate_s { * effect of the dispatch once initialization. * * Per Lemma 2, when the DONE transition happens in a thread zone { N+3, N+4 }, - * then threads can observe this transiton in their { N+2, N+3 } zone at the + * then threads can observe this transition in their { N+2, N+3 } zone at the * earliest. * * Hence for an initializer bracket of { N, N+1 }, the first safe bracket for diff --git a/src/shims/priority.h b/src/shims/priority.h index 3a79c5efb..92646c3db 100644 --- a/src/shims/priority.h +++ b/src/shims/priority.h @@ -210,6 +210,71 @@ _dispatch_qos_to_pp(dispatch_qos_t qos) return pp | _PTHREAD_PRIORITY_PRIORITY_MASK; } + +#if defined(__linux__) +// These presets roughly match the `android.os.Process' constants +// used for `setThreadPriority()'. +// +// Be aware that with the Completely Fair Scheduler (CFS) the weight is computed +// as 1024 / (1.25) ^ (nice) where nice is in the range -20 to 19. +// This means that nice is not a linear scale. +#define DISPATCH_NICE_BACKGROUND 10 +#define DISPATCH_NICE_UTILITY 2 +#define DISPATCH_NICE_DEFAULT 0 +// Note that you might not have permission to increase the priority +// of a thread beyond the default priority. +#define DISPATCH_NICE_USER_INITIATED -2 +#define DISPATCH_NICE_USER_INTERACTIVE -4 + +DISPATCH_ALWAYS_INLINE +static inline int _dispatch_pp_to_nice(pthread_priority_t pp) +{ + // FIXME: What about relative priorities? + uint32_t qos = _dispatch_qos_from_pp(pp); + + switch (qos) { + case DISPATCH_QOS_BACKGROUND: + return DISPATCH_NICE_BACKGROUND; + case DISPATCH_QOS_UTILITY: + return DISPATCH_NICE_UTILITY; + case DISPATCH_QOS_DEFAULT: + return DISPATCH_NICE_DEFAULT; + case DISPATCH_QOS_USER_INITIATED: + return DISPATCH_NICE_USER_INITIATED; + case DISPATCH_QOS_USER_INTERACTIVE: + return DISPATCH_NICE_USER_INTERACTIVE; + } + + return DISPATCH_NICE_DEFAULT; +} +#endif // defined(__linux__) + +#if defined(_WIN32) +DISPATCH_ALWAYS_INLINE +static inline int _dispatch_pp_to_win32_priority(pthread_priority_t pp) { + uint32_t qos = _dispatch_qos_from_pp(pp); + + switch (qos) { + case DISPATCH_QOS_BACKGROUND: + // Make sure to end background mode before exiting the thread! + return THREAD_MODE_BACKGROUND_BEGIN; + case DISPATCH_QOS_UTILITY: + return THREAD_PRIORITY_BELOW_NORMAL; + case DISPATCH_QOS_DEFAULT: + return THREAD_PRIORITY_NORMAL; + // User input threads should be THREAD_PRIORITY_NORMAL, to + // avoid unintentionally starving the system + case DISPATCH_QOS_USER_INITIATED: + return THREAD_PRIORITY_NORMAL; + case DISPATCH_QOS_USER_INTERACTIVE: + return THREAD_PRIORITY_NORMAL; + } + + return THREAD_PRIORITY_NORMAL; +} +#endif // defined(_WIN32) + + // including maintenance DISPATCH_ALWAYS_INLINE static inline bool diff --git a/src/shims/time.h b/src/shims/time.h index b57731c9a..9befd19e1 100644 --- a/src/shims/time.h +++ b/src/shims/time.h @@ -263,7 +263,7 @@ _dispatch_time_to_clock_and_value(dispatch_time_t time, if (time & DISPATCH_WALLTIME_MASK) { // Wall time (value 11 in bits 63, 62) *clock = DISPATCH_CLOCK_WALL; - actual_value = time == DISPATCH_WALLTIME_NOW ? + actual_value = time == (dispatch_time_t)DISPATCH_WALLTIME_NOW ? _dispatch_get_nanoseconds() : (uint64_t)-time; } else { // Continuous time (value 10 in bits 63, 62). diff --git a/src/shims/tsd.h b/src/shims/tsd.h index 446c4d796..f8b5ec9ba 100644 --- a/src/shims/tsd.h +++ b/src/shims/tsd.h @@ -158,7 +158,7 @@ struct dispatch_tsd { extern _Thread_local struct dispatch_tsd __dispatch_tsd; extern void libdispatch_tsd_init(void); -extern void _libdispatch_tsd_cleanup(void *ctx); +extern void DISPATCH_TSD_DTOR_CC _libdispatch_tsd_cleanup(void *ctx); DISPATCH_ALWAYS_INLINE static inline struct dispatch_tsd * diff --git a/src/shims/yield.c b/src/shims/yield.c index 43f0017ee..cf1f5cefd 100644 --- a/src/shims/yield.c +++ b/src/shims/yield.c @@ -25,6 +25,11 @@ static void * __DISPATCH_WAIT_FOR_ENQUEUER__(void **ptr) { int spins = 0; + // Different platforms may expand `_dispatch_preemption_yield` to a + // no-op, but `(void)++spins` is not considered a use like + // `(void)spins` is. Add a use to avoid unused var warnings. + (void)spins; + void *value; while ((value = os_atomic_load(ptr, relaxed)) == NULL) { _dispatch_preemption_yield(++spins); @@ -36,7 +41,7 @@ void * _dispatch_wait_for_enqueuer(void **ptr) { #if !DISPATCH_HW_CONFIG_UP -#if defined(__arm__) || defined(__arm64__) +#if (defined(__arm__) && defined(__APPLE__)) || defined(__arm64__) int spins = DISPATCH_WAIT_SPINS_WFE; void *value; while (unlikely(spins-- > 0)) { diff --git a/src/shims/yield.h b/src/shims/yield.h index 53eb80065..ee47de5b4 100644 --- a/src/shims/yield.h +++ b/src/shims/yield.h @@ -98,22 +98,17 @@ void *_dispatch_wait_for_enqueuer(void **ptr); #define _dispatch_contention_spins() \ ((DISPATCH_CONTENTION_SPINS_MIN) + ((DISPATCH_CONTENTION_SPINS_MAX) - \ (DISPATCH_CONTENTION_SPINS_MIN)) / 2) -#elif defined(_WIN32) +#else // Use randomness to prevent threads from resonating at the same frequency and // permanently contending. Windows doesn't provide rand_r(), so use a simple // LCG. (msvcrt has rand_s(), but its security guarantees aren't optimal here.) +// The implementation of rand() can contain a lock (known with glibc at least). #define _dispatch_contention_spins() ({ \ static os_atomic(unsigned int) _seed = 1; \ unsigned int _next = os_atomic_load(&_seed, relaxed); \ os_atomic_store(&_seed, _next * 1103515245 + 12345, relaxed); \ ((_next >> 24) & (DISPATCH_CONTENTION_SPINS_MAX)) | \ (DISPATCH_CONTENTION_SPINS_MIN); }) -#else -// Use randomness to prevent threads from resonating at the same -// frequency and permanently contending. -#define _dispatch_contention_spins() ({ \ - ((unsigned int)rand() & (DISPATCH_CONTENTION_SPINS_MAX)) | \ - (DISPATCH_CONTENTION_SPINS_MIN); }) #endif #define _dispatch_contention_wait_until(c) ({ \ bool _out = false; \ diff --git a/src/source.c b/src/source.c index 1010da197..69b731d92 100644 --- a/src/source.c +++ b/src/source.c @@ -1398,11 +1398,12 @@ _dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz) dispatch_source_refs_t dr = ds->ds_refs; dispatch_queue_flags_t dqf = _dispatch_queue_atomic_flags(ds); dispatch_unote_state_t du_state = _dispatch_unote_state(dr); - return dsnprintf(buf, bufsiz, "target = %s[%p], ident = 0x%x, " + 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, - dr->du_ident, dr->du_fflags, (unsigned long long)dr->ds_pending_data, + 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, " : "", (dqf & DSF_NEEDS_EVENT) ? "needs-event, " : "", diff --git a/src/swift/Block.swift b/src/swift/Block.swift index 71d998ba6..6459ba786 100644 --- a/src/swift/Block.swift +++ b/src/swift/Block.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// import CDispatch -import _SwiftDispatchOverlayShims +@_implementationOnly import _DispatchOverlayShims public struct DispatchWorkItemFlags : OptionSet, RawRepresentable { public let rawValue: UInt @@ -40,11 +40,7 @@ public class DispatchWorkItem { internal var _block: _DispatchBlock public init(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], block: @escaping @convention(block) () -> ()) { -#if os(Windows) && (arch(arm64) || arch(x86_64)) - let flags = dispatch_block_flags_t(UInt32(flags.rawValue)) -#else - let flags: dispatch_block_flags_t = numericCast(flags.rawValue) -#endif + let flags: dispatch_block_flags_t = dispatch_block_flags_t(CUnsignedLong(flags.rawValue)) _block = dispatch_block_create_with_qos_class(flags, qos.qosClass.rawValue.rawValue, Int32(qos.relativePriority), block) } @@ -52,11 +48,7 @@ public class DispatchWorkItem { // Used by DispatchQueue.synchronously to provide a path through // dispatch_block_t, as we know the lifetime of the block in question. internal init(flags: DispatchWorkItemFlags = [], noescapeBlock: () -> ()) { -#if os(Windows) && (arch(arm64) || arch(x86_64)) - let flags = dispatch_block_flags_t(UInt32(flags.rawValue)) -#else - let flags: dispatch_block_flags_t = numericCast(flags.rawValue) -#endif + let flags: dispatch_block_flags_t = dispatch_block_flags_t(CUnsignedLong(flags.rawValue)) _block = _swift_dispatch_block_create_noescape(flags, noescapeBlock) } diff --git a/src/swift/CMakeLists.txt b/src/swift/CMakeLists.txt index ba987e747..a0082fb1e 100644 --- a/src/swift/CMakeLists.txt +++ b/src/swift/CMakeLists.txt @@ -1,22 +1,9 @@ - -# NOTE(compnerd) Today regardless of whether or not ObjC interop is enabled, -# swift will use an autoreleased return value convention for certain CF -# functions (including some that are used/related to dispatch). This means that -# the swift compiler in callers to such functions will call the function, and -# then pass the result of the function to objc_retainAutoreleasedReturnValue. In -# a context where we have ObjC interop disabled, we do not have access to the -# objc runtime so an implementation of objc_retainAutoreleasedReturnValue is not -# available. To work around this, we provide a shim for -# objc_retainAutoreleasedReturnValue in DispatchStubs.cc that just calls retain -# on the object. Once we fix the swift compiler to switch to a different model -# for handling these arguments with objc-interop disabled these shims can be -# eliminated. -add_library(DispatchStubs STATIC - DispatchStubs.cc) -target_include_directories(DispatchStubs PRIVATE - ${PROJECT_SOURCE_DIR}) -set_target_properties(DispatchStubs PROPERTIES - POSITION_INDEPENDENT_CODE YES) +if(HAVE_OBJC) + add_library(DispatchStubs STATIC + DispatchStubs.m) + target_include_directories(DispatchStubs PRIVATE + ${PROJECT_SOURCE_DIR}) +endif() add_library(swiftDispatch Block.swift @@ -31,30 +18,32 @@ add_library(swiftDispatch target_compile_options(swiftDispatch PRIVATE "SHELL:-Xcc -fblocks" "SHELL:-Xcc -fmodule-map-file=${PROJECT_SOURCE_DIR}/dispatch/module.modulemap" - "SHELL:-Xcc -I${PROJECT_SOURCE_DIR}") + "SHELL:-Xcc -I${PROJECT_SOURCE_DIR}" + "SHELL:-Xcc -I${PROJECT_SOURCE_DIR}/src/swift/shims") +target_compile_options(swiftDispatch PUBLIC + "SHELL:-vfsoverlay ${CMAKE_BINARY_DIR}/dispatch-vfs-overlay.yaml") set_target_properties(swiftDispatch PROPERTIES Swift_MODULE_NAME Dispatch Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/swift INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/swift) target_link_libraries(swiftDispatch PRIVATE - DispatchStubs + $<$:DispatchStubs> BlocksRuntime::BlocksRuntime) target_link_libraries(swiftDispatch PUBLIC dispatch) -add_dependencies(swiftDispatch module-maps) +if(NOT APPLE AND NOT WIN32) + target_link_options(swiftDispatch PRIVATE "SHELL:-no-toolchain-stdlib-rpath") + set_target_properties(swiftDispatch PROPERTIES INSTALL_RPATH "$ORIGIN") +endif() -get_swift_host_arch(swift_arch) -install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/swift/Dispatch.swiftmodule - ${CMAKE_CURRENT_BINARY_DIR}/swift/Dispatch.swiftdoc - DESTINATION ${INSTALL_TARGET_DIR}/${swift_arch}) +install_swift_module(swiftDispatch) set_property(GLOBAL APPEND PROPERTY DISPATCH_EXPORTS swiftDispatch) install(TARGETS swiftDispatch EXPORT dispatchExports ARCHIVE DESTINATION ${INSTALL_TARGET_DIR} LIBRARY DESTINATION ${INSTALL_TARGET_DIR} - RUNTIME DESTINATION bin) -if(NOT BUILD_SHARED_LIBS) + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +if(HAVE_OBJC AND NOT BUILD_SHARED_LIBS) set_property(GLOBAL APPEND PROPERTY DISPATCH_EXPORTS DispatchStubs) install(TARGETS DispatchStubs EXPORT dispatchExports diff --git a/src/swift/Data.swift b/src/swift/Data.swift index 3b81e68b3..ac962d71c 100644 --- a/src/swift/Data.swift +++ b/src/swift/Data.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// import CDispatch -import _SwiftDispatchOverlayShims +@_implementationOnly import _DispatchOverlayShims public struct DispatchData : RandomAccessCollection { public typealias Iterator = DispatchDataIterator @@ -110,7 +110,7 @@ public struct DispatchData : RandomAccessCollection { { var ptr: UnsafeRawPointer? = nil var size = 0 - let data = CDispatch.dispatch_data_create_map(__wrapped.__wrapped, &ptr, &size) + let data = DispatchData(data: CDispatch.dispatch_data_create_map(__wrapped.__wrapped, &ptr, &size)) let contentPtr = ptr!.bindMemory( to: ContentType.self, capacity: size / MemoryLayout.stride) defer { _fixLifetime(data) } diff --git a/src/swift/Dispatch.swift b/src/swift/Dispatch.swift index 2ba819223..b14794d85 100644 --- a/src/swift/Dispatch.swift +++ b/src/swift/Dispatch.swift @@ -17,7 +17,7 @@ import CDispatch /// dispatch_assert @available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *) -public enum DispatchPredicate { +public enum DispatchPredicate : Sendable { case onQueue(DispatchQueue) case onQueueAsBarrier(DispatchQueue) case notOnQueue(DispatchQueue) @@ -46,7 +46,7 @@ public func dispatchPrecondition(condition: @autoclosure () -> DispatchPredicate /// qos_class_t -public struct DispatchQoS : Equatable { +public struct DispatchQoS : Equatable, Sendable { public let qosClass: QoSClass public let relativePriority: Int @@ -67,7 +67,7 @@ public struct DispatchQoS : Equatable { public static let unspecified = DispatchQoS(qosClass: .unspecified, relativePriority: 0) - public enum QoSClass { + public enum QoSClass : Sendable { @available(macOS 10.10, iOS 8.0, *) case background @@ -125,7 +125,7 @@ public func ==(a: DispatchQoS, b: DispatchQoS) -> Bool { /// -public enum DispatchTimeoutResult { +public enum DispatchTimeoutResult : Sendable { static let KERN_OPERATION_TIMED_OUT:Int = 49 case success case timedOut diff --git a/src/swift/DispatchStubs.cc b/src/swift/DispatchStubs.m similarity index 57% rename from src/swift/DispatchStubs.cc rename to src/swift/DispatchStubs.m index 0625cc91f..1f72233eb 100644 --- a/src/swift/DispatchStubs.cc +++ b/src/swift/DispatchStubs.m @@ -11,13 +11,6 @@ //===----------------------------------------------------------------------===// #include -#include - -#if defined(__ELF__) || defined(__MACH__) || defined(__WASM__) -#define DISPATCH_RUNTIME_STDLIB_INTERFACE __attribute__((__visibility__("default"))) -#else -#define DISPATCH_RUNTIME_STDLIB_INTERFACE __declspec(dllexport) -#endif #if USE_OBJC @protocol OS_dispatch_source; @@ -34,11 +27,9 @@ @protocol OS_dispatch_source_vnode; @protocol OS_dispatch_source_write; -// #include -__attribute__((constructor)) +__attribute__((__constructor__)) static void _dispatch_overlay_constructor() { - Class source = objc_lookUpClass("OS_dispatch_source"); - if (source) { + if (Class source = objc_lookUpClass("OS_dispatch_source")) { class_addProtocol(source, @protocol(OS_dispatch_source)); class_addProtocol(source, @protocol(OS_dispatch_source_mach_send)); class_addProtocol(source, @protocol(OS_dispatch_source_mach_recv)); @@ -54,42 +45,4 @@ static void _dispatch_overlay_constructor() { class_addProtocol(source, @protocol(OS_dispatch_source_write)); } } - #endif /* USE_OBJC */ - -#if !USE_OBJC -DISPATCH_RUNTIME_STDLIB_INTERFACE -extern "C" void * objc_retainAutoreleasedReturnValue(void *obj); -#endif - -#if !USE_OBJC - -// For CF functions with 'Get' semantics, the compiler currently assumes that -// the result is autoreleased and must be retained. It does so on all platforms -// by emitting a call to objc_retainAutoreleasedReturnValue. On Darwin, this is -// implemented by the ObjC runtime. On non-ObjC platforms, there is no runtime, -// and therefore we have to stub it out here ourselves. The compiler will -// eventually call swift_release to balance the retain below. This is a -// workaround until the compiler no longer emits this callout on non-ObjC -// platforms. -extern "C" -#if defined(_WIN32) -__declspec(dllimport) -#endif -void swift_retain(void *); - -DISPATCH_RUNTIME_STDLIB_INTERFACE -extern "C" void * objc_retainAutoreleasedReturnValue(void *obj) { - if (obj) { - swift_retain(obj); - return obj; - } - else return NULL; -} - -#if defined(_WIN32) -extern "C" void *(*__imp_objc_retainAutoreleasedReturnValue)(void *) = - &objc_retainAutoreleasedReturnValue; -#endif - -#endif // !USE_OBJC diff --git a/src/swift/IO.swift b/src/swift/IO.swift index ad985c944..3c0e22484 100644 --- a/src/swift/IO.swift +++ b/src/swift/IO.swift @@ -67,7 +67,7 @@ extension DispatchIO { public convenience init( type: StreamType, - fileDescriptor: Int32, + fileDescriptor: dispatch_fd_t, queue: DispatchQueue, cleanupHandler: @escaping (_ error: Int32) -> Void) { diff --git a/src/swift/Private.swift b/src/swift/Private.swift index 89b1bb2f4..1683e2e51 100644 --- a/src/swift/Private.swift +++ b/src/swift/Private.swift @@ -27,7 +27,7 @@ public func dispatch_queue_create_with_target(_ label: UnsafePointer?, _ a } @available(*, unavailable, renamed:"DispatchIO.init(type:fileDescriptor:queue:cleanupHandler:)") -public func dispatch_io_create(_ type: UInt, _ fd: Int32, _ queue: DispatchQueue, _ cleanup_handler: @escaping (Int32) -> Void) -> DispatchIO +public func dispatch_io_create(_ type: UInt, _ fd: dispatch_fd_t, _ queue: DispatchQueue, _ cleanup_handler: @escaping (Int32) -> Void) -> DispatchIO { fatalError() } diff --git a/src/swift/Queue.swift b/src/swift/Queue.swift index fe7406c42..f590c32df 100644 --- a/src/swift/Queue.swift +++ b/src/swift/Queue.swift @@ -13,12 +13,15 @@ // dispatch/queue.h import CDispatch -import _SwiftDispatchOverlayShims +@_implementationOnly import _DispatchOverlayShims public final class DispatchSpecificKey { public init() {} } +extension DispatchSpecificKey : Sendable where T : Sendable { +} + internal class _DispatchSpecificValue { internal let value: T internal init(value: T) { self.value = value } @@ -240,7 +243,6 @@ extension DispatchQueue { /// /// - parameter group: the dispatch group to associate with the submitted /// work item. If this is `nil`, the work item is not associated with a group. - /// - parameter flags: flags that control the execution environment of the /// - parameter qos: the QoS at which the work item should be executed. /// Defaults to `DispatchQoS.unspecified`. /// - parameter flags: flags that control the execution environment of the @@ -254,7 +256,7 @@ extension DispatchQueue { group: DispatchGroup? = nil, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], - execute work: @escaping @convention(block) () -> Void) + execute work: @escaping @Sendable @convention(block) () -> Void) { if group == nil && qos == .unspecified { // Fast-path route for the most common API usage @@ -388,7 +390,7 @@ extension DispatchQueue { deadline: DispatchTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], - execute work: @escaping @convention(block) () -> Void) + execute work: @escaping @Sendable @convention(block) () -> Void) { if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: work) @@ -419,7 +421,7 @@ extension DispatchQueue { wallDeadline: DispatchWallTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], - execute work: @escaping @convention(block) () -> Void) + execute work: @escaping @Sendable @convention(block) () -> Void) { if #available(macOS 10.10, iOS 8.0, *), qos != .unspecified || !flags.isEmpty { let item = DispatchWorkItem(qos: qos, flags: flags, block: work) diff --git a/src/swift/Source.swift b/src/swift/Source.swift index b4315c6cf..0c3abc7f9 100644 --- a/src/swift/Source.swift +++ b/src/swift/Source.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// import CDispatch -import _SwiftDispatchOverlayShims +@_implementationOnly import _DispatchOverlayShims #if os(Windows) import WinSDK #endif @@ -124,8 +124,20 @@ extension DispatchSource { public static let exit = ProcessEvent(rawValue: 0x80000000) public static let fork = ProcessEvent(rawValue: 0x40000000) public static let exec = ProcessEvent(rawValue: 0x20000000) + +#if canImport(Darwin) public static let signal = ProcessEvent(rawValue: 0x08000000) +#endif +#if os(FreeBSD) || os(OpenBSD) + public static let track = ProcessEvent(rawValue: 0x00000001) +#endif + +#if canImport(Darwin) public static let all: ProcessEvent = [.exit, .fork, .exec, .signal] +#endif +#if os(FreeBSD) || os(OpenBSD) + public static let all: ProcessEvent = [.exit, .fork, .exec, .track] +#endif } #endif @@ -224,7 +236,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) && !os(FreeBSD) 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 @@ -293,7 +305,7 @@ extension DispatchSourceMemoryPressure { #if !os(Linux) && !os(Android) && !os(Windows) extension DispatchSourceProcess { public var handle: pid_t { - return pid_t(dispatch_source_get_handle(self as! DispatchSource)) + return pid_t(CDispatch.dispatch_source_get_handle((self as! DispatchSource).__wrapped)) } public var data: DispatchSource.ProcessEvent { @@ -646,7 +658,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/Time.swift b/src/swift/Time.swift index b30e1f1b8..bc93d5e72 100644 --- a/src/swift/Time.swift +++ b/src/swift/Time.swift @@ -16,7 +16,7 @@ import CDispatch -public struct DispatchTime : Comparable { +public struct DispatchTime : Comparable, Sendable { #if HAVE_MACH private static let timebaseInfo: mach_timebase_info_data_t = { var info = mach_timebase_info_data_t(numer: 1, denom: 1) @@ -96,7 +96,7 @@ extension DispatchTime { } } -public struct DispatchWallTime : Comparable { +public struct DispatchWallTime : Comparable, Sendable { public let rawValue: dispatch_time_t public static func now() -> DispatchWallTime { @@ -165,7 +165,7 @@ private func toInt64Clamped(_ value: Double) -> Int64 { /// let t1 = DispatchTimeInterval.seconds(Int.max) /// let t2 = DispatchTimeInterval.milliseconds(Int.max) /// let result = t1 == t2 // true -public enum DispatchTimeInterval { +public enum DispatchTimeInterval: Equatable, Sendable { case seconds(Int) case milliseconds(Int) case microseconds(Int) diff --git a/src/swift/Wrapper.swift b/src/swift/Wrapper.swift index 678631b03..a260697f2 100644 --- a/src/swift/Wrapper.swift +++ b/src/swift/Wrapper.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// import CDispatch -import _SwiftDispatchOverlayShims +@_implementationOnly import _DispatchOverlayShims // This file contains declarations that are provided by the // importer via Dispatch.apinote when the platform has Objective-C support @@ -44,7 +44,7 @@ public class DispatchObject { } -public class DispatchGroup : DispatchObject { +public class DispatchGroup : DispatchObject, @unchecked Sendable { internal let __wrapped:dispatch_group_t; final internal override func wrapped() -> dispatch_object_t { @@ -68,7 +68,7 @@ public class DispatchGroup : DispatchObject { } } -public class DispatchSemaphore : DispatchObject { +public class DispatchSemaphore : DispatchObject, @unchecked Sendable { internal let __wrapped: dispatch_semaphore_t; final internal override func wrapped() -> dispatch_object_t { @@ -84,14 +84,14 @@ public class DispatchSemaphore : DispatchObject { } } -public class DispatchIO : DispatchObject { - internal let __wrapped:dispatch_io_t +public class DispatchIO : DispatchObject, @unchecked Sendable { + internal let __wrapped: dispatch_io_t final internal override func wrapped() -> dispatch_object_t { return unsafeBitCast(__wrapped, to: dispatch_object_t.self) } - internal init(__type: UInt, fd: Int32, queue: DispatchQueue, + internal init(__type: UInt, fd: dispatch_fd_t, queue: DispatchQueue, handler: @escaping (_ error: Int32) -> Void) { __wrapped = dispatch_io_create(dispatch_io_type_t(__type), dispatch_fd_t(fd), queue.__wrapped, handler) } @@ -127,7 +127,7 @@ public class DispatchIO : DispatchObject { } } -public class DispatchQueue : DispatchObject { +public class DispatchQueue : DispatchObject, @unchecked Sendable { internal let __wrapped:dispatch_queue_t; final internal override func wrapped() -> dispatch_object_t { @@ -156,6 +156,7 @@ public class DispatchQueue : DispatchObject { } public class DispatchSource : DispatchObject, + @unchecked Sendable, DispatchSourceProtocol, DispatchSourceRead, DispatchSourceSignal, DispatchSourceTimer, DispatchSourceUserDataAdd, DispatchSourceUserDataOr, @@ -182,8 +183,12 @@ extension DispatchSource : DispatchSourceMachSend, #endif #if !os(Linux) && !os(Android) && !os(Windows) -extension DispatchSource : DispatchSourceProcess, - DispatchSourceFileSystemObject { +extension DispatchSource : DispatchSourceProcess { +} +#endif + +#if !os(Linux) && !os(Android) && !os(Windows) && !os(FreeBSD) && !os(OpenBSD) +extension DispatchSource : DispatchSourceFileSystemObject { } #endif @@ -302,7 +307,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 } diff --git a/src/swift/shims/DispatchOverlayShims.h b/src/swift/shims/DispatchOverlayShims.h new file mode 100644 index 000000000..fbcf0116e --- /dev/null +++ b/src/swift/shims/DispatchOverlayShims.h @@ -0,0 +1,258 @@ +//===--- DispatchOverlayShims.h - Compatibility decls -----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// + +#ifndef SWIFT_SHIMS_DISPATCH_OVERLAY_SHIMS_H +#define SWIFT_SHIMS_DISPATCH_OVERLAY_SHIMS_H + +#include + +#ifdef __OBJC__ +#define SWIFT_DISPATCH_RETURNS_RETAINED __attribute__((__ns_returns_retained__)) +#else +#define SWIFT_DISPATCH_RETURNS_RETAINED +#endif + +#define SWIFT_DISPATCH_NOESCAPE __attribute__((__noescape__)) + +#pragma clang assume_nonnull begin + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (^__swift_shims_dispatch_block_t)(void); + +#ifdef __OBJC__ +typedef id __swift_shims_dispatch_data_t; +#else +typedef void *__swift_shims_dispatch_data_t; +#endif + + +static inline dispatch_queue_attr_t +_swift_dispatch_queue_concurrent(void) { + return DISPATCH_QUEUE_CONCURRENT; +} + +static inline dispatch_queue_t +_swift_dispatch_get_main_queue(void) { + return dispatch_get_main_queue(); +} + +static inline dispatch_data_t +_swift_dispatch_data_empty(void) { + return dispatch_data_empty; +} + +static inline __swift_shims_dispatch_block_t _Nullable +_swift_dispatch_data_destructor_default(void) { + return DISPATCH_DATA_DESTRUCTOR_DEFAULT; +} + +static inline __swift_shims_dispatch_block_t +_swift_dispatch_data_destructor_free(void) { + return _dispatch_data_destructor_free; +} + +static inline __swift_shims_dispatch_block_t +_swift_dispatch_data_destructor_munmap(void) { + return _dispatch_data_destructor_munmap; +} + +#define SWIFT_DISPATCH_SOURCE_TYPE(t) \ + static inline dispatch_source_type_t _swift_dispatch_source_type_##t(void) { \ + return DISPATCH_SOURCE_TYPE_##t; \ + } + +SWIFT_DISPATCH_SOURCE_TYPE(DATA_ADD) +SWIFT_DISPATCH_SOURCE_TYPE(DATA_OR) +SWIFT_DISPATCH_SOURCE_TYPE(DATA_REPLACE) +SWIFT_DISPATCH_SOURCE_TYPE(READ) +SWIFT_DISPATCH_SOURCE_TYPE(SIGNAL) +SWIFT_DISPATCH_SOURCE_TYPE(TIMER) +SWIFT_DISPATCH_SOURCE_TYPE(WRITE) + +#if __APPLE__ +SWIFT_DISPATCH_SOURCE_TYPE(MACH_SEND) +SWIFT_DISPATCH_SOURCE_TYPE(MACH_RECV) +SWIFT_DISPATCH_SOURCE_TYPE(MEMORYPRESSURE) +SWIFT_DISPATCH_SOURCE_TYPE(PROC) +SWIFT_DISPATCH_SOURCE_TYPE(VNODE) +#endif + +#if defined(__FreeBSD__) || defined(__OpenBSD__) +SWIFT_DISPATCH_SOURCE_TYPE(PROC) +SWIFT_DISPATCH_SOURCE_TYPE(VNODE) +#endif + +extern void +_swift_dispatch_source_create_abort(void); + +SWIFT_DISPATCH_RETURNS_RETAINED +static inline dispatch_source_t +_swift_dispatch_source_create( + dispatch_source_type_t type, + uintptr_t handle, + unsigned long mask, + dispatch_queue_t _Nullable queue) +{ + dispatch_source_t source = dispatch_source_create(type, handle, mask, queue); + if (!source) { + _swift_dispatch_source_create_abort(); + } + return source; +} + +static inline SWIFT_DISPATCH_RETURNS_RETAINED __swift_shims_dispatch_block_t +_swift_dispatch_block_create_with_qos_class( + dispatch_block_flags_t flags, dispatch_qos_class_t qos, + int relative_priority, __swift_shims_dispatch_block_t _Nonnull block) { + return dispatch_block_create_with_qos_class( + flags, qos, relative_priority, block); +} + +static inline __swift_shims_dispatch_block_t +_swift_dispatch_block_create_noescape( + dispatch_block_flags_t flags, + __swift_shims_dispatch_block_t SWIFT_DISPATCH_NOESCAPE block) { + return dispatch_block_create(flags, block); +} + +static inline int _swift_dispatch_block_wait( + __swift_shims_dispatch_block_t block, + unsigned long long timeout) { + return dispatch_block_wait(block, timeout); +} + +static inline void _swift_dispatch_block_notify( + __swift_shims_dispatch_block_t block, + dispatch_queue_t queue, + __swift_shims_dispatch_block_t notifier) { + dispatch_block_notify(block, queue, notifier); +} + +static inline void _swift_dispatch_block_cancel( + __swift_shims_dispatch_block_t block) { + dispatch_block_cancel(block); +} + +static inline int _swift_dispatch_block_testcancel( + __swift_shims_dispatch_block_t block) { + return dispatch_block_testcancel(block); +} + +static inline void _swift_dispatch_async( + dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) { + dispatch_async(queue, block); +} + +static inline void _swift_dispatch_sync( + dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) { + dispatch_sync(queue, block); +} + +static inline void _swift_dispatch_barrier_async( + dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) { + dispatch_barrier_async(queue, block); +} + +static inline void _swift_dispatch_group_async( + dispatch_group_t group, + dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) { + dispatch_group_async((dispatch_group_t)group, queue, block); +} + +static inline void _swift_dispatch_group_notify( + dispatch_group_t group, + dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) { + dispatch_group_notify(group, queue, block); +} + +static inline void _swift_dispatch_after( + dispatch_time_t when, + dispatch_queue_t queue, + __swift_shims_dispatch_block_t block) { + dispatch_after(when, queue, block); +} + + +static inline void _swift_dispatch_apply_current( + size_t iterations, + void SWIFT_DISPATCH_NOESCAPE (^block)(intptr_t)) { + dispatch_apply(iterations, (dispatch_queue_t _Nonnull)0, ^(size_t i){ + block((intptr_t)i); + }); +} + +SWIFT_DISPATCH_RETURNS_RETAINED +static inline __swift_shims_dispatch_data_t +_swift_dispatch_data_create( + const void *buffer, + size_t size, + dispatch_queue_t _Nullable queue, + __swift_shims_dispatch_block_t _Nullable destructor) { + return dispatch_data_create(buffer, size, queue, destructor); +} + +typedef unsigned int (^__swift_shims_dispatch_data_applier)(__swift_shims_dispatch_data_t, size_t, const void *, size_t); + +static inline unsigned int +_swift_dispatch_data_apply( + __swift_shims_dispatch_data_t data, + __swift_shims_dispatch_data_applier SWIFT_DISPATCH_NOESCAPE applier) { + return dispatch_data_apply((dispatch_data_t)data, ^bool(dispatch_data_t data, size_t off, const void *loc, size_t size){ + return applier((__swift_shims_dispatch_data_t)data, off, loc, size); + }); +} + +static inline void _swift_dispatch_source_set_event_handler( + dispatch_source_t source, + __swift_shims_dispatch_block_t _Nullable block) { + dispatch_source_set_event_handler(source, block); +} + +static inline void _swift_dispatch_source_set_cancel_handler( + dispatch_source_t source, + __swift_shims_dispatch_block_t _Nullable block) { + dispatch_source_set_cancel_handler(source, block); +} + +static inline void _swift_dispatch_source_set_registration_handler( + dispatch_source_t source, + __swift_shims_dispatch_block_t _Nullable block) { + dispatch_source_set_registration_handler(source, block); +} + +#if defined(__ANDROID__) +extern void _dispatch_install_thread_detach_callback(void (*cb)(void)); +#endif + +static inline void _swift_dispatch_retain(dispatch_object_t object) { + dispatch_retain(object); +} + +static inline void _swift_dispatch_release(dispatch_object_t object) { + dispatch_release(object); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#pragma clang assume_nonnull end + +#endif // SWIFT_SHIMS_DISPATCH_OVERLAY_SHIMS_H + diff --git a/src/swift/shims/module.modulemap b/src/swift/shims/module.modulemap new file mode 100644 index 000000000..788b0c06e --- /dev/null +++ b/src/swift/shims/module.modulemap @@ -0,0 +1,3 @@ +module _DispatchOverlayShims { + header "DispatchOverlayShims.h" +} diff --git a/src/transform.c b/src/transform.c index b1b84aafd..6e65567ad 100644 --- a/src/transform.c +++ b/src/transform.c @@ -26,7 +26,7 @@ #include #define OSLittleEndian __LITTLE_ENDIAN #define OSBigEndian __BIG_ENDIAN -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__OpenBSD__) #include #define OSLittleEndian _LITTLE_ENDIAN #define OSBigEndian _BIG_ENDIAN @@ -35,7 +35,7 @@ #define OSBigEndian 4321 #endif -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) #define OSSwapLittleToHostInt16 le16toh #define OSSwapBigToHostInt16 be16toh #define OSSwapHostToLittleInt16 htole16 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a27603559..ed5d2f444 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ -if(CMAKE_SYSTEM_NAME STREQUAL Windows) +if(WIN32) execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/private" "${CMAKE_CURRENT_BINARY_DIR}/dispatch") @@ -15,7 +15,7 @@ else() "${CMAKE_CURRENT_BINARY_DIR}/leaks-wrapper") endif() -if(CMAKE_SYSTEM_NAME STREQUAL Linux) +if(LINUX) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lrt") endif() @@ -23,6 +23,8 @@ add_library(bsdtests STATIC bsdtests.c dispatch_test.c) +target_link_libraries(bsdtests PUBLIC + dispatch) target_include_directories(bsdtests PRIVATE ${CMAKE_CURRENT_BINARY_DIR} @@ -81,13 +83,20 @@ function(add_unit_test name) target_include_directories(${name} SYSTEM BEFORE PRIVATE "${BlocksRuntime_INCLUDE_DIR}") - if("${CMAKE_C_SIMULATE_ID}" STREQUAL "MSVC") + if("${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "MSVC") target_compile_options(${name} PRIVATE -Xclang -fblocks) target_compile_options(${name} PRIVATE /W3 -Wno-deprecated-declarations) else() target_compile_options(${name} PRIVATE -fblocks) target_compile_options(${name} PRIVATE -Wall -Wno-deprecated-declarations) endif() + # Without this flag, cross-compiling static test executables for Android armv7 + # fails with the multiple definition errors seen in android/ndk#176, so I + # pulled in this workaround noted there. The tests build and run with this + # flag applied. + if(NOT BUILD_SHARED_LIBS AND ANDROID AND CMAKE_SYSTEM_PROCESSOR STREQUAL armv7-a) + target_link_options(${name} PRIVATE "LINKER:--allow-multiple-definition") + endif() target_link_libraries(${name} PRIVATE dispatch @@ -154,7 +163,7 @@ if(EXTENDED_TEST_SUITE) endif() # add C tests for platform-specific functionality when applicable -if(CMAKE_SYSTEM_NAME STREQUAL Darwin) +if(APPLE) list(APPEND DISPATCH_C_TESTS deadname proc @@ -175,6 +184,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/bsdtestharness.c b/tests/bsdtestharness.c index ca52d6e97..5014cba37 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__)) @@ -72,13 +73,6 @@ main(int argc, char *argv[]) assert(res == 0); #endif - uint64_t to = 0; - char *tos = getenv("BSDTEST_TIMEOUT"); - if (tos) { - to = strtoul(tos, NULL, 0); - to *= NSEC_PER_SEC; - } - #ifdef __APPLE__ char *arch = getenv("BSDTEST_ARCH"); if (arch) { @@ -151,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; @@ -166,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); @@ -218,7 +212,7 @@ main(int argc, char *argv[]) #else dispatch_queue_t main_q = dispatch_get_main_queue(); - dispatch_source_t tmp_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, main_q); + dispatch_source_t tmp_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, (uintptr_t)pid, DISPATCH_PROC_EXIT, main_q); assert(tmp_ds); dispatch_source_set_event_handler(tmp_ds, ^{ int status; @@ -245,6 +239,13 @@ main(int argc, char *argv[]) }); dispatch_resume(tmp_ds); + uint64_t to = 0; + char *tos = getenv("BSDTEST_TIMEOUT"); + if (tos) { + to = strtoul(tos, NULL, 0); + to *= NSEC_PER_SEC; + } + if (!to) { #if TARGET_OS_EMBEDDED to = 180LL * NSEC_PER_SEC; diff --git a/tests/bsdtests.c b/tests/bsdtests.c index af716626b..3ea91a381 100644 --- a/tests/bsdtests.c +++ b/tests/bsdtests.c @@ -164,6 +164,20 @@ test_uint32_format(uint32_t actual, uint32_t expected, const char *format, ...) _test_uint32(NULL, 0, desc, actual, expected); } +void +_test_uint32_not(const char* file, long line, const char* desc, uint32_t actual, uint32_t unexpected) +{ + _test_print(file, line, desc, + (actual != unexpected), "%u", actual, "!%u", unexpected); +} + +void +test_uint32_not_format(uint32_t actual, uint32_t unexpected, const char *format, ...) +{ + GENERATE_DESC + _test_uint32_not(NULL, 0, desc, actual, unexpected); +} + void _test_int32(const char* file, long line, const char* desc, int32_t actual, int32_t expected) { @@ -347,8 +361,8 @@ _test_errno(const char* file, long line, const char* desc, int actual, int expec { char* actual_str; char* expected_str; - asprintf(&actual_str, "%d\t%s", actual, actual ? strerror(actual) : ""); - asprintf(&expected_str, "%d\t%s", expected, expected ? strerror(expected) : ""); + (void)asprintf(&actual_str, "%d\t%s", actual, actual ? strerror(actual) : ""); + (void)asprintf(&expected_str, "%d\t%s", expected, expected ? strerror(expected) : ""); _test_print(file, line, desc, (actual == expected), "%s", actual_str, "%s", expected_str); free(actual_str); @@ -369,8 +383,8 @@ _test_mach_error(const char* file, long line, const char* desc, { char* actual_str; char* expected_str; - asprintf(&actual_str, "%d %s", actual, actual ? mach_error_string(actual) : ""); - asprintf(&expected_str, "%d %s", expected, expected ? mach_error_string(expected) : ""); + (void)asprintf(&actual_str, "%d %s", actual, actual ? mach_error_string(actual) : ""); + (void)asprintf(&expected_str, "%d %s", expected, expected ? mach_error_string(expected) : ""); _test_print(file, line, desc, (actual == expected), "%s", actual_str, "%s", expected_str); free(actual_str); @@ -416,12 +430,12 @@ test_cferror(const char *desc, CFErrorRef actual, CFIndex expectedCode) if (code != expectedCode) { char buffer[BUFSIZ]; CFStringGetCString(errDesc, buffer, sizeof(buffer), kCFStringEncodingUTF8); - asprintf(&actual_str, "%ld\t%s", code, buffer); + (void)asprintf(&actual_str, "%ld\t%s", code, buffer); } else { - asprintf(&actual_str, "%ld", code); + (void)asprintf(&actual_str, "%ld", code); } - asprintf(&expected_str, "%ld", expectedCode); + (void)asprintf(&expected_str, "%ld", expectedCode); _test_print("", (long) 0, desc, (code == expectedCode), "%s", actual_str, "%s", expected_str); diff --git a/tests/bsdtests.h b/tests/bsdtests.h index e8e292e6e..3437a51ed 100644 --- a/tests/bsdtests.h +++ b/tests/bsdtests.h @@ -111,6 +111,10 @@ void _test_uint32(const char* file, long line, const char* desc, uint32_t actual #define test_uint32(a,b,c) _test_uint32(__SOURCE_FILE__, __LINE__, a, b, c) void test_uint32_format(uint32_t actual, uint32_t expected, const char *format, ...) __printflike(3,4); +void _test_uint32_not(const char* file, long line, const char* desc, uint32_t actual, uint32_t unexpected); +#define test_uint32_not(a,b,c) _test_uint32_not(__SOURCE_FILE__, __LINE__, a, b, c) +void test_uint32_not_format(uint32_t actual, uint32_t unexpected, const char *format, ...) __printflike(3,4); + void _test_int32(const char* file, long line, const char* desc, int32_t actual, int32_t expected); #define test_int32(a,b,c) _test_int32(__SOURCE_FILE__, __LINE__, a, b, c) void test_int32_format(int32_t actual, int32_t expected, const char* format, ...) __printflike(3,4); diff --git a/tests/dispatch_apply.c b/tests/dispatch_apply.c index 86b4bd497..4f80aac71 100644 --- a/tests/dispatch_apply.c +++ b/tests/dispatch_apply.c @@ -21,6 +21,7 @@ #include #include #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include #include #ifdef __ANDROID__ #include @@ -65,6 +66,7 @@ static void busythread(void *ignored) j += i; i += 1; } + (void)j; OSAtomicIncrement32(&busy_threads_finished); } @@ -80,8 +82,16 @@ static void busythread(void *ignored) static void test_apply_contended(dispatch_queue_t dq) { uint32_t activecpu; -#ifdef __linux__ +#if defined(__linux__) || defined(__OpenBSD__) || defined(__FreeBSD__) activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN); + +#if defined(__linux__) && __USE_GNU + cpu_set_t cpuset; + if (pthread_getaffinity_np(pthread_self(), + sizeof(cpu_set_t), + &cpuset) == 0) + activecpu = (uint32_t)CPU_COUNT(&cpuset); +#endif #elif defined(_WIN32) SYSTEM_INFO si; GetSystemInfo(&si); diff --git a/tests/dispatch_io_muxed.c b/tests/dispatch_io_muxed.c index b99b26226..bc7c57cda 100644 --- a/tests/dispatch_io_muxed.c +++ b/tests/dispatch_io_muxed.c @@ -64,7 +64,7 @@ test_file_muxed(void) const char *path_separator = "/"; #endif char *path = NULL; - asprintf(&path, "%s%sdispatchtest_io.XXXXXX", temp_dir, path_separator); + (void)asprintf(&path, "%s%sdispatchtest_io.XXXXXX", temp_dir, path_separator); dispatch_fd_t fd = mkstemp(path); if (fd == -1) { test_errno("mkstemp", errno, 0); diff --git a/tests/dispatch_io_net.c b/tests/dispatch_io_net.c index 1e722a840..560c93262 100644 --- a/tests/dispatch_io_net.c +++ b/tests/dispatch_io_net.c @@ -55,7 +55,7 @@ extern char **environ; #endif #endif -#if defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32) +#if defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32) || defined(__OpenBSD__) #define _NSGetExecutablePath(ef,bs) (*(bs)=(size_t)snprintf(ef,*(bs),"%s",argv[0]),0) #endif diff --git a/tests/dispatch_io_pipe.c b/tests/dispatch_io_pipe.c index f94438483..90c212236 100644 --- a/tests/dispatch_io_pipe.c +++ b/tests/dispatch_io_pipe.c @@ -90,6 +90,10 @@ test_get_pipe_buffer_size(int kind) CloseHandle(write_handle); }); return size; +#elif defined(__FreeBSD__) + // buffer size of blocking and non-blocking pipes is different on FreeBSD + (void)kind; + return PIPE_BUF; #else (void)kind; static dispatch_once_t once; diff --git a/tests/dispatch_overcommit.c b/tests/dispatch_overcommit.c index 4c187411d..d2fca3b5c 100644 --- a/tests/dispatch_overcommit.c +++ b/tests/dispatch_overcommit.c @@ -48,7 +48,7 @@ main(void) int i; for (i = 0; i < final; ++i) { char* name; - asprintf(&name, "test.overcommit.%d", i); + (void)asprintf(&name, "test.overcommit.%d", i); dispatch_queue_t queue = dispatch_queue_create(name, NULL); test_ptr_notnull("dispatch_queue_create", queue); diff --git a/tests/dispatch_read2.c b/tests/dispatch_read2.c index 0e4535961..d8ebba76c 100644 --- a/tests/dispatch_read2.c +++ b/tests/dispatch_read2.c @@ -150,7 +150,7 @@ test_read(void) test_stop(); } #else - // investigate what the impact of lack of file cache disabling has + // investigate what the impact of lack of file cache disabling has // for this test #endif size_t size = (size_t)dispatch_test_fd_lseek(fd, 0, SEEK_END); diff --git a/tests/dispatch_select.c b/tests/dispatch_select.c index 6202711cb..8a1147a5b 100644 --- a/tests/dispatch_select.c +++ b/tests/dispatch_select.c @@ -39,7 +39,7 @@ void finish(void* cxt); void stage1(int stage) { -#if defined(_WIN32) +#if defined(_WIN32) || defined(__FreeBSD__) char *path = dispatch_test_get_large_file(); dispatch_fd_t fd = dispatch_test_fd_open(path, O_RDONLY); if (fd == -1) @@ -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); diff --git a/tests/dispatch_test.c b/tests/dispatch_test.c index d84a7b228..5c2aef948 100644 --- a/tests/dispatch_test.c +++ b/tests/dispatch_test.c @@ -33,6 +33,8 @@ #if __has_include() #define HAS_SYS_EVENT_H 1 #include +#elif __has_include() +#include #else #include #endif diff --git a/tests/dispatch_test.h b/tests/dispatch_test.h index 9de95b3c2..99094ebbf 100644 --- a/tests/dispatch_test.h +++ b/tests/dispatch_test.h @@ -21,12 +21,16 @@ #include #include -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) #include #elif defined(_WIN32) #include #endif +#if defined(__FreeBSD__) +#include +#endif + #define test_group_wait(g) do { \ if (dispatch_group_wait(g, dispatch_time(DISPATCH_TIME_NOW, \ 25ull * NSEC_PER_SEC))) { \ diff --git a/tests/dispatch_workqueue.c b/tests/dispatch_workqueue.c new file mode 100644 index 000000000..cce8071d3 --- /dev/null +++ b/tests/dispatch_workqueue.c @@ -0,0 +1,94 @@ +#include +#include "dispatch_test.h" + +#if defined(__linux__) +// For pthread_getaffinity_np() +#include +#elif defined(__FreeBSD__) +// for pthread_getaffinity_np / cpu_set_t +#include +#include +#endif + +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 = 0xa1a1a1; +#if defined(__linux__) || defined(__OpenBSD__) || defined(__FreeBSD__) + activecpu = (uint32_t)sysconf(_SC_NPROCESSORS_ONLN); + +#if defined(__FreeBSD__) || (defined(__linux__) && __USE_GNU) + cpu_set_t cpuset; + if (pthread_getaffinity_np(pthread_self(), + sizeof(cpu_set_t), + &cpuset) == 0) + activecpu = (uint32_t)CPU_COUNT(&cpuset); +#endif + +#elif defined(_WIN32) + SYSTEM_INFO si; + GetSystemInfo(&si); + activecpu = si.dwNumberOfProcessors; +#else + size_t s = sizeof(activecpu); + if (sysctlbyname("hw.activecpu", &activecpu, &s, NULL, 0) != 0) + return 0; +#endif + return activecpu; +} + +struct test_context ctx; + +int +main(void) +{ + uint32_t ncpu = activecpu(); + test_uint32_not("Failed to get CPU count", ncpu, 0); + + 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; +} diff --git a/tests/generic_win_port.c b/tests/generic_win_port.c index ba685a18b..f10acbe79 100644 --- a/tests/generic_win_port.c +++ b/tests/generic_win_port.c @@ -188,7 +188,7 @@ gettimeofday(struct timeval *tp, void *tzp) typedef void (WINAPI *QueryUnbiasedInterruptTimePreciseT)(PULONGLONG); static QueryUnbiasedInterruptTimePreciseT QueryUnbiasedInterruptTimePrecisePtr; -static BOOL +static BOOL WINAPI mach_absolute_time_init(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *lpContext) { // QueryUnbiasedInterruptTimePrecise() is declared in the Windows headers @@ -238,6 +238,7 @@ randomize_name(char *out) } } +#ifndef HAVE_MKSTEMP dispatch_fd_t mkstemp(char *tmpl) { @@ -257,6 +258,7 @@ mkstemp(char *tmpl) errno = EEXIST; return -1; } +#endif void print_winapi_error(const char *function_name, DWORD error) diff --git a/tests/generic_win_port.h b/tests/generic_win_port.h index d693c7453..f7e1d6152 100644 --- a/tests/generic_win_port.h +++ b/tests/generic_win_port.h @@ -7,7 +7,9 @@ #include typedef int kern_return_t; +#ifndef HAVE_PID_T typedef int pid_t; +#endif #if defined(_WIN64) typedef long long ssize_t; @@ -68,8 +70,10 @@ mach_timebase_info(mach_timebase_info_t tbi) return 0; } +#ifndef HAVE_MKSTEMP dispatch_fd_t mkstemp(char *tmpl); +#endif void print_winapi_error(const char *function_name, DWORD error);