diff --git a/Utilities/lockless.h b/Utilities/lockless.h index ced11f0d1c..ff7f53c630 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -331,76 +331,187 @@ public: // Helper type, linked list element template -class lf_item final +class lf_queue_item final { - lf_item* m_link = nullptr; + lf_queue_item* m_link = nullptr; T m_data; + template + friend class lf_queue_iterator; + + template + friend class lf_queue_slice; + template friend class lf_queue; - constexpr lf_item() = default; + constexpr lf_queue_item() = default; template - constexpr lf_item(lf_item* link, Args&&... args) + constexpr lf_queue_item(lf_queue_item* link, Args&&... args) : m_link(link) , m_data(std::forward(args)...) { } public: - lf_item(const lf_item&) = delete; + lf_queue_item(const lf_queue_item&) = delete; - lf_item& operator=(const lf_item&) = delete; + lf_queue_item& operator=(const lf_queue_item&) = delete; - ~lf_item() + ~lf_queue_item() { - for (lf_item* ptr = m_link; ptr;) + for (lf_queue_item* ptr = m_link; ptr;) { delete std::exchange(ptr, std::exchange(ptr->m_link, nullptr)); } } +}; - // Withdraw all other elements - std::unique_ptr> pop_all() +// Forward iterator: non-owning pointer to the list element in lf_queue_slice<> +template +class lf_queue_iterator +{ + lf_queue_item* m_ptr = nullptr; + + template + friend class lf_queue_slice; + +public: + constexpr lf_queue_iterator() = default; + + bool operator ==(const lf_queue_iterator& rhs) const { - return std::unique_ptr>(std::exchange(m_link, nullptr)); + return m_ptr == rhs.m_ptr; } - [[nodiscard]] T& get() + bool operator !=(const lf_queue_iterator& rhs) const { - return m_data; + return m_ptr != rhs.m_ptr; } - [[nodiscard]] const T& get() const + T& operator *() const { - return m_data; + return m_ptr->m_data; + } + + T* operator ->() const + { + return &m_ptr->m_data; + } + + lf_queue_iterator& operator ++() + { + m_ptr = m_ptr->m_link; + return *this; + } + + lf_queue_iterator operator ++(int) + { + lf_queue_iterator result; + result.m_ptr = m_ptr; + m_ptr = m_ptr->m_link; + return result; } }; -// Full-dynamic multi-producer queue (consumer consumes everything or nothing, thread-safe) +// Owning pointer to the linked list taken from the lf_queue<> +template +class lf_queue_slice +{ + lf_queue_item* m_head = nullptr; + + template + friend class lf_queue; + +public: + constexpr lf_queue_slice() = default; + + lf_queue_slice(const lf_queue_slice&) = delete; + + lf_queue_slice(lf_queue_slice&& r) noexcept + : m_head(r.m_head) + { + r.m_head = nullptr; + } + + lf_queue_slice& operator =(const lf_queue_slice&) = delete; + + lf_queue_slice& operator =(lf_queue_slice&& r) noexcept + { + if (this != &r) + { + delete m_head; + m_head = r.m_head; + r.m_head = nullptr; + } + + return *this; + } + + ~lf_queue_slice() + { + delete m_head; + } + + T& operator *() const + { + return m_head->m_data; + } + + T* operator ->() const + { + return &m_head->m_data; + } + + explicit operator bool() const + { + return m_head != nullptr; + } + + lf_queue_iterator begin() const + { + lf_queue_iterator result; + result.m_ptr = m_head; + return result; + } + + lf_queue_iterator end() const + { + return {}; + } + + lf_queue_slice& pop_front() + { + delete std::exchange(m_head, std::exchange(m_head->m_link, nullptr)); + return *this; + } +}; + +// Linked list-based multi-producer queue (the consumer drains the whole queue at once) template class lf_queue { // Elements are added by replacing m_head - atomic_t*> m_head = nullptr; + atomic_t*> m_head = nullptr; // Extract all elements and reverse element order (FILO to FIFO) - lf_item* reverse() noexcept + lf_queue_item* reverse() noexcept { - if (lf_item* head = m_head.load() ? m_head.exchange(nullptr) : nullptr) + if (auto* head = m_head.load() ? m_head.exchange(nullptr) : nullptr) { - if (lf_item* prev = head->m_link) + if (auto* prev = head->m_link) { head->m_link = nullptr; do { - lf_item* pprev = prev->m_link; - prev->m_link = head; - head = std::exchange(prev, pprev); - } while (prev); + auto* pprev = prev->m_link; + prev->m_link = head; + head = std::exchange(prev, pprev); + } + while (prev); } return head; @@ -420,8 +531,8 @@ public: template void push(Args&&... args) { - lf_item* old = m_head.load(); - lf_item* item = new lf_item(old, std::forward(args)...); + auto* old = m_head.load(); + auto* item = new lf_queue_item(old, std::forward(args)...); while (!m_head.compare_exchange(old, item)) { @@ -429,35 +540,37 @@ public: } } - // Withdraw the list - std::unique_ptr> pop_all() + // Withdraw the list, supports range-for loop: for (auto&& x : y.pop_all()) ... + lf_queue_slice pop_all() { - return std::unique_ptr>(reverse()); + lf_queue_slice result; + result.m_head = reverse(); + return result; } - // Withdraw the list and apply func(data) to each element, return the total length + // Apply func(data) to each element, return the total length template std::size_t apply(F&& func) { std::size_t count = 0; - for (std::unique_ptr> ptr(reverse()); ptr; ptr = ptr->pop_all(), count++) + for (auto slice = pop_all(); slice; slice.pop_front()) { - std::invoke(std::forward(func), ptr->m_data); + std::invoke(std::forward(func), *slice); } return count; } - // apply_all() overload for callable template argument + // apply() overload for callable template argument template std::size_t apply() { std::size_t count = 0; - for (std::unique_ptr> ptr(reverse()); ptr; ptr = ptr->pop_all(), count++) + for (auto slice = pop_all(); slice; slice.pop_front()) { - std::invoke(F, ptr->m_data); + std::invoke(F, *slice); } return count; diff --git a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp index 9818efcb31..22cef83e7d 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysutil.cpp @@ -246,9 +246,9 @@ error_code cellSysutilCheckCallback(ppu_thread& ppu) const auto cbm = fxm::get_always(); - for (auto list = cbm->registered.pop_all(); list; list = list->pop_all()) + for (auto&& func : cbm->registered.pop_all()) { - if (s32 res = list->get()(ppu)) + if (s32 res = func(ppu)) { // Currently impossible return not_an_error(res); diff --git a/rpcs3/Emu/Cell/Modules/cellVdec.cpp b/rpcs3/Emu/Cell/Modules/cellVdec.cpp index 3ddf3570e5..88e3f7841e 100644 --- a/rpcs3/Emu/Cell/Modules/cellVdec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellVdec.cpp @@ -162,7 +162,7 @@ struct vdec_context final std::unique_lock cv_lock(in_cv); - for (auto cmds = in_cmd.pop_all(); !Emu.IsStopped(); cmds ? cmds = cmds->pop_all() : cmds = in_cmd.pop_all()) + for (auto cmds = in_cmd.pop_all(); !Emu.IsStopped(); cmds ? cmds.pop_front() : cmds = in_cmd.pop_all()) { if (!cmds) { @@ -170,7 +170,7 @@ struct vdec_context final continue; } - if (std::get_if(&cmds->get())) + if (std::get_if(&*cmds)) { avcodec_flush_buffers(ctx); @@ -179,7 +179,7 @@ struct vdec_context final next_dts = 0; cellVdec.trace("Start sequence..."); } - else if (auto* cmd = std::get_if(&cmds->get())) + else if (auto* cmd = std::get_if(&*cmds)) { AVPacket packet{}; packet.pos = -1; @@ -381,7 +381,7 @@ struct vdec_context final in_cv.wait(cv_lock, 1000); } } - else if (auto* frc = std::get_if(&cmds->get())) + else if (auto* frc = std::get_if(&*cmds)) { frc_set = *frc; }