diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index b4cc587c..d296d93d 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -1,33 +1,49 @@ -set(CLI11_PRECOMPILED OFF CACHE BOOL "do not precompile CLI11") - add_subdirectory(CLI11 EXCLUDE_FROM_ALL) -unset(CLI11_PRECOMPILED CACHE) +# Bundled googletest requires CMake 3.16. Keep XSched's platform tests +# buildable with older toolchains without enabling libipc's own tests. +set(XSCHED_BUILD_TEST_TMP "${BUILD_TEST}") +if(CMAKE_VERSION VERSION_LESS 3.16) + set(BUILD_TEST OFF) +endif() +add_subdirectory(ipc EXCLUDE_FROM_ALL) +set(BUILD_TEST "${XSCHED_BUILD_TEST_TMP}") +unset(XSCHED_BUILD_TEST_TMP) -set(HTTPLIB_INSTALL OFF CACHE BOOL "do not install cpp-httplib") -set(HTTPLIB_USE_OPENSSL_IF_AVAILABLE OFF CACHE BOOL "disable openssl for cpp-httplib") -set(HTTPLIB_USE_ZLIB_IF_AVAILABLE OFF CACHE BOOL "disable zlib for cpp-httplib") -set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF CACHE BOOL "disable brotli for cpp-httplib") - add_subdirectory(cpp-httplib EXCLUDE_FROM_ALL) -unset(HTTPLIB_USE_OPENSSL_IF_AVAILABLE CACHE) -unset(HTTPLIB_USE_ZLIB_IF_AVAILABLE CACHE) -unset(HTTPLIB_USE_BROTLI_IF_AVAILABLE CACHE) -unset(HTTPLIB_INSTALL CACHE) +if(PLATFORM_CUDA) + add_subdirectory(cuxtra EXCLUDE_FROM_ALL) +endif() -add_subdirectory(ipc EXCLUDE_FROM_ALL) +if(BUILD_SERVICE) + if(CMAKE_VERSION VERSION_LESS 3.14) + message(FATAL_ERROR "BUILD_SERVICE requires CMake 3.14 or newer") + endif() -add_subdirectory(cuxtra EXCLUDE_FROM_ALL) + set(CLI11_PRECOMPILED OFF CACHE BOOL "do not precompile CLI11") + add_subdirectory(CLI11 EXCLUDE_FROM_ALL) + unset(CLI11_PRECOMPILED CACHE) + + set(HTTPLIB_INSTALL OFF CACHE BOOL "do not install cpp-httplib") + set(HTTPLIB_USE_OPENSSL_IF_AVAILABLE OFF CACHE BOOL "disable openssl for cpp-httplib") + set(HTTPLIB_USE_ZLIB_IF_AVAILABLE OFF CACHE BOOL "disable zlib for cpp-httplib") + set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF CACHE BOOL "disable brotli for cpp-httplib") + add_subdirectory(cpp-httplib EXCLUDE_FROM_ALL) + unset(HTTPLIB_USE_OPENSSL_IF_AVAILABLE CACHE) + unset(HTTPLIB_USE_ZLIB_IF_AVAILABLE CACHE) + unset(HTTPLIB_USE_BROTLI_IF_AVAILABLE CACHE) + unset(HTTPLIB_INSTALL CACHE) -set(JSONCPP_WITH_TESTS OFF CACHE BOOL "do not build jsoncpp tests") -set(JSONCPP_WITH_POST_BUILD_UNITTEST OFF CACHE BOOL "disable jsoncpp post build unit test") -set(JSONCPP_WITH_PKGCONFIG_SUPPORT OFF CACHE BOOL "disable jsoncpp pkgconfig support") -set(JSONCPP_WITH_CMAKE_PACKAGE OFF CACHE BOOL "disable jsoncpp cmake package") -set(BUILD_SHARED_LIBS OFF CACHE BOOL "do not build jsoncpp shared library") -set(BUILD_OBJECT_LIBS OFF CACHE BOOL "do not build jsoncpp object library") + set(JSONCPP_WITH_TESTS OFF CACHE BOOL "do not build jsoncpp tests") + set(JSONCPP_WITH_POST_BUILD_UNITTEST OFF CACHE BOOL "disable jsoncpp post build unit test") + set(JSONCPP_WITH_PKGCONFIG_SUPPORT OFF CACHE BOOL "disable jsoncpp pkgconfig support") + set(JSONCPP_WITH_CMAKE_PACKAGE OFF CACHE BOOL "disable jsoncpp cmake package") + set(BUILD_SHARED_LIBS OFF CACHE BOOL "do not build jsoncpp shared library") + set(BUILD_OBJECT_LIBS OFF CACHE BOOL "do not build jsoncpp object library") add_subdirectory(jsoncpp EXCLUDE_FROM_ALL) -unset(JSONCPP_WITH_TESTS CACHE) -unset(JSONCPP_WITH_POST_BUILD_UNITTEST CACHE) -unset(JSONCPP_WITH_PKGCONFIG_SUPPORT CACHE) -unset(JSONCPP_WITH_CMAKE_PACKAGE CACHE) -unset(BUILD_SHARED_LIBS CACHE) -unset(BUILD_OBJECT_LIBS CACHE) + unset(JSONCPP_WITH_TESTS CACHE) + unset(JSONCPP_WITH_POST_BUILD_UNITTEST CACHE) + unset(JSONCPP_WITH_PKGCONFIG_SUPPORT CACHE) + unset(JSONCPP_WITH_CMAKE_PACKAGE CACHE) + unset(BUILD_SHARED_LIBS CACHE) + unset(BUILD_OBJECT_LIBS CACHE) -add_subdirectory(ftxui EXCLUDE_FROM_ALL) + add_subdirectory(ftxui EXCLUDE_FROM_ALL) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index e482a121..d8396bbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,14 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.13) project(XSched VERSION 1.2.0) option(SHIM_SOFTLINK "Create softlink for shim library." OFF) option(BUILD_TEST "Build test cases." OFF) +option(BUILD_SERVICE "Build scheduler service and CLI tools." ON) + +if(BUILD_TEST) + enable_testing() +endif() set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_EXTENSIONS OFF) @@ -22,9 +27,12 @@ add_subdirectory(utils) add_subdirectory(protocol) add_subdirectory(sched) add_subdirectory(preempt) -add_subdirectory(service) +if(BUILD_SERVICE) + add_subdirectory(service) +endif() add_subdirectory(platforms) +include(GNUInstallDirs) include(CMakePackageConfigHelpers) configure_package_config_file( diff --git a/preempt/src/sched/executor.cpp b/preempt/src/sched/executor.cpp index 3c12c1e0..86b9056d 100644 --- a/preempt/src/sched/executor.cpp +++ b/preempt/src/sched/executor.cpp @@ -27,7 +27,8 @@ void SchedExecutor::Execute(std::shared_ptr op) { OperationType type = op->Type(); XASSERT(op->Pid() == GetProcessId(), - "operation %ld sent to the wrong process, target: %d, receiver %d", + "operation " FMT_64U " sent to the wrong process, target: " + FMT_PID ", receiver " FMT_PID, op->Id(), op->Pid(), GetProcessId()); if (!executing_.load()) type = kOperationNone; diff --git a/sched/src/scheduler/local.cpp b/sched/src/scheduler/local.cpp index 47ff2a25..9028451e 100644 --- a/sched/src/scheduler/local.cpp +++ b/sched/src/scheduler/local.cpp @@ -74,10 +74,12 @@ void LocalScheduler::RecvEvent(std::shared_ptr event) std::unique_lock lock(op_mtx_); auto it = ops_.find(pid); if (it == ops_.end()) { - XWARN("operation %ld completed of unknown process %d", op_id, pid); + XWARN("operation " FMT_64U " completed of unknown process " + FMT_PID, op_id, pid); return; } - XASSERT(op_id <= it->second.issued_id, "unknown operation %ld completed", op_id); + XASSERT(op_id <= it->second.issued_id, + "unknown operation " FMT_64U " completed", op_id); it->second.completed_id = std::max(it->second.completed_id, op_id); lock.unlock(); op_cv_.notify_all(); diff --git a/utils/include/xsched/utils/common.h b/utils/include/xsched/utils/common.h index c02346c4..05f93311 100644 --- a/utils/include/xsched/utils/common.h +++ b/utils/include/xsched/utils/common.h @@ -87,6 +87,11 @@ #elif defined(__aarch64__) || defined(_M_ARM64) #define ARCH_AARCH64 #define ARCH_STR "aarch64" +#elif defined(__loongarch__) + #define ARCH_LOONGARCH64 + #define ARCH_STR "loongarch64" +#else + #error unsupported architecture #endif // operating system detection diff --git a/utils/include/xsched/utils/lock.h b/utils/include/xsched/utils/lock.h index 73da960c..bfddc26e 100644 --- a/utils/include/xsched/utils/lock.h +++ b/utils/include/xsched/utils/lock.h @@ -78,10 +78,12 @@ class MCSLock : public MutexLock struct alignas(64) MCSNode { - volatile LockStatus flag; - volatile MCSNode *next; + std::atomic flag { kLockWaiting }; + std::atomic next { nullptr }; }; + /// @FIXME: current implementation is one node per-thread, + /// should be one node per-thread per-lock. static thread_local MCSNode me; std::atomic tail_{nullptr}; }; diff --git a/utils/include/xsched/utils/region.h b/utils/include/xsched/utils/region.h index 9014c45e..975f3c36 100644 --- a/utils/include/xsched/utils/region.h +++ b/utils/include/xsched/utils/region.h @@ -141,7 +141,8 @@ class Region uint64_t intersect_begin = std::max(begin, cur_begin); uint64_t intersect_end = std::min(end, cur_end); XASSERT(intersect_begin < intersect_end, - "[%lu, %lu] must intersect with [%lu, %lu]", + "[" FMT_64U ", " FMT_64U "] must intersect with [" + FMT_64U ", " FMT_64U "]", begin, end, cur_begin, cur_end); result[intersect_end] = intersect_begin; diff --git a/utils/src/lock.cpp b/utils/src/lock.cpp index 0c39eab6..90da4f3f 100644 --- a/utils/src/lock.cpp +++ b/utils/src/lock.cpp @@ -1,45 +1,63 @@ #include "xsched/utils/lock.h" #include "xsched/utils/common.h" -#if defined(__linux__) - #if defined(ARCH_X86_64) - #define memory_barrier() asm volatile("pause" ::: "memory") - #elif defined(ARCH_AARCH64) - #define memory_barrier() asm volatile("yield" ::: "memory") - #endif -#elif defined(_WIN32) +#if defined(_WIN32) #include - #define memory_barrier() _mm_pause() #endif using namespace xsched::utils; +namespace +{ + +inline void CpuRelax() +{ +#if defined(_WIN32) + _mm_pause(); +#elif defined(ARCH_X86) || defined(ARCH_X86_64) + asm volatile("pause" ::: "memory"); +#elif defined(ARCH_ARM) || defined(ARCH_AARCH64) + asm volatile("yield" ::: "memory"); +#else + // LoongArch has no userspace pause/yield instruction. The Linux kernel's + // cpu_relax() is a compiler barrier on this architecture as well. + std::atomic_signal_fence(std::memory_order_seq_cst); +#endif +} + +} // namespace + thread_local MCSLock::MCSNode MCSLock::me; void MCSLock::lock() { - MCSNode *tail = nullptr; - me.flag = kLockWaiting; - me.next = nullptr; - tail = tail_.exchange(&me); - if (tail) { - tail->next = &me; - while (me.flag != kLockGranted) { - memory_barrier(); + me.flag.store(kLockWaiting, std::memory_order_relaxed); + me.next.store(nullptr, std::memory_order_relaxed); + + MCSNode *predecessor = tail_.exchange(&me, std::memory_order_acq_rel); + if (predecessor != nullptr) { + predecessor->next.store(&me, std::memory_order_release); + while (me.flag.load(std::memory_order_acquire) != kLockGranted) { + CpuRelax(); } } } void MCSLock::unlock() { - if (!me.next) { - MCSNode *me_ptr = &me; - if (tail_.compare_exchange_strong(me_ptr, nullptr)) { + MCSNode *successor = me.next.load(std::memory_order_acquire); + if (successor == nullptr) { + MCSNode *expected = &me; + if (tail_.compare_exchange_strong(expected, nullptr, + std::memory_order_release, + std::memory_order_relaxed)) { return; } - while (!me.next) { - memory_barrier(); - } + do { + CpuRelax(); + successor = me.next.load(std::memory_order_acquire); + } while (successor == nullptr); } - me.next->flag = kLockGranted; + + successor->flag.store(kLockGranted, std::memory_order_release); }