1a101e543SSergey Makeev // The MIT License (MIT)
2a101e543SSergey Makeev //
3a101e543SSergey Makeev // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
4a101e543SSergey Makeev //
5a101e543SSergey Makeev // 	Permission is hereby granted, free of charge, to any person obtaining a copy
6a101e543SSergey Makeev // 	of this software and associated documentation files (the "Software"), to deal
7a101e543SSergey Makeev // 	in the Software without restriction, including without limitation the rights
8a101e543SSergey Makeev // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9a101e543SSergey Makeev // 	copies of the Software, and to permit persons to whom the Software is
10a101e543SSergey Makeev // 	furnished to do so, subject to the following conditions:
11a101e543SSergey Makeev //
12a101e543SSergey Makeev //  The above copyright notice and this permission notice shall be included in
13a101e543SSergey Makeev // 	all copies or substantial portions of the Software.
14a101e543SSergey Makeev //
15a101e543SSergey Makeev // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16a101e543SSergey Makeev // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17a101e543SSergey Makeev // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18a101e543SSergey Makeev // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19a101e543SSergey Makeev // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20a101e543SSergey Makeev // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21a101e543SSergey Makeev // 	THE SOFTWARE.
22a101e543SSergey Makeev 
23a101e543SSergey Makeev #pragma once
24a101e543SSergey Makeev 
25a101e543SSergey Makeev #include <MTPlatform.h>
26a101e543SSergey Makeev #include <MTTools.h>
2702d170cfSs.makeev_local #include <MTAppInterop.h>
28a101e543SSergey Makeev 
29a101e543SSergey Makeev namespace MT
30a101e543SSergey Makeev {
31a101e543SSergey Makeev 	template<int N>
32a101e543SSergey Makeev 	struct is_power_of_two
33a101e543SSergey Makeev 	{
34a101e543SSergey Makeev 		enum {value = N && !(N & (N - 1))};
35a101e543SSergey Makeev 	};
36a101e543SSergey Makeev 
37a101e543SSergey Makeev 
38a101e543SSergey Makeev 	/// \class ConcurrentRingBuffer
39a101e543SSergey Makeev 	/// \brief Very naive implementation of thread safe ring buffer. When ring buffer is full and a subsequent write is performed, then it starts overwriting the oldest data.
40a101e543SSergey Makeev 	template<typename T, size_t numElements>
41a101e543SSergey Makeev 	class ConcurrentRingBuffer
42a101e543SSergey Makeev 	{
43*c7362320Ss.makeev_local 		static const int32 ALIGNMENT = 16;
44*c7362320Ss.makeev_local 
45a101e543SSergey Makeev 		MT::Mutex mutex;
46a101e543SSergey Makeev 
478112dedfSSergey Makeev 		void* data;
48a101e543SSergey Makeev 
49a101e543SSergey Makeev 		size_t writeIndex;
50a101e543SSergey Makeev 		size_t readIndex;
51a101e543SSergey Makeev 		size_t size;
52a101e543SSergey Makeev 
Buffer()538112dedfSSergey Makeev 		inline T* Buffer()
548112dedfSSergey Makeev 		{
558112dedfSSergey Makeev 			return (T*)(data);
568112dedfSSergey Makeev 		}
578112dedfSSergey Makeev 
MoveCtor(T * element,T && val)588112dedfSSergey Makeev 		inline void MoveCtor(T* element, T && val)
598112dedfSSergey Makeev 		{
608112dedfSSergey Makeev 			new(element) T(std::move(val));
618112dedfSSergey Makeev 		}
628112dedfSSergey Makeev 
Dtor(T * element)638112dedfSSergey Makeev 		inline void Dtor(T* element)
648112dedfSSergey Makeev 		{
652e846c40SSergey Makeev 			MT_UNUSED(element);
668112dedfSSergey Makeev 			element->~T();
678112dedfSSergey Makeev 		}
688112dedfSSergey Makeev 
NextIndex(size_t index)69a101e543SSergey Makeev 		size_t NextIndex(size_t index)
70a101e543SSergey Makeev 		{
71a101e543SSergey Makeev 			size_t ret = index + 1;
72a101e543SSergey Makeev 			size_t mask = (numElements - 1);
73a101e543SSergey Makeev 			return (ret & mask);
74a101e543SSergey Makeev 		}
75a101e543SSergey Makeev 
76a101e543SSergey Makeev 	public:
77a101e543SSergey Makeev 
782e846c40SSergey Makeev 		MT_NOCOPYABLE(ConcurrentRingBuffer);
792e846c40SSergey Makeev 
ConcurrentRingBuffer()80a101e543SSergey Makeev 		ConcurrentRingBuffer()
81a101e543SSergey Makeev 			: writeIndex(0)
82a101e543SSergey Makeev 			, readIndex(0)
83a101e543SSergey Makeev 			, size(0)
84a101e543SSergey Makeev 		{
85*c7362320Ss.makeev_local 			data = Memory::Alloc(sizeof(T) * numElements, ALIGNMENT);
868112dedfSSergey Makeev 
87a101e543SSergey Makeev 			static_assert(is_power_of_two<numElements>::value == true, "NumElements used in MT::ConcurrentRingBuffer must be power of two");
88a101e543SSergey Makeev 		}
89a101e543SSergey Makeev 
~ConcurrentRingBuffer()908112dedfSSergey Makeev 		~ConcurrentRingBuffer()
918112dedfSSergey Makeev 		{
9251901c6bSSergey Makeev 			Memory::Free(data);
938112dedfSSergey Makeev 			data = nullptr;
948112dedfSSergey Makeev 		}
958112dedfSSergey Makeev 
Push(T && item)968112dedfSSergey Makeev 		void Push(T && item)
97a101e543SSergey Makeev 		{
98a101e543SSergey Makeev 			MT::ScopedGuard guard(mutex);
99a101e543SSergey Makeev 
100a101e543SSergey Makeev 			if (size >= numElements)
101a101e543SSergey Makeev 			{
102a101e543SSergey Makeev 				// RingBuffer is full. Overwrite old data.
1038112dedfSSergey Makeev 				Dtor(Buffer() + readIndex);
104a101e543SSergey Makeev 				readIndex = NextIndex(readIndex);
105a101e543SSergey Makeev 			} else
106a101e543SSergey Makeev 			{
107a101e543SSergey Makeev 				size++;
108a101e543SSergey Makeev 			}
109a101e543SSergey Makeev 
1108112dedfSSergey Makeev 			MoveCtor(Buffer() + writeIndex, std::move(item));
111a101e543SSergey Makeev 			writeIndex = NextIndex(writeIndex);
112a101e543SSergey Makeev 		}
113a101e543SSergey Makeev 
PopAll(T * dstBuffer,size_t dstBufferSize)114a101e543SSergey Makeev 		size_t PopAll(T * dstBuffer, size_t dstBufferSize)
115a101e543SSergey Makeev 		{
116a101e543SSergey Makeev 			MT::ScopedGuard guard(mutex);
117a101e543SSergey Makeev 
118a101e543SSergey Makeev 			size_t elementsCount = size;
1191e8563ffSSergey Makeev 			elementsCount = MT::Min(elementsCount, dstBufferSize);
120a101e543SSergey Makeev 
121a101e543SSergey Makeev 			for (size_t i = 0; i < elementsCount; i++)
122a101e543SSergey Makeev 			{
1238112dedfSSergey Makeev 				dstBuffer[i] = std::move(Buffer()[readIndex]);
1248112dedfSSergey Makeev 				Dtor(Buffer() + readIndex);
125a101e543SSergey Makeev 				readIndex = NextIndex(readIndex);
126a101e543SSergey Makeev 			}
127a101e543SSergey Makeev 
128a101e543SSergey Makeev 			size -= elementsCount;
129a101e543SSergey Makeev 			return elementsCount;
130a101e543SSergey Makeev 		}
131a101e543SSergey Makeev 
132a101e543SSergey Makeev 
133a101e543SSergey Makeev 
134a101e543SSergey Makeev 
135a101e543SSergey Makeev 	};
136a101e543SSergey Makeev 
137a101e543SSergey Makeev }
138