diff --git a/Sources/CSFBAudioEngine/Player/AudioPlayer.h b/Sources/CSFBAudioEngine/Player/AudioPlayer.h index d38953ee..db92946a 100644 --- a/Sources/CSFBAudioEngine/Player/AudioPlayer.h +++ b/Sources/CSFBAudioEngine/Player/AudioPlayer.h @@ -25,7 +25,9 @@ #import #import #import +#if defined(__has_include) && __has_include() #import +#endif /* defined(__has_include) && __has_include() */ #import #import @@ -67,12 +69,20 @@ class AudioPlayer final { mutable mtx::UnfairMutex queuedDecodersMutex_; /// 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 dsema::Semaphore decodingSemaphore_{0}; /// 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 dsema::Semaphore eventSemaphore_{0}; @@ -209,6 +219,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) */ }; // Enable bitmask operations for `Flags` @@ -242,7 +258,11 @@ class AudioPlayer final { /// Dequeues and processes decoders from the decoder queue /// - note: This is the thread entry point for the decoding thread +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L void processDecoders(std::stop_token stoken) noexcept; +#else + void processDecoders() noexcept; +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ /// Writes an error event to `decodingEvents_` and signals `eventSemaphore_` void submitDecodingErrorEvent(NSError *error) noexcept; @@ -277,7 +297,11 @@ class AudioPlayer final { /// Reads and sequences event headers from `decodingEvents_` and `renderingEvents_` for processing in order /// - note: This is the thread entry point for the event processing thread +#if defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L void sequenceAndProcessEvents(std::stop_token stoken) noexcept; +#else + void sequenceAndProcessEvents() noexcept; +#endif /* defined(__cpp_lib_jthread) && __cpp_lib_jthread >= 201911L */ /// Reads and processes an event payload from `decodingEvents_` bool processDecodingEvent(DecodingEventCommand command) noexcept; diff --git a/Sources/CSFBAudioEngine/Player/AudioPlayer.mm b/Sources/CSFBAudioEngine/Player/AudioPlayer.mm index 6c079445..ab5542be 100644 --- a/Sources/CSFBAudioEngine/Player/AudioPlayer.mm +++ b/Sources/CSFBAudioEngine/Player/AudioPlayer.mm @@ -496,8 +496,13 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re // 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; @@ -557,23 +562,37 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re clearDecoderQueue(); cancelActiveDecoders(); +#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] { decodingSemaphore_.signal(); }); // 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] { eventSemaphore_.signal(); }); // 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) { @@ -1151,7 +1170,12 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re // MARK: - Decoding -void sfb::AudioPlayer::processDecoders(std::stop_token stoken) noexcept { +#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); @@ -1162,7 +1186,16 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re // 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; @@ -1629,13 +1662,27 @@ Flags clearFlags(Flags flags, std::memory_order order = std::memory_order_acq_re // MARK: - Event Processing -void sfb::AudioPlayer::sequenceAndProcessEvents(std::stop_token stoken) noexcept { +#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_.readAll(decodingEventCommand, decodingEventIdentificationNumber);