rippled
Workers.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/core/impl/Workers.h>
21 #include <ripple/basics/PerfLog.h>
22 #include <ripple/beast/core/CurrentThreadName.h>
23 #include <cassert>
24 
25 namespace ripple {
26 
28  Callback& callback,
29  perf::PerfLog* perfLog,
30  std::string const& threadNames,
31  int numberOfThreads)
32  : m_callback (callback)
33  , perfLog_ (perfLog)
34  , m_threadNames (threadNames)
35  , m_allPaused (true)
36  , m_semaphore (0)
37  , m_numberOfThreads (0)
38  , m_activeCount (0)
39  , m_pauseCount (0)
40  , m_runningTaskCount (0)
41 {
42  setNumberOfThreads (numberOfThreads);
43 }
44 
46 {
48 
50 }
51 
52 int Workers::getNumberOfThreads () const noexcept
53 {
54  return m_numberOfThreads;
55 }
56 
57 // VFALCO NOTE if this function is called quickly to reduce then
58 // increase the number of threads, it could result in
59 // more paused threads being created than expected.
60 //
61 void Workers::setNumberOfThreads (int numberOfThreads)
62 {
63  static int instance {0};
64  if (m_numberOfThreads != numberOfThreads)
65  {
66  if (perfLog_)
67  perfLog_->resizeJobs(numberOfThreads);
68 
69  if (numberOfThreads > m_numberOfThreads)
70  {
71  // Increasing the number of working threads
72  int const amount = numberOfThreads - m_numberOfThreads;
73 
74  for (int i = 0; i < amount; ++i)
75  {
76  // See if we can reuse a paused worker
77  Worker* worker = m_paused.pop_front ();
78 
79  if (worker != nullptr)
80  {
81  // If we got here then the worker thread is at [1]
82  // This will unblock their call to wait()
83  //
84  worker->notify ();
85  }
86  else
87  {
88  worker = new Worker (*this, m_threadNames, instance++);
89  m_everyone.push_front (worker);
90  }
91  }
92  }
93  else
94  {
95  // Decreasing the number of working threads
96  int const amount = m_numberOfThreads - numberOfThreads;
97 
98  for (int i = 0; i < amount; ++i)
99  {
100  ++m_pauseCount;
101 
102  // Pausing a thread counts as one "internal task"
103  m_semaphore.notify ();
104  }
105  }
106 
107  m_numberOfThreads = numberOfThreads;
108  }
109 }
110 
112 {
113  setNumberOfThreads (0);
114 
116  m_cv.wait(lk, [this]{return m_allPaused;});
117  lk.unlock();
118 
119  assert (numberOfCurrentlyRunningTasks () == 0);
120 }
121 
123 {
124  m_semaphore.notify ();
125 }
126 
128 {
129  return m_runningTaskCount.load ();
130 }
131 
133 {
134  for (;;)
135  {
136  Worker* const worker = stack.pop_front ();
137 
138  if (worker != nullptr)
139  {
140  // This call blocks until the thread orderly exits
141  delete worker;
142  }
143  else
144  {
145  break;
146  }
147  }
148 }
149 
150 //------------------------------------------------------------------------------
151 
152 Workers::Worker::Worker (Workers& workers, std::string const& threadName,
153  int const instance)
154  : m_workers {workers}
155  , threadName_ {threadName}
156  , instance_ {instance}
157  , wakeCount_ {0}
158  , shouldExit_ {false}
159 {
160  thread_ = std::thread {&Workers::Worker::run, this};
161 }
162 
164 {
165  {
166  std::lock_guard lock {mutex_};
167  ++wakeCount_;
168  shouldExit_ = true;
169  }
170 
171  wakeup_.notify_one();
172  thread_.join();
173 }
174 
176 {
177  std::lock_guard lock {mutex_};
178  ++wakeCount_;
179  wakeup_.notify_one();
180 }
181 
183 {
184  bool shouldExit = true;
185  do
186  {
187  // Increment the count of active workers, and if
188  // we are the first one then reset the "all paused" event
189  //
190  if (++m_workers.m_activeCount == 1)
191  {
192  std::lock_guard lk{m_workers.m_mut};
193  m_workers.m_allPaused = false;
194  }
195 
196  for (;;)
197  {
198  // Put the name back in case the callback changed it
199  beast::setCurrentThreadName (threadName_);
200 
201  // Acquire a task or "internal task."
202  //
203  m_workers.m_semaphore.wait ();
204 
205  // See if there's a pause request. This
206  // counts as an "internal task."
207  //
208  int pauseCount = m_workers.m_pauseCount.load ();
209 
210  if (pauseCount > 0)
211  {
212  // Try to decrement
213  pauseCount = --m_workers.m_pauseCount;
214 
215  if (pauseCount >= 0)
216  {
217  // We got paused
218  break;
219  }
220  else
221  {
222  // Undo our decrement
223  ++m_workers.m_pauseCount;
224  }
225  }
226 
227  // We couldn't pause so we must have gotten
228  // unblocked in order to process a task.
229  //
230  ++m_workers.m_runningTaskCount;
231  m_workers.m_callback.processTask (instance_);
232  --m_workers.m_runningTaskCount;
233  }
234 
235  // Any worker that goes into the paused list must
236  // guarantee that it will eventually block on its
237  // event object.
238  //
239  m_workers.m_paused.push_front (this);
240 
241  // Decrement the count of active workers, and if we
242  // are the last one then signal the "all paused" event.
243  //
244  if (--m_workers.m_activeCount == 0)
245  {
246  std::lock_guard lk{m_workers.m_mut};
247  m_workers.m_allPaused = true;
248  m_workers.m_cv.notify_all();
249  }
250 
251  // Set inactive thread name.
252  beast::setCurrentThreadName ("(" + threadName_ + ")");
253 
254  // [1] We will be here when the paused list is popped
255  //
256  // We block on our condition_variable, wakeup_, a requirement of being
257  // put into the paused list.
258  //
259  // wakeup_ will get signaled by either Worker::notify() or ~Worker.
260  {
261  std::unique_lock <std::mutex> lock {mutex_};
262  wakeup_.wait (lock, [this] {return this->wakeCount_ > 0;});
263 
264  shouldExit = shouldExit_;
265  --wakeCount_;
266  }
267  } while (! shouldExit);
268 }
269 
270 } // ripple
ripple::Workers::m_cv
std::condition_variable m_cv
Definition: Workers.h:171
ripple::Workers::perfLog_
perf::PerfLog * perfLog_
Definition: Workers.h:169
std::string
STL class.
ripple::Workers::deleteWorkers
static void deleteWorkers(beast::LockFreeStack< Worker > &stack)
Definition: Workers.cpp:132
ripple::Workers::m_pauseCount
std::atomic< int > m_pauseCount
Definition: Workers.h:177
ripple::Workers::getNumberOfThreads
int getNumberOfThreads() const noexcept
Retrieve the desired number of threads.
Definition: Workers.cpp:52
ripple::Workers::m_paused
beast::LockFreeStack< Worker, PausedTag > m_paused
Definition: Workers.h:180
ripple::Workers::m_everyone
beast::LockFreeStack< Worker > m_everyone
Definition: Workers.h:179
ripple::Workers::setNumberOfThreads
void setNumberOfThreads(int numberOfThreads)
Set the desired number of threads.
Definition: Workers.cpp:61
std::lock_guard
STL class.
ripple::Workers::Worker::~Worker
~Worker()
Definition: Workers.cpp:163
ripple::perf::PerfLog
Singleton class that maintains performance counters and optionally writes Json-formatted data to a di...
Definition: PerfLog.h:44
ripple::Workers::Worker::Worker
Worker(Workers &workers, std::string const &threadName, int const instance)
Definition: Workers.cpp:152
ripple::Workers::Callback
Called to perform tasks as needed.
Definition: Workers.h:44
ripple::Workers::m_threadNames
std::string m_threadNames
Definition: Workers.h:170
ripple::Workers::m_numberOfThreads
int m_numberOfThreads
Definition: Workers.h:175
ripple::Workers::~Workers
~Workers()
Definition: Workers.cpp:45
ripple::Workers::m_runningTaskCount
std::atomic< int > m_runningTaskCount
Definition: Workers.h:178
std::thread
STL class.
std::atomic::load
T load(T... args)
ripple::perf::PerfLog::resizeJobs
virtual void resizeJobs(int const resize)=0
Ensure enough room to store each currently executing job.
std::unique_lock
STL class.
ripple::Workers::m_semaphore
semaphore m_semaphore
Definition: Workers.h:174
ripple::Workers::pauseAllThreadsAndWait
void pauseAllThreadsAndWait()
Pause all threads and wait until they are paused.
Definition: Workers.cpp:111
std::condition_variable::wait
T wait(T... args)
ripple::Workers::Workers
Workers(Callback &callback, perf::PerfLog *perfLog, std::string const &threadNames="Worker", int numberOfThreads=static_cast< int >(std::thread::hardware_concurrency()))
Create the object.
Definition: Workers.cpp:27
ripple::basic_semaphore::notify
void notify()
Increment the count and unblock one waiting thread.
Definition: semaphore.h:48
ripple::Workers
A group of threads that process tasks.
Definition: Workers.h:40
ripple::Workers::Worker
Definition: Workers.h:136
ripple::Workers::Worker::notify
void notify()
Definition: Workers.cpp:175
beast::LockFreeStack::pop_front
Element * pop_front()
Pop an element off the stack.
Definition: LockFreeStack.h:233
ripple::Workers::Worker::run
void run()
Definition: Workers.cpp:182
beast::setCurrentThreadName
void setCurrentThreadName(std::string_view name)
Changes the name of the caller thread.
Definition: CurrentThreadName.cpp:113
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
cassert
ripple::Workers::addTask
void addTask()
Add a task to be performed.
Definition: Workers.cpp:122
ripple::Workers::m_mut
std::mutex m_mut
Definition: Workers.h:172
ripple::Workers::numberOfCurrentlyRunningTasks
int numberOfCurrentlyRunningTasks() const noexcept
Get the number of currently executing calls of Callback::processTask.
Definition: Workers.cpp:127
ripple::Workers::m_allPaused
bool m_allPaused
Definition: Workers.h:173
beast::LockFreeStack
Multiple Producer, Multiple Consumer (MPMC) intrusive stack.
Definition: LockFreeStack.h:143