diff --git a/include/boost/corosio/native/detail/iocp/win_mutex.hpp b/include/boost/corosio/native/detail/iocp/win_mutex.hpp index e83e235d..650493fa 100644 --- a/include/boost/corosio/native/detail/iocp/win_mutex.hpp +++ b/include/boost/corosio/native/detail/iocp/win_mutex.hpp @@ -27,6 +27,10 @@ namespace boost::corosio::detail { Each call to `lock()` or successful `try_lock()` must be balanced by a corresponding call to `unlock()`. + When disabled via `set_enabled(false)`, all locking operations + become no-ops. This supports single-threaded (lockless) mode + where cross-thread posting is undefined behavior. + Satisfies the Lockable named requirement and is compatible with `std::lock_guard`, `std::unique_lock`, and `std::scoped_lock`. */ @@ -46,23 +50,29 @@ class win_mutex win_mutex(win_mutex const&) = delete; win_mutex& operator=(win_mutex const&) = delete; + void set_enabled(bool v) noexcept { enabled_ = v; } + bool enabled() const noexcept { return enabled_; } + void lock() noexcept { - ::EnterCriticalSection(&cs_); + if (enabled_) + ::EnterCriticalSection(&cs_); } void unlock() noexcept { - ::LeaveCriticalSection(&cs_); + if (enabled_) + ::LeaveCriticalSection(&cs_); } bool try_lock() noexcept { - return ::TryEnterCriticalSection(&cs_) != 0; + return !enabled_ || ::TryEnterCriticalSection(&cs_) != 0; } private: ::CRITICAL_SECTION cs_; + bool enabled_ = true; }; } // namespace boost::corosio::detail diff --git a/include/boost/corosio/native/detail/iocp/win_scheduler.hpp b/include/boost/corosio/native/detail/iocp/win_scheduler.hpp index efef0914..c4842496 100644 --- a/include/boost/corosio/native/detail/iocp/win_scheduler.hpp +++ b/include/boost/corosio/native/detail/iocp/win_scheduler.hpp @@ -89,6 +89,17 @@ class BOOST_COROSIO_DECL win_scheduler final gqcs_timeout_ms_ = gqcs_timeout_ms; } + /** Enable or disable single-threaded (lockless) mode. + + When enabled, the dispatch mutex becomes a no-op. + Cross-thread post() is undefined behavior. + */ + void configure_single_threaded(bool v) noexcept + { + single_threaded_ = v; + dispatch_mutex_.set_enabled(!v); + } + /** Signal that an overlapped I/O operation is now pending. Coordinates with do_one() via the ready_ CAS protocol. */ void on_pending(overlapped_op* op) const; @@ -112,6 +123,7 @@ class BOOST_COROSIO_DECL win_scheduler final long stop_event_posted_; mutable long dispatch_required_; unsigned long gqcs_timeout_ms_ = 500; + bool single_threaded_ = false; mutable win_mutex dispatch_mutex_; mutable op_queue completed_ops_; diff --git a/src/corosio/src/io_context.cpp b/src/corosio/src/io_context.cpp index 72b74e75..d0b9ff54 100644 --- a/src/corosio/src/io_context.cpp +++ b/src/corosio/src/io_context.cpp @@ -153,8 +153,10 @@ apply_scheduler_options( #endif #if BOOST_COROSIO_HAS_IOCP - static_cast(sched).configure_iocp( - opts.gqcs_timeout_ms); + auto& iocp_sched = static_cast(sched); + iocp_sched.configure_iocp(opts.gqcs_timeout_ms); + if (opts.single_threaded) + iocp_sched.configure_single_threaded(true); #endif (void)sched;