1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "base/mutex.h"
31 
32 #ifdef OS_WIN
33 #include <Windows.h>
34 #else
35 #include <pthread.h>
36 #endif
37 
38 #include <atomic>
39 
40 #include "base/port.h"
41 
42 #if defined(OS_WIN)
43 // We do not use pthread on Windows
44 #elif defined(OS_NACL)
45 // TODO(team): Consider to use glibc rwlock.
46 #else
47 #define MOZC_PTHREAD_HAS_READER_WRITER_LOCK
48 #endif
49 
50 namespace mozc {
51 namespace {
52 
53 // State for once_t.
54 enum CallOnceState {
55   ONCE_INIT = 0,
56   ONCE_RUNNING = 1,
57   ONCE_DONE = 2,
58 };
59 
60 }  // namespace
61 
62 #ifdef OS_WIN  // Hereafter, we have Win32 implementations
63 namespace {
64 
65 template <typename T>
AsCriticalSection(T * opaque_buffer)66 CRITICAL_SECTION *AsCriticalSection(T* opaque_buffer) {
67   static_assert(sizeof(T) >= sizeof(CRITICAL_SECTION),
68                 "The opaque buffer must have sufficient space to store a "
69                 "CRITICAL_SECTION structure");
70   return reinterpret_cast<CRITICAL_SECTION *>(opaque_buffer);
71 }
72 
73 template <typename T>
AsSRWLock(T * opaque_buffer)74 SRWLOCK *AsSRWLock(T* opaque_buffer) {
75   static_assert(sizeof(T) >= sizeof(SRWLOCK),
76                 "The opaque buffer must have sufficient space to store a "
77                 "SRWLOCK structure");
78   return reinterpret_cast<SRWLOCK *>(opaque_buffer);
79 }
80 
81 }  // namespace
82 
Mutex()83 Mutex::Mutex() {
84   ::InitializeCriticalSection(AsCriticalSection(&opaque_buffer_));
85 }
86 
~Mutex()87 Mutex::~Mutex() {
88   ::DeleteCriticalSection(AsCriticalSection(&opaque_buffer_));
89 }
90 
Lock()91 void Mutex::Lock() {
92   ::EnterCriticalSection(AsCriticalSection(&opaque_buffer_));
93 }
94 
TryLock()95 bool Mutex::TryLock() {
96   return ::TryEnterCriticalSection(AsCriticalSection(&opaque_buffer_)) != FALSE;
97 }
98 
Unlock()99 void Mutex::Unlock() {
100   ::LeaveCriticalSection(AsCriticalSection(&opaque_buffer_));
101 }
102 
ReaderWriterMutex()103 ReaderWriterMutex::ReaderWriterMutex() {
104   ::InitializeSRWLock(AsSRWLock(&opaque_buffer_));
105 }
106 
~ReaderWriterMutex()107 ReaderWriterMutex::~ReaderWriterMutex() {
108 }
109 
ReaderLock()110 void ReaderWriterMutex::ReaderLock() {
111   ::AcquireSRWLockShared(AsSRWLock(&opaque_buffer_));
112 }
113 
WriterLock()114 void ReaderWriterMutex::WriterLock() {
115   ::AcquireSRWLockExclusive(AsSRWLock(&opaque_buffer_));
116 }
117 
ReaderUnlock()118 void ReaderWriterMutex::ReaderUnlock() {
119   ::ReleaseSRWLockShared(AsSRWLock(&opaque_buffer_));
120 }
121 
WriterUnlock()122 void ReaderWriterMutex::WriterUnlock() {
123   ::ReleaseSRWLockExclusive(AsSRWLock(&opaque_buffer_));
124 }
125 
MultipleReadersThreadsSupported()126 bool ReaderWriterMutex::MultipleReadersThreadsSupported() {
127   return true;
128 }
129 
130 #else  // Hereafter, we have pthread-based implementation
131 
132 namespace {
133 
134 template <typename T>
AsPthreadMutexT(T * opaque_buffer)135 pthread_mutex_t *AsPthreadMutexT(T* opaque_buffer) {
136   static_assert(sizeof(T) >= sizeof(pthread_mutex_t),
137                 "The opaque buffer must have sufficient space to store a "
138                 "pthread_mutex_t structure");
139   return reinterpret_cast<pthread_mutex_t *>(opaque_buffer);
140 }
141 
142 }  // namespace
143 
Mutex()144 Mutex::Mutex() {
145   // make RECURSIVE lock:
146   // The default is no-recursive lock.
147   // When mutex.Lock() is called while the mutex is already locked,
148   // mutex will be blocked. This behavior is not compatible with windows.
149   // We set PTHREAD_MUTEX_RECURSIVE_NP to make the mutex recursive-lock
150 
151   // PTHREAD_MUTEX_RECURSIVE_NP and PTHREAD_MUTEX_RECURSIVE seem to be
152   // variants.  For example, Mac OS X 10.4 had
153   // PTHREAD_MUTEX_RECURSIVE_NP but Mac OS X 10.5 does not
154   pthread_mutexattr_t attr;
155   pthread_mutexattr_init(&attr);
156 #if defined(OS_MACOSX) || defined(OS_DRAGONFLY)
157   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
158 #elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_NACL)
159   pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
160 #else
161 #error "This platform is not supported."
162 #endif
163   pthread_mutex_init(AsPthreadMutexT(&opaque_buffer_), &attr);
164 }
165 
~Mutex()166 Mutex::~Mutex() {
167   pthread_mutex_destroy(AsPthreadMutexT(&opaque_buffer_));
168 }
169 
Lock()170 void Mutex::Lock() {
171   pthread_mutex_lock(AsPthreadMutexT(&opaque_buffer_));
172 }
173 
TryLock()174 bool Mutex::TryLock() {
175   return pthread_mutex_trylock(AsPthreadMutexT(&opaque_buffer_)) == 0;
176 }
177 
Unlock()178 void Mutex::Unlock() {
179   pthread_mutex_unlock(AsPthreadMutexT(&opaque_buffer_));
180 }
181 
182 #ifdef MOZC_USE_PEPPER_FILE_IO
raw_mutex()183 pthread_mutex_t *Mutex::raw_mutex() {
184   return AsPthreadMutexT(&opaque_buffer_);
185 }
186 #endif  // MOZC_USE_PEPPER_FILE_IO
187 
188 #ifdef MOZC_PTHREAD_HAS_READER_WRITER_LOCK
189 // Use pthread native reader writer lock.
190 namespace {
191 
192 template <typename T>
AsPthreadRWLockT(T * opaque_buffer)193 pthread_rwlock_t *AsPthreadRWLockT(T* opaque_buffer) {
194   static_assert(sizeof(T) >= sizeof(pthread_rwlock_t),
195                 "The opaque buffer must have sufficient space to store a "
196                 "pthread_rwlock_t structure");
197   return reinterpret_cast<pthread_rwlock_t *>(opaque_buffer);
198 }
199 
200 }  // namespace
201 
ReaderWriterMutex()202 ReaderWriterMutex::ReaderWriterMutex() {
203   pthread_rwlock_init(AsPthreadRWLockT(&opaque_buffer_), NULL);
204 }
205 
~ReaderWriterMutex()206 ReaderWriterMutex::~ReaderWriterMutex() {
207   pthread_rwlock_destroy(AsPthreadRWLockT(&opaque_buffer_));
208 }
209 
ReaderLock()210 void ReaderWriterMutex::ReaderLock() {
211   pthread_rwlock_rdlock(AsPthreadRWLockT(&opaque_buffer_));
212 }
213 
ReaderUnlock()214 void ReaderWriterMutex::ReaderUnlock() {
215   pthread_rwlock_unlock(AsPthreadRWLockT(&opaque_buffer_));
216 }
217 
WriterLock()218 void ReaderWriterMutex::WriterLock() {
219   pthread_rwlock_wrlock(AsPthreadRWLockT(&opaque_buffer_));
220 }
221 
WriterUnlock()222 void ReaderWriterMutex::WriterUnlock() {
223   pthread_rwlock_unlock(AsPthreadRWLockT(&opaque_buffer_));
224 }
225 
MultipleReadersThreadsSupported()226 bool ReaderWriterMutex::MultipleReadersThreadsSupported() {
227   return true;
228 }
229 
230 #else
231 
232 // Fallback implementations as ReaderWriterLock is not available.
ReaderWriterMutex()233 ReaderWriterMutex::ReaderWriterMutex() {
234   // Non-recursive lock is OK.
235   pthread_mutex_init(AsPthreadMutexT(&opaque_buffer_), NULL);
236 }
237 
~ReaderWriterMutex()238 ReaderWriterMutex::~ReaderWriterMutex() {
239   pthread_mutex_destroy(AsPthreadMutexT(&opaque_buffer_));
240 }
241 
ReaderLock()242 void ReaderWriterMutex::ReaderLock() {
243   pthread_mutex_lock(AsPthreadMutexT(&opaque_buffer_));
244 }
245 
WriterLock()246 void ReaderWriterMutex::WriterLock() {
247   pthread_mutex_lock(AsPthreadMutexT(&opaque_buffer_));
248 }
249 
ReaderUnlock()250 void ReaderWriterMutex::ReaderUnlock() {
251   pthread_mutex_unlock(AsPthreadMutexT(&opaque_buffer_));
252 }
253 
WriterUnlock()254 void ReaderWriterMutex::WriterUnlock() {
255   pthread_mutex_unlock(AsPthreadMutexT(&opaque_buffer_));
256 }
257 
MultipleReadersThreadsSupported()258 bool ReaderWriterMutex::MultipleReadersThreadsSupported() {
259   return false;
260 }
261 
262 #endif  // MOZC_PTHREAD_HAS_READER_WRITER_LOCK
263 
264 #endif  // OS_WIN or pthread
265 
CallOnce(once_t * once,void (* func)())266 void CallOnce(once_t *once, void (*func)()) {
267   if (once == nullptr || func == nullptr) {
268     return;
269   }
270   int expected_state = ONCE_INIT;
271   if (once->compare_exchange_strong(expected_state, ONCE_RUNNING)) {
272     (*func)();
273     *once = ONCE_DONE;
274     return;
275   }
276   // If the above compare_exchange_strong() returns false, it stores the value
277   // of once to expected_state, which is ONCE_RUNNING or ONCE_DONE.
278   if (expected_state == ONCE_DONE) {
279     return;
280   }
281   // Here's the case where expected_state == ONCE_RUNNING, indicating that
282   // another thread is calling func.  Wait for it to complete.
283   while (*once == ONCE_RUNNING) {
284 #ifdef OS_WIN
285     ::YieldProcessor();
286 #endif  // OS_WIN
287   }  // Busy loop
288 }
289 
ResetOnce(once_t * once)290 void ResetOnce(once_t *once) {
291   if (once) {
292     *once = ONCE_INIT;
293   }
294 }
295 
296 }  // namespace mozc
297