proxygen
folly::ThreadedRepeatingFunctionRunner Class Referencefinal

#include <ThreadedRepeatingFunctionRunner.h>

Public Types

using RepeatingFn = folly::Function< std::chrono::milliseconds() noexcept >
 

Public Member Functions

 ThreadedRepeatingFunctionRunner ()
 
 ~ThreadedRepeatingFunctionRunner ()
 
void stop ()
 
void add (std::string name, RepeatingFn f, std::chrono::milliseconds initialSleep=std::chrono::milliseconds(0))
 
size_t size () const
 

Private Member Functions

bool stopImpl ()
 
bool waitFor (std::chrono::milliseconds duration) noexcept
 
void executeInLoop (RepeatingFn, std::chrono::milliseconds initialSleep) noexcept
 

Private Attributes

std::mutex stopMutex_
 
bool stopping_ {false}
 
std::condition_variable stopCv_
 
std::vector< std::thread > threads_
 

Detailed Description

For each function fn you add to this object, fn will be run in a loop in its own thread, with the thread sleeping between invocations of fn for the duration returned by fn's previous run.

To clean up these threads, invoke stop(), which will interrupt sleeping threads. stop() will wait for already-running functions to return.

== Alternatives ==

If you want to multiplex multiple functions on the same thread, you can either use EventBase with AsyncTimeout objects, or FunctionScheduler for a slightly simpler API.

== Thread-safety ==

This type follows the common rule that: (1) const member functions are safe to call concurrently with const member functions, but (2) non-const member functions are not safe to call concurrently with any member functions.

== Pitfalls ==

Threads and classes don't mix well in C++, so you have to be very careful if you want to have ThreadedRepeatingFunctionRunner as a member of your class. A reasonable pattern looks like this:

// Your class must be final because inheriting from a class with // threads can cause all sorts of subtle issues: // - Your base class might start threads that attempt to access derived // class state before that state was constructed. // - Your base class's destructor will only be able to stop threads // after the derived class state was destroyed – and that state // might be accessed by the threads. // In short, any derived class would have to do work to manage the // threads itself, which makes inheritance a poor means of composition. struct MyClass final { // Note that threads are NOT added in the constructor, for two reasons: // // (1) If you first added some threads, and then had additional // initialization (e.g. derived class constructors), this might // not be fully constructed by the time the function threads // started running, causing heisenbugs. // // (2) If your constructor threw after thread creation, the class // destructor would not be invoked, potentially leaving the // threads running too long. // // It is much safer to have explicit two-step initialization, or to // lazily add threads the first time they are needed. MyClass() : count_(0) {}

// You must stop the threads as early as possible in the destruction // process (or even before). If MyClass had derived classes, the final // derived class MUST always call stop() as the first thing in its // destructor – otherwise, the worker threads might access already- // destroyed state. ~MyClass() { threads_.stop(); // Stop threads BEFORE destroying any state they use. }

// See the constructor for why two-stage initialization is preferred. void init() { threads_.add(bind(&MyClass::incrementCount, this)); }

std::chrono::milliseconds incrementCount() { ++count_; return 10; }

private: std::atomic<int> count_; // CAUTION: Declare last since the threads access other members of this. ThreadedRepeatingFunctionRunner threads_; };

Definition at line 104 of file ThreadedRepeatingFunctionRunner.h.

Member Typedef Documentation

using folly::ThreadedRepeatingFunctionRunner::RepeatingFn = folly::Function<std::chrono::milliseconds() noexcept>

Definition at line 107 of file ThreadedRepeatingFunctionRunner.h.

Constructor & Destructor Documentation

folly::ThreadedRepeatingFunctionRunner::ThreadedRepeatingFunctionRunner ( )

Definition at line 24 of file ThreadedRepeatingFunctionRunner.cpp.

24 {}
folly::ThreadedRepeatingFunctionRunner::~ThreadedRepeatingFunctionRunner ( )

Definition at line 26 of file ThreadedRepeatingFunctionRunner.cpp.

References stopImpl().

26  {
27  if (stopImpl()) {
28  LOG(ERROR)
29  << "ThreadedRepeatingFunctionRunner::stop() should already have been "
30  << "called, since we are now in the Runner's destructor. This is "
31  << "because it means that its threads may be accessing object state "
32  << "that was already destroyed -- e.g. members that were declared "
33  << "after the ThreadedRepeatingFunctionRunner.";
34  }
35 }

Member Function Documentation

void folly::ThreadedRepeatingFunctionRunner::add ( std::string  name,
RepeatingFn  f,
std::chrono::milliseconds  initialSleep = std::chrono::milliseconds(0) 
)

Run your noexcept function f in a background loop, sleeping between calls for a duration returned by f. Optionally waits for initialSleep before calling f for the first time. Names the thread using up to the first 15 chars of name.

DANGER: If a non-final class has a ThreadedRepeatingFunctionRunner member (which, by the way, must be declared last in the class), then you must not call add() in your constructor. Otherwise, your thread risks accessing uninitialized data belonging to a child class. To avoid this design bug, prefer to use two-stage initialization to start your threads.

Definition at line 56 of file ThreadedRepeatingFunctionRunner.cpp.

References executeInLoop(), folly::gen::move, folly::setThreadName(), and threads_.

59  {
60  threads_.emplace_back([name = std::move(name),
61  fn = std::move(fn),
62  initialSleep,
63  this]() mutable {
65  executeInLoop(std::move(fn), initialSleep);
66  });
67 }
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
const char * name
Definition: http_parser.c:437
void executeInLoop(RepeatingFn, std::chrono::milliseconds initialSleep) noexcept
bool setThreadName(std::thread::id tid, StringPiece name)
Definition: ThreadName.cpp:109
void folly::ThreadedRepeatingFunctionRunner::executeInLoop ( RepeatingFn  fn,
std::chrono::milliseconds  initialSleep 
)
privatenoexcept

Definition at line 79 of file ThreadedRepeatingFunctionRunner.cpp.

References waitFor().

Referenced by add(), and size().

81  {
82  auto duration = initialSleep;
83  while (waitFor(duration)) {
84  duration = fn();
85  }
86 }
bool waitFor(std::chrono::milliseconds duration) noexcept
size_t folly::ThreadedRepeatingFunctionRunner::size ( ) const
inline

Definition at line 138 of file ThreadedRepeatingFunctionRunner.h.

References executeInLoop(), folly::pushmi::__adl::noexcept(), stopImpl(), threads_, and waitFor().

138  {
139  return threads_.size();
140  }
void folly::ThreadedRepeatingFunctionRunner::stop ( )

Ideally, you will call this before initiating the destruction of the host object. Otherwise, this should be the first thing in the destruction sequence. If it comes any later, worker threads may access class state that had already been destroyed.

Definition at line 37 of file ThreadedRepeatingFunctionRunner.cpp.

References stopImpl().

bool folly::ThreadedRepeatingFunctionRunner::stopImpl ( )
private

Definition at line 41 of file ThreadedRepeatingFunctionRunner.cpp.

References folly::lock(), stopCv_, stopMutex_, stopping_, folly::pushmi::detail::t, and threads_.

Referenced by size(), stop(), and ~ThreadedRepeatingFunctionRunner().

41  {
42  {
43  std::unique_lock<std::mutex> lock(stopMutex_);
44  if (stopping_) {
45  return false; // Do nothing if stop() is called twice.
46  }
47  stopping_ = true;
48  }
49  stopCv_.notify_all();
50  for (auto& t : threads_) {
51  t.join();
52  }
53  return true;
54 }
auto lock(Synchronized< D, M > &synchronized, Args &&...args)
bool folly::ThreadedRepeatingFunctionRunner::waitFor ( std::chrono::milliseconds  duration)
privatenoexcept

Definition at line 69 of file ThreadedRepeatingFunctionRunner.cpp.

References folly::lock(), now(), stopCv_, stopMutex_, and stopping_.

Referenced by executeInLoop(), and size().

70  {
71  using clock = std::chrono::steady_clock;
72  const auto deadline = clock::now() + duration;
73  std::unique_lock<std::mutex> lock(stopMutex_);
74  stopCv_.wait_until(
75  lock, deadline, [&] { return stopping_ || clock::now() > deadline; });
76  return !stopping_;
77 }
std::chrono::steady_clock::time_point now()
auto lock(Synchronized< D, M > &synchronized, Args &&...args)

Member Data Documentation

std::condition_variable folly::ThreadedRepeatingFunctionRunner::stopCv_
private

Definition at line 157 of file ThreadedRepeatingFunctionRunner.h.

Referenced by stopImpl(), and waitFor().

std::mutex folly::ThreadedRepeatingFunctionRunner::stopMutex_
private

Definition at line 155 of file ThreadedRepeatingFunctionRunner.h.

Referenced by stopImpl(), and waitFor().

bool folly::ThreadedRepeatingFunctionRunner::stopping_ {false}
private

Definition at line 156 of file ThreadedRepeatingFunctionRunner.h.

Referenced by stopImpl(), and waitFor().

std::vector<std::thread> folly::ThreadedRepeatingFunctionRunner::threads_
private

Definition at line 159 of file ThreadedRepeatingFunctionRunner.h.

Referenced by add(), size(), and stopImpl().


The documentation for this class was generated from the following files: