diff options
Diffstat (limited to 'gdbsupport/work-queue.h')
-rw-r--r-- | gdbsupport/work-queue.h | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/gdbsupport/work-queue.h b/gdbsupport/work-queue.h new file mode 100644 index 0000000..66f073d --- /dev/null +++ b/gdbsupport/work-queue.h @@ -0,0 +1,96 @@ +/* Synchronized work queue. + + Copyright (C) 2025 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef GDBSUPPORT_WORK_QUEUE_H +#define GDBSUPPORT_WORK_QUEUE_H + +#include "gdbsupport/iterator-range.h" + +namespace gdb +{ + +/* Implementation of a thread-safe work queue. + + The work items are specified by two iterators of type RandomIt. + + BATCH_SIZE is the number of work items to pop in a batch. */ + +template<typename RandomIt, std::size_t batch_size> +class work_queue +{ +public: + /* The work items are specified by the range `[first, last[`. */ + work_queue (const RandomIt first, const RandomIt last) noexcept + : m_next (first), + m_last (last) + { + gdb_assert (first <= last); + } + + DISABLE_COPY_AND_ASSIGN (work_queue); + + /* Pop a batch of work items. + + The return value is an iterator range delimiting the work items. */ + iterator_range<RandomIt> pop_batch () noexcept + { + for (;;) + { + /* Grab a snapshot of M_NEXT. */ + auto next = m_next.load (); + gdb_assert (next <= m_last); + + /* The number of items remaining in the queue. */ + const auto n_remaining = static_cast<std::size_t> (m_last - next); + + /* Are we done? */ + if (n_remaining == 0) + return { m_last, m_last }; + + /* The batch size is proportional to the number of items remaining in + the queue. We do this to try to stike a balance, avoiding + synchronization overhead when there are many items to process at the + start, and avoiding workload imbalance when there are few items to + process at the end. */ + const auto this_batch_size = std::min (batch_size, n_remaining); + + /* The range of items in this batch. */ + const auto this_batch_first = next; + const auto this_batch_last = next + this_batch_size; + + /* Update M_NEXT. If the current value of M_NEXT doesn't match NEXT, it + means another thread updated it concurrently, restart. */ + if (!m_next.compare_exchange_weak (next, this_batch_last)) + continue; + + return { this_batch_first, this_batch_last }; + } + } + +private: + /* The next work item to hand out. */ + std::atomic<RandomIt> m_next; + + /* The end of the work item range. */ + RandomIt m_last; +}; + +} /* namespace gdb */ + +#endif /* GDBSUPPORT_WORK_QUEUE_H */ |