// 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 |