| // 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 <pthread.h> |
| #include <unistd.h> |
| |
| #include <openssl/mem.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. |
| static pthread_key_t g_thread_local_key = 0; |
| static int g_thread_local_key_created = 0; |
| |
| // Control the creation of the global thread local key. |
| pthread_once_t g_thread_local_once_control = PTHREAD_ONCE_INIT; |
| |
| 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_created = pthread_key_create(&g_thread_local_key, &ThreadLocalDestructor) == 0; |
| } |
| |
| 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) { |
| usleep(1000); // 1ms |
| } |
| } |
| |
| } // namespace |
| |
| void CRYPTO_MUTEX_init(CRYPTO_MUTEX* lock) { |
| if (pthread_mutex_init(&lock->mutex, nullptr) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| if (pthread_cond_init(&lock->condition, nullptr) != 0) { |
| 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 (pthread_mutex_lock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| while (lock->writing) { |
| if (pthread_cond_wait(&lock->condition, &lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| ++(lock->readers); |
| if (pthread_mutex_unlock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| // https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock |
| void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX* lock) { |
| if (pthread_mutex_lock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| while (lock->writing) { |
| if (pthread_cond_wait(&lock->condition, &lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| lock->writing = true; |
| while (lock->readers > 0) { |
| if (pthread_cond_wait(&lock->condition, &lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| if (pthread_mutex_unlock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| // https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock |
| void CRYPTO_MUTEX_unlock_read(CRYPTO_MUTEX* lock) { |
| if (pthread_mutex_lock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| if (--(lock->readers) == 0) { |
| pthread_cond_broadcast(&lock->condition); |
| } |
| if (pthread_mutex_unlock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| // https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock |
| void CRYPTO_MUTEX_unlock_write(CRYPTO_MUTEX* lock) { |
| if (pthread_mutex_lock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| lock->writing = false; |
| pthread_cond_broadcast(&lock->condition); |
| if (pthread_mutex_unlock(&lock->mutex) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| } |
| |
| void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX* lock) { |
| if (pthread_cond_destroy(&lock->condition) != 0) { |
| SbSystemBreakIntoDebugger(); |
| } |
| if (pthread_mutex_destroy(&lock->mutex) != 0) { |
| 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) { |
| pthread_once(&g_thread_local_once_control, &ThreadLocalInit); |
| if (!g_thread_local_key_created) { |
| return nullptr; |
| } |
| |
| ThreadLocalEntry* thread_locals = static_cast<ThreadLocalEntry*>( |
| pthread_getspecific(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) { |
| pthread_once(&g_thread_local_once_control, &ThreadLocalInit); |
| if (!g_thread_local_key_created) { |
| destructor(value); |
| return 0; |
| } |
| |
| ThreadLocalEntry* thread_locals = static_cast<ThreadLocalEntry*>( |
| pthread_getspecific(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 (pthread_setspecific(g_thread_local_key, thread_locals) !=0 ) { |
| OPENSSL_free(thread_locals); |
| destructor(value); |
| return 0; |
| } |
| } |
| |
| thread_locals[index].destructor = destructor; |
| thread_locals[index].value = value; |
| return 1; |
| } |
| |
| #endif // defined(STARBOARD) |