BitShares-Core  4.0.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 
34  static fc::context* get_tail( fc::context* list_head, fc::context*& context_to_unblock ) {
35  context_to_unblock = 0;
36  fc::context* list_context_iter = list_head;
37  if( !list_context_iter )
38  return list_context_iter;
39  while( list_context_iter->next_blocked_mutex )
40  {
41  context_to_unblock = list_context_iter;
42  list_context_iter = list_context_iter->next_blocked_mutex;
43  }
44  return list_context_iter;
45  }
46 
47  static fc::context* remove( fc::context* head, fc::context* target ) {
48  fc::context* context_iter = head;
49  fc::context* previous = 0;
50  while( context_iter )
51  {
52  if( context_iter == target )
53  {
54  if( previous )
55  {
56  previous->next_blocked_mutex = context_iter->next_blocked_mutex;
57  return head;
58  }
59  return context_iter->next_blocked_mutex;
60  }
61  previous = context_iter;
62  context_iter = context_iter->next_blocked_mutex;
63  }
64  return head;
65  }
66  static void cleanup( fc::mutex& m, fc::spin_yield_lock& syl, fc::context*& bl, fc::context* cc ) {
67  {
69  if( cc->next_blocked_mutex ) {
70  bl = remove(bl, cc);
71  cc->next_blocked_mutex = nullptr;
72  return;
73  }
74  }
75  m.unlock();
76  }
77 
82  bool mutex::try_lock() {
83  assert(false); // this is currently broken re: recursive mutexes
85  fc::context* cc = ct->my->current;
86  fc::context* n = 0;
87 
89  if( !lock )
90  return false;
91 
92  if( !m_blist ) {
93  m_blist = cc;
94  return true;
95  }
96  // allow recursive locks.
97  return ( get_tail( m_blist, n ) == cc );
98  }
99 
100  bool mutex::try_lock_until( const fc::time_point& abs_time ) {
101  assert(false); // this is currently broken re: recursive mutexes
102  fc::context* n = 0;
104 
105  { // lock scope
106  fc::unique_lock<fc::spin_yield_lock> lock(m_blist_lock,abs_time);
107  if( !lock ) return false;
108 
109  if( !m_blist ) {
110  m_blist = cc;
111  return true;
112  }
113 
114  // allow recusive locks
115  if ( get_tail( m_blist, n ) == cc )
116  return true;
117 
118  cc->next_blocked_mutex = m_blist;
119  m_blist = cc;
120  } // end lock scope
121 
122 
124  try {
125  fc::thread::current().my->yield_until( abs_time, false );
126  return( 0 == cc->next_blocked_mutex );
127  } catch (...) {
128  e = std::current_exception();
129  }
130  assert(e);
131  cleanup( *this, m_blist_lock, m_blist, cc);
132  std::rethrow_exception(e);
133  }
134 
135  void mutex::lock() {
136  fc::context* current_context = fc::thread::current().my->current;
137  if( !current_context )
138  current_context = fc::thread::current().my->current = new fc::context( &fc::thread::current() );
139 
140  {
142  if( !m_blist )
143  {
144  // nobody else owns the mutex, so we get it; add our context as the last and only element on the mutex's list
145  m_blist = current_context;
146  assert(recursive_lock_count == 0);
147  recursive_lock_count = 1;
148  assert(!current_context->next_blocked_mutex);
149  return;
150  }
151 
152  // allow recusive locks
153  fc::context* dummy_context_to_unblock = 0;
154  if ( get_tail( m_blist, dummy_context_to_unblock ) == current_context ) {
155  assert(recursive_lock_count > 0);
156  ++recursive_lock_count;
157  return;
158  }
159  // add ourselves to the head of the list
160  current_context->next_blocked_mutex = m_blist;
161  m_blist = current_context;
162 
163 #if 0
164  int cnt = 0;
165  auto i = m_blist;
166  while( i ) {
167  i = i->next_blocked_mutex;
168  ++cnt;
169  }
170  //wlog( "wait queue len %1%", cnt );
171 #endif
172  }
173 
174  std::exception_ptr e; // cleanup calls yield so we need to move the exception outside of the catch block
175  try
176  {
177  fc::thread::current().yield(false);
178  // if yield() returned normally, we should now own the lock (we should be at the tail of the list)
179  BOOST_ASSERT( current_context->next_blocked_mutex == 0 );
180  assert(recursive_lock_count == 0);
181  recursive_lock_count = 1;
182  }
183  catch ( ... )
184  {
185  e = std::current_exception();
186  }
187  if( e ) {
188  cleanup( *this, m_blist_lock, m_blist, current_context);
189  std::rethrow_exception(e);
190  }
191  }
192 
194  {
195  fc::context* context_to_unblock = 0;
196 
198  assert(recursive_lock_count > 0);
199  --recursive_lock_count;
200  if (recursive_lock_count != 0)
201  return;
202 
203  get_tail(m_blist, context_to_unblock);
204  if( context_to_unblock )
205  {
206  context_to_unblock->next_blocked_mutex = 0;
207  context_to_unblock->ctx_thread->my->unblock( context_to_unblock );
208  }
209  else
210  m_blist = 0;
211  }
212 
213 } // fc
214 
215 
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:82
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:135
fc::context * next_blocked_mutex
Definition: context.hpp:191
~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
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:100
void unlock()
Definition: mutex.cpp:193
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:193