BitShares-Core  5.1.0
BitShares blockchain implementation and command-line interface software
mutex.cpp
Go to the documentation of this file.
1 #include <fc/thread/mutex.hpp>
2 #include <fc/thread/thread.hpp>
4 #include <fc/log/logger.hpp>
5 #include "context.hpp"
6 #include "thread_d.hpp"
7 
8 namespace fc {
9 
11  m_blist(0),
12  recursive_lock_count(0)
13  {}
14 
16  if( m_blist )
17  {
18  fc::thread::current().debug("~mutex");
19 #if 0
20  context* c = m_blist;
21  while( c ) {
22  // elog( "still blocking on context %p (%s)", m_blist, (m_blist->cur_task ? m_blist->cur_task->get_desc() : "no current task") );
23  c = c->next_blocked_mutex;
24  }
25 #endif
26  BOOST_ASSERT( false && "Attempt to free mutex while others are blocking on lock." );
27  }
28  }
29 
35  static fc::context* get_tail( fc::context* list_head, fc::context*& context_to_unblock ) {
36  context_to_unblock = 0;
37  fc::context* list_context_iter = list_head;
38  if( !list_context_iter )
39  return list_context_iter;
40  while( list_context_iter->next_blocked_mutex )
41  {
42  context_to_unblock = list_context_iter;
43  list_context_iter = list_context_iter->next_blocked_mutex;
44  }
45  return list_context_iter;
46  }
47 
48  static fc::context* remove( fc::context* head, fc::context* target ) {
49  fc::context* context_iter = head;
50  fc::context* previous = 0;
51  while( context_iter )
52  {
53  if( context_iter == target )
54  {
55  if( previous )
56  {
57  previous->next_blocked_mutex = context_iter->next_blocked_mutex;
58  return head;
59  }
60  return context_iter->next_blocked_mutex;
61  }
62  previous = context_iter;
63  context_iter = context_iter->next_blocked_mutex;
64  }
65  return head;
66  }
67  static void cleanup( fc::mutex& m, fc::spin_yield_lock& syl, fc::context*& bl, fc::context* cc ) {
68  {
70  if( cc->next_blocked_mutex ) {
71  bl = remove(bl, cc);
72  cc->next_blocked_mutex = nullptr;
73  return;
74  }
75  }
76  m.unlock();
77  }
78 
83  bool mutex::try_lock() {
84  assert(false); // this is currently broken re: recursive mutexes
86  fc::context* cc = ct->my->current;
87  fc::context* n = 0;
88 
90  if( !lock )
91  return false;
92 
93  if( !m_blist ) {
94  m_blist = cc;
95  return true;
96  }
97  // allow recursive locks.
98  return ( get_tail( m_blist, n ) == cc );
99  }
100 
101  bool mutex::try_lock_until( const fc::time_point& abs_time ) {
102  assert(false); // this is currently broken re: recursive mutexes
103  fc::context* n = 0;
105 
106  { // lock scope
107  fc::unique_lock<fc::spin_yield_lock> lock(m_blist_lock,abs_time);
108  if( !lock ) return false;
109 
110  if( !m_blist ) {
111  m_blist = cc;
112  return true;
113  }
114 
115  // allow recusive locks
116  if ( get_tail( m_blist, n ) == cc )
117  return true;
118 
119  cc->next_blocked_mutex = m_blist;
120  m_blist = cc;
121  } // end lock scope
122 
123 
125  try {
126  fc::thread::current().my->yield_until( abs_time, false );
127  return( 0 == cc->next_blocked_mutex );
128  } catch (...) {
129  e = std::current_exception();
130  }
131  assert(e);
132  cleanup( *this, m_blist_lock, m_blist, cc);
133  std::rethrow_exception(e);
134  }
135 
136  void mutex::lock() {
137  fc::context* current_context = fc::thread::current().my->current;
138  if( !current_context )
139  current_context = fc::thread::current().my->current = new fc::context( &fc::thread::current() );
140 
141  {
143  if( !m_blist )
144  {
145  // nobody else owns the mutex, so we get it; add our context as the last and only element on the mutex's list
146  m_blist = current_context;
147  assert(recursive_lock_count == 0);
148  recursive_lock_count = 1;
149  assert(!current_context->next_blocked_mutex);
150  return;
151  }
152 
153  // allow recusive locks
154  fc::context* dummy_context_to_unblock = 0;
155  if ( get_tail( m_blist, dummy_context_to_unblock ) == current_context ) {
156  assert(recursive_lock_count > 0);
157  ++recursive_lock_count;
158  return;
159  }
160  // add ourselves to the head of the list
161  current_context->next_blocked_mutex = m_blist;
162  m_blist = current_context;
163 
164 #if 0
165  int cnt = 0;
166  auto i = m_blist;
167  while( i ) {
168  i = i->next_blocked_mutex;
169  ++cnt;
170  }
171  //wlog( "wait queue len %1%", cnt );
172 #endif
173  }
174 
175  std::exception_ptr e; // cleanup calls yield so we need to move the exception outside of the catch block
176  try
177  {
178  fc::thread::current().yield(false);
179  // if yield() returned normally, we should now own the lock (we should be at the tail of the list)
180  BOOST_ASSERT( current_context->next_blocked_mutex == 0 );
181  assert(recursive_lock_count == 0);
182  recursive_lock_count = 1;
183  }
184  catch ( ... )
185  {
186  e = std::current_exception();
187  }
188  if( e ) {
189  cleanup( *this, m_blist_lock, m_blist, current_context);
190  std::rethrow_exception(e);
191  }
192  }
193 
195  {
196  fc::context* context_to_unblock = 0;
197 
199  assert(recursive_lock_count > 0);
200  --recursive_lock_count;
201  if (recursive_lock_count != 0)
202  return;
203 
204  get_tail(m_blist, context_to_unblock);
205  if( context_to_unblock )
206  {
207  context_to_unblock->next_blocked_mutex = 0;
208  context_to_unblock->ctx_thread->my->unblock( context_to_unblock );
209  }
210  else
211  m_blist = 0;
212  }
213 
214 } // fc
215 
216 
friend void yield()
Definition: thread.cpp:365
fc::context * current
Definition: thread_d.hpp:106
mutex()
Definition: mutex.cpp:10
bool try_lock()
Definition: mutex.cpp:83
mutex
Definition: mutex.hpp:91
std::shared_ptr< exception > exception_ptr
Definition: exception.hpp:131
static thread & current()
Definition: thread.cpp:125
void lock()
Definition: mutex.cpp:136
fc::context * next_blocked_mutex
Definition: context.hpp:192
~mutex()
Definition: mutex.cpp:15
void debug(const std::string &d)
print debug info about the state of every context / promise.
Definition: thread.cpp:161
defines wrappers for boost::asio functions
Definition: api.hpp:15
void yield_until(const time_point &tp, bool reschedule)
Definition: thread_d.hpp:704
void unblock(fc::context *c)
Definition: thread_d.hpp:692
bool try_lock_until(const time_point &abs_time)
Definition: mutex.cpp:101
void unlock()
Definition: mutex.cpp:194
modified spin-lock that yields on failure, but becomes a &#39;spin lock&#39; if there are no other tasks to y...
fc::thread * ctx_thread
Definition: context.hpp:194