| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "base/mutex.h" |
| |
| #ifdef OS_WIN |
| #include <Windows.h> |
| #else |
| #include <pthread.h> |
| #endif |
| |
| #ifdef OS_MACOSX |
| #include <libkern/OSAtomic.h> |
| #endif // OS_MACOSX |
| |
| #include "base/port.h" |
| |
| #if defined(OS_WIN) |
| // We do not use pthread on Windows |
| #elif defined(__native_client__) |
| // TODO(team): Consider to use glibc rwlock. |
| #else |
| #define MOZC_PTHREAD_HAS_READER_WRITER_LOCK |
| #endif |
| |
| namespace mozc { |
| |
| // Wrapper for Windows InterlockedCompareExchange |
| namespace { |
| #ifdef OS_LINUX |
| // Linux doesn't provide InterlockedCompareExchange-like function. |
| inline int InterlockedCompareExchange(volatile int *target, |
| int new_value, |
| int old_value) { |
| // TODO(yusukes): For now, we use the architecture-neutral implementation, |
| // but I believe it's definitely better to port Chromium's singleton to Mozc. |
| // The implementation should be much faster and supports ARM Linux. |
| // http://src.chromium.org/viewvc/chrome/trunk/src/base/singleton.h |
| |
| static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; |
| pthread_mutex_lock(&lock); |
| int result = *target; |
| if (result == old_value) { |
| *target = new_value; |
| } |
| pthread_mutex_unlock(&lock); |
| return result; |
| } |
| #endif // OS_LINUX |
| |
| // Use OSAtomicCompareAndSwapInt on Mac OSX |
| // http://developer.apple.com/iphone/library/documentation/ |
| // system/conceptual/manpages_iphoneos/man3/OSAtomicCompareAndSwapInt.3.html |
| // TODO(taku): should we use OSAtomicCompareAndSwapIntBarrier? |
| #ifdef OS_MACOSX |
| inline int InterlockedCompareExchange(volatile int *target, |
| int new_value, |
| int old_value) { |
| return OSAtomicCompareAndSwapInt(old_value, new_value, target) |
| ? old_value : *target; |
| } |
| #endif // OX_MACOSX |
| |
| } // namespace |
| |
| #ifdef OS_WIN // Hereafter, we have Win32 implementations |
| namespace { |
| |
| template <typename T> |
| CRITICAL_SECTION *AsCriticalSection(T* opaque_buffer) { |
| static_assert(sizeof(T) >= sizeof(CRITICAL_SECTION), |
| "The opaque buffer must have sufficient space to store a " |
| "CRITICAL_SECTION structure"); |
| return reinterpret_cast<CRITICAL_SECTION *>(opaque_buffer); |
| } |
| |
| template <typename T> |
| SRWLOCK *AsSRWLock(T* opaque_buffer) { |
| static_assert(sizeof(T) >= sizeof(SRWLOCK), |
| "The opaque buffer must have sufficient space to store a " |
| "SRWLOCK structure"); |
| return reinterpret_cast<SRWLOCK *>(opaque_buffer); |
| } |
| |
| } // namespace |
| |
| Mutex::Mutex() { |
| ::InitializeCriticalSection(AsCriticalSection(&opaque_buffer_)); |
| } |
| |
| Mutex::~Mutex() { |
| ::DeleteCriticalSection(AsCriticalSection(&opaque_buffer_)); |
| } |
| |
| void Mutex::Lock() { |
| ::EnterCriticalSection(AsCriticalSection(&opaque_buffer_)); |
| } |
| |
| bool Mutex::TryLock() { |
| return ::TryEnterCriticalSection(AsCriticalSection(&opaque_buffer_)) != FALSE; |
| } |
| |
| void Mutex::Unlock() { |
| ::LeaveCriticalSection(AsCriticalSection(&opaque_buffer_)); |
| } |
| |
| ReaderWriterMutex::ReaderWriterMutex() { |
| ::InitializeSRWLock(AsSRWLock(&opaque_buffer_)); |
| } |
| |
| ReaderWriterMutex::~ReaderWriterMutex() { |
| } |
| |
| void ReaderWriterMutex::ReaderLock() { |
| ::AcquireSRWLockShared(AsSRWLock(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::WriterLock() { |
| ::AcquireSRWLockExclusive(AsSRWLock(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::ReaderUnlock() { |
| ::ReleaseSRWLockShared(AsSRWLock(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::WriterUnlock() { |
| ::ReleaseSRWLockExclusive(AsSRWLock(&opaque_buffer_)); |
| } |
| |
| bool ReaderWriterMutex::MultipleReadersThreadsSupported() { |
| return true; |
| } |
| |
| #else // Hereafter, we have pthread-based implementation |
| |
| namespace { |
| |
| template <typename T> |
| pthread_mutex_t *AsPthreadMutexT(T* opaque_buffer) { |
| static_assert(sizeof(T) >= sizeof(pthread_mutex_t), |
| "The opaque buffer must have sufficient space to store a " |
| "pthread_mutex_t structure"); |
| return reinterpret_cast<pthread_mutex_t *>(opaque_buffer); |
| } |
| |
| } // namespace |
| |
| Mutex::Mutex() { |
| // make RECURSIVE lock: |
| // The default is no-recursive lock. |
| // When mutex.Lock() is called while the mutex is already locked, |
| // mutex will be blocked. This behavior is not compatible with windows. |
| // We set PTHREAD_MUTEX_RECURSIVE_NP to make the mutex recursive-lock |
| |
| // PTHREAD_MUTEX_RECURSIVE_NP and PTHREAD_MUTEX_RECURSIVE seem to be |
| // variants. For example, Mac OS X 10.4 had |
| // PTHREAD_MUTEX_RECURSIVE_NP but Mac OS X 10.5 does not |
| pthread_mutexattr_t attr; |
| pthread_mutexattr_init(&attr); |
| #if defined(OS_MACOSX) |
| pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); |
| #elif defined(OS_LINUX) |
| pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); |
| #else |
| #error "This platform is not supported." |
| #endif |
| pthread_mutex_init(AsPthreadMutexT(&opaque_buffer_), &attr); |
| } |
| |
| Mutex::~Mutex() { |
| pthread_mutex_destroy(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| void Mutex::Lock() { |
| pthread_mutex_lock(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| bool Mutex::TryLock() { |
| return pthread_mutex_trylock(AsPthreadMutexT(&opaque_buffer_)) == 0; |
| } |
| |
| void Mutex::Unlock() { |
| pthread_mutex_unlock(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| #ifdef MOZC_USE_PEPPER_FILE_IO |
| pthread_mutex_t *Mutex::raw_mutex() { |
| return AsPthreadMutexT(&opaque_buffer_); |
| } |
| #endif // MOZC_USE_PEPPER_FILE_IO |
| |
| #ifdef MOZC_PTHREAD_HAS_READER_WRITER_LOCK |
| // Use pthread native reader writer lock. |
| namespace { |
| |
| template <typename T> |
| pthread_rwlock_t *AsPthreadRWLockT(T* opaque_buffer) { |
| static_assert(sizeof(T) >= sizeof(pthread_rwlock_t), |
| "The opaque buffer must have sufficient space to store a " |
| "pthread_rwlock_t structure"); |
| return reinterpret_cast<pthread_rwlock_t *>(opaque_buffer); |
| } |
| |
| } // namespace |
| |
| ReaderWriterMutex::ReaderWriterMutex() { |
| pthread_rwlock_init(AsPthreadRWLockT(&opaque_buffer_), NULL); |
| } |
| |
| ReaderWriterMutex::~ReaderWriterMutex() { |
| pthread_rwlock_destroy(AsPthreadRWLockT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::ReaderLock() { |
| pthread_rwlock_rdlock(AsPthreadRWLockT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::ReaderUnlock() { |
| pthread_rwlock_unlock(AsPthreadRWLockT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::WriterLock() { |
| pthread_rwlock_wrlock(AsPthreadRWLockT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::WriterUnlock() { |
| pthread_rwlock_unlock(AsPthreadRWLockT(&opaque_buffer_)); |
| } |
| |
| bool ReaderWriterMutex::MultipleReadersThreadsSupported() { |
| return true; |
| } |
| |
| #else |
| |
| // Fallback implementations as ReaderWriterLock is not available. |
| ReaderWriterMutex::ReaderWriterMutex() { |
| // Non-recursive lock is OK. |
| pthread_mutex_init(AsPthreadMutexT(&opaque_buffer_), NULL); |
| } |
| |
| ReaderWriterMutex::~ReaderWriterMutex() { |
| pthread_mutex_destroy(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::ReaderLock() { |
| pthread_mutex_lock(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::WriterLock() { |
| pthread_mutex_lock(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::ReaderUnlock() { |
| pthread_mutex_unlock(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| void ReaderWriterMutex::WriterUnlock() { |
| pthread_mutex_unlock(AsPthreadMutexT(&opaque_buffer_)); |
| } |
| |
| bool ReaderWriterMutex::MultipleReadersThreadsSupported() { |
| return false; |
| } |
| |
| #endif // MOZC_PTHREAD_HAS_READER_WRITER_LOCK |
| |
| #endif // OS_WIN or pthread |
| |
| void CallOnce(once_t *once, void (*func)()) { |
| if (once == NULL || func == NULL) { |
| return; |
| } |
| |
| if (once->state != ONCE_INIT) { |
| return; |
| } |
| |
| // change the counter in atomic |
| if (0 == InterlockedCompareExchange(&(once->counter), 1, 0)) { |
| // call func |
| (*func)(); |
| // change the status to be ONCE_DONE in atomic |
| // Maybe we won't use it, but in order to make memory barrier, |
| // we use InterlockedCompareExchange just in case. |
| InterlockedCompareExchange(&(once->state), ONCE_DONE, ONCE_INIT); |
| } else { |
| while (once->state == ONCE_INIT) { |
| #ifdef OS_WIN |
| ::YieldProcessor(); |
| #endif // OS_WIN |
| } // busy loop |
| } |
| } |
| |
| void ResetOnce(once_t *once) { |
| InterlockedCompareExchange(&(once->state), ONCE_INIT, ONCE_DONE); |
| InterlockedCompareExchange(&(once->counter), 0, 1); |
| } |
| |
| } // namespace mozc |