forked from mitsuba-renderer/mitsuba2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtls.cpp
177 lines (145 loc) · 4.68 KB
/
tls.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include <mitsuba/core/tls.h>
#include <tbb/spin_mutex.h>
#include <tbb/mutex.h>
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include <list>
#if defined(__OSX__)
#include <pthread.h>
#endif
NAMESPACE_BEGIN(mitsuba)
struct TLSEntry {
void *data;
ThreadLocalBase::DestructFunctor destruct;
std::list<TLSEntry *>::iterator iterator;
};
struct PerThreadData {
tbb::spin_mutex mutex;
std::unordered_map<ThreadLocalBase *, TLSEntry> entries;
std::list<TLSEntry *> entries_ordered;
uint32_t ref_count = 1;
};
#if defined(__WINDOWS__)
static __declspec(thread) PerThreadData *ptd_local = nullptr;
#elif defined(__LINUX__)
static __thread PerThreadData *ptd_local = nullptr;
#elif defined(__OSX__)
static pthread_key_t ptd_local;
#endif
/// List of all PerThreadData data structures (one for each thread)
static std::unordered_set<PerThreadData *> ptd_global;
/// Lock to protect ptd_global
static tbb::mutex ptd_global_lock;
ThreadLocalBase::ThreadLocalBase(const ConstructFunctor &construct_functor,
const DestructFunctor &destruct_functor)
: m_construct_functor(construct_functor), m_destruct_functor(destruct_functor) { }
ThreadLocalBase::~ThreadLocalBase() {
clear();
}
void ThreadLocalBase::clear() {
tbb::mutex::scoped_lock guard(ptd_global_lock);
/* For every thread */
for (auto ptd : ptd_global) {
tbb::spin_mutex::scoped_lock guard2(ptd->mutex);
/* If the current TLS object is referenced, destroy the contents */
auto it2 = ptd->entries.find(this);
if (it2 == ptd->entries.end())
continue;
auto entry = it2->second;
ptd->entries_ordered.erase(entry.iterator);
ptd->entries.erase(it2);
guard2.release();
m_destruct_functor(entry.data);
}
}
void *ThreadLocalBase::get() {
/* Look up a TLS entry. The goal is to make this entire operation very fast! */
#if defined(__OSX__)
PerThreadData *ptd = (PerThreadData *) pthread_getspecific(ptd_local);
#else
PerThreadData *ptd = ptd_local;
#endif
if (unlikely(!ptd))
throw std::runtime_error(
"Internal error: call to ThreadLocalPrivate::get() precedes the "
"construction of thread-specific data structures!");
/* This is an uncontended thread-local lock (i.e. not to worry) */
tbb::spin_mutex::scoped_lock guard(ptd->mutex);
auto it = ptd->entries.find(this);
if (likely(it != ptd->entries.end()))
return it->second.data;
/* This is the first access from this thread */
ptd->entries.insert(std::make_pair(this, TLSEntry {
m_construct_functor(),
m_destruct_functor,
std::list<TLSEntry *>::iterator()
}));
TLSEntry &entry = ptd->entries.find(this)->second;
ptd->entries_ordered.push_back(&entry);
entry.iterator = --ptd->entries_ordered.end();
return entry.data;
}
void ThreadLocalBase::static_initialization() {
#if defined(__OSX__)
pthread_key_create(&ptd_local, nullptr);
#endif
}
void ThreadLocalBase::static_shutdown() {
#if defined(__OSX__)
pthread_key_delete(ptd_local);
memset(&ptd_local, 0, sizeof(pthread_key_t));
#endif
}
bool ThreadLocalBase::register_thread() {
tbb::mutex::scoped_lock guard(ptd_global_lock);
#if defined(__OSX__)
PerThreadData *ptd = (PerThreadData *) pthread_getspecific(ptd_local);
if (!ptd) {
ptd = new PerThreadData();
ptd_global.insert(ptd);
pthread_setspecific(ptd_local, ptd);
return true;
} else {
ptd->ref_count++;
}
#else
if (!ptd_local) {
auto ptd = new PerThreadData();
ptd_local = ptd;
ptd_global.insert(ptd);
return true;
} else {
ptd_local->ref_count++;
}
#endif
return false;
}
/// A thread has died -- destroy any remaining TLS entries associated with it
bool ThreadLocalBase::unregister_thread() {
tbb::mutex::scoped_lock guard(ptd_global_lock);
#if defined(__OSX__)
PerThreadData *ptd = (PerThreadData *) pthread_getspecific(ptd_local);
#else
PerThreadData *ptd = ptd_local;
#endif
if (!ptd)
return false;
ptd->ref_count--;
if (ptd->ref_count == 0) {
tbb::spin_mutex::scoped_lock local_guard(ptd->mutex);
for (auto it = ptd->entries_ordered.rbegin();
it != ptd->entries_ordered.rend(); ++it)
(*it)->destruct((*it)->data);
local_guard.release();
ptd_global.erase(ptd);
delete ptd;
#if defined(__OSX__)
pthread_setspecific(ptd_local, nullptr);
#else
ptd_local = nullptr;
#endif
}
return true;
}
NAMESPACE_END(mitsuba)