1 | /* |
2 | Copyright (c) 2005-2019 Intel Corporation |
3 | |
4 | Licensed under the Apache License, Version 2.0 (the "License"); |
5 | you may not use this file except in compliance with the License. |
6 | You may obtain a copy of the License at |
7 | |
8 | http://www.apache.org/licenses/LICENSE-2.0 |
9 | |
10 | Unless required by applicable law or agreed to in writing, software |
11 | distributed under the License is distributed on an "AS IS" BASIS, |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | See the License for the specific language governing permissions and |
14 | limitations under the License. |
15 | */ |
16 | |
17 | #ifndef __TBB_queuing_rw_mutex_H |
18 | #define __TBB_queuing_rw_mutex_H |
19 | |
20 | #include <cstring> |
21 | #include "atomic.h" |
22 | #include "tbb_profiling.h" |
23 | |
24 | namespace tbb { |
25 | |
26 | //! Queuing reader-writer mutex with local-only spinning. |
27 | /** Adapted from Krieger, Stumm, et al. pseudocode at |
28 | http://www.eecg.toronto.edu/parallel/pubs_abs.html#Krieger_etal_ICPP93 |
29 | @ingroup synchronization */ |
30 | class queuing_rw_mutex : internal::mutex_copy_deprecated_and_disabled { |
31 | public: |
32 | //! Construct unacquired mutex. |
33 | queuing_rw_mutex() { |
34 | q_tail = NULL; |
35 | #if TBB_USE_THREADING_TOOLS |
36 | internal_construct(); |
37 | #endif |
38 | } |
39 | |
40 | //! Destructor asserts if the mutex is acquired, i.e. q_tail is non-NULL |
41 | ~queuing_rw_mutex() { |
42 | #if TBB_USE_ASSERT |
43 | __TBB_ASSERT( !q_tail, "destruction of an acquired mutex" ); |
44 | #endif |
45 | } |
46 | |
47 | //! The scoped locking pattern |
48 | /** It helps to avoid the common problem of forgetting to release lock. |
49 | It also nicely provides the "node" for queuing locks. */ |
50 | class scoped_lock: internal::no_copy { |
51 | //! Initialize fields to mean "no lock held". |
52 | void initialize() { |
53 | my_mutex = NULL; |
54 | my_internal_lock = 0; |
55 | my_going = 0; |
56 | #if TBB_USE_ASSERT |
57 | my_state = 0xFF; // Set to invalid state |
58 | internal::poison_pointer(my_next); |
59 | internal::poison_pointer(my_prev); |
60 | #endif /* TBB_USE_ASSERT */ |
61 | } |
62 | |
63 | public: |
64 | //! Construct lock that has not acquired a mutex. |
65 | /** Equivalent to zero-initialization of *this. */ |
66 | scoped_lock() {initialize();} |
67 | |
68 | //! Acquire lock on given mutex. |
69 | scoped_lock( queuing_rw_mutex& m, bool write=true ) { |
70 | initialize(); |
71 | acquire(m,write); |
72 | } |
73 | |
74 | //! Release lock (if lock is held). |
75 | ~scoped_lock() { |
76 | if( my_mutex ) release(); |
77 | } |
78 | |
79 | //! Acquire lock on given mutex. |
80 | void acquire( queuing_rw_mutex& m, bool write=true ); |
81 | |
82 | //! Acquire lock on given mutex if free (i.e. non-blocking) |
83 | bool try_acquire( queuing_rw_mutex& m, bool write=true ); |
84 | |
85 | //! Release lock. |
86 | void release(); |
87 | |
88 | //! Upgrade reader to become a writer. |
89 | /** Returns whether the upgrade happened without releasing and re-acquiring the lock */ |
90 | bool upgrade_to_writer(); |
91 | |
92 | //! Downgrade writer to become a reader. |
93 | bool downgrade_to_reader(); |
94 | |
95 | private: |
96 | //! The pointer to the mutex owned, or NULL if not holding a mutex. |
97 | queuing_rw_mutex* my_mutex; |
98 | |
99 | //! The pointer to the previous and next competitors for a mutex |
100 | scoped_lock *__TBB_atomic my_prev, *__TBB_atomic my_next; |
101 | |
102 | typedef unsigned char state_t; |
103 | |
104 | //! State of the request: reader, writer, active reader, other service states |
105 | atomic<state_t> my_state; |
106 | |
107 | //! The local spin-wait variable |
108 | /** Corresponds to "spin" in the pseudocode but inverted for the sake of zero-initialization */ |
109 | unsigned char __TBB_atomic my_going; |
110 | |
111 | //! A tiny internal lock |
112 | unsigned char my_internal_lock; |
113 | |
114 | //! Acquire the internal lock |
115 | void acquire_internal_lock(); |
116 | |
117 | //! Try to acquire the internal lock |
118 | /** Returns true if lock was successfully acquired. */ |
119 | bool try_acquire_internal_lock(); |
120 | |
121 | //! Release the internal lock |
122 | void release_internal_lock(); |
123 | |
124 | //! Wait for internal lock to be released |
125 | void wait_for_release_of_internal_lock(); |
126 | |
127 | //! A helper function |
128 | void unblock_or_wait_on_internal_lock( uintptr_t ); |
129 | }; |
130 | |
131 | void __TBB_EXPORTED_METHOD internal_construct(); |
132 | |
133 | // Mutex traits |
134 | static const bool is_rw_mutex = true; |
135 | static const bool is_recursive_mutex = false; |
136 | static const bool is_fair_mutex = true; |
137 | |
138 | private: |
139 | //! The last competitor requesting the lock |
140 | atomic<scoped_lock*> q_tail; |
141 | |
142 | }; |
143 | |
144 | __TBB_DEFINE_PROFILING_SET_NAME(queuing_rw_mutex) |
145 | |
146 | } // namespace tbb |
147 | |
148 | #endif /* __TBB_queuing_rw_mutex_H */ |
149 | |