diff --git a/Sources/CSFBAudioEngine/Player/AudioPlayer.h b/Sources/CSFBAudioEngine/Player/AudioPlayer.h index 98ba548d..9a74a1b9 100644 --- a/Sources/CSFBAudioEngine/Player/AudioPlayer.h +++ b/Sources/CSFBAudioEngine/Player/AudioPlayer.h @@ -11,7 +11,9 @@ #import #import #import +#if defined(__has_include) && __has_include() #import +#endif /* defined(__has_include) && __has_include() */ #import #import @@ -64,12 +66,20 @@ class AudioPlayer final { mutable CXXUnfairLock::UnfairLock queuedDecodersLock_; /// Thread used for decoding +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L std::jthread decodingThread_; +#else + std::thread decodingThread_; +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ /// Dispatch semaphore used for communication with the decoding thread dispatch_semaphore_t decodingSemaphore_ {nil}; /// Thread used for event processing +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L std::jthread eventThread_; +#else + std::thread eventThread_; +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ /// Dispatch semaphore used for communication with the event processing thread dispatch_semaphore_t eventSemaphore_ {nil}; @@ -197,6 +207,12 @@ class AudioPlayer final { isMuted = 1u << 2, /// The ring buffer needs to be drained during the next render cycle drainRequired = 1u << 3, +#if !(defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L) + /// The decoding thread should exit + stopDecodingThread = 1u << 4, + /// The event thread should exit + stopEventThread = 1u << 5, +#endif /* !(defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L) */ }; // MARK: - Decoding diff --git a/Sources/CSFBAudioEngine/Player/AudioPlayer.mm b/Sources/CSFBAudioEngine/Player/AudioPlayer.mm index 082bda87..8902dd23 100644 --- a/Sources/CSFBAudioEngine/Player/AudioPlayer.mm +++ b/Sources/CSFBAudioEngine/Player/AudioPlayer.mm @@ -394,8 +394,13 @@ T fetch_update(std::atomic& atom, Func&& func, std::memory_order order = std: // Launch the decoding and event processing threads try { +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L decodingThread_ = std::jthread(std::bind_front(&SFB::AudioPlayer::ProcessDecoders, this)); eventThread_ = std::jthread(std::bind_front(&SFB::AudioPlayer::SequenceAndProcessEvents, this)); +#else + decodingThread_ = std::thread(&SFB::AudioPlayer::ProcessDecoders, this); + eventThread_ = std::thread(&SFB::AudioPlayer::SequenceAndProcessEvents, this); +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ } catch(const std::exception& e) { os_log_error(log_, "Unable to create thread: %{public}s", e.what()); throw; @@ -441,6 +446,7 @@ T fetch_update(std::atomic& atom, Func&& func, std::memory_order order = std: Stop(); +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L // Register a stop callback for the decoding thread std::stop_callback decodingThreadStopCallback(decodingThread_.get_stop_token(), [this] { dispatch_semaphore_signal(decodingSemaphore_); @@ -448,12 +454,20 @@ T fetch_update(std::atomic& atom, Func&& func, std::memory_order order = std: // Issue a stop request to the decoding thread and wait for it to exit decodingThread_.request_stop(); +#else + // Stop the decoding thread + flags_.fetch_or(static_cast(Flags::stopDecodingThread), std::memory_order_acq_rel); + dispatch_semaphore_signal(decodingSemaphore_); +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ + try { decodingThread_.join(); } catch(const std::exception& e) { os_log_error(log_, "Unable to join decoding thread: %{public}s", e.what()); } + +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L // Register a stop callback for the event processing thread std::stop_callback eventThreadStopCallback(eventThread_.get_stop_token(), [this] { dispatch_semaphore_signal(eventSemaphore_); @@ -461,6 +475,12 @@ T fetch_update(std::atomic& atom, Func&& func, std::memory_order order = std: // Issue a stop request to the event processing thread and wait for it to exit eventThread_.request_stop(); +#else + // Stop the event processing thread + flags_.fetch_or(static_cast(Flags::stopEventThread), std::memory_order_acq_rel); + dispatch_semaphore_signal(eventSemaphore_); +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ + try { eventThread_.join(); } catch(const std::exception& e) { @@ -932,7 +952,11 @@ T fetch_update(std::atomic& atom, Func&& func, std::memory_order order = std: // MARK: - Decoding +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L void SFB::AudioPlayer::ProcessDecoders(std::stop_token stoken) noexcept +#else +void SFB::AudioPlayer::ProcessDecoders() noexcept +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ { pthread_setname_np("AudioPlayer.Decoding"); pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0); @@ -944,7 +968,16 @@ T fetch_update(std::atomic& atom, Func&& func, std::memory_order order = std: // Whether there is a mismatch between the rendering format and the next decoder's processing format auto formatMismatch = false; - while(!stoken.stop_requested()) { + // Returns true if the decoding thread should exit + const auto stop_requested = [&] { +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L + return stoken.stop_requested(); +#else + return (flags_.load(std::memory_order_acquire) & static_cast(Flags::stopDecodingThread)); +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ + }; + + while(!stop_requested()) { // The decoder state being processed DecoderState *decoderState = nullptr; auto ringBufferStale = false; @@ -1339,14 +1372,27 @@ T fetch_update(std::atomic& atom, Func&& func, std::memory_order order = std: // MARK: - Event Processing +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L void SFB::AudioPlayer::SequenceAndProcessEvents(std::stop_token stoken) noexcept +#else +void SFB::AudioPlayer::SequenceAndProcessEvents() noexcept +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ { pthread_setname_np("AudioPlayer.Events"); pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0); os_log_debug(log_, " event processing thread starting", this); - while(!stoken.stop_requested()) { + // Returns true if the event processing thread should exit + const auto stop_requested = [&] { +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L + return stoken.stop_requested(); +#else + return (flags_.load(std::memory_order_acquire) & static_cast(Flags::stopEventThread)); +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ + }; + + while(!stop_requested()) { DecodingEventCommand decodingEventCommand; uint64_t decodingEventIdentificationNumber; auto gotDecodingEvent = decodingEvents_.ReadValues(decodingEventCommand, decodingEventIdentificationNumber);