source: rrlib_thread/tConditionVariable.cpp @ 30:fa354798480f

Last change on this file since 30:fa354798480f was 30:fa354798480f, checked in by Max Reichardt <mreichardt@…>, 5 years ago

Dependency to pthread library is now optional. Added RRLIB_SINGLE_THREADED #ifndefs to all places where they were still missing.

File size: 12.2 KB
Line 
1//
2// You received this file as part of RRLib
3// Robotics Research Library
4//
5// Copyright (C) Finroc GbR (finroc.org)
6//
7// This program is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License along
18// with this program; if not, write to the Free Software Foundation, Inc.,
19// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20//
21//----------------------------------------------------------------------
22/*!\file    rrlib/thread/tConditionVariable.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2012-06-12
27 *
28 */
29//----------------------------------------------------------------------
30#include "rrlib/thread/tConditionVariable.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include "rrlib/design_patterns/singleton.h"
36#include "rrlib/time/tTimeStretchingListener.h"
37#include "rrlib/logging/messages.h"
38
39//----------------------------------------------------------------------
40// Internal includes with ""
41//----------------------------------------------------------------------
42
43//----------------------------------------------------------------------
44// Debugging
45//----------------------------------------------------------------------
46#include <cassert>
47
48//----------------------------------------------------------------------
49// Namespace usage
50//----------------------------------------------------------------------
51
52//----------------------------------------------------------------------
53// Namespace declaration
54//----------------------------------------------------------------------
55namespace rrlib
56{
57namespace thread
58{
59
60//----------------------------------------------------------------------
61// Forward declarations / typedefs / enums
62//----------------------------------------------------------------------
63
64//----------------------------------------------------------------------
65// Const values
66//----------------------------------------------------------------------
67
68//----------------------------------------------------------------------
69// Implementation
70//----------------------------------------------------------------------
71
72#ifndef RRLIB_SINGLE_THREADED
73typedef rrlib::design_patterns::tSingletonHolder<std::vector<tConditionVariable*>> tConditionVariableListSingleton;
74static std::vector<tConditionVariable*>* GetConditionVariableList()
75{
76  try
77  {
78    return &tConditionVariableListSingleton::Instance();
79  }
80  catch (const std::logic_error& e)
81  {}
82  return NULL;
83}
84
85class tTimeStretchingListenerImpl : public rrlib::time::tTimeStretchingListener
86{
87  rrlib::time::tTimeMode old_mode;
88
89  virtual void TimeChanged(const rrlib::time::tTimestamp& current_time) override
90  {
91    if (!GetConditionVariableList())
92    {
93      return;
94    }
95
96    // we don't need any locks, because TimeChanged is called with rrlib::time::mutex acquired
97    std::vector<tConditionVariable*>& l = *GetConditionVariableList();
98    for (auto it = l.begin(); it < l.end(); it++)
99    {
100      tLock l((*it)->mutex);
101      if ((*it)->waiting_on_monitor && (*it)->waiting_until_application_time != rrlib::time::cNO_TIME)
102      {
103        (*it)->time_scaling_factor_changed = true;
104        if (current_time >= (*it)->waiting_until_application_time)
105        {
106          (*it)->wrapped.notify_all();
107        }
108        else
109        {
110          rrlib::time::tDuration wait_for = (*it)->waiting_until_application_time - current_time;
111          if (wait_for > (*it)->waiting_for_application_duration)
112          {
113            // clock inconsistency
114            RRLIB_LOG_PRINT(DEBUG_WARNING, "Detected clock inconsistency. New timestamp is more than ",
115                            rrlib::time::ToIsoString(wait_for - (*it)->waiting_for_application_duration), " in the past.");
116            (*it)->waiting_for_application_duration = (*it)->waiting_for_application_duration / 2;
117            RRLIB_LOG_PRINT(DEBUG_WARNING, "Recovering by waiting another ", rrlib::time::ToString((*it)->waiting_for_application_duration), " (half the original timeout).");
118
119            (*it)->waiting_until_application_time = current_time - (*it)->waiting_for_application_duration;
120          }
121          else
122          {
123            (*it)->waiting_for_application_duration = wait_for;
124          }
125        }
126      }
127    }
128  }
129
130  virtual void TimeModeChanged(rrlib::time::tTimeMode new_mode) override
131  {
132    if (!GetConditionVariableList())
133    {
134      return;
135    }
136
137    if (old_mode == rrlib::time::tTimeMode::CUSTOM_CLOCK && new_mode != old_mode)
138    {
139      // we don't need any locks, because TimeChanged is called with rrlib::time::mutex acquired
140      std::vector<tConditionVariable*>& l = *GetConditionVariableList();
141      for (auto it = l.begin(); it < l.end(); it++)
142      {
143        tLock l((*it)->mutex);
144        if ((*it)->waiting_on_monitor && (*it)->waiting_until_application_time != rrlib::time::cNO_TIME)
145        {
146          (*it)->wrapped.notify_all();
147        }
148      }
149    }
150    old_mode = new_mode;
151  }
152
153  virtual void TimeStretchingFactorChanged(bool app_time_faster) override
154  {
155    if (!GetConditionVariableList())
156    {
157      return;
158    }
159
160    // we don't need any locks, because TimeChanged is called with rrlib::time::mutex acquired
161    std::vector<tConditionVariable*>& l = *GetConditionVariableList();
162    for (auto it = l.begin(); it < l.end(); it++)
163    {
164      tLock l((*it)->mutex);
165      if ((*it)->waiting_on_monitor && (*it)->waiting_until_application_time != rrlib::time::cNO_TIME)
166      {
167        (*it)->time_scaling_factor_changed = true;
168        if (app_time_faster)
169        {
170          (*it)->wrapped.notify_one();
171        }
172      }
173    }
174  }
175
176public:
177
178  tTimeStretchingListenerImpl() : old_mode(rrlib::time::GetTimeMode()) {}
179};
180
181static tTimeStretchingListenerImpl time_stretching_listener;
182
183/*!
184 * This object increments value while it exists
185 */
186struct tTemporalIncrement : private util::tNoncopyable
187{
188  int& value;
189  tTemporalIncrement(int& value) : value(value)
190  {
191    value++;
192  }
193
194  ~tTemporalIncrement()
195  {
196    value--;
197  }
198};
199
200tConditionVariable::tConditionVariable(tMutex& mutex) :
201  mutex(mutex),
202  wrapped(),
203  registered_in_list(false),
204  waiting_on_monitor(0),
205  waiting_until_application_time(rrlib::time::cNO_TIME),
206  waiting_for_application_duration(-1),
207  time_scaling_factor_changed(false),
208  notified(false)
209{}
210
211tConditionVariable::~tConditionVariable()
212{
213  assert(waiting_on_monitor == 0);
214  if (registered_in_list && GetConditionVariableList())
215  {
216    try
217    {
218      std::lock_guard<std::mutex> lock(rrlib::time::internal::tTimeMutex::Instance()); // we should not hold any critical locks - otherwise this will dead-lock
219      std::vector<tConditionVariable*>& l = *GetConditionVariableList();
220      l.erase(std::remove(l.begin(), l.end(), this), l.end());
221    }
222    catch (const std::logic_error& le) // can happen if tTimeMutex has already been destructed. In this case, we do not need to worry about unregistering anymore.
223    {}
224  }
225}
226
227bool tConditionVariable::ConditionVariableLockCorrectlyAcquired(const tLock& l) const
228{
229#ifdef RRLIB_THREAD_ENFORCE_LOCK_ORDER
230  return internal::tLockStack::ConditionVariableLockCorrectlyAcquired(l);
231#else
232  return l.IsLocked(mutex);
233#endif
234}
235
236void tConditionVariable::Notify(tLock& l)
237{
238  assert(ConditionVariableLockCorrectlyAcquired(l));
239  if (waiting_on_monitor)
240  {
241    notified = true;
242    wrapped.notify_one();
243  }
244}
245
246void tConditionVariable::NotifyAll(tLock& l)
247{
248  assert(ConditionVariableLockCorrectlyAcquired(l));
249  if (waiting_on_monitor)
250  {
251    notified = true;
252    wrapped.notify_all();
253  }
254}
255
256void tConditionVariable::Wait(tLock& l)
257{
258  assert(ConditionVariableLockCorrectlyAcquired(l));
259  tTemporalIncrement count_waiting_thread(waiting_on_monitor);
260  wrapped.wait(l.GetSimpleLock());
261}
262
263void tConditionVariable::Wait(tLock& l, const rrlib::time::tDuration& wait_for, bool use_application_time, rrlib::time::tTimestamp wait_until)
264{
265  assert(ConditionVariableLockCorrectlyAcquired(l));
266  tTemporalIncrement count_waiting_thread(waiting_on_monitor);
267
268  if (!use_application_time)
269  {
270    wrapped.wait_for(l.GetSimpleLock(), wait_for);
271  }
272  else
273  {
274    // Put condition variable in list
275    if (!registered_in_list)
276    {
277      try
278      {
279        std::lock_guard<std::mutex> l(rrlib::time::internal::tTimeMutex::Instance()); // this won't dead-lock, because this condition variable is not in listener list yet
280        GetConditionVariableList()->push_back(this);
281        registered_in_list = true;
282      }
283      catch (const std::logic_error&) // tTimeMutex no longer exists
284      {
285        RRLIB_LOG_PRINT(DEBUG_WARNING, "Won't wait after rrlibs have been (partly) destructed.");
286        return;
287      }
288    }
289
290    // Init variables
291    time_scaling_factor_changed = false;
292    notified = false;
293    assert(waiting_until_application_time == rrlib::time::cNO_TIME && "Only one thread may wait on condition variable using 'application time' timeout.");
294    if (wait_until == rrlib::time::cNO_TIME)
295    {
296      wait_until = rrlib::time::Now() + wait_for;
297    }
298    waiting_until_application_time = wait_until;
299    waiting_for_application_duration = wait_for;
300
301    // Wait...
302    rrlib::time::tDuration system_duration = rrlib::time::ToSystemDuration(wait_for);
303    rrlib::time::tTimeMode time_mode = rrlib::time::GetTimeMode();
304    while (true)
305    {
306      if (time_mode == rrlib::time::tTimeMode::CUSTOM_CLOCK)
307      {
308        wrapped.wait(l.GetSimpleLock());
309        time_mode = rrlib::time::GetTimeMode();
310        if (notified || time_mode == rrlib::time::tTimeMode::CUSTOM_CLOCK)
311        {
312          break;
313        }
314      }
315      else
316      {
317        wrapped.wait_for(l.GetSimpleLock(), system_duration);
318        rrlib::time::tTimeMode new_time_mode = rrlib::time::GetTimeMode();
319        if (notified || (!time_scaling_factor_changed && new_time_mode == time_mode))
320        {
321          break;
322        }
323        time_mode = new_time_mode;
324      }
325      rrlib::time::tTimestamp current_app_time = rrlib::time::Now();
326      if (current_app_time >= wait_until)
327      {
328        break;
329      }
330
331      // ok... so we need to wait more
332      assert(!notified);
333      rrlib::time::tDuration waiting_for_application_duration_new = wait_until - current_app_time;
334      if (waiting_for_application_duration_new > waiting_for_application_duration)
335      {
336        // clock inconsistency
337        RRLIB_LOG_PRINT(DEBUG_WARNING, "Detected clock inconsistency. New timestamp is more than ",
338                        rrlib::time::ToIsoString(waiting_for_application_duration_new - waiting_for_application_duration), " in the past.");
339        waiting_for_application_duration = waiting_for_application_duration / 2;
340        RRLIB_LOG_PRINT(DEBUG_WARNING, "Recovering by waiting another ", rrlib::time::ToString(waiting_for_application_duration), " (half the original timeout).");
341
342        waiting_until_application_time = current_app_time - waiting_for_application_duration;
343      }
344      else
345      {
346        waiting_for_application_duration = waiting_until_application_time - current_app_time;
347      }
348      system_duration = rrlib::time::ToSystemDuration(waiting_for_application_duration);
349      time_scaling_factor_changed = false;
350    }
351
352    waiting_until_application_time = rrlib::time::cNO_TIME;
353  }
354}
355#else // RRLIB_SINGLE_THREADED
356
357tConditionVariable::tConditionVariable(tMutex& mutex)
358{}
359
360tConditionVariable::~tConditionVariable()
361{}
362
363void tConditionVariable::Notify(tLock& l)
364{}
365
366void tConditionVariable::NotifyAll(tLock& l)
367{}
368
369void tConditionVariable::Wait(tLock& l)
370{}
371
372void tConditionVariable::Wait(tLock& l, const rrlib::time::tDuration& wait_for, bool use_application_time, rrlib::time::tTimestamp wait_until)
373{}
374
375#endif // RRLIB_SINGLE_THREADED
376
377//----------------------------------------------------------------------
378// End of namespace declaration
379//----------------------------------------------------------------------
380}
381}
Note: See TracBrowser for help on using the repository browser.