source: rrlib_thread/tConditionVariable.cpp @ 19:f2fff7b7cf13

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

Added 'override' keyword where appropriate

File size: 11.7 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
72typedef rrlib::design_patterns::tSingletonHolder<std::vector<tConditionVariable*>> tConditionVariableListSingleton;
73static std::vector<tConditionVariable*>* GetConditionVariableList()
74{
75  try
76  {
77    return &tConditionVariableListSingleton::Instance();
78  }
79  catch (const std::logic_error& e)
80  {}
81  return NULL;
82}
83
84class tTimeStretchingListenerImpl : public rrlib::time::tTimeStretchingListener
85{
86  rrlib::time::tTimeMode old_mode;
87
88  virtual void TimeChanged(const rrlib::time::tTimestamp& current_time) override
89  {
90    if (!GetConditionVariableList())
91    {
92      return;
93    }
94
95    // we don't need any locks, because TimeChanged is called with rrlib::time::mutex acquired
96    std::vector<tConditionVariable*>& l = *GetConditionVariableList();
97    for (auto it = l.begin(); it < l.end(); it++)
98    {
99      tLock l((*it)->mutex);
100      if ((*it)->waiting_on_monitor && (*it)->waiting_until_application_time != rrlib::time::cNO_TIME)
101      {
102        (*it)->time_scaling_factor_changed = true;
103        if (current_time >= (*it)->waiting_until_application_time)
104        {
105          (*it)->wrapped.notify_all();
106        }
107        else
108        {
109          rrlib::time::tDuration wait_for = (*it)->waiting_until_application_time - current_time;
110          if (wait_for > (*it)->waiting_for_application_duration)
111          {
112            // clock inconsistency
113            RRLIB_LOG_PRINT(DEBUG_WARNING, "Detected clock inconsistency. New timestamp is more than ",
114                            rrlib::time::ToIsoString(wait_for - (*it)->waiting_for_application_duration), " in the past.");
115            (*it)->waiting_for_application_duration = (*it)->waiting_for_application_duration / 2;
116            RRLIB_LOG_PRINT(DEBUG_WARNING, "Recovering by waiting another ", rrlib::time::ToString((*it)->waiting_for_application_duration), " (half the original timeout).");
117
118            (*it)->waiting_until_application_time = current_time - (*it)->waiting_for_application_duration;
119          }
120          else
121          {
122            (*it)->waiting_for_application_duration = wait_for;
123          }
124        }
125      }
126    }
127  }
128
129  virtual void TimeModeChanged(rrlib::time::tTimeMode new_mode) override
130  {
131    if (!GetConditionVariableList())
132    {
133      return;
134    }
135
136    if (old_mode == rrlib::time::tTimeMode::CUSTOM_CLOCK && new_mode != old_mode)
137    {
138      // we don't need any locks, because TimeChanged is called with rrlib::time::mutex acquired
139      std::vector<tConditionVariable*>& l = *GetConditionVariableList();
140      for (auto it = l.begin(); it < l.end(); it++)
141      {
142        tLock l((*it)->mutex);
143        if ((*it)->waiting_on_monitor && (*it)->waiting_until_application_time != rrlib::time::cNO_TIME)
144        {
145          (*it)->wrapped.notify_all();
146        }
147      }
148    }
149    old_mode = new_mode;
150  }
151
152  virtual void TimeStretchingFactorChanged(bool app_time_faster) override
153  {
154    if (!GetConditionVariableList())
155    {
156      return;
157    }
158
159    // we don't need any locks, because TimeChanged is called with rrlib::time::mutex acquired
160    std::vector<tConditionVariable*>& l = *GetConditionVariableList();
161    for (auto it = l.begin(); it < l.end(); it++)
162    {
163      tLock l((*it)->mutex);
164      if ((*it)->waiting_on_monitor && (*it)->waiting_until_application_time != rrlib::time::cNO_TIME)
165      {
166        (*it)->time_scaling_factor_changed = true;
167        if (app_time_faster)
168        {
169          (*it)->wrapped.notify_one();
170        }
171      }
172    }
173  }
174
175public:
176
177  tTimeStretchingListenerImpl() : old_mode(rrlib::time::GetTimeMode()) {}
178};
179
180static tTimeStretchingListenerImpl time_stretching_listener;
181
182/*!
183 * This object increments value while it exists
184 */
185struct tTemporalIncrement : private util::tNoncopyable
186{
187  int& value;
188  tTemporalIncrement(int& value) : value(value)
189  {
190    value++;
191  }
192
193  ~tTemporalIncrement()
194  {
195    value--;
196  }
197};
198
199tConditionVariable::tConditionVariable(tMutex& mutex) :
200  mutex(mutex),
201  wrapped(),
202  registered_in_list(false),
203  waiting_on_monitor(0),
204  waiting_until_application_time(rrlib::time::cNO_TIME),
205  waiting_for_application_duration(-1),
206  time_scaling_factor_changed(false),
207  notified(false)
208{}
209
210tConditionVariable::~tConditionVariable()
211{
212  assert(waiting_on_monitor == 0);
213  if (registered_in_list && GetConditionVariableList())
214  {
215    try
216    {
217      std::lock_guard<std::mutex> lock(rrlib::time::internal::tTimeMutex::Instance()); // we should not hold any critical locks - otherwise this will dead-lock
218      std::vector<tConditionVariable*>& l = *GetConditionVariableList();
219      l.erase(std::remove(l.begin(), l.end(), this), l.end());
220    }
221    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.
222    {}
223  }
224}
225
226bool tConditionVariable::ConditionVariableLockCorrectlyAcquired(const tLock& l) const
227{
228#ifdef RRLIB_THREAD_ENFORCE_LOCK_ORDER
229  return internal::tLockStack::ConditionVariableLockCorrectlyAcquired(l);
230#else
231  return l.IsLocked(mutex);
232#endif
233}
234
235void tConditionVariable::Notify(tLock& l)
236{
237  assert(ConditionVariableLockCorrectlyAcquired(l));
238  if (waiting_on_monitor)
239  {
240    notified = true;
241    wrapped.notify_one();
242  }
243}
244
245void tConditionVariable::NotifyAll(tLock& l)
246{
247  assert(ConditionVariableLockCorrectlyAcquired(l));
248  if (waiting_on_monitor)
249  {
250    notified = true;
251    wrapped.notify_all();
252  }
253}
254
255void tConditionVariable::Wait(tLock& l)
256{
257  assert(ConditionVariableLockCorrectlyAcquired(l));
258  tTemporalIncrement count_waiting_thread(waiting_on_monitor);
259  wrapped.wait(l.GetSimpleLock());
260}
261
262void tConditionVariable::Wait(tLock& l, const rrlib::time::tDuration& wait_for, bool use_application_time, rrlib::time::tTimestamp wait_until)
263{
264  assert(ConditionVariableLockCorrectlyAcquired(l));
265  tTemporalIncrement count_waiting_thread(waiting_on_monitor);
266
267  if (!use_application_time)
268  {
269    wrapped.wait_for(l.GetSimpleLock(), wait_for);
270  }
271  else
272  {
273    // Put condition variable in list
274    if (!registered_in_list)
275    {
276      try
277      {
278        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
279        GetConditionVariableList()->push_back(this);
280        registered_in_list = true;
281      }
282      catch (const std::logic_error&) // tTimeMutex no longer exists
283      {
284        RRLIB_LOG_PRINT(DEBUG_WARNING, "Won't wait after rrlibs have been (partly) destructed.");
285        return;
286      }
287    }
288
289    // Init variables
290    time_scaling_factor_changed = false;
291    notified = false;
292    assert(waiting_until_application_time == rrlib::time::cNO_TIME && "Only one thread may wait on condition variable using 'application time' timeout.");
293    if (wait_until == rrlib::time::cNO_TIME)
294    {
295      wait_until = rrlib::time::Now() + wait_for;
296    }
297    waiting_until_application_time = wait_until;
298    waiting_for_application_duration = wait_for;
299
300    // Wait...
301    rrlib::time::tDuration system_duration = rrlib::time::ToSystemDuration(wait_for);
302    rrlib::time::tTimeMode time_mode = rrlib::time::GetTimeMode();
303    while (true)
304    {
305      if (time_mode == rrlib::time::tTimeMode::CUSTOM_CLOCK)
306      {
307        wrapped.wait(l.GetSimpleLock());
308        time_mode = rrlib::time::GetTimeMode();
309        if (notified || time_mode == rrlib::time::tTimeMode::CUSTOM_CLOCK)
310        {
311          break;
312        }
313      }
314      else
315      {
316        wrapped.wait_for(l.GetSimpleLock(), system_duration);
317        rrlib::time::tTimeMode new_time_mode = rrlib::time::GetTimeMode();
318        if (notified || (!time_scaling_factor_changed && new_time_mode == time_mode))
319        {
320          break;
321        }
322        time_mode = new_time_mode;
323      }
324      rrlib::time::tTimestamp current_app_time = rrlib::time::Now();
325      if (current_app_time >= wait_until)
326      {
327        break;
328      }
329
330      // ok... so we need to wait more
331      assert(!notified);
332      rrlib::time::tDuration waiting_for_application_duration_new = wait_until - current_app_time;
333      if (waiting_for_application_duration_new > waiting_for_application_duration)
334      {
335        // clock inconsistency
336        RRLIB_LOG_PRINT(DEBUG_WARNING, "Detected clock inconsistency. New timestamp is more than ",
337                        rrlib::time::ToIsoString(waiting_for_application_duration_new - waiting_for_application_duration), " in the past.");
338        waiting_for_application_duration = waiting_for_application_duration / 2;
339        RRLIB_LOG_PRINT(DEBUG_WARNING, "Recovering by waiting another ", rrlib::time::ToString(waiting_for_application_duration), " (half the original timeout).");
340
341        waiting_until_application_time = current_app_time - waiting_for_application_duration;
342      }
343      else
344      {
345        waiting_for_application_duration = waiting_until_application_time - current_app_time;
346      }
347      system_duration = rrlib::time::ToSystemDuration(waiting_for_application_duration);
348      time_scaling_factor_changed = false;
349    }
350
351    waiting_until_application_time = rrlib::time::cNO_TIME;
352  }
353}
354
355//----------------------------------------------------------------------
356// End of namespace declaration
357//----------------------------------------------------------------------
358}
359}
Note: See TracBrowser for help on using the repository browser.