| // Copyright 2015 Google Inc. 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. | |
| #include "starboard/log.h" | |
| #include "starboard/memory.h" | |
| #include "starboard/once.h" | |
| #include "starboard/shared/starboard/thread_local_storage_internal.h" | |
| struct SbThreadLocalKeyPrivate { | |
| int index; | |
| }; | |
| namespace starboard { | |
| namespace shared { | |
| namespace { | |
| TLSKeyManager* s_instance = NULL; | |
| SbOnceControl s_instance_control = SB_ONCE_INITIALIZER; | |
| void InitializeTLS() { | |
| s_instance = new TLSKeyManager(); | |
| } | |
| } // namespace | |
| TLSKeyManager* TLSKeyManager::Get() { | |
| SbOnce(&s_instance_control, &InitializeTLS); | |
| return s_instance; | |
| } | |
| TLSKeyManager::TLSKeyManager() { | |
| available_thread_ids_.reserve(kMaxThreads); | |
| for (int i = 0; i < kMaxThreads; ++i) { | |
| available_thread_ids_.push_back(i); | |
| } | |
| } | |
| SbThreadLocalKey TLSKeyManager::CreateKey(SbThreadLocalDestructor destructor) { | |
| SbThreadLocalKey key = new SbThreadLocalKeyPrivate(); | |
| ScopedLock lock(mutex_); | |
| key->index = GetUnusedKeyIndex(); | |
| KeyRecord* record = &key_table_[key->index]; | |
| record->destructor = destructor; | |
| SbMemorySet(record->values, 0, sizeof(record->values)); | |
| record->valid = true; | |
| return key; | |
| } | |
| void TLSKeyManager::DestroyKey(SbThreadLocalKey key) { | |
| if (!SbThreadIsValidLocalKey(key)) { | |
| return; | |
| } | |
| ScopedLock lock(mutex_); | |
| SB_DCHECK(IsKeyActive(key)); | |
| key_table_[key->index].valid = false; | |
| delete key; | |
| } | |
| bool TLSKeyManager::SetLocalValue(SbThreadLocalKey key, void* value) { | |
| if (!SbThreadIsValidLocalKey(key)) { | |
| return false; | |
| } | |
| ScopedLock lock(mutex_); | |
| if (!IsKeyActive(key)) { | |
| return false; | |
| } | |
| key_table_[key->index].values[GetCurrentThreadId()] = value; | |
| return true; | |
| } | |
| void* TLSKeyManager::GetLocalValue(SbThreadLocalKey key) { | |
| if (!SbThreadIsValidLocalKey(key)) { | |
| return NULL; | |
| } | |
| ScopedLock lock(mutex_); | |
| if (!IsKeyActive(key)) { | |
| return NULL; | |
| } | |
| return key_table_[key->index].values[GetCurrentThreadId()]; | |
| } | |
| void TLSKeyManager::InitializeTLSForThread() { | |
| ScopedLock lock(mutex_); | |
| int current_thread_id = GetCurrentThreadId(); | |
| for (int i = 0; i < key_table_.size(); ++i) { | |
| KeyRecord* key_record = &key_table_[i]; | |
| if (key_record->valid) { | |
| key_record->values[current_thread_id] = NULL; | |
| } | |
| } | |
| } | |
| void TLSKeyManager::ShutdownTLSForThread() { | |
| ScopedLock lock(mutex_); | |
| int current_thread_id = GetCurrentThreadId(); | |
| // Apply the destructors multiple times (4 is the minimum value | |
| // according to the specifications). This is necessary if one of | |
| // the destructors adds new values to the key map. | |
| for (int d = 0; d < 4; ++d) { | |
| // Move the map into a new temporary map so that we can iterate | |
| // through that while the original s_tls_thread_keys may have more | |
| // values added to it via destructor calls. | |
| for (int i = 0; i < key_table_.size(); ++i) { | |
| KeyRecord* key_record = &key_table_[i]; | |
| if (key_record->valid) { | |
| void* value = key_record->values[current_thread_id]; | |
| key_record->values[current_thread_id] = NULL; | |
| SbThreadLocalDestructor destructor = key_record->destructor; | |
| if (value && destructor) { | |
| mutex_.Release(); | |
| destructor(value); | |
| mutex_.Acquire(); | |
| } | |
| } | |
| } | |
| } | |
| thread_id_map_.erase(SbThreadGetId()); | |
| available_thread_ids_.push_back(current_thread_id); | |
| } | |
| bool TLSKeyManager::IsKeyActive(SbThreadLocalKey key) { | |
| return key_table_[key->index].valid; | |
| } | |
| int TLSKeyManager::GetUnusedKeyIndex() { | |
| for (int i = 0; i < key_table_.size(); ++i) { | |
| if (!key_table_[i].valid) { | |
| return i; | |
| } | |
| } | |
| key_table_.push_back(KeyRecord()); | |
| key_table_.back().valid = false; | |
| return key_table_.size() - 1; | |
| } | |
| int TLSKeyManager::GetCurrentThreadId() { | |
| std::map<SbThreadId, int>::const_iterator found = | |
| thread_id_map_.find(SbThreadGetId()); | |
| if (found != thread_id_map_.end()) { | |
| return found->second; | |
| } | |
| SB_DCHECK(!available_thread_ids_.empty()); | |
| int thread_tls_id = available_thread_ids_.back(); | |
| available_thread_ids_.pop_back(); | |
| thread_id_map_.insert(std::make_pair(SbThreadGetId(), thread_tls_id)); | |
| return thread_tls_id; | |
| } | |
| } // namespace shared | |
| } // namespace starboard |