Rename TaggedThread to TaggedThreadID, drop .name attribute

This renames the class to make it clearer what it does, and drops the
.name attribute from it so that it can cheaply be passed around.  This
then means it can be cheaply passed by value (using std::optionals)
rather than by pointer when specifying a thread.
This commit is contained in:
Jason Rhinelander 2020-06-05 14:04:32 -03:00
parent 44b91534c2
commit 8caab97355
4 changed files with 26 additions and 27 deletions

View File

@ -185,8 +185,8 @@ public:
/// invocation of the jobs. /// invocation of the jobs.
/// ///
/// \param job the callback /// \param job the callback
/// \param thread an optional TaggedThread on which this job must run /// \param thread an optional TaggedThreadID indicating a thread in which this job must run
void add_job(std::function<R()> job, const TaggedThread* thread = nullptr) { void add_job(std::function<R()> job, std::optional<TaggedThreadID> thread = std::nullopt) {
check_not_started(); check_not_started();
if (thread && thread->_id == -1) if (thread && thread->_id == -1)
// There are some special case internal jobs where we allow this, but they use the // There are some special case internal jobs where we allow this, but they use the
@ -208,7 +208,7 @@ public:
/// block for any reason. This is only intended for the case where the completion job is so /// block for any reason. This is only intended for the case where the completion job is so
/// trivial that it will take less time than simply queuing the job to be executed by another /// trivial that it will take less time than simply queuing the job to be executed by another
/// thread. /// thread.
void completion(CompletionFunc comp, const TaggedThread* thread = nullptr) { void completion(CompletionFunc comp, std::optional<TaggedThreadID> thread = std::nullopt) {
check_not_started(); check_not_started();
if (complete) if (complete)
throw std::logic_error("Completion function can only be set once"); throw std::logic_error("Completion function can only be set once");

View File

@ -26,7 +26,7 @@ void LokiMQ::proxy_batch(detail::Batch* batch) {
proxy_skip_one_poll = true; proxy_skip_one_poll = true;
} }
void LokiMQ::job(std::function<void()> f, const TaggedThread* thread) { void LokiMQ::job(std::function<void()> f, std::optional<TaggedThreadID> thread) {
if (thread && thread->_id == -1) if (thread && thread->_id == -1)
throw std::logic_error{"job() cannot be used to queue an in-proxy job"}; throw std::logic_error{"job() cannot be used to queue an in-proxy job"};
auto* b = new Batch<void>; auto* b = new Batch<void>;
@ -107,7 +107,7 @@ void LokiMQ::_queue_timer_job(int timer_id) {
auto it = timer_jobs.find(timer_id); auto it = timer_jobs.find(timer_id);
if (it != timer_jobs.end()) if (it != timer_jobs.end())
it->second.running = false; it->second.running = false;
}, &LokiMQ::run_in_proxy); }, LokiMQ::run_in_proxy);
} }
batches.insert(b); batches.insert(b);
LMQ_TRACE("b: ", b->size().first, ", ", b->size().second, "; thread = ", thread); LMQ_TRACE("b: ", b->size().first, ", ", b->size().second, "; thread = ", thread);
@ -118,7 +118,7 @@ void LokiMQ::_queue_timer_job(int timer_id) {
queue.emplace(static_cast<detail::Batch*>(b), 0); queue.emplace(static_cast<detail::Batch*>(b), 0);
} }
void LokiMQ::add_timer(std::function<void()> job, std::chrono::milliseconds interval, bool squelch, const TaggedThread* thread) { void LokiMQ::add_timer(std::function<void()> job, std::chrono::milliseconds interval, bool squelch, std::optional<TaggedThreadID> thread) {
int th_id = thread ? thread->_id : 0; int th_id = thread ? thread->_id : 0;
if (proxy_thread.joinable()) { if (proxy_thread.joinable()) {
detail::send_control(get_control_socket(), "TIMER", bt_serialize(bt_list{{ detail::send_control(get_control_socket(), "TIMER", bt_serialize(bt_list{{
@ -133,7 +133,7 @@ void LokiMQ::add_timer(std::function<void()> job, std::chrono::milliseconds inte
void LokiMQ::TimersDeleter::operator()(void* timers) { zmq_timers_destroy(&timers); } void LokiMQ::TimersDeleter::operator()(void* timers) { zmq_timers_destroy(&timers); }
TaggedThread LokiMQ::add_tagged_thread(std::string name, std::function<void()> init, std::function<void()> start) { TaggedThreadID LokiMQ::add_tagged_thread(std::string name, std::function<void()> start) {
if (proxy_thread.joinable()) if (proxy_thread.joinable())
throw std::logic_error{"Cannot add tagged threads after calling `start()`"}; throw std::logic_error{"Cannot add tagged threads after calling `start()`"};
@ -151,7 +151,7 @@ TaggedThread LokiMQ::add_tagged_thread(std::string name, std::function<void()> i
return worker_thread(id, name, std::move(start)); return worker_thread(id, name, std::move(start));
}}; }};
return {std::move(name), static_cast<int>(run.worker_id)}; return TaggedThreadID{static_cast<int>(run.worker_id)};
} }
} }

View File

@ -86,13 +86,12 @@ static constexpr size_t MAX_COMMAND_LENGTH = 200;
class CatHelper; class CatHelper;
/// Handle for a tagged thread constructed by add_tagged_thread(...). Not directly constructible, but /// Opaque handle for a tagged thread constructed by add_tagged_thread(...). Not directly
/// is safe to copy. /// constructible, but is safe (and cheap) to copy.
struct TaggedThread { struct TaggedThreadID {
const std::string name;
private: private:
const int _id; const int _id;
TaggedThread(std::string name, int id) : name{std::move(name)}, _id{id} {} explicit constexpr TaggedThreadID(int id) : _id{id} {}
friend class LokiMQ; friend class LokiMQ;
template <typename R> friend class Batch; template <typename R> friend class Batch;
}; };
@ -264,9 +263,9 @@ public:
*/ */
int STARTUP_UMASK = -1; int STARTUP_UMASK = -1;
/// A special TaggedThread value that always refers to the proxy thread; the main use of this is /// A special TaggedThreadID value that always refers to the proxy thread; the main use of this is
/// to direct very simple batch completion jobs to be executed directly in the proxy thread. /// to direct very simple batch completion jobs to be executed directly in the proxy thread.
inline static const TaggedThread run_in_proxy{"_proxy", -1}; inline static constexpr TaggedThreadID run_in_proxy{-1};
/// Writes a message to the logging system; intended mostly for internal use. /// Writes a message to the logging system; intended mostly for internal use.
template <typename... T> template <typename... T>
@ -836,10 +835,10 @@ public:
* \param start - similar to init, but this is called immediately *after* the LokiMQ object has * \param start - similar to init, but this is called immediately *after* the LokiMQ object has
* started up and so can use LokiMQ object functionality. * started up and so can use LokiMQ object functionality.
* *
* \returns a TaggedThread object that can be passed to job(), batch(), or add_timer() to direct * \returns a TaggedThreadID object that can be passed to job(), batch(), or add_timer() to
* the task to the tagged thread. * direct the task to the tagged thread.
*/ */
TaggedThread add_tagged_thread(std::string name, std::function<void()> init = nullptr, std::function<void()> start = nullptr); TaggedThreadID add_tagged_thread(std::string name, std::function<void()> init = nullptr, std::function<void()> start = nullptr);
/** /**
* Sets the number of worker threads reserved for batch jobs. If not explicitly called then * Sets the number of worker threads reserved for batch jobs. If not explicitly called then
@ -1132,7 +1131,7 @@ public:
* \param thread an optional tagged thread in which this job should run. You may *not* pass the * \param thread an optional tagged thread in which this job should run. You may *not* pass the
* proxy thread here. * proxy thread here.
*/ */
void job(std::function<void()> f, const TaggedThread *thread = nullptr); void job(std::function<void()> f, std::optional<TaggedThreadID> = std::nullopt);
/** /**
* Adds a timer that gets scheduled periodically in the job queue. Normally jobs are not * Adds a timer that gets scheduled periodically in the job queue. Normally jobs are not
@ -1143,7 +1142,7 @@ public:
* *
* \param thread specifies a thread (added with add_tagged_thread()) on which this timer must run. * \param thread specifies a thread (added with add_tagged_thread()) on which this timer must run.
*/ */
void add_timer(std::function<void()> job, std::chrono::milliseconds interval, bool squelch = true, const TaggedThread* thread = nullptr); void add_timer(std::function<void()> job, std::chrono::milliseconds interval, bool squelch = true, std::optional<TaggedThreadID> = std::nullopt);
}; };
/// Helper class that slightly simplifies adding commands to a category. /// Helper class that slightly simplifies adding commands to a category.

View File

@ -48,7 +48,7 @@ TEST_CASE("batch jobs to tagged threads", "[tagged][batch]") {
} }
done = false; done = false;
lmq.job([&] { id = std::this_thread::get_id(); done = true; }, &t_abc); lmq.job([&] { id = std::this_thread::get_id(); done = true; }, t_abc);
wait_for([&] { return done.load(); }); wait_for([&] { return done.load(); });
{ {
auto lock = catch_lock(); auto lock = catch_lock();
@ -56,7 +56,7 @@ TEST_CASE("batch jobs to tagged threads", "[tagged][batch]") {
} }
done = false; done = false;
lmq.job([&] { id = std::this_thread::get_id(); done = true; }, &t_def); lmq.job([&] { id = std::this_thread::get_id(); done = true; }, t_def);
wait_for([&] { return done.load(); }); wait_for([&] { return done.load(); });
{ {
auto lock = catch_lock(); auto lock = catch_lock();
@ -74,7 +74,7 @@ TEST_CASE("batch jobs to tagged threads", "[tagged][batch]") {
std::this_thread::sleep_for(50ms); std::this_thread::sleep_for(50ms);
done = false; done = false;
lmq.job([&] { id = std::this_thread::get_id(); done = true; }, &t_abc); lmq.job([&] { id = std::this_thread::get_id(); done = true; }, t_abc);
wait_for([&] { return done.load(); }); wait_for([&] { return done.load(); });
{ {
auto lock = catch_lock(); auto lock = catch_lock();
@ -86,9 +86,9 @@ TEST_CASE("batch jobs to tagged threads", "[tagged][batch]") {
// We can queue up a bunch of jobs which should all happen in order, and all on the abc thread. // We can queue up a bunch of jobs which should all happen in order, and all on the abc thread.
std::vector<int> v; std::vector<int> v;
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
lmq.job([&] { if (std::this_thread::get_id() == id_abc) v.push_back(v.size()); }, &t_abc); lmq.job([&] { if (std::this_thread::get_id() == id_abc) v.push_back(v.size()); }, t_abc);
} }
lmq.job([&] { done = true; }, &t_abc); lmq.job([&] { done = true; }, t_abc);
wait_for([&] { return done.load(); }); wait_for([&] { return done.load(); });
{ {
auto lock = catch_lock(); auto lock = catch_lock();
@ -125,7 +125,7 @@ TEST_CASE("batch job completion on tagged threads", "[tagged][batch-completion]"
for (auto& r : result) for (auto& r : result)
sum += r.get(); sum += r.get();
result_sum = std::this_thread::get_id() == id_abc ? sum : -sum; result_sum = std::this_thread::get_id() == id_abc ? sum : -sum;
}, &t_abc); }, t_abc);
lmq.batch(std::move(batch)); lmq.batch(std::move(batch));
wait_for([&] { return result_sum.load() != -1; }); wait_for([&] { return result_sum.load() != -1; });
{ {
@ -148,7 +148,7 @@ TEST_CASE("timer job completion on tagged threads", "[tagged][timer]") {
std::atomic<int> ticks = 0; std::atomic<int> ticks = 0;
std::atomic<int> abc_ticks = 0; std::atomic<int> abc_ticks = 0;
lmq.add_timer([&] { ticks++; }, 10ms); lmq.add_timer([&] { ticks++; }, 10ms);
lmq.add_timer([&] { if (std::this_thread::get_id() == id_abc) abc_ticks++; }, 10ms, true, &t_abc); lmq.add_timer([&] { if (std::this_thread::get_id() == id_abc) abc_ticks++; }, 10ms, true, t_abc);
wait_for([&] { return ticks.load() > 2 && abc_ticks > 2; }); wait_for([&] { return ticks.load() > 2 && abc_ticks > 2; });
{ {