diff --git a/README.md b/README.md
index ba87ecf..a241d00 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+
 # A single-producer, single-consumer lock-free queue for C++
 
 This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++.
@@ -115,7 +116,37 @@ q.wait_dequeue_timed(number, std::chrono::milliseconds(10));
 ```
 
 
-## CMake installation
+## CMake
+### Using targets in your project
+Using this project as a part of an existing CMake project is easy.
+
+In your CMakeLists.txt:
+```
+include(FetchContent)
+
+FetchContent_Declare(
+  readerwriterqueue
+  GIT_REPOSITORY    https://github.com/cameron314/readerwriterqueue
+  GIT_TAG           master
+)
+
+FetchContent_MakeAvailable(readerwriterqueue)
+
+add_library(my_target main.cpp)
+target_link_libraries(my_target PUBLIC readerwriterqueue)
+```
+
+In main.cpp:
+```cpp
+#include <readerwriterqueue.h>
+
+int main()
+{
+    moodycamel::ReaderWriterQueue<int> q(100);
+}
+```
+
+### Installing into system directories
 As an alternative to including the source files in your project directly,
 you can use CMake to install the library in your system's include directory:
 
diff --git a/atomicops.h b/atomicops.h
index c1b8ab6..b103bc6 100644
--- a/atomicops.h
+++ b/atomicops.h
@@ -43,16 +43,27 @@
 // AE_UNUSED
 #define AE_UNUSED(x) ((void)x)
 
-// AE_NO_TSAN
+// AE_NO_TSAN/AE_TSAN_ANNOTATE_*
 #if defined(__has_feature)
 #if __has_feature(thread_sanitizer)
+#if __cplusplus >= 201703L  // inline variables require C++17
+namespace moodycamel { inline int ae_tsan_global; }
+#define AE_TSAN_ANNOTATE_RELEASE() AnnotateHappensBefore(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global))
+#define AE_TSAN_ANNOTATE_ACQUIRE() AnnotateHappensAfter(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global))
+extern "C" void AnnotateHappensBefore(const char*, int, void*);
+extern "C" void AnnotateHappensAfter(const char*, int, void*);
+#else  // when we can't work with tsan, attempt to disable its warnings
 #define AE_NO_TSAN __attribute__((no_sanitize("thread")))
-#else
-#define AE_NO_TSAN
 #endif
-#else
+#endif
+#endif
+#ifndef AE_NO_TSAN
 #define AE_NO_TSAN
 #endif
+#ifndef AE_TSAN_ANNOTATE_RELEASE
+#define AE_TSAN_ANNOTATE_RELEASE()
+#define AE_TSAN_ANNOTATE_ACQUIRE()
+#endif
 
 
 // AE_FORCEINLINE
@@ -208,10 +219,10 @@ AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
 {
 	switch (order) {
 		case memory_order_relaxed: break;
-		case memory_order_acquire: std::atomic_thread_fence(std::memory_order_acquire); break;
-		case memory_order_release: std::atomic_thread_fence(std::memory_order_release); break;
-		case memory_order_acq_rel: std::atomic_thread_fence(std::memory_order_acq_rel); break;
-		case memory_order_seq_cst: std::atomic_thread_fence(std::memory_order_seq_cst); break;
+		case memory_order_acquire: AE_TSAN_ANNOTATE_ACQUIRE(); std::atomic_thread_fence(std::memory_order_acquire); break;
+		case memory_order_release: AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_release); break;
+		case memory_order_acq_rel: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_acq_rel); break;
+		case memory_order_seq_cst: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_seq_cst); break;
 		default: assert(false);
 	}
 }
@@ -352,6 +363,10 @@ extern "C" {
 #include <mach/mach.h>
 #elif defined(__unix__)
 #include <semaphore.h>
+#elif defined(FREERTOS)
+#include <FreeRTOS.h>
+#include <semphr.h>
+#include <task.h>
 #endif
 
 namespace moodycamel
@@ -566,6 +581,73 @@ namespace moodycamel
 		        }
 		    }
 		};
+#elif defined(FREERTOS)
+		//---------------------------------------------------------
+		// Semaphore (FreeRTOS)
+		//---------------------------------------------------------
+		class Semaphore
+		{
+		private:
+			SemaphoreHandle_t m_sema;
+
+			Semaphore(const Semaphore& other);
+			Semaphore& operator=(const Semaphore& other);
+
+		public:
+			AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema()
+			{
+				assert(initialCount >= 0);
+				m_sema = xSemaphoreCreateCounting(static_cast<UBaseType_t>(~0ull), static_cast<UBaseType_t>(initialCount));
+				assert(m_sema);
+			}
+
+			AE_NO_TSAN ~Semaphore()
+			{
+				vSemaphoreDelete(m_sema);
+			}
+
+			bool wait() AE_NO_TSAN
+			{
+				return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE;
+			}
+
+			bool try_wait() AE_NO_TSAN
+			{
+				// Note: In an ISR context, if this causes a task to unblock,
+				// the caller won't know about it
+				if (xPortIsInsideInterrupt())
+					return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE;
+				return xSemaphoreTake(m_sema, 0) == pdTRUE;
+			}
+
+			bool timed_wait(std::uint64_t usecs) AE_NO_TSAN
+			{
+				std::uint64_t msecs = usecs / 1000;
+				TickType_t ticks = static_cast<TickType_t>(msecs / portTICK_PERIOD_MS);
+				if (ticks == 0)
+					return try_wait();
+				return xSemaphoreTake(m_sema, ticks) == pdTRUE;
+			}
+
+			void signal() AE_NO_TSAN
+			{
+				// Note: In an ISR context, if this causes a task to unblock,
+				// the caller won't know about it
+				BaseType_t rc;
+				if (xPortIsInsideInterrupt())
+					rc = xSemaphoreGiveFromISR(m_sema, NULL);
+				else
+					rc = xSemaphoreGive(m_sema);
+				assert(rc == pdTRUE);
+				AE_UNUSED(rc);
+			}
+
+			void signal(int count) AE_NO_TSAN
+			{
+				while (count-- > 0)
+					signal();
+			}
+		};
 #else
 #error Unsupported platform! (No semaphore wrapper available)
 #endif
diff --git a/debian/changelog b/debian/changelog
index 2cae7c1..0a3a0b8 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+readerwriterqueue (1.0.6-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Mon, 16 May 2022 13:22:22 -0000
+
 readerwriterqueue (1.0.5-1) unstable; urgency=medium
 
   * Team Upload.
diff --git a/debian/patches/hardening.patch b/debian/patches/hardening.patch
index ce9356f..158394d 100644
--- a/debian/patches/hardening.patch
+++ b/debian/patches/hardening.patch
@@ -1,9 +1,11 @@
 Description: Propagate hardening options to fix blhc
 Author: Nilesh Patra <nilesh@debian.org>
 Last-Update: 2021-04-21
---- a/benchmarks/makefile
-+++ b/benchmarks/makefile
-@@ -16,7 +16,7 @@
+Index: readerwriterqueue/benchmarks/makefile
+===================================================================
+--- readerwriterqueue.orig/benchmarks/makefile
++++ readerwriterqueue/benchmarks/makefile
+@@ -16,7 +16,7 @@ endif
  default: benchmarks$(EXT)
  
  benchmarks$(EXT): bench.cpp ../readerwriterqueue.h ../readerwritercircularbuffer.h ../atomicops.h ext/1024cores/spscqueue.h ext/folly/ProducerConsumerQueue.h ../tests/common/simplethread.h ../tests/common/simplethread.cpp systemtime.h systemtime.cpp makefile
@@ -12,9 +14,11 @@ Last-Update: 2021-04-21
  
  run: benchmarks$(EXT)
  	./benchmarks$(EXT)
---- a/tests/stabtest/makefile
-+++ b/tests/stabtest/makefile
-@@ -18,7 +18,7 @@
+Index: readerwriterqueue/tests/stabtest/makefile
+===================================================================
+--- readerwriterqueue.orig/tests/stabtest/makefile
++++ readerwriterqueue/tests/stabtest/makefile
+@@ -18,7 +18,7 @@ endif
  default: stabtest$(EXT)
  
  stabtest$(EXT): stabtest.cpp ../../readerwriterqueue.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp makefile
@@ -23,9 +27,11 @@ Last-Update: 2021-04-21
  
  run: stabtest$(EXT)
  	./stabtest$(EXT)
---- a/tests/unittests/makefile
-+++ b/tests/unittests/makefile
-@@ -21,7 +21,7 @@
+Index: readerwriterqueue/tests/unittests/makefile
+===================================================================
+--- readerwriterqueue.orig/tests/unittests/makefile
++++ readerwriterqueue/tests/unittests/makefile
+@@ -21,7 +21,7 @@ endif
  default: unittests$(EXT)
  
  unittests$(EXT): unittests.cpp ../../readerwriterqueue.h ../../readerwritercircularbuffer.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp minitest.h makefile
diff --git a/readerwritercircularbuffer.h b/readerwritercircularbuffer.h
index dbaabe8..ea25df5 100644
--- a/readerwritercircularbuffer.h
+++ b/readerwritercircularbuffer.h
@@ -33,7 +33,7 @@ public:
 public:
 	explicit BlockingReaderWriterCircularBuffer(std::size_t capacity)
 		: maxcap(capacity), mask(), rawData(), data(),
-		slots(new spsc_sema::LightweightSemaphore(static_cast<spsc_sema::LightweightSemaphore::ssize_t>(capacity))),
+		slots_(new spsc_sema::LightweightSemaphore(static_cast<spsc_sema::LightweightSemaphore::ssize_t>(capacity))),
 		items(new spsc_sema::LightweightSemaphore(0)),
 		nextSlot(0), nextItem(0)
 	{
@@ -52,7 +52,7 @@ public:
 
 	BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer&& other)
 		: maxcap(0), mask(0), rawData(nullptr), data(nullptr),
-		slots(new spsc_sema::LightweightSemaphore(0)),
+		slots_(new spsc_sema::LightweightSemaphore(0)),
 		items(new spsc_sema::LightweightSemaphore(0)),
 		nextSlot(), nextItem()
 	{
@@ -86,7 +86,7 @@ public:
 		std::swap(mask, other.mask);
 		std::swap(rawData, other.rawData);
 		std::swap(data, other.data);
-		std::swap(slots, other.slots);
+		std::swap(slots_, other.slots_);
 		std::swap(items, other.items);
 		std::swap(nextSlot, other.nextSlot);
 		std::swap(nextItem, other.nextItem);
@@ -98,7 +98,7 @@ public:
 	// No exception guarantee (state will be corrupted) if constructor of T throws.
 	bool try_enqueue(T const& item)
 	{
-		if (!slots->tryWait())
+		if (!slots_->tryWait())
 			return false;
 		inner_enqueue(item);
 		return true;
@@ -110,7 +110,7 @@ public:
 	// No exception guarantee (state will be corrupted) if constructor of T throws.
 	bool try_enqueue(T&& item)
 	{
-		if (!slots->tryWait())
+		if (!slots_->tryWait())
 			return false;
 		inner_enqueue(std::move(item));
 		return true;
@@ -122,7 +122,7 @@ public:
 	// No exception guarantee (state will be corrupted) if constructor of T throws.
 	void wait_enqueue(T const& item)
 	{
-		while (!slots->wait());
+		while (!slots_->wait());
 		inner_enqueue(item);
 	}
 
@@ -132,7 +132,7 @@ public:
 	// No exception guarantee (state will be corrupted) if constructor of T throws.
 	void wait_enqueue(T&& item)
 	{
-		while (!slots->wait());
+		while (!slots_->wait());
 		inner_enqueue(std::move(item));
 	}
 
@@ -143,7 +143,7 @@ public:
 	// No exception guarantee (state will be corrupted) if constructor of T throws.
 	bool wait_enqueue_timed(T const& item, std::int64_t timeout_usecs)
 	{
-		if (!slots->wait(timeout_usecs))
+		if (!slots_->wait(timeout_usecs))
 			return false;
 		inner_enqueue(item);
 		return true;
@@ -156,7 +156,7 @@ public:
 	// No exception guarantee (state will be corrupted) if constructor of T throws.
 	bool wait_enqueue_timed(T&& item, std::int64_t timeout_usecs)
 	{
-		if (!slots->wait(timeout_usecs))
+		if (!slots_->wait(timeout_usecs))
 			return false;
 		inner_enqueue(std::move(item));
 		return true;
@@ -262,7 +262,7 @@ private:
 		T& element = reinterpret_cast<T*>(data)[i & mask];
 		item = std::move(element);
 		element.~T();
-		slots->signal();
+		slots_->signal();
 	}
 
 	template<typename U>
@@ -277,8 +277,8 @@ private:
 	std::size_t mask;                             // circular buffer capacity mask (for cheap modulo)
 	char* rawData;                                // raw circular buffer memory
 	char* data;                                   // circular buffer memory aligned to element alignment
-	std::unique_ptr<spsc_sema::LightweightSemaphore> slots;  // number of slots currently free
-	std::unique_ptr<spsc_sema::LightweightSemaphore> items;  // number of elements currently enqueued
+	std::unique_ptr<spsc_sema::LightweightSemaphore> slots_;  // number of slots currently free (named with underscore to accommodate Qt's 'slots' macro)
+	std::unique_ptr<spsc_sema::LightweightSemaphore> items;   // number of elements currently enqueued
 	char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(char*) * 2 - sizeof(std::size_t) * 2 - sizeof(std::unique_ptr<spsc_sema::LightweightSemaphore>) * 2];
 	std::size_t nextSlot;                         // index of next free slot to enqueue into
 	char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(std::size_t)];