source: rrlib_thread/internal/tLockStack.cpp @ 5:6a3516d3ee5c

Last change on this file since 5:6a3516d3ee5c was 5:6a3516d3ee5c, checked in by Tobias Föhst <foehst@…>, 7 years ago

Adapted to changes in rrlib_logging

File size: 7.5 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
8// modify it under the terms of the GNU General Public License
9// as published by the Free Software Foundation; either version 2
10// of the License, or (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
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20//
21//----------------------------------------------------------------------
22/*!\file    rrlib/thread/internal/tLockStack.cpp
23 *
24 * \author  Max Reichardt
25 *
26 * \date    2012-07-04
27 *
28 */
29//----------------------------------------------------------------------
30#include "rrlib/thread/internal/tLockStack.h"
31
32//----------------------------------------------------------------------
33// External includes (system with <>, local with "")
34//----------------------------------------------------------------------
35#include <vector>
36
37//----------------------------------------------------------------------
38// Internal includes with ""
39//----------------------------------------------------------------------
40#include "rrlib/thread/tLock.h"
41#include "rrlib/thread/tThread.h"
42
43//----------------------------------------------------------------------
44// Debugging
45//----------------------------------------------------------------------
46#include <cassert>
47
48//----------------------------------------------------------------------
49// Namespace usage
50//----------------------------------------------------------------------
51using namespace rrlib::logging;
52
53//----------------------------------------------------------------------
54// Namespace declaration
55//----------------------------------------------------------------------
56namespace rrlib
57{
58namespace thread
59{
60namespace internal
61{
62#ifndef RRLIB_SINGLE_THREADED
63#ifdef RRLIB_THREAD_ENFORCE_LOCK_ORDER
64
65//----------------------------------------------------------------------
66// Forward declarations / typedefs / enums
67//----------------------------------------------------------------------
68
69//----------------------------------------------------------------------
70// Const values
71//----------------------------------------------------------------------
72
73/*! Maximum number of nested locks */
74static const size_t cMAX_NESTED_LOCKS = 100;
75
76//----------------------------------------------------------------------
77// Implementation
78//----------------------------------------------------------------------
79
80/*!
81 * Thread-Local Lock stack (contains all locks that were acquired by this thread)
82 * (note that we do not use a std::vector here, because it cannot be used in __thread variables directly -
83 * and it could pose problems during static (de)initialization)
84 *
85 * (Having this information entirely thread-local is more efficient and simpler than maintaining linked-lists across threads,
86 * since variables would need to be volatile etc.)
87 */
88struct tLockStackData : boost::noncopyable
89{
90  /*! Stack entries */
91  std::vector<const tLock*> entries;
92
93  tLockStackData() :
94    entries()
95  {
96    entries.reserve(cMAX_NESTED_LOCKS);
97  }
98};
99
100static __thread tLockStackData* stack_data = NULL;
101
102static std::string GetLogDescription()
103{
104  return std::string("Lock stack for thread '") + tThread::CurrentThread().GetName() + "'";
105}
106
107/*!
108 * Is it okay to call a monitor method with this lock?
109 */
110bool tLockStack::ConditionVariableLockCorrectlyAcquired(const tLock& lock)
111{
112  tLockStackData* s = stack_data;
113  return s && lock.locked_simple && s->entries.size() > 0 && (*s->entries.rbegin()) == &lock;
114}
115
116void tLockStack::DumpStack()
117{
118  tLockStackData* s = stack_data;
119  if (!s)
120  {
121    RRLIB_LOG_PRINT(USER, "Current thread has no lock stack (yet).");
122    return;
123  }
124  RRLIB_LOG_PRINT(USER, "Lock Stack Dump:");
125  for (auto it = s->entries.rbegin(); it < s->entries.rend(); it++)
126  {
127    const tLock* l = *it;
128    if (l->locked_ordered)
129    {
130      RRLIB_LOG_PRINTF(USER, "  %s %p ('%s', primary %d, secondary %d)", l->locked_simple ? "OrderedMutex" : "RecursiveMutex", l->locked_ordered, l->locked_ordered->GetDescription(), l->locked_ordered->GetPrimary(), l->locked_ordered->GetSecondary());
131    }
132    else
133    {
134      RRLIB_LOG_PRINTF(USER, "  Simple Mutex %p", l->locked_simple);
135    }
136  }
137}
138
139void tLockStack::Push(const tLock* lock)
140{
141  // make sure that stack exists
142  if (!stack_data)
143  {
144    stack_data = new tLockStackData();
145    tThread& t = tThread::CurrentThread();
146    // tLock l(*t); no thread-safety issue, since this is always the current thread itself
147    t.lock_stack = std::shared_ptr<tLockStackData>(stack_data); // make sure it will be deleted with thread
148  }
149
150  tLockStackData* s = stack_data;
151  assert(s->entries.size() < (cMAX_NESTED_LOCKS - 1) && "Maximum number of locks exceeded. This likely a programming error or a bad/inefficient implementation.");
152  if (s->entries.size() > 0)
153  {
154    if (!(*s->entries.rbegin())->locked_ordered)
155    {
156      if (lock->locked_ordered)
157      {
158        RRLIB_LOG_PRINTF(ERROR, "Attempt failed to lock ordered mutex %p ('%s', primary %d, secondary %d). You are not allowed to lock another mutex after a simple one.", lock->locked_ordered, lock->locked_ordered->GetDescription(), lock->locked_ordered->GetPrimary(), lock->locked_ordered->GetSecondary());
159      }
160      else
161      {
162        RRLIB_LOG_PRINTF(ERROR, "Attempt failed to lock simple mutex %p. You are not allowed to lock another mutex after a simple one.", lock->locked_simple);
163      }
164      DumpStack();
165      abort();
166    }
167    else if (lock->locked_ordered && (!lock->locked_ordered->ValidAfter(*(*s->entries.rbegin())->locked_ordered)))
168    {
169      bool found = false;
170      for (auto it = s->entries.begin(); it < s->entries.end(); it++)
171      {
172        found |= (lock->locked_ordered == (*it)->locked_ordered);
173      }
174      if (!found)
175      {
176        RRLIB_LOG_PRINTF(ERROR, "Attempt failed to lock ordered mutex %p ('%s', primary %d, secondary %d). Lock may not be acquired in this order.", lock->locked_ordered, lock->locked_ordered->GetDescription(), lock->locked_ordered->GetPrimary(), lock->locked_ordered->GetSecondary());
177        DumpStack();
178        abort();
179      }
180    }
181    else if (lock->locked_ordered && lock->locked_simple)
182    {
183      bool found = false;
184      for (auto it = s->entries.begin(); it < s->entries.end(); it++)
185      {
186        if (lock->locked_ordered == (*it)->locked_ordered)
187        {
188          RRLIB_LOG_PRINTF(ERROR, "Attempt failed to lock ordered mutex %p ('%s', primary %d, secondary %d). Only recursive mutexes may be locked twice.", lock->locked_ordered, lock->locked_ordered->GetDescription(), lock->locked_ordered->GetPrimary(), lock->locked_ordered->GetSecondary());
189          DumpStack();
190          abort();
191        }
192      }
193    }
194  }
195
196  s->entries.push_back(lock);
197}
198
199const tLock* tLockStack::Pop()
200{
201  tLockStackData* s = stack_data;
202  assert(s && "No lock stack exists");
203  assert(s->entries.size() > 0 && "Lock is not in stack. This should never happen.");
204  const tLock* ret = *s->entries.rbegin();
205  s->entries.pop_back();
206  return ret;
207}
208
209//----------------------------------------------------------------------
210// End of namespace declaration
211//----------------------------------------------------------------------
212#endif
213#endif
214}
215}
216}
Note: See TracBrowser for help on using the repository browser.