xref: /trafficserver/iocore/eventsystem/I_Lock.h (revision affba2de)
1 /** @file
2 
3   Basic locks for threads
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #pragma once
25 
26 #include "tscore/ink_platform.h"
27 #include "tscore/Diags.h"
28 #include "I_Thread.h"
29 
30 #define MAX_LOCK_TIME HRTIME_MSECONDS(200)
31 #define THREAD_MUTEX_THREAD_HOLDING (-1024 * 1024)
32 
33 /*------------------------------------------------------*\
34 |  Macros                                                |
35 \*------------------------------------------------------*/
36 
37 /**
38   Blocks until the lock to the ProxyMutex is acquired.
39 
40   This macro performs a blocking call until the lock to the ProxyMutex
41   is acquired. This call allocates a special object that holds the
42   lock to the ProxyMutex only for the scope of the function or
43   region. It is a good practice to delimit such scope explicitly
44   with '{' and '}'.
45 
46   @param _l Arbitrary name for the lock to use in this call
47   @param _m A pointer to (or address of) a ProxyMutex object
48   @param _t The current EThread executing your code.
49 
50 */
51 #ifdef DEBUG
52 #define SCOPED_MUTEX_LOCK(_l, _m, _t) MutexLock _l(MakeSourceLocation(), nullptr, _m, _t)
53 #else
54 #define SCOPED_MUTEX_LOCK(_l, _m, _t) MutexLock _l(_m, _t)
55 #endif // DEBUG
56 
57 #ifdef DEBUG
58 /**
59   Attempts to acquire the lock to the ProxyMutex.
60 
61   This macro attempts to acquire the lock to the specified ProxyMutex
62   object in a non-blocking manner. After using the macro you can
63   see if it was successful by comparing the lock variable with true
64   or false (the variable name passed in the _l parameter).
65 
66   @param _l Arbitrary name for the lock to use in this call (lock variable)
67   @param _m A pointer to (or address of) a ProxyMutex object
68   @param _t The current EThread executing your code.
69 
70 */
71 #define MUTEX_TRY_LOCK(_l, _m, _t) MutexTryLock _l(MakeSourceLocation(), (char *)nullptr, _m, _t)
72 
73 #else // DEBUG
74 #define MUTEX_TRY_LOCK(_l, _m, _t) MutexTryLock _l(_m, _t)
75 #endif // DEBUG
76 
77 /**
78   Releases the lock on a ProxyMutex.
79 
80   This macro releases the lock on the ProxyMutex, provided it is
81   currently held. The lock must have been successfully acquired
82   with one of the MUTEX macros.
83 
84   @param _l Arbitrary name for the lock to use in this call (lock
85     variable) It must be the same name as the one used to acquire the
86     lock.
87 
88 */
89 #define MUTEX_RELEASE(_l) (_l).release()
90 
91 /////////////////////////////////////
92 // DEPRECATED DEPRECATED DEPRECATED
93 #ifdef DEBUG
94 #define MUTEX_TAKE_TRY_LOCK(_m, _t) Mutex_trylock(MakeSourceLocation(), (char *)nullptr, _m, _t)
95 #else
96 #define MUTEX_TAKE_TRY_LOCK(_m, _t) Mutex_trylock(_m, _t)
97 #endif
98 
99 #ifdef DEBUG
100 #define MUTEX_TAKE_LOCK(_m, _t) Mutex_lock(MakeSourceLocation(), (char *)nullptr, _m, _t)
101 #define MUTEX_TAKE_LOCK_FOR(_m, _t, _c) Mutex_lock(MakeSourceLocation(), nullptr, _m, _t)
102 #else
103 #define MUTEX_TAKE_LOCK(_m, _t) Mutex_lock(_m, _t)
104 #define MUTEX_TAKE_LOCK_FOR(_m, _t, _c) Mutex_lock(_m, _t)
105 #endif // DEBUG
106 
107 #define MUTEX_UNTAKE_LOCK(_m, _t) Mutex_unlock(_m, _t)
108 // DEPRECATED DEPRECATED DEPRECATED
109 /////////////////////////////////////
110 
111 class EThread;
112 typedef EThread *EThreadPtr;
113 
114 #if DEBUG
115 inkcoreapi extern void lock_waiting(const SourceLocation &, const char *handler);
116 inkcoreapi extern void lock_holding(const SourceLocation &, const char *handler);
117 inkcoreapi extern void lock_taken(const SourceLocation &, const char *handler);
118 #endif
119 
120 /**
121   Lock object used in continuations and threads.
122 
123   The ProxyMutex class is the main synchronization object used
124   throughout the Event System. It is a reference counted object
125   that provides mutually exclusive access to a resource. Since the
126   Event System is multithreaded by design, the ProxyMutex is required
127   to protect data structures and state information that could
128   otherwise be affected by the action of concurrent threads.
129 
130   A ProxyMutex object has an ink_mutex member (defined in ink_mutex.h)
131   which is a wrapper around the platform dependent mutex type. This
132   member allows the ProxyMutex to provide the functionality required
133   by the users of the class without the burden of platform specific
134   function calls.
135 
136   The ProxyMutex also has a reference to the current EThread holding
137   the lock as a back pointer for verifying that it is released
138   correctly.
139 
140   Acquiring/Releasing locks:
141 
142   Included with the ProxyMutex class, there are several macros that
143   allow you to lock/unlock the underlying mutex object.
144 
145 */
146 class ProxyMutex : public RefCountObj
147 {
148 public:
149   /**
150     Underlying mutex object.
151 
152     The platform independent mutex for the ProxyMutex class. You
153     must not modify or set it directly.
154 
155   */
156   // coverity[uninit_member]
157   ink_mutex the_mutex;
158 
159   /**
160     Backpointer to owning thread.
161 
162     This is a pointer to the thread currently holding the mutex
163     lock.  You must not modify or set this value directly.
164 
165   */
166   EThreadPtr thread_holding;
167 
168   int nthread_holding;
169 
170 #ifdef DEBUG
171   ink_hrtime hold_time;
172   SourceLocation srcloc;
173   const char *handler;
174 
175 #ifdef MAX_LOCK_TAKEN
176   int taken;
177 #endif // MAX_LOCK_TAKEN
178 
179 #ifdef LOCK_CONTENTION_PROFILING
180   int total_acquires, blocking_acquires, nonblocking_acquires, successful_nonblocking_acquires, unsuccessful_nonblocking_acquires;
181   void print_lock_stats(int flag);
182 #endif // LOCK_CONTENTION_PROFILING
183 #endif // DEBUG
184   void free() override;
185 
186   /**
187     Constructor - use new_ProxyMutex() instead.
188 
189     The constructor of a ProxyMutex object. Initializes the state
190     of the object but leaves the initialization of the mutex member
191     until it is needed (through init()). Do not use this constructor,
192     the preferred mechanism for creating a ProxyMutex is via the
193     new_ProxyMutex function, which provides a faster allocation.
194 
195   */
196   ProxyMutex()
197 #ifdef DEBUG
198     : srcloc(nullptr, nullptr, 0)
199 #endif
200   {
201     thread_holding  = nullptr;
202     nthread_holding = 0;
203 #ifdef DEBUG
204     hold_time = 0;
205     handler   = nullptr;
206 #ifdef MAX_LOCK_TAKEN
207     taken = 0;
208 #endif // MAX_LOCK_TAKEN
209 #ifdef LOCK_CONTENTION_PROFILING
210     total_acquires                    = 0;
211     blocking_acquires                 = 0;
212     nonblocking_acquires              = 0;
213     successful_nonblocking_acquires   = 0;
214     unsuccessful_nonblocking_acquires = 0;
215 #endif // LOCK_CONTENTION_PROFILING
216 #endif // DEBUG
217     // coverity[uninit_member]
218   }
219 
220   /**
221     Initializes the underlying mutex object.
222 
223     After constructing your ProxyMutex object, use this function
224     to initialize the underlying mutex object with an optional name.
225 
226     @param name Name to identify this ProxyMutex. Its use depends
227       on the given platform.
228 
229   */
230   void
231   init(const char *name = "UnnamedMutex")
232   {
233     ink_mutex_init(&the_mutex);
234   }
235 };
236 
237 // The ClassAllocator for ProxyMutexes
238 extern inkcoreapi ClassAllocator<ProxyMutex> mutexAllocator;
239 
240 inline bool
241 Mutex_trylock(
242 #ifdef DEBUG
243   const SourceLocation &location, const char *ahandler,
244 #endif
245   Ptr<ProxyMutex> &m, EThread *t)
246 {
247   ink_assert(t != nullptr);
248   ink_assert(t == (EThread *)this_thread());
249   if (m->thread_holding != t) {
250     if (!ink_mutex_try_acquire(&m->the_mutex)) {
251 #ifdef DEBUG
252       lock_waiting(m->srcloc, m->handler);
253 #ifdef LOCK_CONTENTION_PROFILING
254       m->unsuccessful_nonblocking_acquires++;
255       m->nonblocking_acquires++;
256       m->total_acquires++;
257       m->print_lock_stats(0);
258 #endif // LOCK_CONTENTION_PROFILING
259 #endif // DEBUG
260       return false;
261     }
262     m->thread_holding = t;
263 #ifdef DEBUG
264     m->srcloc    = location;
265     m->handler   = ahandler;
266     m->hold_time = Thread::get_hrtime();
267 #ifdef MAX_LOCK_TAKEN
268     m->taken++;
269 #endif // MAX_LOCK_TAKEN
270 #endif // DEBUG
271   }
272 #ifdef DEBUG
273 #ifdef LOCK_CONTENTION_PROFILING
274   m->successful_nonblocking_acquires++;
275   m->nonblocking_acquires++;
276   m->total_acquires++;
277   m->print_lock_stats(0);
278 #endif // LOCK_CONTENTION_PROFILING
279 #endif // DEBUG
280   m->nthread_holding++;
281   return true;
282 }
283 
284 inline int
285 Mutex_lock(
286 #ifdef DEBUG
287   const SourceLocation &location, const char *ahandler,
288 #endif
289   Ptr<ProxyMutex> &m, EThread *t)
290 {
291   ink_assert(t != nullptr);
292   if (m->thread_holding != t) {
293     ink_mutex_acquire(&m->the_mutex);
294     m->thread_holding = t;
295     ink_assert(m->thread_holding);
296 #ifdef DEBUG
297     m->srcloc    = location;
298     m->handler   = ahandler;
299     m->hold_time = Thread::get_hrtime();
300 #ifdef MAX_LOCK_TAKEN
301     m->taken++;
302 #endif // MAX_LOCK_TAKEN
303 #endif // DEBUG
304   }
305 #ifdef DEBUG
306 #ifdef LOCK_CONTENTION_PROFILING
307   m->blocking_acquires++;
308   m->total_acquires++;
309   m->print_lock_stats(0);
310 #endif // LOCK_CONTENTION_PROFILING
311 #endif // DEBUG
312   m->nthread_holding++;
313   return true;
314 }
315 
316 inline void
317 Mutex_unlock(Ptr<ProxyMutex> &m, EThread *t)
318 {
319   if (m->nthread_holding) {
320     ink_assert(t == m->thread_holding);
321     m->nthread_holding--;
322     if (!m->nthread_holding) {
323 #ifdef DEBUG
324       if (Thread::get_hrtime() - m->hold_time > MAX_LOCK_TIME)
325         lock_holding(m->srcloc, m->handler);
326 #ifdef MAX_LOCK_TAKEN
327       if (m->taken > MAX_LOCK_TAKEN)
328         lock_taken(m->srcloc, m->handler);
329 #endif // MAX_LOCK_TAKEN
330       m->srcloc  = SourceLocation(nullptr, nullptr, 0);
331       m->handler = nullptr;
332 #endif // DEBUG
333       ink_assert(m->thread_holding);
334       m->thread_holding = nullptr;
335       ink_mutex_release(&m->the_mutex);
336     }
337   }
338 }
339 
340 /** Scoped lock class for ProxyMutex
341  */
342 class MutexLock
343 {
344 private:
345   Ptr<ProxyMutex> m;
346   bool locked_p;
347 
348 public:
349   MutexLock(
350 #ifdef DEBUG
351     const SourceLocation &location, const char *ahandler,
352 #endif // DEBUG
353     Ptr<ProxyMutex> &am, EThread *t)
354     : m(am), locked_p(true)
355   {
356     Mutex_lock(
357 #ifdef DEBUG
358       location, ahandler,
359 #endif // DEBUG
360       m, t);
361   }
362 
363   void
364   release()
365   {
366     if (locked_p) {
367       Mutex_unlock(m, m->thread_holding);
368     }
369     locked_p = false;
370   }
371 
372   ~MutexLock() { this->release(); }
373 };
374 
375 /** Scoped try lock class for ProxyMutex
376  */
377 class MutexTryLock
378 {
379 private:
380   Ptr<ProxyMutex> m;
381   bool lock_acquired;
382 
383 public:
384   MutexTryLock(
385 #ifdef DEBUG
386     const SourceLocation &location, const char *ahandler,
387 #endif // DEBUG
388     Ptr<ProxyMutex> &am, EThread *t)
389     : m(am)
390   {
391     if (am) {
392       lock_acquired = Mutex_trylock(
393 #ifdef DEBUG
394         location, ahandler,
395 #endif // DEBUG
396         m, t);
397     } else {
398       lock_acquired = true;
399     }
400   }
401 
402   ~MutexTryLock()
403   {
404     if (lock_acquired && m.get()) {
405       Mutex_unlock(m, m->thread_holding);
406     }
407   }
408 
409   /** Spin till lock is acquired
410    */
411   void
412   acquire(EThread *t)
413   {
414     lock_acquired = true;
415     if (m.get()) {
416       MUTEX_TAKE_LOCK(m, t);
417     }
418   }
419 
420   void
421   release()
422   {
423     if (lock_acquired && m.get()) {
424       Mutex_unlock(m, m->thread_holding);
425     }
426     lock_acquired = false;
427   }
428 
429   bool
430   is_locked() const
431   {
432     return lock_acquired;
433   }
434 
435   const ProxyMutex *
436   get_mutex() const
437   {
438     return m.get();
439   }
440 };
441 
442 inline void
443 ProxyMutex::free()
444 {
445 #ifdef DEBUG
446 #ifdef LOCK_CONTENTION_PROFILING
447   print_lock_stats(1);
448 #endif
449 #endif
450   ink_mutex_destroy(&the_mutex);
451   mutexAllocator.free(this);
452 }
453 
454 // TODO should take optional mutex "name" identifier, to pass along to the init() fun
455 /**
456   Creates a new ProxyMutex object.
457 
458   This is the preferred mechanism for constructing objects of the
459   ProxyMutex class. It provides you with faster allocation than
460   that of the normal constructor.
461 
462   @return A pointer to a ProxyMutex object appropriate for the build
463     environment.
464 
465 */
466 inline ProxyMutex *
467 new_ProxyMutex()
468 {
469   ProxyMutex *m = mutexAllocator.alloc();
470   m->init();
471   return m;
472 }
473