Use Thread Safety Analysis annotations throughout SwiftShader.

Switch from std::mutex and std::unique_lock to marl::mutex and marl::lock.
The marl versions are TSA-friendly replacements, and do not involve the
marl scheduler.

Bug: b/153194656
Change-Id: Ief3758fb0d52b8fcdaa4208d2c25ad04c2d40108
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43436
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Result: kokoro <noreply+kokoro@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e5ce24f..792ac8c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -582,6 +582,10 @@
     add_subdirectory(${THIRD_PARTY_DIR}/marl)
 endif()
 
+if(MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED)
+    list(APPEND SWIFTSHADER_COMPILE_OPTIONS "-Wthread-safety")
+endif()
+
 ###########################################################
 # cppdap
 ###########################################################
diff --git a/src/Device/Blitter.cpp b/src/Device/Blitter.cpp
index ccdfc91..a7d1256 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -1683,7 +1683,7 @@
 
 Blitter::BlitRoutineType Blitter::getBlitRoutine(const State &state)
 {
-	std::unique_lock<std::mutex> lock(blitMutex);
+	marl::lock lock(blitMutex);
 	auto blitRoutine = blitCache.lookup(state);
 
 	if(!blitRoutine)
@@ -1697,7 +1697,7 @@
 
 Blitter::CornerUpdateRoutineType Blitter::getCornerUpdateRoutine(const State &state)
 {
-	std::unique_lock<std::mutex> lock(cornerUpdateMutex);
+	marl::lock lock(cornerUpdateMutex);
 	auto cornerUpdateRoutine = cornerUpdateCache.lookup(state);
 
 	if(!cornerUpdateRoutine)
diff --git a/src/Device/Blitter.hpp b/src/Device/Blitter.hpp
index 8735feb..c7687d2 100644
--- a/src/Device/Blitter.hpp
+++ b/src/Device/Blitter.hpp
@@ -20,8 +20,10 @@
 #include "Reactor/Reactor.hpp"
 #include "Vulkan/VkFormat.h"
 
+#include "marl/mutex.h"
+#include "marl/tsa.h"
+
 #include <cstring>
-#include <mutex>
 
 namespace vk {
 
@@ -186,10 +188,11 @@
 	                  const VkImageSubresourceLayers &dstSubresourceLayers, Edge dstEdge,
 	                  const VkImageSubresourceLayers &srcSubresourceLayers, Edge srcEdge);
 
-	std::mutex blitMutex;
-	RoutineCache<State, BlitFunction::CFunctionType> blitCache;  // guarded by blitMutex
-	std::mutex cornerUpdateMutex;
-	RoutineCache<State, CornerUpdateFunction::CFunctionType> cornerUpdateCache;  // guarded by cornerUpdateMutex
+	marl::mutex blitMutex;
+	RoutineCache<State, BlitFunction::CFunctionType> blitCache GUARDED_BY(blitMutex);
+
+	marl::mutex cornerUpdateMutex;
+	RoutineCache<State, CornerUpdateFunction::CFunctionType> cornerUpdateCache GUARDED_BY(cornerUpdateMutex);
 };
 
 }  // namespace sw
diff --git a/src/System/Synchronization.hpp b/src/System/Synchronization.hpp
index 0a5068d..81794ab 100644
--- a/src/System/Synchronization.hpp
+++ b/src/System/Synchronization.hpp
@@ -25,9 +25,10 @@
 #include <assert.h>
 #include <chrono>
 #include <condition_variable>
-#include <mutex>
 #include <queue>
 
+#include "marl/mutex.h"
+
 namespace sw {
 
 // TaskEvents is an interface for notifying when tasks begin and end.
@@ -64,7 +65,7 @@
 	// add() begins a new task.
 	void add()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
+		marl::lock lock(mutex);
 		++count_;
 	}
 
@@ -73,7 +74,7 @@
 	// WaitGroup.
 	bool done()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
+		marl::lock lock(mutex);
 		assert(count_ > 0);
 		--count_;
 		if(count_ == 0)
@@ -86,8 +87,8 @@
 	// wait() blocks until all the tasks have been finished.
 	void wait()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
-		condition.wait(lock, [this] { return count_ == 0; });
+		marl::lock lock(mutex);
+		lock.wait(condition, [this]() REQUIRES(mutex) { return count_ == 0; });
 	}
 
 	// wait() blocks until all the tasks have been finished or the timeout
@@ -96,8 +97,8 @@
 	template<class CLOCK, class DURATION>
 	bool wait(const std::chrono::time_point<CLOCK, DURATION> &timeout)
 	{
-		std::unique_lock<std::mutex> lock(mutex);
-		return condition.wait_until(lock, timeout, [this] { return count_ == 0; });
+		marl::lock lock(mutex);
+		return condition.wait_until(lock, timeout, [this]() REQUIRES(mutex) { return count_ == 0; });
 	}
 
 	// count() returns the number of times add() has been called without a call
@@ -106,7 +107,7 @@
 	// change after returning.
 	int32_t count()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
+		marl::lock lock(mutex);
 		return count_;
 	}
 
@@ -115,8 +116,8 @@
 	void finish() override { done(); }
 
 private:
-	int32_t count_ = 0;  // guarded by mutex
-	std::mutex mutex;
+	marl::mutex mutex;
+	int32_t count_ GUARDED_BY(mutex) = 0;
 	std::condition_variable condition;
 };
 
@@ -147,8 +148,8 @@
 	size_t count();
 
 private:
-	std::queue<T> queue;
-	std::mutex mutex;
+	marl::mutex mutex;
+	std::queue<T> queue GUARDED_BY(mutex);
 	std::condition_variable added;
 };
 
@@ -159,9 +160,9 @@
 template<typename T>
 T Chan<T>::take()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	// Wait for item to be added.
-	added.wait(lock, [this] { return queue.size() > 0; });
+	lock.wait(added, [this]() REQUIRES(mutex) { return queue.size() > 0; });
 	T out = queue.front();
 	queue.pop();
 	return out;
@@ -170,7 +171,7 @@
 template<typename T>
 std::pair<T, bool> Chan<T>::tryTake()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	if(queue.size() == 0)
 	{
 		return std::make_pair(T{}, false);
@@ -183,7 +184,7 @@
 template<typename T>
 void Chan<T>::put(const T &item)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	queue.push(item);
 	added.notify_one();
 }
@@ -191,7 +192,7 @@
 template<typename T>
 size_t Chan<T>::count()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	return queue.size();
 }
 
diff --git a/src/Vulkan/Debug/Context.cpp b/src/Vulkan/Debug/Context.cpp
index 1f51617..f79d345 100644
--- a/src/Vulkan/Debug/Context.cpp
+++ b/src/Vulkan/Debug/Context.cpp
@@ -130,7 +130,6 @@
 	Broadcaster broadcaster;
 
 	std::mutex mutex;
-	std::vector<EventListener *> eventListeners;
 	std::unordered_map<std::thread::id, std::shared_ptr<Thread>> threadsByStdId;
 	std::unordered_set<std::string> functionBreakpoints;
 	std::unordered_map<std::string, std::vector<int>> pendingBreakpoints;
diff --git a/src/Vulkan/Debug/File.cpp b/src/Vulkan/Debug/File.cpp
index 9a3094e..b14c0e5 100644
--- a/src/Vulkan/Debug/File.cpp
+++ b/src/Vulkan/Debug/File.cpp
@@ -14,7 +14,8 @@
 
 #include "File.hpp"
 
-#include <mutex>
+#include "marl/mutex.h"
+
 #include <unordered_set>
 
 namespace {
@@ -33,8 +34,8 @@
 	FileBase(ID id, std::string dir, std::string name, std::string source);
 
 private:
-	mutable std::mutex breakpointMutex;
-	std::unordered_set<int> breakpoints;  // guarded by breakpointMutex
+	mutable marl::mutex breakpointMutex;
+	std::unordered_set<int> breakpoints GUARDED_BY(breakpointMutex);
 };
 
 FileBase::FileBase(ID id, std::string dir, std::string name, std::string source)
@@ -43,19 +44,19 @@
 
 void FileBase::clearBreakpoints()
 {
-	std::unique_lock<std::mutex> lock(breakpointMutex);
+	marl::lock lock(breakpointMutex);
 	breakpoints.clear();
 }
 
 void FileBase::addBreakpoint(int line)
 {
-	std::unique_lock<std::mutex> lock(breakpointMutex);
+	marl::lock lock(breakpointMutex);
 	breakpoints.emplace(line);
 }
 
 bool FileBase::hasBreakpoint(int line) const
 {
-	std::unique_lock<std::mutex> lock(breakpointMutex);
+	marl::lock lock(breakpointMutex);
 	return breakpoints.count(line) > 0;
 }
 
diff --git a/src/Vulkan/Debug/Thread.cpp b/src/Vulkan/Debug/Thread.cpp
index 753b7db..6b4fa96 100644
--- a/src/Vulkan/Debug/Thread.cpp
+++ b/src/Vulkan/Debug/Thread.cpp
@@ -28,17 +28,17 @@
 
 void Thread::setName(const std::string &name)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	name_ = name;
 }
 
 std::string Thread::name() const
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	return name_;
 }
 
-void Thread::onLocationUpdate(std::unique_lock<std::mutex> &lock)
+void Thread::onLocationUpdate(marl::lock &lock)
 {
 	auto location = frames.back()->location;
 
@@ -55,7 +55,7 @@
 	{
 		case State::Paused:
 		{
-			stateCV.wait(lock, [this] { return state_ != State::Paused; });
+			lock.wait(stateCV, [this]() REQUIRES(mutex) { return state_ != State::Paused; });
 			break;
 		}
 
@@ -65,7 +65,7 @@
 			{
 				broadcast->onThreadStepped(id);
 				state_ = State::Paused;
-				stateCV.wait(lock, [this] { return state_ != State::Paused; });
+				lock.wait(stateCV, [this]() REQUIRES(mutex) { return state_ != State::Paused; });
 				pauseAtFrame = 0;
 			}
 			break;
@@ -81,7 +81,7 @@
 	auto frame = ctxlck.createFrame(file, function);
 	auto isFunctionBreakpoint = ctxlck.isFunctionBreakpoint(function);
 
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	frames.push_back(frame);
 	if(isFunctionBreakpoint)
 	{
@@ -92,13 +92,13 @@
 
 void Thread::exit()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	frames.pop_back();
 }
 
 void Thread::update(std::function<void(Frame &)> f)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	auto &frame = *frames.back();
 	auto oldLocation = frame.location;
 	f(frame);
@@ -110,13 +110,13 @@
 
 Frame Thread::frame() const
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	return *frames.back();
 }
 
 std::vector<Frame> Thread::stack() const
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	std::vector<Frame> out;
 	out.reserve(frames.size());
 	for(auto frame : frames)
@@ -128,27 +128,28 @@
 
 Thread::State Thread::state() const
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	return state_;
 }
 
 void Thread::resume()
 {
-	std::unique_lock<std::mutex> lock(mutex);
-	state_ = State::Running;
-	lock.unlock();
+	{
+		marl::lock lock(mutex);
+		state_ = State::Running;
+	}
 	stateCV.notify_all();
 }
 
 void Thread::pause()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	state_ = State::Paused;
 }
 
 void Thread::stepIn()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	state_ = State::Stepping;
 	pauseAtFrame.reset();
 	stateCV.notify_all();
@@ -156,7 +157,7 @@
 
 void Thread::stepOver()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	state_ = State::Stepping;
 	pauseAtFrame = frames.back();
 	stateCV.notify_all();
@@ -164,7 +165,7 @@
 
 void Thread::stepOut()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	state_ = State::Stepping;
 	pauseAtFrame = (frames.size() > 1) ? frames[frames.size() - 1] : nullptr;
 	stateCV.notify_all();
diff --git a/src/Vulkan/Debug/Thread.hpp b/src/Vulkan/Debug/Thread.hpp
index a59f47f..4c719f2 100644
--- a/src/Vulkan/Debug/Thread.hpp
+++ b/src/Vulkan/Debug/Thread.hpp
@@ -19,9 +19,11 @@
 #include "ID.hpp"
 #include "Location.hpp"
 
+#include "marl/mutex.h"
+#include "marl/tsa.h"
+
 #include <condition_variable>
 #include <memory>
-#include <mutex>
 #include <string>
 #include <vector>
 
@@ -173,14 +175,15 @@
 private:
 	EventListener *const broadcast;
 
-	void onLocationUpdate(std::unique_lock<std::mutex> &lock);
+	void onLocationUpdate(marl::lock &lock) REQUIRES(mutex);
 
-	mutable std::mutex mutex;
-	std::string name_;
-	std::vector<std::shared_ptr<Frame>> frames;
+	mutable marl::mutex mutex;
+	std::string name_ GUARDED_BY(mutex);
+	std::vector<std::shared_ptr<Frame>> frames GUARDED_BY(mutex);
+	State state_ GUARDED_BY(mutex) = State::Running;
+	std::shared_ptr<Frame> pauseAtFrame GUARDED_BY(mutex);
+
 	std::condition_variable stateCV;
-	State state_ = State::Running;
-	std::shared_ptr<Frame> pauseAtFrame;
 };
 
 }  // namespace dbg
diff --git a/src/Vulkan/Debug/Variable.hpp b/src/Vulkan/Debug/Variable.hpp
index 4b76c7a..c916d75 100644
--- a/src/Vulkan/Debug/Variable.hpp
+++ b/src/Vulkan/Debug/Variable.hpp
@@ -19,9 +19,11 @@
 #include "Type.hpp"
 #include "Value.hpp"
 
+#include "marl/mutex.h"
+#include "marl/tsa.h"
+
 #include <algorithm>
 #include <memory>
-#include <mutex>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -81,10 +83,10 @@
 	inline std::shared_ptr<Type> type() const override;
 	inline const void *get() const override;
 
-	mutable std::mutex mutex;
-	std::vector<Variable> variables;
-	std::unordered_map<std::string, int> indices;
-	std::vector<std::shared_ptr<VariableContainer>> extends;
+	mutable marl::mutex mutex;
+	std::vector<Variable> variables GUARDED_BY(mutex);
+	std::unordered_map<std::string, int> indices GUARDED_BY(mutex);
+	std::vector<std::shared_ptr<VariableContainer>> extends GUARDED_BY(mutex);
 };
 
 VariableContainer::VariableContainer(ID id)
@@ -101,7 +103,7 @@
 template<typename F>
 void VariableContainer::foreach(ForeachIndex &index, const F &cb) const
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	for(size_t i = index.start; i < variables.size() && i < index.count; i++)
 	{
 		cb(variables[i]);
@@ -119,7 +121,7 @@
 template<typename F>
 bool VariableContainer::find(const std::string &name, const F &cb) const
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	for(auto const &var : variables)
 	{
 		if(var.name == name)
@@ -140,7 +142,7 @@
 
 void VariableContainer::put(const Variable &var)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	auto it = indices.find(var.name);
 	if(it == indices.end())
 	{
@@ -161,7 +163,7 @@
 
 void VariableContainer::extend(const std::shared_ptr<VariableContainer> &base)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	extends.emplace_back(base);
 }
 
diff --git a/src/Vulkan/VkDevice.cpp b/src/Vulkan/VkDevice.cpp
index 0961243..389348b 100644
--- a/src/Vulkan/VkDevice.cpp
+++ b/src/Vulkan/VkDevice.cpp
@@ -40,7 +40,7 @@
 
 void Device::SamplingRoutineCache::updateSnapshot()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 
 	if(snapshotNeedsUpdate)
 	{
@@ -62,7 +62,7 @@
 
 uint32_t Device::SamplerIndexer::index(const SamplerState &samplerState)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 
 	auto it = map.find(samplerState);
 
@@ -81,7 +81,7 @@
 
 void Device::SamplerIndexer::remove(const SamplerState &samplerState)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 
 	auto it = map.find(samplerState);
 	ASSERT(it != map.end());
diff --git a/src/Vulkan/VkDevice.hpp b/src/Vulkan/VkDevice.hpp
index abf1191..8d41499 100644
--- a/src/Vulkan/VkDevice.hpp
+++ b/src/Vulkan/VkDevice.hpp
@@ -20,9 +20,11 @@
 #include "Reactor/Routine.hpp"
 #include "System/LRUCache.hpp"
 
+#include "marl/mutex.h"
+#include "marl/tsa.h"
+
 #include <map>
 #include <memory>
-#include <mutex>
 #include <unordered_map>
 
 namespace marl {
@@ -99,7 +101,7 @@
 			auto it = snapshot.find(key);
 			if(it != snapshot.end()) { return it->second; }
 
-			std::unique_lock<std::mutex> lock(mutex);
+			marl::lock lock(mutex);
 			if(auto existingRoutine = cache.lookup(key))
 			{
 				return existingRoutine;
@@ -118,8 +120,8 @@
 		bool snapshotNeedsUpdate = false;
 		std::unordered_map<Key, std::shared_ptr<rr::Routine>, Key::Hash> snapshot;
 
-		sw::LRUCache<Key, std::shared_ptr<rr::Routine>, Key::Hash> cache;  // guarded by mutex
-		std::mutex mutex;
+		marl::mutex mutex;
+		sw::LRUCache<Key, std::shared_ptr<rr::Routine>, Key::Hash> cache GUARDED_BY(mutex);
 	};
 
 	SamplingRoutineCache *getSamplingRoutineCache() const;
@@ -140,8 +142,8 @@
 			uint32_t count;  // Number of samplers sharing this state identifier.
 		};
 
-		std::map<SamplerState, Identifier> map;  // guarded by mutex
-		std::mutex mutex;
+		marl::mutex mutex;
+		std::map<SamplerState, Identifier> map GUARDED_BY(mutex);
 
 		uint32_t nextID = 0;
 	};
diff --git a/src/Vulkan/VkEvent.hpp b/src/Vulkan/VkEvent.hpp
index 1ec9f26..9e33ba4 100644
--- a/src/Vulkan/VkEvent.hpp
+++ b/src/Vulkan/VkEvent.hpp
@@ -16,8 +16,11 @@
 #define VK_EVENT_HPP_
 
 #include "VkObject.hpp"
+
+#include "marl/mutex.h"
+#include "marl/tsa.h"
+
 #include <condition_variable>
-#include <mutex>
 
 namespace vk {
 
@@ -35,35 +38,35 @@
 
 	void signal()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
-		status = VK_EVENT_SET;
-		lock.unlock();
+		{
+			marl::lock lock(mutex);
+			status = VK_EVENT_SET;
+		}
 		condition.notify_all();
 	}
 
 	void reset()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
+		marl::lock lock(mutex);
 		status = VK_EVENT_RESET;
 	}
 
 	VkResult getStatus()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
+		marl::lock lock(mutex);
 		auto result = status;
-		lock.unlock();
 		return result;
 	}
 
 	void wait()
 	{
-		std::unique_lock<std::mutex> lock(mutex);
-		condition.wait(lock, [this] { return status == VK_EVENT_SET; });
+		marl::lock lock(mutex);
+		lock.wait(condition, [this]() REQUIRES(mutex) { return status == VK_EVENT_SET; });
 	}
 
 private:
-	VkResult status = VK_EVENT_RESET;  // guarded by mutex
-	std::mutex mutex;
+	marl::mutex mutex;
+	VkResult status GUARDED_BY(mutex) = VK_EVENT_RESET;
 	std::condition_variable condition;
 };
 
diff --git a/src/Vulkan/VkPipelineCache.cpp b/src/Vulkan/VkPipelineCache.cpp
index 8c4c894..375334f 100644
--- a/src/Vulkan/VkPipelineCache.cpp
+++ b/src/Vulkan/VkPipelineCache.cpp
@@ -136,14 +136,14 @@
 		PipelineCache *srcCache = Cast(pSrcCaches[i]);
 
 		{
-			std::unique_lock<std::mutex> thisLock(spirvShadersMutex);
-			std::unique_lock<std::mutex> srcLock(srcCache->spirvShadersMutex);
+			marl::lock thisLock(spirvShadersMutex);
+			marl::lock srcLock(srcCache->spirvShadersMutex);
 			spirvShaders.insert(srcCache->spirvShaders.begin(), srcCache->spirvShaders.end());
 		}
 
 		{
-			std::unique_lock<std::mutex> thisLock(computeProgramsMutex);
-			std::unique_lock<std::mutex> srcLock(srcCache->computeProgramsMutex);
+			marl::lock thisLock(computeProgramsMutex);
+			marl::lock srcLock(srcCache->computeProgramsMutex);
 			computePrograms.insert(srcCache->computePrograms.begin(), srcCache->computePrograms.end());
 		}
 	}
diff --git a/src/Vulkan/VkPipelineCache.hpp b/src/Vulkan/VkPipelineCache.hpp
index 5e89e60..83664b6 100644
--- a/src/Vulkan/VkPipelineCache.hpp
+++ b/src/Vulkan/VkPipelineCache.hpp
@@ -18,11 +18,13 @@
 #include "VkObject.hpp"
 #include "VkSpecializationInfo.hpp"
 
+#include "marl/mutex.h"
+#include "marl/tsa.h"
+
 #include <cstring>
 #include <functional>
 #include <map>
 #include <memory>
-#include <mutex>
 #include <string>
 #include <vector>
 
@@ -127,11 +129,11 @@
 	size_t dataSize = 0;
 	uint8_t *data = nullptr;
 
-	std::mutex spirvShadersMutex;
-	std::map<SpirvShaderKey, std::shared_ptr<sw::SpirvShader>> spirvShaders;  // guarded by spirvShadersMutex
+	marl::mutex spirvShadersMutex;
+	std::map<SpirvShaderKey, std::shared_ptr<sw::SpirvShader>> spirvShaders GUARDED_BY(spirvShadersMutex);
 
-	std::mutex computeProgramsMutex;
-	std::map<ComputeProgramKey, std::shared_ptr<sw::ComputeProgram>> computePrograms;  // guarded by computeProgramsMutex
+	marl::mutex computeProgramsMutex;
+	std::map<ComputeProgramKey, std::shared_ptr<sw::ComputeProgram>> computePrograms GUARDED_BY(computeProgramsMutex);
 };
 
 static inline PipelineCache *Cast(VkPipelineCache object)
@@ -142,7 +144,7 @@
 template<typename Function>
 std::shared_ptr<sw::ComputeProgram> PipelineCache::getOrCreateComputeProgram(const PipelineCache::ComputeProgramKey &key, Function &&create)
 {
-	std::unique_lock<std::mutex> lock(computeProgramsMutex);
+	marl::lock lock(computeProgramsMutex);
 
 	auto it = computePrograms.find(key);
 	if(it != computePrograms.end()) { return it->second; }
@@ -155,7 +157,7 @@
 template<typename Function>
 std::shared_ptr<sw::SpirvShader> PipelineCache::getOrCreateShader(const PipelineCache::SpirvShaderKey &key, Function &&create)
 {
-	std::unique_lock<std::mutex> lock(spirvShadersMutex);
+	marl::lock lock(spirvShadersMutex);
 
 	auto it = spirvShaders.find(key);
 	if(it != spirvShaders.end()) { return it->second; }
diff --git a/src/Vulkan/VkSemaphore.cpp b/src/Vulkan/VkSemaphore.cpp
index df9f72c..21a05f9 100644
--- a/src/Vulkan/VkSemaphore.cpp
+++ b/src/Vulkan/VkSemaphore.cpp
@@ -130,7 +130,7 @@
 
 void Semaphore::wait()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	External *ext = tempExternal ? tempExternal : external;
 	if(ext)
 	{
@@ -141,11 +141,11 @@
 			// call, it is assumed that this is negligible
 			// compared with the actual semaphore wait()
 			// operation.
-			lock.unlock();
+			lock.unlock_no_tsa();
 			marl::blocking_call([ext]() {
 				ext->wait();
 			});
-			lock.lock();
+			lock.lock_no_tsa();
 		}
 
 		// If the import was temporary, reset the semaphore to its previous state.
@@ -164,7 +164,7 @@
 
 void Semaphore::signal()
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	External *ext = tempExternal ? tempExternal : external;
 	if(ext)
 	{
@@ -187,7 +187,7 @@
 
 void Semaphore::destroy(const VkAllocationCallbacks *pAllocator)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	while(tempExternal)
 	{
 		External *ext = tempExternal;
@@ -227,7 +227,7 @@
                                   ALLOC_FUNC alloc_func,
                                   IMPORT_FUNC import_func)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 
 	// Create new External instance if needed.
 	External *ext = external;
@@ -260,7 +260,7 @@
 template<typename ALLOC_FUNC, typename EXPORT_FUNC>
 VkResult Semaphore::exportPayload(ALLOC_FUNC alloc_func, EXPORT_FUNC export_func)
 {
-	std::unique_lock<std::mutex> lock(mutex);
+	marl::lock lock(mutex);
 	// Sanity check, do not try to export a semaphore that has a temporary import.
 	if(tempExternal != nullptr)
 	{
diff --git a/src/Vulkan/VkSemaphore.hpp b/src/Vulkan/VkSemaphore.hpp
index b67d4d9..652389b 100644
--- a/src/Vulkan/VkSemaphore.hpp
+++ b/src/Vulkan/VkSemaphore.hpp
@@ -19,7 +19,8 @@
 #include "VkObject.hpp"
 
 #include "marl/event.h"
-#include <mutex>
+#include "marl/mutex.h"
+#include "marl/tsa.h"
 
 #if VK_USE_PLATFORM_FUCHSIA
 #	include <zircon/types.h>
@@ -147,10 +148,10 @@
 
 	const VkAllocationCallbacks *allocator = nullptr;
 	VkExternalSemaphoreHandleTypeFlags exportableHandleTypes = (VkExternalSemaphoreHandleTypeFlags)0;
-	std::mutex mutex;
 	marl::Event internal;
-	External *external = nullptr;
-	External *tempExternal = nullptr;
+	marl::mutex mutex;
+	External *external GUARDED_BY(mutex) = nullptr;
+	External *tempExternal GUARDED_BY(mutex) = nullptr;
 };
 
 static inline Semaphore *Cast(VkSemaphore object)
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 07d2889..54273a6 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -71,8 +71,10 @@
 
 #include "Reactor/Nucleus.hpp"
 
+#include "marl/mutex.h"
 #include "marl/scheduler.h"
 #include "marl/thread.h"
+#include "marl/tsa.h"
 
 #include "System/CPUID.hpp"
 
@@ -133,21 +135,27 @@
 
 std::shared_ptr<marl::Scheduler> getOrCreateScheduler()
 {
-	static std::mutex mutex;
-	static std::weak_ptr<marl::Scheduler> schedulerWeak;
-	std::unique_lock<std::mutex> lock(mutex);
-	auto scheduler = schedulerWeak.lock();
-	if(!scheduler)
+	struct Scheduler
 	{
-		scheduler = std::make_shared<marl::Scheduler>();
-		scheduler->setThreadInitializer([] {
+		marl::mutex mutex;
+		std::weak_ptr<marl::Scheduler> weakptr GUARDED_BY(mutex);
+	};
+
+	static Scheduler scheduler;
+
+	marl::lock lock(scheduler.mutex);
+	auto sptr = scheduler.weakptr.lock();
+	if(!sptr)
+	{
+		sptr = std::make_shared<marl::Scheduler>();
+		sptr->setThreadInitializer([] {
 			sw::CPUID::setFlushToZero(true);
 			sw::CPUID::setDenormalsAreZero(true);
 		});
-		scheduler->setWorkerThreadCount(std::min<size_t>(marl::Thread::numLogicalCPUs(), 16));
-		schedulerWeak = scheduler;
+		sptr->setWorkerThreadCount(std::min<size_t>(marl::Thread::numLogicalCPUs(), 16));
+		scheduler.weakptr = sptr;
 	}
-	return scheduler;
+	return sptr;
 }
 
 // initializeLibrary() is called by vkCreateInstance() to perform one-off global