lf_queue: add iterator support

Allow range-for loop over an object returned by `pop_all()`
This commit is contained in:
Nekotekina 2019-01-03 18:18:31 +03:00
parent cfdf50dcff
commit 435f60d503
3 changed files with 154 additions and 41 deletions

View file

@ -331,76 +331,187 @@ public:
// Helper type, linked list element // Helper type, linked list element
template <typename T> template <typename T>
class lf_item final class lf_queue_item final
{ {
lf_item* m_link = nullptr; lf_queue_item* m_link = nullptr;
T m_data; T m_data;
template <typename U>
friend class lf_queue_iterator;
template <typename U>
friend class lf_queue_slice;
template <typename U> template <typename U>
friend class lf_queue; friend class lf_queue;
constexpr lf_item() = default; constexpr lf_queue_item() = default;
template <typename... Args> template <typename... Args>
constexpr lf_item(lf_item* link, Args&&... args) constexpr lf_queue_item(lf_queue_item* link, Args&&... args)
: m_link(link) : m_link(link)
, m_data(std::forward<Args>(args)...) , m_data(std::forward<Args>(args)...)
{ {
} }
public: 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)); delete std::exchange(ptr, std::exchange(ptr->m_link, nullptr));
} }
} }
};
// Withdraw all other elements // Forward iterator: non-owning pointer to the list element in lf_queue_slice<>
std::unique_ptr<lf_item<T>> pop_all() template <typename T>
class lf_queue_iterator
{
lf_queue_item<T>* m_ptr = nullptr;
template <typename U>
friend class lf_queue_slice;
public:
constexpr lf_queue_iterator() = default;
bool operator ==(const lf_queue_iterator& rhs) const
{ {
return std::unique_ptr<lf_item<T>>(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 <typename T>
class lf_queue_slice
{
lf_queue_item<T>* m_head = nullptr;
template <typename U>
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<T> begin() const
{
lf_queue_iterator<T> result;
result.m_ptr = m_head;
return result;
}
lf_queue_iterator<T> 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 <typename T> template <typename T>
class lf_queue class lf_queue
{ {
// Elements are added by replacing m_head // Elements are added by replacing m_head
atomic_t<lf_item<T>*> m_head = nullptr; atomic_t<lf_queue_item<T>*> m_head = nullptr;
// Extract all elements and reverse element order (FILO to FIFO) // Extract all elements and reverse element order (FILO to FIFO)
lf_item<T>* reverse() noexcept lf_queue_item<T>* reverse() noexcept
{ {
if (lf_item<T>* head = m_head.load() ? m_head.exchange(nullptr) : nullptr) if (auto* head = m_head.load() ? m_head.exchange(nullptr) : nullptr)
{ {
if (lf_item<T>* prev = head->m_link) if (auto* prev = head->m_link)
{ {
head->m_link = nullptr; head->m_link = nullptr;
do do
{ {
lf_item<T>* pprev = prev->m_link; auto* pprev = prev->m_link;
prev->m_link = head; prev->m_link = head;
head = std::exchange(prev, pprev); head = std::exchange(prev, pprev);
} while (prev); }
while (prev);
} }
return head; return head;
@ -420,8 +531,8 @@ public:
template <typename... Args> template <typename... Args>
void push(Args&&... args) void push(Args&&... args)
{ {
lf_item<T>* old = m_head.load(); auto* old = m_head.load();
lf_item<T>* item = new lf_item<T>(old, std::forward<Args>(args)...); auto* item = new lf_queue_item<T>(old, std::forward<Args>(args)...);
while (!m_head.compare_exchange(old, item)) while (!m_head.compare_exchange(old, item))
{ {
@ -429,35 +540,37 @@ public:
} }
} }
// Withdraw the list // Withdraw the list, supports range-for loop: for (auto&& x : y.pop_all()) ...
std::unique_ptr<lf_item<T>> pop_all() lf_queue_slice<T> pop_all()
{ {
return std::unique_ptr<lf_item<T>>(reverse()); lf_queue_slice<T> 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 <typename F> template <typename F>
std::size_t apply(F&& func) std::size_t apply(F&& func)
{ {
std::size_t count = 0; std::size_t count = 0;
for (std::unique_ptr<lf_item<T>> ptr(reverse()); ptr; ptr = ptr->pop_all(), count++) for (auto slice = pop_all(); slice; slice.pop_front())
{ {
std::invoke(std::forward<F>(func), ptr->m_data); std::invoke(std::forward<F>(func), *slice);
} }
return count; return count;
} }
// apply_all() overload for callable template argument // apply() overload for callable template argument
template <auto F> template <auto F>
std::size_t apply() std::size_t apply()
{ {
std::size_t count = 0; std::size_t count = 0;
for (std::unique_ptr<lf_item<T>> 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; return count;

View file

@ -246,9 +246,9 @@ error_code cellSysutilCheckCallback(ppu_thread& ppu)
const auto cbm = fxm::get_always<sysutil_cb_manager>(); const auto cbm = fxm::get_always<sysutil_cb_manager>();
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 // Currently impossible
return not_an_error(res); return not_an_error(res);

View file

@ -162,7 +162,7 @@ struct vdec_context final
std::unique_lock cv_lock(in_cv); 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) if (!cmds)
{ {
@ -170,7 +170,7 @@ struct vdec_context final
continue; continue;
} }
if (std::get_if<vdec_start_seq_t>(&cmds->get())) if (std::get_if<vdec_start_seq_t>(&*cmds))
{ {
avcodec_flush_buffers(ctx); avcodec_flush_buffers(ctx);
@ -179,7 +179,7 @@ struct vdec_context final
next_dts = 0; next_dts = 0;
cellVdec.trace("Start sequence..."); cellVdec.trace("Start sequence...");
} }
else if (auto* cmd = std::get_if<vdec_cmd>(&cmds->get())) else if (auto* cmd = std::get_if<vdec_cmd>(&*cmds))
{ {
AVPacket packet{}; AVPacket packet{};
packet.pos = -1; packet.pos = -1;
@ -381,7 +381,7 @@ struct vdec_context final
in_cv.wait(cv_lock, 1000); in_cv.wait(cv_lock, 1000);
} }
} }
else if (auto* frc = std::get_if<CellVdecFrameRate>(&cmds->get())) else if (auto* frc = std::get_if<CellVdecFrameRate>(&*cmds))
{ {
frc_set = *frc; frc_set = *frc;
} }