1b7f5a220SSergey Makeev // The MIT License (MIT)
2b7f5a220SSergey Makeev //
3b7f5a220SSergey Makeev // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
4b7f5a220SSergey Makeev //
5b7f5a220SSergey Makeev // 	Permission is hereby granted, free of charge, to any person obtaining a copy
6b7f5a220SSergey Makeev // 	of this software and associated documentation files (the "Software"), to deal
7b7f5a220SSergey Makeev // 	in the Software without restriction, including without limitation the rights
8b7f5a220SSergey Makeev // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9b7f5a220SSergey Makeev // 	copies of the Software, and to permit persons to whom the Software is
10b7f5a220SSergey Makeev // 	furnished to do so, subject to the following conditions:
11b7f5a220SSergey Makeev //
12b7f5a220SSergey Makeev //  The above copyright notice and this permission notice shall be included in
13b7f5a220SSergey Makeev // 	all copies or substantial portions of the Software.
14b7f5a220SSergey Makeev //
15b7f5a220SSergey Makeev // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16b7f5a220SSergey Makeev // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17b7f5a220SSergey Makeev // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18b7f5a220SSergey Makeev // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19b7f5a220SSergey Makeev // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20b7f5a220SSergey Makeev // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21b7f5a220SSergey Makeev // 	THE SOFTWARE.
22b7f5a220SSergey Makeev 
23b7f5a220SSergey Makeev #pragma once
24b7f5a220SSergey Makeev 
2502d170cfSs.makeev_local #include <MTConfig.h>
26b7f5a220SSergey Makeev 
27b7f5a220SSergey Makeev 
28b7f5a220SSergey Makeev namespace MT
29b7f5a220SSergey Makeev {
30b7f5a220SSergey Makeev 
31b7f5a220SSergey Makeev 	namespace TaskID
32b7f5a220SSergey Makeev 	{
33b7f5a220SSergey Makeev 		//unused_id is any odd number, valid_id should be always only even numbers
34b7f5a220SSergey Makeev 		static const int UNUSED = 1;
35b7f5a220SSergey Makeev 	}
36b7f5a220SSergey Makeev 
37b7f5a220SSergey Makeev 
38b7f5a220SSergey Makeev 	//forward declaration
39b7f5a220SSergey Makeev 	class TaskHandle;
40b7f5a220SSergey Makeev 
41b7f5a220SSergey Makeev 
4258d12dadSSergey Makeev 	/// \class PoolElementHeader
43b7f5a220SSergey Makeev 	/// \brief
44b7f5a220SSergey Makeev 	//////////////////////////////////////////////////////////////////////////
45b7f5a220SSergey Makeev 	struct PoolElementHeader
46b7f5a220SSergey Makeev 	{
4758d12dadSSergey Makeev 		//Task id (timestamp)
48721f8c0bSs.makeev_local 		Atomic32<int32> id;
49b7f5a220SSergey Makeev 
5058d12dadSSergey Makeev 		internal::TaskDesc desc;
51b7f5a220SSergey Makeev 
52b7f5a220SSergey Makeev 	public:
53b7f5a220SSergey Makeev 
PoolElementHeaderPoolElementHeader54b7f5a220SSergey Makeev 		PoolElementHeader(int _id)
55b7f5a220SSergey Makeev 			: id(_id)
56b7f5a220SSergey Makeev 		{
57b7f5a220SSergey Makeev 		}
58b7f5a220SSergey Makeev 
59b7f5a220SSergey Makeev 		static bool DestoryByHandle(const MT::TaskHandle & handle);
60b7f5a220SSergey Makeev 	};
61b7f5a220SSergey Makeev 
62b7f5a220SSergey Makeev 
63b7f5a220SSergey Makeev 	/// \class TaskPoolElement
64b7f5a220SSergey Makeev 	/// \brief
65b7f5a220SSergey Makeev 	//////////////////////////////////////////////////////////////////////////
66b7f5a220SSergey Makeev 	template<typename T>
67b7f5a220SSergey Makeev 	class PoolElement : public PoolElementHeader
68b7f5a220SSergey Makeev 	{
69b7f5a220SSergey Makeev 	public:
70b7f5a220SSergey Makeev 
7158d12dadSSergey Makeev 		// Storage for task
72b7f5a220SSergey Makeev 		T task;
73b7f5a220SSergey Makeev 
PoolElement(int _id,T && _task)74b7f5a220SSergey Makeev 		PoolElement(int _id, T && _task)
75b7f5a220SSergey Makeev 			: PoolElementHeader(_id)
76b7f5a220SSergey Makeev 			, task( std::move(_task) )
77b7f5a220SSergey Makeev 		{
7802d170cfSs.makeev_local 
7902d170cfSs.makeev_local #if MT_CLANG_COMPILER_FAMILY
8002d170cfSs.makeev_local #pragma clang diagnostic push
8102d170cfSs.makeev_local #pragma clang diagnostic ignored "-Winvalid-offsetof"
8202d170cfSs.makeev_local #endif
8302d170cfSs.makeev_local 			static_assert( offsetof(PoolElement<T>, task) == sizeof(PoolElementHeader), "Invalid offset for task in PoolElement");
8402d170cfSs.makeev_local #if MT_CLANG_COMPILER_FAMILY
8502d170cfSs.makeev_local #pragma clang diagnostic pop
8602d170cfSs.makeev_local #endif
8758d12dadSSergey Makeev 
8858d12dadSSergey Makeev 			desc.poolDestroyFunc = T::PoolTaskDestroy;
8958d12dadSSergey Makeev 			desc.taskFunc = T::TaskEntryPoint;
90f4db079dSs.makeev 			desc.stackRequirements = T::GetStackRequirements();
91*b23bdf5aSs.makeev_local 			desc.priority = T::GetTaskPriority();
9258d12dadSSergey Makeev 			desc.userData = &task;
9358d12dadSSergey Makeev 
9458d12dadSSergey Makeev #ifdef MT_INSTRUMENTED_BUILD
9558d12dadSSergey Makeev 			desc.debugID = T::GetDebugID();
964a90b4a6SSergey Makeev 			desc.debugColor = T::GetDebugColor();
9758d12dadSSergey Makeev #endif
98b7f5a220SSergey Makeev 		}
99b7f5a220SSergey Makeev 
100b7f5a220SSergey Makeev 	};
101b7f5a220SSergey Makeev 
102b7f5a220SSergey Makeev 
103b7f5a220SSergey Makeev 	/// \class TaskHandle
104b7f5a220SSergey Makeev 	/// \brief
105b7f5a220SSergey Makeev 	//////////////////////////////////////////////////////////////////////////
106b7f5a220SSergey Makeev 	class TaskHandle
107b7f5a220SSergey Makeev 	{
1082e846c40SSergey Makeev 		int32 check_id;
109b7f5a220SSergey Makeev 
110b7f5a220SSergey Makeev 	protected:
111b7f5a220SSergey Makeev 
112b7f5a220SSergey Makeev 		friend struct PoolElementHeader;
11358d12dadSSergey Makeev 
114b7f5a220SSergey Makeev 		PoolElementHeader* task;
115b7f5a220SSergey Makeev 
116b7f5a220SSergey Makeev 	public:
117b7f5a220SSergey Makeev 
118b7f5a220SSergey Makeev 		//default ctor
TaskHandle()119b7f5a220SSergey Makeev 		TaskHandle()
120b7f5a220SSergey Makeev 			: check_id(TaskID::UNUSED)
121b7f5a220SSergey Makeev 			, task(nullptr)
122b7f5a220SSergey Makeev 		{
123b7f5a220SSergey Makeev 
124b7f5a220SSergey Makeev 		}
125b7f5a220SSergey Makeev 
126b7f5a220SSergey Makeev 		//ctor
TaskHandle(int _id,PoolElementHeader * _task)127b7f5a220SSergey Makeev 		TaskHandle(int _id, PoolElementHeader* _task)
128b7f5a220SSergey Makeev 			: check_id(_id)
129b7f5a220SSergey Makeev 			, task(_task)
130b7f5a220SSergey Makeev 		{
131b7f5a220SSergey Makeev 		}
132b7f5a220SSergey Makeev 
133b7f5a220SSergey Makeev 		//copy ctor
TaskHandle(const TaskHandle & other)134b7f5a220SSergey Makeev 		TaskHandle(const TaskHandle & other)
135b7f5a220SSergey Makeev 			: check_id(other.check_id)
136b7f5a220SSergey Makeev 			, task(other.task)
137b7f5a220SSergey Makeev 		{
138b7f5a220SSergey Makeev 		}
139b7f5a220SSergey Makeev 
140b7f5a220SSergey Makeev 		//move ctor
TaskHandle(TaskHandle && other)141b7f5a220SSergey Makeev 		TaskHandle(TaskHandle && other)
142b7f5a220SSergey Makeev 			: check_id(other.check_id)
143b7f5a220SSergey Makeev 			, task(other.task)
144b7f5a220SSergey Makeev 		{
145b7f5a220SSergey Makeev 			other.check_id = TaskID::UNUSED;
146b7f5a220SSergey Makeev 			other.task = nullptr;
147b7f5a220SSergey Makeev 		}
148b7f5a220SSergey Makeev 
~TaskHandle()149b7f5a220SSergey Makeev 		~TaskHandle()
150b7f5a220SSergey Makeev 		{
151b7f5a220SSergey Makeev 		}
152b7f5a220SSergey Makeev 
IsValid()153b7f5a220SSergey Makeev 		bool IsValid() const
154b7f5a220SSergey Makeev 		{
155b7f5a220SSergey Makeev 			if (task == nullptr)
156b7f5a220SSergey Makeev 			{
157b7f5a220SSergey Makeev 				return false;
158b7f5a220SSergey Makeev 			}
159b7f5a220SSergey Makeev 
16081ec7369SSergey Makeev 			if (check_id != task->id.Load())
161b7f5a220SSergey Makeev 			{
162b7f5a220SSergey Makeev 				return false;
163b7f5a220SSergey Makeev 			}
164b7f5a220SSergey Makeev 
165b7f5a220SSergey Makeev 			return true;
166b7f5a220SSergey Makeev 		}
167b7f5a220SSergey Makeev 
1681196c666SSergey Makeev 
1691196c666SSergey Makeev 		// assignment operator
1701196c666SSergey Makeev 		TaskHandle & operator= (const TaskHandle & other)
1711196c666SSergey Makeev 		{
1721196c666SSergey Makeev 			check_id = other.check_id;
1731196c666SSergey Makeev 			task = other.task;
1741196c666SSergey Makeev 
1751196c666SSergey Makeev 			return *this;
1761196c666SSergey Makeev 		}
1771196c666SSergey Makeev 
1781196c666SSergey Makeev 		// move assignment operator
1791196c666SSergey Makeev 		TaskHandle & operator= (TaskHandle && other)
1801196c666SSergey Makeev 		{
1811196c666SSergey Makeev 			check_id = other.check_id;
1821196c666SSergey Makeev 			task = other.task;
1831196c666SSergey Makeev 
1841196c666SSergey Makeev 			other.check_id = TaskID::UNUSED;
1851196c666SSergey Makeev 			other.task = nullptr;
1861196c666SSergey Makeev 
1871196c666SSergey Makeev 			return *this;
1881196c666SSergey Makeev 		}
1891196c666SSergey Makeev 
GetDesc()190f4db079dSs.makeev 		const internal::TaskDesc & GetDesc() const
19158d12dadSSergey Makeev 		{
19258d12dadSSergey Makeev 			MT_ASSERT(IsValid(), "Task handle is invalid");
19358d12dadSSergey Makeev 			return task->desc;
19458d12dadSSergey Makeev 		}
19558d12dadSSergey Makeev 
19658d12dadSSergey Makeev 
197b7f5a220SSergey Makeev 	};
198b7f5a220SSergey Makeev 
199b7f5a220SSergey Makeev 
200b7f5a220SSergey Makeev 
201b7f5a220SSergey Makeev 
202b7f5a220SSergey Makeev 	//////////////////////////////////////////////////////////////////////////
DestoryByHandle(const MT::TaskHandle & handle)203b7f5a220SSergey Makeev 	inline bool PoolElementHeader::DestoryByHandle(const MT::TaskHandle & handle)
204b7f5a220SSergey Makeev 	{
205b7f5a220SSergey Makeev 		if (!handle.IsValid())
206b7f5a220SSergey Makeev 		{
207b7f5a220SSergey Makeev 			return false;
208b7f5a220SSergey Makeev 		}
209b7f5a220SSergey Makeev 
21058d12dadSSergey Makeev 		if (handle.task->desc.poolDestroyFunc == nullptr)
21158d12dadSSergey Makeev 		{
21258d12dadSSergey Makeev 			return false;
21358d12dadSSergey Makeev 		}
21458d12dadSSergey Makeev 
21558d12dadSSergey Makeev 		if (handle.task->desc.userData == nullptr)
21658d12dadSSergey Makeev 		{
21758d12dadSSergey Makeev 			return false;
21858d12dadSSergey Makeev 		}
21958d12dadSSergey Makeev 
22058d12dadSSergey Makeev 		//call destroy func
22158d12dadSSergey Makeev 		handle.task->desc.poolDestroyFunc(handle.task->desc.userData);
222b7f5a220SSergey Makeev 		return true;
223b7f5a220SSergey Makeev 	}
224b7f5a220SSergey Makeev 
225b7f5a220SSergey Makeev 
226b7f5a220SSergey Makeev 
227b7f5a220SSergey Makeev 
228b7f5a220SSergey Makeev 
229b7f5a220SSergey Makeev 	/// \class TaskPool
230b7f5a220SSergey Makeev 	/// \brief
231b7f5a220SSergey Makeev 	//////////////////////////////////////////////////////////////////////////
232b7f5a220SSergey Makeev 	template<typename T, size_t N>
233b7f5a220SSergey Makeev 	class TaskPool
234b7f5a220SSergey Makeev 	{
235c7362320Ss.makeev_local 		static const int32 ALIGNMENT = 16;
236c7362320Ss.makeev_local 
237b7f5a220SSergey Makeev 		typedef PoolElement<T> PoolItem;
238b7f5a220SSergey Makeev 
239b7f5a220SSergey Makeev 		//
240b7f5a220SSergey Makeev 		static const size_t MASK = (N - 1);
241b7f5a220SSergey Makeev 
242b7f5a220SSergey Makeev 		void* data;
243721f8c0bSs.makeev_local 		Atomic32<int32> idGenerator;
244721f8c0bSs.makeev_local 		Atomic32<int32> index;
245b7f5a220SSergey Makeev 
Buffer()246b7f5a220SSergey Makeev 		inline PoolItem* Buffer()
247b7f5a220SSergey Makeev 		{
248b7f5a220SSergey Makeev 			return (PoolItem*)(data);
249b7f5a220SSergey Makeev 		}
250b7f5a220SSergey Makeev 
MoveCtor(PoolItem * element,int id,T && val)251b7f5a220SSergey Makeev 		inline void MoveCtor(PoolItem* element, int id, T && val)
252b7f5a220SSergey Makeev 		{
253b7f5a220SSergey Makeev 			new(element) PoolItem(id, std::move(val));
254b7f5a220SSergey Makeev 		}
255b7f5a220SSergey Makeev 
256b7f5a220SSergey Makeev 	public:
257b7f5a220SSergey Makeev 
2582e846c40SSergey Makeev 		MT_NOCOPYABLE(TaskPool);
2592e846c40SSergey Makeev 
TaskPool()260b7f5a220SSergey Makeev 		TaskPool()
261b7f5a220SSergey Makeev 			: idGenerator(0)
262b7f5a220SSergey Makeev 			, index(0)
263b7f5a220SSergey Makeev 		{
264b7f5a220SSergey Makeev 			static_assert( MT::StaticIsPow2<N>::result, "Task pool capacity must be power of 2");
265b7f5a220SSergey Makeev 
266b7f5a220SSergey Makeev 			size_t bytesCount = sizeof(PoolItem) * N;
267c7362320Ss.makeev_local 			data = Memory::Alloc(bytesCount, ALIGNMENT);
268b7f5a220SSergey Makeev 
269b7f5a220SSergey Makeev 			for(size_t idx = 0; idx < N; idx++)
270b7f5a220SSergey Makeev 			{
271b7f5a220SSergey Makeev 				PoolItem* pElement = Buffer() + idx;
27281ec7369SSergey Makeev 				pElement->id.Store(TaskID::UNUSED);
273b7f5a220SSergey Makeev 			}
274b7f5a220SSergey Makeev 		}
275b7f5a220SSergey Makeev 
~TaskPool()276b7f5a220SSergey Makeev 		~TaskPool()
277b7f5a220SSergey Makeev 		{
278b7f5a220SSergey Makeev 			if (data != nullptr)
279b7f5a220SSergey Makeev 			{
280b7f5a220SSergey Makeev 
281b7f5a220SSergey Makeev 				for(size_t idx = 0; idx < N; idx++)
282b7f5a220SSergey Makeev 				{
283b7f5a220SSergey Makeev 					PoolItem* pElement = Buffer() + idx;
284b7f5a220SSergey Makeev 
285721f8c0bSs.makeev_local 					int preValue = pElement->id.Exchange(TaskID::UNUSED);
286b7f5a220SSergey Makeev 					if (preValue != TaskID::UNUSED)
287b7f5a220SSergey Makeev 					{
288b7f5a220SSergey Makeev 						pElement->task.~T();
289b7f5a220SSergey Makeev 					}
290b7f5a220SSergey Makeev 				}
291b7f5a220SSergey Makeev 
292b7f5a220SSergey Makeev 				Memory::Free(data);
293b7f5a220SSergey Makeev 				data = nullptr;
294b7f5a220SSergey Makeev 			}
295b7f5a220SSergey Makeev 		}
296b7f5a220SSergey Makeev 
TryAlloc(T && task)29753334f79SSergey Makeev 		TaskHandle TryAlloc(T && task)
298b7f5a220SSergey Makeev 		{
29981ec7369SSergey Makeev 			int idx = index.IncFetch() - 1;
300b7f5a220SSergey Makeev 
301b7f5a220SSergey Makeev 			int clampedIdx = (idx & MASK);
302b7f5a220SSergey Makeev 
303b7f5a220SSergey Makeev 			PoolItem* pElement = Buffer() + clampedIdx;
304b7f5a220SSergey Makeev 
30581ec7369SSergey Makeev 			bool isUnused = ((pElement->id.Load() & 1 ) != 0);
306e455330fSSergey Makeev 
30753334f79SSergey Makeev 			if (isUnused == false)
30853334f79SSergey Makeev 			{
309b7f5a220SSergey Makeev 				//Can't allocate more, next element in circular buffer is already used
31053334f79SSergey Makeev 				return TaskHandle();
31153334f79SSergey Makeev 			}
312b7f5a220SSergey Makeev 
313b7f5a220SSergey Makeev 			//generate next even number for id
31481ec7369SSergey Makeev 			int id = idGenerator.AddFetch(2);
315b7f5a220SSergey Makeev 			MoveCtor( pElement, id, std::move(task) );
316b7f5a220SSergey Makeev 			return TaskHandle(id, pElement);
317b7f5a220SSergey Makeev 		}
318b7f5a220SSergey Makeev 
31953334f79SSergey Makeev 
Alloc(T && task)32053334f79SSergey Makeev 		TaskHandle Alloc(T && task)
32153334f79SSergey Makeev 		{
32253334f79SSergey Makeev 			TaskHandle res = TryAlloc(std::move(task));
32353334f79SSergey Makeev 			MT_ASSERT(res.IsValid(), "Pool allocation failed");
32453334f79SSergey Makeev 			return res;
32553334f79SSergey Makeev 		}
32653334f79SSergey Makeev 
327b7f5a220SSergey Makeev 	};
328b7f5a220SSergey Makeev 
329b7f5a220SSergey Makeev }
330