blob: d5a62187018eaf2c75e6e221b3fc9ac2673524b9 [file] [log] [blame]
// 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