Skip to content

Commit 78a317a

Browse files
committed
Add support for declaring thread-local variables in the runtime.
Use native thread-locals when available and simulated thread-locals when not. The simulation layer uses pthread_getspecific. Using TLS is significantly more annoying this way, but I kindof like it because it reinforces that TLS accesses aren't as cheap as they look.
1 parent ca16548 commit 78a317a

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed

include/swift/Runtime/ThreadLocal.h

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//===--- ThreadLocal.h - Thread-local storage -------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Declarations and macros for working with thread-local storage in the
14+
// Swift runtime.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_RUNTIME_THREADLOCAL_H
19+
#define SWIFT_RUNTIME_THREADLOCAL_H
20+
21+
/// SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL - Does the current configuration
22+
/// allow the use of SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL?
23+
#if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME)
24+
// We define SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL to nothing in this
25+
// configuration and just use a global variable, so this is okay.
26+
#define SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL 1
27+
#elif __has_feature(tls)
28+
// If __has_feature reports that TLS is available, use it.
29+
#define SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL 1
30+
#elif !defined(__clang__)
31+
// If we're not using Clang, assume that __has_feature is unreliable
32+
// and that we can safely use TLS.
33+
#else
34+
// Otherwise we can't use TLS and have to fall back on something else.
35+
#define SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL 0
36+
#endif
37+
38+
/// SWIFT_RUNTIME_THREAD_LOCAL - Declare that something is a
39+
/// thread-local variable in the runtime.
40+
#if defined(SWIFT_STDLIB_SINGLE_THREADED_RUNTIME)
41+
// In a single-threaded runtime, thread-locals are global.
42+
#define SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL
43+
#elif defined(__GNUC__)
44+
// In GCC-compatible compilers, we prefer __thread because it's understood
45+
// to guarantee a constant initializer, which permits more efficient access
46+
// patterns.
47+
#define SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL __thread
48+
#else
49+
// Otherwise, just fall back on the standard C++ feature.
50+
#define SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL thread_local
51+
#endif
52+
53+
// Implementation of SWIFT_RUNTIME_DECLARE_THREAD_LOCAL
54+
#if !SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL
55+
#include <pthread.h>
56+
#include <dispatch/dispatch.h>
57+
#endif
58+
59+
namespace swift {
60+
// A wrapper class for thread-local storage.
61+
//
62+
// - On platforms that report SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL
63+
// above, an object of this type is declared with
64+
// SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL. This makes the object
65+
// itself thread-local, and no internal support is required.
66+
//
67+
// Note that this includes platforms that set
68+
// SWIFT_STDLIB_SINGLE_THREADED_RUNTIME, for whhch
69+
// SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL is empty;
70+
// thread-local declarations then create an ordinary global.
71+
//
72+
// - On platforms that don't report SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL,
73+
// we have to simulate thread-local storage. Fortunately, all of
74+
// these platforms (at least for now) support pthread_getspecific.
75+
template <class T>
76+
class ThreadLocal {
77+
static_assert(sizeof(T) <= sizeof(void*), "cannot store more than a pointer");
78+
79+
#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL
80+
T value;
81+
#else
82+
// We rely on the zero-initialization of objects with static storage
83+
// duration.
84+
dispatch_once_t once;
85+
pthread_key_t key;
86+
87+
pthread_key_t getKey() {
88+
dispatch_once_f(&once, this, [](void *ctx) {
89+
pthread_key_create(&reinterpret_cast<ThreadLocal*>(ctx)->key, nullptr);
90+
});
91+
return key;
92+
}
93+
#endif
94+
95+
public:
96+
constexpr ThreadLocal() {}
97+
98+
T get() {
99+
#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL
100+
return value;
101+
#else
102+
void *storedValue = pthread_getspecific(getKey());
103+
T value;
104+
memcpy(&value, &storedValue, sizeof(T));
105+
return value;
106+
#endif
107+
}
108+
109+
void set(T newValue) {
110+
#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL
111+
value = newValue;
112+
#else
113+
void *storedValue;
114+
memcpy(&storedValue, &newValue, sizeof(T));
115+
pthread_setspecific(getKey(), storedValue);
116+
#endif
117+
}
118+
};
119+
} // end namespace swift
120+
121+
/// SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME) - Declare a variable
122+
/// to be a thread-local variable. The declaration must have static
123+
/// storage duration; it may be prefixed with "static".
124+
///
125+
/// Because of the fallback path, the default-initialization of the
126+
/// type must be equivalent to a bitwise zero-initialization, and the
127+
/// type must be small and trivially copyable.
128+
#if SWIFT_RUNTIME_SUPPORTS_THREAD_LOCAL
129+
#define SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME) \
130+
SWIFT_RUNTIME_ATTRIBUTE_THREAD_LOCAL swift::ThreadLocal<TYPE> NAME
131+
#else
132+
#define SWIFT_RUNTIME_DECLARE_THREAD_LOCAL(TYPE, NAME) \
133+
swift::ThreadLocal<TYPE> NAME
134+
#endif
135+
136+
#endif

0 commit comments

Comments
 (0)