| // Copyright 2019 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Based on thread_pthread.c. |
| |
| #include "internal.h" |
| |
| #if defined(STARBOARD) |
| |
| #include <openssl/mem.h> |
| |
| #include "starboard/once.h" |
| #include "starboard/thread.h" |
| |
| namespace { |
| |
| // Each enum of thread_local_data_t corresponds to a ThreadLocalEntry. |
| struct ThreadLocalEntry { |
| thread_local_destructor_t destructor; |
| void* value; |
| }; |
| |
| // One thread local key will point to an array of ThreadLocalEntry. |
| SbThreadLocalKey g_thread_local_key = kSbThreadLocalKeyInvalid; |
| |
| // Control the creation of the global thread local key. |
| SbOnceControl g_thread_local_once_control = SB_ONCE_INITIALIZER; |
| |
| void ThreadLocalDestructor(void* value) { |
| if (value) { |
| ThreadLocalEntry* thread_locals = static_cast<ThreadLocalEntry*>(value); |
| for (int i = 0; i < NUM_OPENSSL_THREAD_LOCALS; ++i) { |
| if (thread_locals[i].destructor != nullptr) { |
| thread_locals[i].destructor(thread_locals[i].value); |
| } |
| } |
| OPENSSL_free(value); |
| } |
| } |
| |
| void ThreadLocalInit() { |
| g_thread_local_key = SbThreadCreateLocalKey(&ThreadLocalDestructor); |
| } |
| |
| void EnsureInitialized(struct CRYPTO_STATIC_MUTEX* lock) { |
| enum { |
| kUninitialized = 0, |
| kInitializing, |
| kInitialized |
| }; |
| |
| if (SbAtomicNoBarrier_Load(&lock->initialized) == kInitialized) { |
| return; |
| } |
| if (SbAtomicNoBarrier_CompareAndSwap(&lock->initialized, |
| kUninitialized, kInitializing) == kUninitialized) { |
| CRYPTO_MUTEX_init(&lock->mutex); |
| SbAtomicNoBarrier_Store(&lock->initialized, kInitialized); |
| return; |
| } |
| while (SbAtomicNoBarrier_Load(&lock->initialized) != kInitialized) { |
| SbThreadSleep(1000); // 1ms |
| } |
| } |
| |
| } // namespace |
| |
| void CRYPTO_MUTEX_init(CRYPTO_MUTEX* lock) { |
| if (!SbMutexCreate(&lock->mutex)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| if (!SbConditionVariableCreate(&lock->condition, &lock->mutex)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| lock->readers = 0; |
| lock->writing = false; |
| } |
| |
| // https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock |
| void CRYPTO_MUTEX_lock_read(CRYPTO_MUTEX* lock) { |
| if (SbMutexAcquire(&lock->mutex) != kSbMutexAcquired) { |
| SbSystemBreakIntoDebugger(); |
| } |
| while (lock->writing) { |
| if (SbConditionVariableWait(&lock->condition, &lock->mutex) == |
| kSbConditionVariableFailed) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| ++(lock->readers); |
| if (!SbMutexRelease(&lock->mutex)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| // https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock |
| void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX* lock) { |
| if (SbMutexAcquire(&lock->mutex) != kSbMutexAcquired) { |
| SbSystemBreakIntoDebugger(); |
| } |
| while (lock->writing) { |
| if (SbConditionVariableWait(&lock->condition, &lock->mutex) == |
| kSbConditionVariableFailed) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| lock->writing = true; |
| while (lock->readers > 0) { |
| if (SbConditionVariableWait(&lock->condition, &lock->mutex) == |
| kSbConditionVariableFailed) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| if (!SbMutexRelease(&lock->mutex)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| // https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock |
| void CRYPTO_MUTEX_unlock_read(CRYPTO_MUTEX* lock) { |
| if (SbMutexAcquire(&lock->mutex) != kSbMutexAcquired) { |
| SbSystemBreakIntoDebugger(); |
| } |
| if (--(lock->readers) == 0) { |
| SbConditionVariableBroadcast(&lock->condition); |
| } |
| if (!SbMutexRelease(&lock->mutex)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| // https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock |
| void CRYPTO_MUTEX_unlock_write(CRYPTO_MUTEX* lock) { |
| if (SbMutexAcquire(&lock->mutex) != kSbMutexAcquired) { |
| SbSystemBreakIntoDebugger(); |
| } |
| lock->writing = false; |
| SbConditionVariableBroadcast(&lock->condition); |
| if (!SbMutexRelease(&lock->mutex)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX* lock) { |
| if (!SbConditionVariableDestroy(&lock->condition)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| if (!SbMutexDestroy(&lock->mutex)) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| void CRYPTO_STATIC_MUTEX_lock_read(struct CRYPTO_STATIC_MUTEX* lock) { |
| EnsureInitialized(lock); |
| CRYPTO_MUTEX_lock_read(&lock->mutex); |
| } |
| |
| void CRYPTO_STATIC_MUTEX_lock_write(struct CRYPTO_STATIC_MUTEX* lock) { |
| EnsureInitialized(lock); |
| CRYPTO_MUTEX_lock_write(&lock->mutex); |
| } |
| |
| void CRYPTO_STATIC_MUTEX_unlock_read(struct CRYPTO_STATIC_MUTEX* lock) { |
| EnsureInitialized(lock); |
| CRYPTO_MUTEX_unlock_read(&lock->mutex); |
| } |
| |
| void CRYPTO_STATIC_MUTEX_unlock_write(struct CRYPTO_STATIC_MUTEX* lock) { |
| EnsureInitialized(lock); |
| CRYPTO_MUTEX_unlock_write(&lock->mutex); |
| } |
| |
| void* CRYPTO_get_thread_local(thread_local_data_t index) { |
| SbOnce(&g_thread_local_once_control, &ThreadLocalInit); |
| if (!SbThreadIsValidLocalKey(g_thread_local_key)) { |
| return nullptr; |
| } |
| |
| ThreadLocalEntry* thread_locals = static_cast<ThreadLocalEntry*>( |
| SbThreadGetLocalValue(g_thread_local_key)); |
| if (thread_locals == nullptr) { |
| return nullptr; |
| } |
| |
| return thread_locals[index].value; |
| } |
| |
| int CRYPTO_set_thread_local(thread_local_data_t index, void *value, |
| thread_local_destructor_t destructor) { |
| SbOnce(&g_thread_local_once_control, &ThreadLocalInit); |
| if (!SbThreadIsValidLocalKey(g_thread_local_key)) { |
| destructor(value); |
| return 0; |
| } |
| |
| ThreadLocalEntry* thread_locals = static_cast<ThreadLocalEntry*>( |
| SbThreadGetLocalValue(g_thread_local_key)); |
| if (thread_locals == nullptr) { |
| size_t thread_locals_size = |
| sizeof(ThreadLocalEntry) * NUM_OPENSSL_THREAD_LOCALS; |
| thread_locals = static_cast<ThreadLocalEntry*>( |
| OPENSSL_malloc(thread_locals_size)); |
| if (thread_locals == nullptr) { |
| destructor(value); |
| return 0; |
| } |
| OPENSSL_memset(thread_locals, 0, thread_locals_size); |
| if (!SbThreadSetLocalValue(g_thread_local_key, thread_locals)) { |
| OPENSSL_free(thread_locals); |
| destructor(value); |
| return 0; |
| } |
| } |
| |
| thread_locals[index].destructor = destructor; |
| thread_locals[index].value = value; |
| return 1; |
| } |
| |
| #endif // defined(STARBOARD) |