diff --git a/src/threadloop.cpp b/src/threadloop.cpp index 3d1918394cbf03d53a225250f64cde27fa0906ff..b1cef7d2349f7d715d9d467288befc22ebfcc1cb 100644 --- a/src/threadloop.cpp +++ b/src/threadloop.cpp @@ -34,27 +34,43 @@ namespace ring { -void ThreadLoop::mainloop() +void +ThreadLoop::mainloop(const std::function<bool()> setup, + const std::function<void()> process, + const std::function<void()> cleanup) { try { - if (setup_()) { - while (running_) - process_(); - cleanup_(); + if (setup()) { + while (state_ == RUNNING) + process(); + cleanup(); } else { RING_ERR("setup failed"); } - } catch (const ThreadLoopException &e) { + } catch (const ThreadLoopException& e) { RING_ERR("%s", e.what()); } } -ThreadLoop::ThreadLoop(const std::function<bool()> &setup, - const std::function<void()> &process, - const std::function<void()> &cleanup) - : setup_(setup), process_(process), cleanup_(cleanup) +ThreadLoop::ThreadLoop(const std::function<bool()>& setup, + const std::function<void()>& process, + const std::function<void()>& cleanup) + : setup_(setup) + , process_(process) + , cleanup_(cleanup) + , thread_() {} +ThreadLoop::ThreadLoop(ThreadLoop&& other) + : setup_(std::move(other.setup_)) + , process_(std::move(other.process_)) + , cleanup_(std::move(other.cleanup_)) + , state_(other.state_.load()) + , thread_(std::move(other.thread_)) +{ + other.state_ = READY; +} + ThreadLoop::~ThreadLoop() { if (isRunning()) { @@ -63,24 +79,35 @@ ThreadLoop::~ThreadLoop() } } -void ThreadLoop::start() +void +ThreadLoop::start() { - if (!running_.exchange(true)) { - // a previous stop() call may be pending - if (thread_.joinable()) - thread_.join(); - thread_ = std::thread(&ThreadLoop::mainloop, this); - } else { - RING_ERR("Thread already started"); + const auto s = state_.load(); + + if (s == RUNNING) { + RING_ERR("already started"); + return; } + + // stop pending but not processed by thread yet? + if (s == STOPPING and thread_.joinable()) { + RING_DBG("stop pending"); + thread_.join(); + } + + state_ = RUNNING; + thread_ = std::thread(&ThreadLoop::mainloop, this, setup_, process_, cleanup_); } -void ThreadLoop::stop() +void +ThreadLoop::stop() { - running_ = false; + if (state_ == RUNNING) + state_ = STOPPING; } -void ThreadLoop::join() +void +ThreadLoop::join() { stop(); if (thread_.joinable()) @@ -93,9 +120,10 @@ void ThreadLoop::exit() throw ThreadLoopException(); } -bool ThreadLoop::isRunning() const +bool +ThreadLoop::isRunning() const noexcept { - return running_; + return thread_.joinable() and state_ == RUNNING; } } // namespace ring diff --git a/src/threadloop.h b/src/threadloop.h index 7ffab94c6c529944973c81c08969a2f84b56e3a1..a15d6d39fe6cf56753dab191ddcb22fb9f75d138 100644 --- a/src/threadloop.h +++ b/src/threadloop.h @@ -29,8 +29,7 @@ * as that of the covered work. */ -#ifndef __THREADLOOP_H__ -#define __THREADLOOP_H__ +#pragma once #include <atomic> #include <thread> @@ -50,17 +49,22 @@ struct ThreadLoopException : public std::runtime_error { class ThreadLoop { public: - ThreadLoop(const std::function<bool()> &setup, - const std::function<void()> &process, - const std::function<void()> &cleanup); + enum ThreadState {READY, RUNNING, STOPPING}; + + ThreadLoop(const std::function<bool()>& setup, + const std::function<void()>& process, + const std::function<void()>& cleanup); + + ThreadLoop(ThreadLoop&&); + ~ThreadLoop(); void start(); - void exit(); void stop(); void join(); - bool isRunning() const; + + bool isRunning() const noexcept; private: // These must be provided by users of ThreadLoop @@ -68,12 +72,12 @@ private: std::function<void()> process_; std::function<void()> cleanup_; - void mainloop(); + void mainloop(const std::function<bool()> setup, + const std::function<void()> process, + const std::function<void()> cleanup); - std::atomic<bool> running_ = {false}; - std::thread thread_ = {}; + std::atomic<ThreadState> state_ {READY}; + std::thread thread_; }; } // namespace ring - -#endif // __THREADLOOP_H__