1*d7cf17b1Ss.makeev_local // The MIT License (MIT)
2*d7cf17b1Ss.makeev_local //
3*d7cf17b1Ss.makeev_local // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
4*d7cf17b1Ss.makeev_local //
5*d7cf17b1Ss.makeev_local // 	Permission is hereby granted, free of charge, to any person obtaining a copy
6*d7cf17b1Ss.makeev_local // 	of this software and associated documentation files (the "Software"), to deal
7*d7cf17b1Ss.makeev_local // 	in the Software without restriction, including without limitation the rights
8*d7cf17b1Ss.makeev_local // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9*d7cf17b1Ss.makeev_local // 	copies of the Software, and to permit persons to whom the Software is
10*d7cf17b1Ss.makeev_local // 	furnished to do so, subject to the following conditions:
11*d7cf17b1Ss.makeev_local //
12*d7cf17b1Ss.makeev_local //  The above copyright notice and this permission notice shall be included in
13*d7cf17b1Ss.makeev_local // 	all copies or substantial portions of the Software.
14*d7cf17b1Ss.makeev_local //
15*d7cf17b1Ss.makeev_local // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*d7cf17b1Ss.makeev_local // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*d7cf17b1Ss.makeev_local // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18*d7cf17b1Ss.makeev_local // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*d7cf17b1Ss.makeev_local // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20*d7cf17b1Ss.makeev_local // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21*d7cf17b1Ss.makeev_local // 	THE SOFTWARE.
22*d7cf17b1Ss.makeev_local 
23*d7cf17b1Ss.makeev_local #pragma once
24*d7cf17b1Ss.makeev_local 
25*d7cf17b1Ss.makeev_local #include <vector>
26*d7cf17b1Ss.makeev_local 
27*d7cf17b1Ss.makeev_local #include <MTPlatform.h>
28*d7cf17b1Ss.makeev_local #include <MTTools.h>
29*d7cf17b1Ss.makeev_local #include <MTAppInterop.h>
30*d7cf17b1Ss.makeev_local 
31*d7cf17b1Ss.makeev_local 
32*d7cf17b1Ss.makeev_local namespace MT
33*d7cf17b1Ss.makeev_local {
34*d7cf17b1Ss.makeev_local 	/// \class TaskQueue
35*d7cf17b1Ss.makeev_local 	/// \brief thread safe task queue
36*d7cf17b1Ss.makeev_local 	///
37*d7cf17b1Ss.makeev_local 	template<typename T>
38*d7cf17b1Ss.makeev_local 	class TaskQueue
39*d7cf17b1Ss.makeev_local 	{
40*d7cf17b1Ss.makeev_local 		static const int32 ALIGNMENT = 16;
41*d7cf17b1Ss.makeev_local 
42*d7cf17b1Ss.makeev_local 	public:
43*d7cf17b1Ss.makeev_local 		static const unsigned int CAPACITY = 4096u;
44*d7cf17b1Ss.makeev_local 	private:
45*d7cf17b1Ss.makeev_local 		static const unsigned int MASK = CAPACITY - 1u;
46*d7cf17b1Ss.makeev_local 
47*d7cf17b1Ss.makeev_local 		MT::Mutex mutex;
48*d7cf17b1Ss.makeev_local 
49*d7cf17b1Ss.makeev_local 		void* data;
50*d7cf17b1Ss.makeev_local 
51*d7cf17b1Ss.makeev_local 		size_t begin;
52*d7cf17b1Ss.makeev_local 		size_t end;
53*d7cf17b1Ss.makeev_local 
54*d7cf17b1Ss.makeev_local 		inline T* Buffer()
55*d7cf17b1Ss.makeev_local 		{
56*d7cf17b1Ss.makeev_local 			return (T*)(data);
57*d7cf17b1Ss.makeev_local 		}
58*d7cf17b1Ss.makeev_local 
59*d7cf17b1Ss.makeev_local 		inline void CopyCtor(T* element, const T & val)
60*d7cf17b1Ss.makeev_local 		{
61*d7cf17b1Ss.makeev_local 			new(element) T(val);
62*d7cf17b1Ss.makeev_local 		}
63*d7cf17b1Ss.makeev_local 
64*d7cf17b1Ss.makeev_local 		inline void MoveCtor(T* element, T && val)
65*d7cf17b1Ss.makeev_local 		{
66*d7cf17b1Ss.makeev_local 			new(element) T(std::move(val));
67*d7cf17b1Ss.makeev_local 		}
68*d7cf17b1Ss.makeev_local 
69*d7cf17b1Ss.makeev_local 		inline void Dtor(T* element)
70*d7cf17b1Ss.makeev_local 		{
71*d7cf17b1Ss.makeev_local 			MT_UNUSED(element);
72*d7cf17b1Ss.makeev_local 			element->~T();
73*d7cf17b1Ss.makeev_local 		}
74*d7cf17b1Ss.makeev_local 
75*d7cf17b1Ss.makeev_local 		inline bool _IsEmpty() const
76*d7cf17b1Ss.makeev_local 		{
77*d7cf17b1Ss.makeev_local 			return (begin == end);
78*d7cf17b1Ss.makeev_local 		}
79*d7cf17b1Ss.makeev_local 
80*d7cf17b1Ss.makeev_local 		inline size_t Size() const
81*d7cf17b1Ss.makeev_local 		{
82*d7cf17b1Ss.makeev_local 			if (_IsEmpty())
83*d7cf17b1Ss.makeev_local 			{
84*d7cf17b1Ss.makeev_local 				return 0;
85*d7cf17b1Ss.makeev_local 			}
86*d7cf17b1Ss.makeev_local 
87*d7cf17b1Ss.makeev_local 			size_t count = ((end & MASK) - (begin & MASK)) & MASK;
88*d7cf17b1Ss.makeev_local 			return count;
89*d7cf17b1Ss.makeev_local 		}
90*d7cf17b1Ss.makeev_local 
91*d7cf17b1Ss.makeev_local 		inline void Clear()
92*d7cf17b1Ss.makeev_local 		{
93*d7cf17b1Ss.makeev_local 			size_t queueSize = Size();
94*d7cf17b1Ss.makeev_local 			for (size_t i = 0; i < queueSize; i++)
95*d7cf17b1Ss.makeev_local 			{
96*d7cf17b1Ss.makeev_local 				T* pElement = Buffer() + ((begin + i) & MASK);
97*d7cf17b1Ss.makeev_local 				Dtor(pElement);
98*d7cf17b1Ss.makeev_local 			}
99*d7cf17b1Ss.makeev_local 
100*d7cf17b1Ss.makeev_local 			begin = 0;
101*d7cf17b1Ss.makeev_local 			end = 0;
102*d7cf17b1Ss.makeev_local 		}
103*d7cf17b1Ss.makeev_local 
104*d7cf17b1Ss.makeev_local 
105*d7cf17b1Ss.makeev_local 	public:
106*d7cf17b1Ss.makeev_local 
107*d7cf17b1Ss.makeev_local 		MT_NOCOPYABLE(TaskQueue);
108*d7cf17b1Ss.makeev_local 
109*d7cf17b1Ss.makeev_local 		/// \name Initializes a new instance of the TaskQueue class.
110*d7cf17b1Ss.makeev_local 		/// \brief
111*d7cf17b1Ss.makeev_local 		TaskQueue()
112*d7cf17b1Ss.makeev_local 			: begin(0)
113*d7cf17b1Ss.makeev_local 			, end(0)
114*d7cf17b1Ss.makeev_local 		{
115*d7cf17b1Ss.makeev_local 			size_t bytesCount = sizeof(T) * CAPACITY;
116*d7cf17b1Ss.makeev_local 			data = Memory::Alloc(bytesCount, ALIGNMENT);
117*d7cf17b1Ss.makeev_local 		}
118*d7cf17b1Ss.makeev_local 
119*d7cf17b1Ss.makeev_local 		~TaskQueue()
120*d7cf17b1Ss.makeev_local 		{
121*d7cf17b1Ss.makeev_local 			if (data != nullptr)
122*d7cf17b1Ss.makeev_local 			{
123*d7cf17b1Ss.makeev_local 				Memory::Free(data);
124*d7cf17b1Ss.makeev_local 				data = nullptr;
125*d7cf17b1Ss.makeev_local 			}
126*d7cf17b1Ss.makeev_local 		}
127*d7cf17b1Ss.makeev_local 
128*d7cf17b1Ss.makeev_local 		/// \brief Add multiple tasks onto queue.
129*d7cf17b1Ss.makeev_local 		/// \param itemArray A pointer to first item. Must not be nullptr
130*d7cf17b1Ss.makeev_local 		/// \param count Items count.
131*d7cf17b1Ss.makeev_local 		bool Add(const T* itemArray, size_t count)
132*d7cf17b1Ss.makeev_local 		{
133*d7cf17b1Ss.makeev_local 			MT::ScopedGuard guard(mutex);
134*d7cf17b1Ss.makeev_local 
135*d7cf17b1Ss.makeev_local 			if ((Size() + count) >= CAPACITY)
136*d7cf17b1Ss.makeev_local 			{
137*d7cf17b1Ss.makeev_local 				return false;
138*d7cf17b1Ss.makeev_local 			}
139*d7cf17b1Ss.makeev_local 
140*d7cf17b1Ss.makeev_local 			for (size_t i = 0; i < count; ++i)
141*d7cf17b1Ss.makeev_local 			{
142*d7cf17b1Ss.makeev_local 				size_t index = (end & MASK);
143*d7cf17b1Ss.makeev_local 				T* pElement = Buffer() + index;
144*d7cf17b1Ss.makeev_local 				CopyCtor( pElement, itemArray[i] );
145*d7cf17b1Ss.makeev_local 				end++;
146*d7cf17b1Ss.makeev_local 			}
147*d7cf17b1Ss.makeev_local 
148*d7cf17b1Ss.makeev_local 			return true;
149*d7cf17b1Ss.makeev_local 		}
150*d7cf17b1Ss.makeev_local 
151*d7cf17b1Ss.makeev_local 
152*d7cf17b1Ss.makeev_local 		/// \brief Try pop oldest item from queue.
153*d7cf17b1Ss.makeev_local 		/// \param item Resultant item
154*d7cf17b1Ss.makeev_local 		/// \return true on success or false if the queue is empty.
155*d7cf17b1Ss.makeev_local 		bool TryPopOldest(T & item)
156*d7cf17b1Ss.makeev_local 		{
157*d7cf17b1Ss.makeev_local 			MT::ScopedGuard guard(mutex);
158*d7cf17b1Ss.makeev_local 
159*d7cf17b1Ss.makeev_local 			if (_IsEmpty())
160*d7cf17b1Ss.makeev_local 			{
161*d7cf17b1Ss.makeev_local 				return false;
162*d7cf17b1Ss.makeev_local 			}
163*d7cf17b1Ss.makeev_local 
164*d7cf17b1Ss.makeev_local 			size_t index = (begin & MASK);
165*d7cf17b1Ss.makeev_local 			T* pElement = Buffer() + index;
166*d7cf17b1Ss.makeev_local 			begin++;
167*d7cf17b1Ss.makeev_local 			item = *pElement;
168*d7cf17b1Ss.makeev_local 			Dtor(pElement);
169*d7cf17b1Ss.makeev_local 			return true;
170*d7cf17b1Ss.makeev_local 		}
171*d7cf17b1Ss.makeev_local 
172*d7cf17b1Ss.makeev_local 		/// \brief Try pop a newest item (recently added) from queue.
173*d7cf17b1Ss.makeev_local 		/// \param item Resultant item
174*d7cf17b1Ss.makeev_local 		/// \return true on success or false if the queue is empty.
175*d7cf17b1Ss.makeev_local 		bool TryPopNewest(T & item)
176*d7cf17b1Ss.makeev_local 		{
177*d7cf17b1Ss.makeev_local 			MT::ScopedGuard guard(mutex);
178*d7cf17b1Ss.makeev_local 
179*d7cf17b1Ss.makeev_local 			if (_IsEmpty())
180*d7cf17b1Ss.makeev_local 			{
181*d7cf17b1Ss.makeev_local 				return false;
182*d7cf17b1Ss.makeev_local 			}
183*d7cf17b1Ss.makeev_local 
184*d7cf17b1Ss.makeev_local 			end--;
185*d7cf17b1Ss.makeev_local 			size_t index = (end & MASK);
186*d7cf17b1Ss.makeev_local 			T* pElement = Buffer() + index;
187*d7cf17b1Ss.makeev_local 			item = *pElement;
188*d7cf17b1Ss.makeev_local 			Dtor(pElement);
189*d7cf17b1Ss.makeev_local 			return true;
190*d7cf17b1Ss.makeev_local 		}
191*d7cf17b1Ss.makeev_local 
192*d7cf17b1Ss.makeev_local 		/// \brief Check if queue is empty.
193*d7cf17b1Ss.makeev_local 		/// \return true - if queue is empty.
194*d7cf17b1Ss.makeev_local 		bool IsEmpty()
195*d7cf17b1Ss.makeev_local 		{
196*d7cf17b1Ss.makeev_local 			MT::ScopedGuard guard(mutex);
197*d7cf17b1Ss.makeev_local 
198*d7cf17b1Ss.makeev_local 			return _IsEmpty();
199*d7cf17b1Ss.makeev_local 		}
200*d7cf17b1Ss.makeev_local 
201*d7cf17b1Ss.makeev_local 	};
202*d7cf17b1Ss.makeev_local }
203