marl: emulate thread_local with pthreads
Yet another attempt to work around the sanitizer false positives.
This introduces the use of global static initializers, which I was half expecting Chromium builds to complain about, but there's not much I can do about that (in a performant way).
Bug: chromium:1337597
Change-Id: Ie8c20252fbf357a285045362b82b6b3cde52ea77
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/70888
Tested-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Geoff Lang <geofflang@google.com>
Presubmit-Ready: Ben Clayton <bclayton@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/third_party/marl/BUILD.gn b/third_party/marl/BUILD.gn
index d7cb142..4c02648 100644
--- a/third_party/marl/BUILD.gn
+++ b/third_party/marl/BUILD.gn
@@ -18,6 +18,9 @@
config("marl_config") {
include_dirs = [ "include" ]
+ if (!is_win) {
+ defines = [ "MARL_USE_PTHREAD_THREAD_LOCAL=1" ]
+ }
}
swiftshader_source_set("Marl_headers") {
diff --git a/third_party/marl/CMakeLists.txt b/third_party/marl/CMakeLists.txt
index c84076d..5f17ee8 100644
--- a/third_party/marl/CMakeLists.txt
+++ b/third_party/marl/CMakeLists.txt
@@ -46,6 +46,7 @@
option_if_not_defined(MARL_BUILD_TESTS "Build tests" OFF)
option_if_not_defined(MARL_BUILD_BENCHMARKS "Build benchmarks" OFF)
option_if_not_defined(MARL_BUILD_SHARED "Build marl as a shared / dynamic library (default static)" OFF)
+option_if_not_defined(MARL_USE_PTHREAD_THREAD_LOCAL "Use pthreads for thread local storage" OFF)
option_if_not_defined(MARL_ASAN "Build marl with address sanitizer" OFF)
option_if_not_defined(MARL_MSAN "Build marl with memory sanitizer" OFF)
option_if_not_defined(MARL_TSAN "Build marl with thread sanitizer" OFF)
@@ -224,6 +225,11 @@
endif()
endif(MARL_WARNINGS_AS_ERRORS)
+ if(MARL_USE_PTHREAD_THREAD_LOCAL)
+ target_compile_definitions(${target} PRIVATE "MARL_USE_PTHREAD_THREAD_LOCAL=1")
+ target_link_libraries(${target} PUBLIC pthread)
+ endif()
+
if(MARL_ASAN)
target_compile_options(${target} PUBLIC "-fsanitize=address")
target_link_libraries(${target} PUBLIC "-fsanitize=address")
diff --git a/third_party/marl/include/marl/sanitizers.h b/third_party/marl/include/marl/sanitizers.h
index 6993066..3f26d4a 100644
--- a/third_party/marl/include/marl/sanitizers.h
+++ b/third_party/marl/include/marl/sanitizers.h
@@ -78,14 +78,4 @@
#define THREAD_SANITIZER_ONLY(x)
#endif // THREAD_SANITIZER_ENABLED
-// The MSAN_UNPOISON macro marks uninitialized memory as initialized for MSAN.
-// It can be used to suppress false-positive MSAN errors before reading
-// thread-local variables. See https://github.com/google/sanitizers/issues/1265
-#if MEMORY_SANITIZER_ENABLED
-#include <sanitizer/msan_interface.h>
-#define MSAN_UNPOISON(p, size) __msan_unpoison(p, size)
-#else
-#define MSAN_UNPOISON(p, size)
-#endif
-
#endif // marl_sanitizers_h
diff --git a/third_party/marl/include/marl/scheduler.h b/third_party/marl/include/marl/scheduler.h
index 48acf02..b3159b8 100644
--- a/third_party/marl/include/marl/scheduler.h
+++ b/third_party/marl/include/marl/scheduler.h
@@ -24,6 +24,7 @@
#include "sanitizers.h"
#include "task.h"
#include "thread.h"
+#include "thread_local.h"
#include <array>
#include <atomic>
@@ -464,7 +465,7 @@
};
// The current worker bound to the current thread.
- static thread_local Worker* current;
+ MARL_DECLARE_THREAD_LOCAL(Worker*, current);
Mode const mode;
Scheduler* const scheduler;
@@ -492,7 +493,7 @@
static void setBound(Scheduler* scheduler);
// The scheduler currently bound to the current thread.
- static thread_local Scheduler* bound;
+ MARL_DECLARE_THREAD_LOCAL(Scheduler*, bound);
// The immutable configuration used to build the scheduler.
const Config cfg;
@@ -574,7 +575,6 @@
}
Scheduler::Worker* Scheduler::Worker::getCurrent() {
- MSAN_UNPOISON(¤t, sizeof(Worker*));
return Worker::current;
}
diff --git a/third_party/marl/include/marl/thread_local.h b/third_party/marl/include/marl/thread_local.h
new file mode 100644
index 0000000..1165866
--- /dev/null
+++ b/third_party/marl/include/marl/thread_local.h
@@ -0,0 +1,60 @@
+// Copyright 2023 The Marl Authors.
+//
+// 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
+//
+// https://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.
+
+// A wrapper around a thread_local variable, or a pthread key
+
+#ifndef marl_thread_local_h
+#define marl_thread_local_h
+
+#ifdef MARL_USE_PTHREAD_THREAD_LOCAL
+#include "debug.h"
+
+#include <pthread.h>
+#include <type_traits>
+
+template <typename T>
+class ThreadLocal {
+ static_assert(std::is_pointer<T>::value,
+ "The current implementation of ThreadLocal requires that T "
+ "must be a pointer");
+
+ public:
+ inline ThreadLocal(T v) {
+ pthread_key_create(&key, NULL);
+ pthread_setspecific(key, v);
+ };
+ inline ~ThreadLocal() { pthread_key_delete(key); }
+ inline operator T() const { return static_cast<T>(pthread_getspecific(key)); }
+ inline ThreadLocal& operator=(T v) {
+ pthread_setspecific(key, v);
+ return *this;
+ }
+
+ private:
+ pthread_key_t key;
+};
+
+#define MARL_DECLARE_THREAD_LOCAL(TYPE, NAME) static ThreadLocal<TYPE> NAME;
+#define MARL_INSTANTIATE_THREAD_LOCAL(TYPE, NAME, VALUE) \
+ ThreadLocal<TYPE> NAME = VALUE;
+
+#else
+
+#define MARL_DECLARE_THREAD_LOCAL(TYPE, NAME) static thread_local TYPE NAME;
+#define MARL_INSTANTIATE_THREAD_LOCAL(TYPE, NAME, VALUE) \
+ thread_local TYPE NAME = VALUE;
+
+#endif
+
+#endif // marl_thread_local_h
diff --git a/third_party/marl/src/scheduler.cpp b/third_party/marl/src/scheduler.cpp
index 2f32582..f5e9df0 100644
--- a/third_party/marl/src/scheduler.cpp
+++ b/third_party/marl/src/scheduler.cpp
@@ -84,15 +84,13 @@
////////////////////////////////////////////////////////////////////////////////
// Scheduler
////////////////////////////////////////////////////////////////////////////////
-thread_local Scheduler* Scheduler::bound = nullptr;
+MARL_INSTANTIATE_THREAD_LOCAL(Scheduler*, Scheduler::bound, nullptr);
Scheduler* Scheduler::get() {
- MSAN_UNPOISON(&bound, sizeof(Scheduler*));
return bound;
}
void Scheduler::setBound(Scheduler* scheduler) {
- MSAN_UNPOISON(&bound, sizeof(Scheduler*));
bound = scheduler;
}
@@ -354,7 +352,9 @@
////////////////////////////////////////////////////////////////////////////////
// Scheduler::Worker
////////////////////////////////////////////////////////////////////////////////
-thread_local Scheduler::Worker* Scheduler::Worker::current = nullptr;
+MARL_INSTANTIATE_THREAD_LOCAL(Scheduler::Worker*,
+ Scheduler::Worker::current,
+ nullptr);
Scheduler::Worker::Worker(Scheduler* scheduler, Mode mode, uint32_t id)
: id(id),