diff --git a/applications/gui/src/src_window.hpp b/applications/gui/src/src_window.hpp
index de14d0d05504895d0b5137a1d9e267def32cde92..dfc00a83b85143fa7b86adae25e86859ff2bbcbc 100644
--- a/applications/gui/src/src_window.hpp
+++ b/applications/gui/src/src_window.hpp
@@ -36,7 +36,7 @@ class SourceWindow : public nanogui::Window {
 	std::vector<GLTexture> thumbs_;
 	bool refresh_thumbs_;
 	nanogui::Widget *ipanel_;
-	std::mutex mutex_;
+	MUTEX mutex_;
 
 	void _updateCameras();
 
diff --git a/applications/reconstruct/src/virtual_source.cpp b/applications/reconstruct/src/virtual_source.cpp
index cf6d268c1acc9d4d8e9452f8887be7f8e17eb4a2..c8dc380743373c591ffcfcea6aae0f713c1068c1 100644
--- a/applications/reconstruct/src/virtual_source.cpp
+++ b/applications/reconstruct/src/virtual_source.cpp
@@ -7,8 +7,6 @@
 #include <loguru.hpp>
 
 using ftl::rgbd::VirtualSource;
-using std::mutex;
-using std::unique_lock;
 
 VirtualSource::VirtualSource(ftl::rgbd::Source *host)
 		: ftl::rgbd::detail::Source(host) {
diff --git a/applications/vision/src/main.cpp b/applications/vision/src/main.cpp
index 141ae56131ec13e1e1cd48608c6f32f62e3b9f06..4c38125ce3270d770a70387ddba96def445ed3a2 100644
--- a/applications/vision/src/main.cpp
+++ b/applications/vision/src/main.cpp
@@ -15,8 +15,6 @@
 #include <vector>
 #include <fstream>
 #include <thread>
-#include <mutex>
-#include <condition_variable>
 
 #include <opencv2/opencv.hpp>
 #include <ftl/rgbd.hpp>
@@ -47,8 +45,6 @@ using std::map;
 using std::condition_variable;
 using std::this_thread::sleep_for;
 using std::chrono::milliseconds;
-using std::mutex;
-using std::unique_lock;
 using cv::Mat;
 using json = nlohmann::json;
 
diff --git a/components/common/cpp/include/ftl/threads.hpp b/components/common/cpp/include/ftl/threads.hpp
index 3091ac64f894b2e8a3ec059c615cfba563e43bee..5d86ce6077be327b24db981412435ba5b29687e1 100644
--- a/components/common/cpp/include/ftl/threads.hpp
+++ b/components/common/cpp/include/ftl/threads.hpp
@@ -7,24 +7,28 @@
 
 #define POOL_SIZE 10
 
-#if defined _DEBUG && DEBUG_MUTEX
+// #define DEBUG_MUTEX
+
+#if defined DEBUG_MUTEX
 #include <loguru.hpp>
 #include <chrono>
 #include <type_traits>
 
-#define UNIQUE_LOCK(M,L) \
-	auto start_##L = std::chrono::high_resolution_clock::now(); \
-	std::unique_lock<std::remove_reference<decltype(M)>::type> L(M); \
-	LOG(INFO) << "LOCK(" << #M "," #L << ") in " << std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - start_##L).count();
+#define MUTEX std::timed_mutex
+#define RECURSIVE_MUTEX std::recursive_timed_mutex
+#define SHARED_MUTEX std::shared_timed_mutex
+
+#define UNIQUE_LOCK(M,L) std::unique_lock<std::remove_reference<decltype(M)>::type> L(M, std::chrono::seconds(5)); if (!L) LOG(FATAL) << "Mutex deadlock";
+#define SHARED_LOCK(M,L) std::shared_lock<std::remove_reference<decltype(M)>::type> L(M, std::chrono::seconds(5)); if (!L) LOG(FATAL) << "Mutex deadlock";
 
-#define SHARED_LOCK(M,L) \
-	auto start_##L = std::chrono::high_resolution_clock::now(); \
-	std::shared_lock<std::remove_reference<decltype(M)>::type> L(M); \
-	LOG(INFO) << "LOCK(" << #M "," #L << ") in " << std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - start_##L).count();
 #else
+#define MUTEX std::mutex
+#define RECURSIVE_MUTEX std::recursive_mutex
+#define SHARED_MUTEX std::shared_mutex
+
 #define UNIQUE_LOCK(M,L) std::unique_lock<std::remove_reference<decltype(M)>::type> L(M);
 #define SHARED_LOCK(M,L) std::shared_lock<std::remove_reference<decltype(M)>::type> L(M);
-#endif  // _DEBUG && DEBUG_MUTEX
+#endif  // DEBUG_MUTEX
 
 namespace ftl {
 	extern ctpl::thread_pool pool;
diff --git a/components/control/cpp/include/ftl/slave.hpp b/components/control/cpp/include/ftl/slave.hpp
index a7ae0075a27b206e1fb6a2d980928075957dcfd8..e3ebd69e46fa5177eebbcc2d8d6b2f2cce70f204 100644
--- a/components/control/cpp/include/ftl/slave.hpp
+++ b/components/control/cpp/include/ftl/slave.hpp
@@ -4,7 +4,7 @@
 #include <ftl/net/universe.hpp>
 #include <ftl/configurable.hpp>
 #include <loguru.hpp>
-#include <mutex>
+#include <ftl/threads.hpp>
 
 namespace ftl {
 namespace ctrl {
@@ -38,7 +38,7 @@ class Slave {
 	private:
 	std::vector<ftl::UUID> log_peers_;
 	ftl::net::Universe *net_;
-	std::recursive_mutex mutex_;
+	RECURSIVE_MUTEX mutex_;
 	bool in_log_;
 	bool active_;
 	SystemState state_;
diff --git a/components/net/cpp/include/ftl/net/peer.hpp b/components/net/cpp/include/ftl/net/peer.hpp
index 16393a55c9cd055971531903cf62f58a84b87a85..f58ab004e59c834e3968b78867a58d00475267f3 100644
--- a/components/net/cpp/include/ftl/net/peer.hpp
+++ b/components/net/cpp/include/ftl/net/peer.hpp
@@ -242,14 +242,14 @@ class Peer {
 	// Receive buffers
 	bool is_waiting_;
 	msgpack::unpacker recv_buf_;
-	std::recursive_mutex recv_mtx_;
+	RECURSIVE_MUTEX recv_mtx_;
 	bool ws_read_header_;
 	
 	// Send buffers
 	msgpack::vrefbuffer send_buf_;
-	std::recursive_mutex send_mtx_;
+	RECURSIVE_MUTEX send_mtx_;
 
-	std::recursive_mutex cb_mtx_;
+	RECURSIVE_MUTEX cb_mtx_;
 	
 	std::string uri_;				// Original connection URI, or assumed URI
 	ftl::UUID peerid_;				// Received in handshake or allocated
@@ -292,7 +292,8 @@ R Peer::call(const std::string &name, ARGS... args) {
 	
 	R result;
 	int id = asyncCall<R>(name, [&](const R &r) {
-		UNIQUE_LOCK(m,lk);
+		//UNIQUE_LOCK(m,lk);
+		std::unique_lock<std::mutex> lk(m);
 		hasreturned = true;
 		result = r;
 		lk.unlock();
@@ -300,7 +301,8 @@ R Peer::call(const std::string &name, ARGS... args) {
 	}, std::forward<ARGS>(args)...);
 	
 	{  // Block thread until async callback notifies us
-		UNIQUE_LOCK(m,lk);
+		//UNIQUE_LOCK(m,lk);
+		std::unique_lock<std::mutex> lk(m);
 		cv.wait_for(lk, std::chrono::seconds(1), [&hasreturned]{return hasreturned;});
 	}
 	
diff --git a/components/net/cpp/include/ftl/net/universe.hpp b/components/net/cpp/include/ftl/net/universe.hpp
index 6afdd610795850af6f1baec58ec0957aa168f74c..720dc8a59759943bab565d42ef98c564e91c8fdf 100644
--- a/components/net/cpp/include/ftl/net/universe.hpp
+++ b/components/net/cpp/include/ftl/net/universe.hpp
@@ -203,8 +203,8 @@ class Universe : public ftl::Configurable {
 	private:
 	bool active_;
 	ftl::UUID this_peer;
-	std::shared_mutex net_mutex_;
-	std::recursive_mutex handler_mutex_;
+	SHARED_MUTEX net_mutex_;
+	RECURSIVE_MUTEX handler_mutex_;
 	fd_set sfderror_;
 	fd_set sfdread_;
 	std::vector<ftl::net::Listener*> listeners_;
@@ -275,7 +275,8 @@ std::optional<R> Universe::findOne(const std::string &name, ARGS... args) {
 	std::optional<R> result;
 
 	auto handler = [&](const std::optional<R> &r) {
-		UNIQUE_LOCK(m,lk);
+		//UNIQUE_LOCK(m,lk);
+		std::unique_lock<std::mutex> lk(m);
 		if (hasreturned || !r) return;
 		hasreturned = true;
 		result = r;
@@ -292,7 +293,8 @@ std::optional<R> Universe::findOne(const std::string &name, ARGS... args) {
 	lk.unlock();
 	
 	{  // Block thread until async callback notifies us
-		UNIQUE_LOCK(m,llk);
+		//UNIQUE_LOCK(m,llk);
+		std::unique_lock<std::mutex> llk(m);
 		cv.wait_for(llk, std::chrono::seconds(1), [&hasreturned]{return hasreturned;});
 
 		// Cancel any further results
@@ -318,7 +320,8 @@ std::vector<R> Universe::findAll(const std::string &name, ARGS... args) {
 	std::vector<R> results;
 
 	auto handler = [&](const std::vector<R> &r) {
-		UNIQUE_LOCK(m,lk);
+		//UNIQUE_LOCK(m,lk);
+		std::unique_lock<std::mutex> lk(m);
 		returncount++;
 		results.insert(results.end(), r.begin(), r.end());
 		lk.unlock();
@@ -337,7 +340,8 @@ std::vector<R> Universe::findAll(const std::string &name, ARGS... args) {
 	lk.unlock();
 	
 	{  // Block thread until async callback notifies us
-		UNIQUE_LOCK(m,llk);
+		//UNIQUE_LOCK(m,llk);
+		std::unique_lock<std::mutex> llk(m);
 		cv.wait_for(llk, std::chrono::seconds(1), [&returncount,&sentcount]{return returncount == sentcount;});
 
 		// Cancel any further results
diff --git a/components/net/cpp/src/peer.cpp b/components/net/cpp/src/peer.cpp
index ba32248ba0ac3da8a9ee1d2affed7ccd30c87eee..c874510963afca5b8f9a7e182bcf4b7719c2b09a 100644
--- a/components/net/cpp/src/peer.cpp
+++ b/components/net/cpp/src/peer.cpp
@@ -41,10 +41,7 @@ using ftl::net::Dispatcher;
 using std::chrono::seconds;
 using ftl::net::Universe;
 using ftl::net::callback_t;
-using std::mutex;
 using std::vector;
-using std::recursive_mutex;
-using std::unique_lock;
 
 /*static std::string hexStr(const std::string &s)
 {
@@ -535,7 +532,8 @@ bool Peer::waitConnection() {
 	if (status_ == kConnected) return true;
 	
 	std::mutex m;
-	UNIQUE_LOCK(m,lk);
+	//UNIQUE_LOCK(m,lk);
+	std::unique_lock<std::mutex> lk(m);
 	std::condition_variable cv;
 
 	callback_t h = universe_->onConnect([this,&cv](Peer *p) {
diff --git a/components/net/cpp/src/universe.cpp b/components/net/cpp/src/universe.cpp
index 488a2acded0c99957817dad4e5c53c00536af42a..2282af25208d48214ff5c1b4d4d40cf912c6c237 100644
--- a/components/net/cpp/src/universe.cpp
+++ b/components/net/cpp/src/universe.cpp
@@ -19,10 +19,6 @@ using ftl::net::Universe;
 using nlohmann::json;
 using ftl::UUID;
 using std::optional;
-using std::unique_lock;
-using std::shared_lock;
-using std::mutex;
-using std::shared_mutex;
 using ftl::config::json_t;
 using ftl::net::callback_t;
 
diff --git a/components/rgbd-sources/include/ftl/rgbd/source.hpp b/components/rgbd-sources/include/ftl/rgbd/source.hpp
index 3cf1a2b520efd3e390713f34e69991c43b959a04..4fd875bc8cdc7166e4c5bb5a82bb7ba881701462 100644
--- a/components/rgbd-sources/include/ftl/rgbd/source.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/source.hpp
@@ -3,12 +3,12 @@
 
 #include <ftl/configuration.hpp>
 #include <ftl/rgbd/camera.hpp>
+#include <ftl/threads.hpp>
 //#include <ftl/net/universe.hpp>
 #include <ftl/uri.hpp>
 #include <ftl/rgbd/detail/source.hpp>
 #include <opencv2/opencv.hpp>
 #include <Eigen/Eigen>
-#include <shared_mutex>
 #include <string>
 
 namespace ftl {
@@ -167,7 +167,7 @@ class Source : public ftl::Configurable {
 
 	void customImplementation(detail::Source *);
 
-	std::shared_mutex &mutex() { return mutex_; }
+	SHARED_MUTEX &mutex() { return mutex_; }
 
 	private:
 	detail::Source *impl_;
@@ -177,7 +177,7 @@ class Source : public ftl::Configurable {
 	Camera params_;		// TODO Find better solution
 	Eigen::Matrix4d pose_;
 	ftl::net::Universe *net_;
-	std::shared_mutex mutex_;
+	SHARED_MUTEX mutex_;
 	bool paused_;
 	bool bullet_;
 
diff --git a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp
index 5dac8fd13252578fa0575c55da7542ebf9451da3..2ab3bbda4281d19837fd3924959ee193c8c5ed0f 100644
--- a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp
@@ -10,7 +10,6 @@
 #include <string>
 #include <vector>
 #include <map>
-#include <shared_mutex>
 #include <atomic>
 
 namespace ftl {
@@ -41,7 +40,7 @@ struct StreamSource {
 	cv::Mat prev_rgb;
 	cv::Mat prev_depth;
 	std::list<detail::StreamClient> clients[10];	// One list per bitrate
-	std::shared_mutex mutex;
+	SHARED_MUTEX mutex;
 	unsigned long long frame;
 };
 
@@ -107,7 +106,7 @@ class Streamer : public ftl::Configurable {
 	private:
 	std::map<std::string, detail::StreamSource*> sources_;
 	//ctpl::thread_pool pool_;
-	std::shared_mutex mutex_;
+	SHARED_MUTEX mutex_;
 	bool active_;
 	ftl::net::Universe *net_;
 	bool late_;
diff --git a/components/rgbd-sources/include/ftl/rgbd_source.hpp b/components/rgbd-sources/include/ftl/rgbd_source.hpp
index 1c147e9614849369b35c12af07c267751a593b76..c065e6e19dcce61f94cbfe6b085c421239bd9297 100644
--- a/components/rgbd-sources/include/ftl/rgbd_source.hpp
+++ b/components/rgbd-sources/include/ftl/rgbd_source.hpp
@@ -4,11 +4,11 @@
 
 #include <ftl/config.h>
 #include <ftl/configurable.hpp>
+#include <ftl/threads.hpp>
 #include <ftl/camera_params.hpp>
 #include <ftl/net/universe.hpp>
 #include <opencv2/opencv.hpp>
 #include <Eigen/Eigen>
-#include <mutex>
 
 namespace ftl {
 namespace rgbd {
@@ -77,7 +77,7 @@ class RGBDSource : public ftl::Configurable {
 	protected:
 	CameraParameters params_;
 	ftl::net::Universe *net_;
-	std::mutex mutex_;
+	MUTEX mutex_;
 	cv::Mat rgb_;
 	cv::Mat depth_;
 
diff --git a/components/rgbd-sources/src/net.cpp b/components/rgbd-sources/src/net.cpp
index 7752f8d1f68531a88f963bba4e44a28d7ef80593..bedac5b67b526118e89b96cbe21a9808b60befc8 100644
--- a/components/rgbd-sources/src/net.cpp
+++ b/components/rgbd-sources/src/net.cpp
@@ -2,7 +2,6 @@
 #include <vector>
 #include <thread>
 #include <chrono>
-#include <shared_mutex>
 #include <tuple>
 
 #include "colour.hpp"
@@ -14,8 +13,6 @@ using ftl::net::Universe;
 using ftl::UUID;
 using std::string;
 using ftl::rgbd::Camera;
-using std::shared_mutex;
-using std::unique_lock;
 using std::vector;
 using std::this_thread::sleep_for;
 using std::chrono::milliseconds;
diff --git a/components/rgbd-sources/src/net.hpp b/components/rgbd-sources/src/net.hpp
index 61bbbe7d71cb65b7ba7cb6a20b53bac238e26649..a962d880a973c1696728de808c30ffd9dae11b55 100644
--- a/components/rgbd-sources/src/net.hpp
+++ b/components/rgbd-sources/src/net.hpp
@@ -4,8 +4,8 @@
 
 #include <ftl/net/universe.hpp>
 #include <ftl/rgbd/source.hpp>
+#include <ftl/threads.hpp>
 #include <string>
-#include <mutex>
 
 namespace ftl {
 namespace rgbd {
@@ -36,7 +36,7 @@ class NetSource : public detail::Source {
 	bool active_;
 	std::string uri_;
 	ftl::net::callback_t h_;
-	std::mutex mutex_;
+	MUTEX mutex_;
 	int chunks_dim_;
 	int chunk_width_;
 	int chunk_height_;
diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp
index 1f6c521cc688c9ff34123376379659ee9c7985ce..f810f38daf7e94ef889f51bb4fbc88d4b7102814 100644
--- a/components/rgbd-sources/src/source.cpp
+++ b/components/rgbd-sources/src/source.cpp
@@ -20,9 +20,6 @@ using ftl::rgbd::detail::RealsenseSource;
 using ftl::rgbd::Source;
 using ftl::Configurable;
 using std::string;
-using std::shared_mutex;
-using std::unique_lock;
-using std::shared_lock;
 using ftl::rgbd::detail::StereoVideoSource;
 using ftl::rgbd::detail::NetSource;
 using ftl::rgbd::detail::ImageSource;
diff --git a/components/rgbd-sources/src/stereovideo.cpp b/components/rgbd-sources/src/stereovideo.cpp
index 9442eefde5686bbab620c32c7c21d79946e664b8..edcd5078a7381f8065ce076c0bb00d10e85668bc 100644
--- a/components/rgbd-sources/src/stereovideo.cpp
+++ b/components/rgbd-sources/src/stereovideo.cpp
@@ -1,17 +1,15 @@
 #include <loguru.hpp>
 #include "stereovideo.hpp"
 #include <ftl/configuration.hpp>
+#include <ftl/threads.hpp>
 #include "calibrate.hpp"
 #include "local.hpp"
 #include "disparity.hpp"
-#include <mutex>
 
 using ftl::rgbd::detail::Calibrate;
 using ftl::rgbd::detail::LocalSource;
 using ftl::rgbd::detail::StereoVideoSource;
 using std::string;
-using std::mutex;
-using std::unique_lock;
 
 StereoVideoSource::StereoVideoSource(ftl::rgbd::Source *host)
 		: ftl::rgbd::detail::Source(host), ready_(false) {
@@ -83,14 +81,14 @@ void StereoVideoSource::init(const string &file) {
 	// Add event handlers to allow calibration changes...
 	host_->on("baseline", [this](const ftl::config::Event &e) {
 		params_.baseline = host_->value("baseline", params_.baseline);
-		std::unique_lock<std::shared_mutex> lk(host_->mutex());
+		UNIQUE_LOCK(host_->mutex(), lk);
 		calib_->updateCalibration(params_);
 	});
 
 	host_->on("focal", [this](const ftl::config::Event &e) {
 		params_.fx = host_->value("focal", params_.fx);
 		params_.fy = params_.fx;
-		std::unique_lock<std::shared_mutex> lk(host_->mutex());
+		UNIQUE_LOCK(host_->mutex(), lk);
 		calib_->updateCalibration(params_);
 	});
 	
diff --git a/components/rgbd-sources/src/streamer.cpp b/components/rgbd-sources/src/streamer.cpp
index 3dc57428b7ce06ed2e744f40995d805ba0657692..6c656ec79d297567b30491b02aaec8948e390330 100644
--- a/components/rgbd-sources/src/streamer.cpp
+++ b/components/rgbd-sources/src/streamer.cpp
@@ -18,10 +18,6 @@ using std::list;
 using std::map;
 using std::optional;
 using std::vector;
-using std::mutex;
-using std::shared_mutex;
-using std::unique_lock;
-using std::shared_lock;
 using std::this_thread::sleep_for;
 using std::chrono::milliseconds;
 using std::tuple;
@@ -259,8 +255,12 @@ void Streamer::wait() {
 	}
 
 	// Wait for all jobs to complete before finishing frame
-	UNIQUE_LOCK(job_mtx_, lk);
-	job_cv_.wait(lk, [this]{ return jobs_ == 0; });
+	//UNIQUE_LOCK(job_mtx_, lk);
+	std::unique_lock<std::mutex> lk(job_mtx_);
+	job_cv_.wait_for(lk, std::chrono::seconds(20), [this]{ return jobs_ == 0; });
+	if (jobs_ != 0) {
+		LOG(FATAL) << "Deadlock detected";
+	}
 }
 
 void Streamer::_schedule() {
@@ -282,12 +282,12 @@ void Streamer::_schedule() {
 
 		// There will be two jobs for this source...
 		//UNIQUE_LOCK(job_mtx_,lk);
-		jobs_ += 1 + kChunkDim*kChunkDim;
+		jobs_ += 1 + kChunkCount;
 		//lk.unlock();
 
 		StreamSource *src = sources_[uri];
 		if (src == nullptr || src->jobs != 0) continue;
-		src->jobs = 1 + kChunkDim*kChunkDim;
+		src->jobs = 1 + kChunkCount;
 
 		// Grab job
 		ftl::pool.push([this,src](int id) {
@@ -310,6 +310,7 @@ void Streamer::_schedule() {
 			_swap(src);
 
 			// Mark job as finished
+			std::unique_lock<std::mutex> lk(job_mtx_);
 			--jobs_;
 			job_cv_.notify_one();
 		});
@@ -388,6 +389,7 @@ void Streamer::_schedule() {
 
 				src->jobs--;
 				_swap(src);
+				std::unique_lock<std::mutex> lk(job_mtx_);
 				--jobs_;
 				job_cv_.notify_one();
 			}, i);