1 // The MIT License (MIT)
2 //
3 // 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
4 //
5 // 	Permission is hereby granted, free of charge, to any person obtaining a copy
6 // 	of this software and associated documentation files (the "Software"), to deal
7 // 	in the Software without restriction, including without limitation the rights
8 // 	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // 	copies of the Software, and to permit persons to whom the Software is
10 // 	furnished to do so, subject to the following conditions:
11 //
12 //  The above copyright notice and this permission notice shall be included in
13 // 	all copies or substantial portions of the Software.
14 //
15 // 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // 	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // 	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // 	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // 	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // 	THE SOFTWARE.
22 
23 #pragma once
24 
25 #include <MTConfig.h>
26 
27 
28 namespace MT
29 {
30 
31 	namespace TaskID
32 	{
33 		//unused_id is any odd number, valid_id should be always only even numbers
34 		static const int UNUSED = 1;
35 	}
36 
37 
38 	//forward declaration
39 	class TaskHandle;
40 
41 
42 	/// \class PoolElementHeader
43 	/// \brief
44 	//////////////////////////////////////////////////////////////////////////
45 	struct PoolElementHeader
46 	{
47 		//Task id (timestamp)
48 		Atomic32<int32> id;
49 
50 		internal::TaskDesc desc;
51 
52 	public:
53 
PoolElementHeaderPoolElementHeader54 		PoolElementHeader(int _id)
55 			: id(_id)
56 		{
57 		}
58 
59 		static bool DestoryByHandle(const MT::TaskHandle & handle);
60 	};
61 
62 
63 	/// \class TaskPoolElement
64 	/// \brief
65 	//////////////////////////////////////////////////////////////////////////
66 	template<typename T>
67 	class PoolElement : public PoolElementHeader
68 	{
69 	public:
70 
71 		// Storage for task
72 		T task;
73 
PoolElement(int _id,T && _task)74 		PoolElement(int _id, T && _task)
75 			: PoolElementHeader(_id)
76 			, task( std::move(_task) )
77 		{
78 
79 #if MT_CLANG_COMPILER_FAMILY
80 #pragma clang diagnostic push
81 #pragma clang diagnostic ignored "-Winvalid-offsetof"
82 #endif
83 			static_assert( offsetof(PoolElement<T>, task) == sizeof(PoolElementHeader), "Invalid offset for task in PoolElement");
84 #if MT_CLANG_COMPILER_FAMILY
85 #pragma clang diagnostic pop
86 #endif
87 
88 			desc.poolDestroyFunc = T::PoolTaskDestroy;
89 			desc.taskFunc = T::TaskEntryPoint;
90 			desc.stackRequirements = T::GetStackRequirements();
91 			desc.priority = T::GetTaskPriority();
92 			desc.userData = &task;
93 
94 #ifdef MT_INSTRUMENTED_BUILD
95 			desc.debugID = T::GetDebugID();
96 			desc.debugColor = T::GetDebugColor();
97 #endif
98 		}
99 
100 	};
101 
102 
103 	/// \class TaskHandle
104 	/// \brief
105 	//////////////////////////////////////////////////////////////////////////
106 	class TaskHandle
107 	{
108 		int32 check_id;
109 
110 	protected:
111 
112 		friend struct PoolElementHeader;
113 
114 		PoolElementHeader* task;
115 
116 	public:
117 
118 		//default ctor
TaskHandle()119 		TaskHandle()
120 			: check_id(TaskID::UNUSED)
121 			, task(nullptr)
122 		{
123 
124 		}
125 
126 		//ctor
TaskHandle(int _id,PoolElementHeader * _task)127 		TaskHandle(int _id, PoolElementHeader* _task)
128 			: check_id(_id)
129 			, task(_task)
130 		{
131 		}
132 
133 		//copy ctor
TaskHandle(const TaskHandle & other)134 		TaskHandle(const TaskHandle & other)
135 			: check_id(other.check_id)
136 			, task(other.task)
137 		{
138 		}
139 
140 		//move ctor
TaskHandle(TaskHandle && other)141 		TaskHandle(TaskHandle && other)
142 			: check_id(other.check_id)
143 			, task(other.task)
144 		{
145 			other.check_id = TaskID::UNUSED;
146 			other.task = nullptr;
147 		}
148 
~TaskHandle()149 		~TaskHandle()
150 		{
151 		}
152 
IsValid()153 		bool IsValid() const
154 		{
155 			if (task == nullptr)
156 			{
157 				return false;
158 			}
159 
160 			if (check_id != task->id.Load())
161 			{
162 				return false;
163 			}
164 
165 			return true;
166 		}
167 
168 
169 		// assignment operator
170 		TaskHandle & operator= (const TaskHandle & other)
171 		{
172 			check_id = other.check_id;
173 			task = other.task;
174 
175 			return *this;
176 		}
177 
178 		// move assignment operator
179 		TaskHandle & operator= (TaskHandle && other)
180 		{
181 			check_id = other.check_id;
182 			task = other.task;
183 
184 			other.check_id = TaskID::UNUSED;
185 			other.task = nullptr;
186 
187 			return *this;
188 		}
189 
GetDesc()190 		const internal::TaskDesc & GetDesc() const
191 		{
192 			MT_ASSERT(IsValid(), "Task handle is invalid");
193 			return task->desc;
194 		}
195 
196 
197 	};
198 
199 
200 
201 
202 	//////////////////////////////////////////////////////////////////////////
DestoryByHandle(const MT::TaskHandle & handle)203 	inline bool PoolElementHeader::DestoryByHandle(const MT::TaskHandle & handle)
204 	{
205 		if (!handle.IsValid())
206 		{
207 			return false;
208 		}
209 
210 		if (handle.task->desc.poolDestroyFunc == nullptr)
211 		{
212 			return false;
213 		}
214 
215 		if (handle.task->desc.userData == nullptr)
216 		{
217 			return false;
218 		}
219 
220 		//call destroy func
221 		handle.task->desc.poolDestroyFunc(handle.task->desc.userData);
222 		return true;
223 	}
224 
225 
226 
227 
228 
229 	/// \class TaskPool
230 	/// \brief
231 	//////////////////////////////////////////////////////////////////////////
232 	template<typename T, size_t N>
233 	class TaskPool
234 	{
235 		static const int32 ALIGNMENT = 16;
236 
237 		typedef PoolElement<T> PoolItem;
238 
239 		//
240 		static const size_t MASK = (N - 1);
241 
242 		void* data;
243 		Atomic32<int32> idGenerator;
244 		Atomic32<int32> index;
245 
Buffer()246 		inline PoolItem* Buffer()
247 		{
248 			return (PoolItem*)(data);
249 		}
250 
MoveCtor(PoolItem * element,int id,T && val)251 		inline void MoveCtor(PoolItem* element, int id, T && val)
252 		{
253 			new(element) PoolItem(id, std::move(val));
254 		}
255 
256 	public:
257 
258 		MT_NOCOPYABLE(TaskPool);
259 
TaskPool()260 		TaskPool()
261 			: idGenerator(0)
262 			, index(0)
263 		{
264 			static_assert( MT::StaticIsPow2<N>::result, "Task pool capacity must be power of 2");
265 
266 			size_t bytesCount = sizeof(PoolItem) * N;
267 			data = Memory::Alloc(bytesCount, ALIGNMENT);
268 
269 			for(size_t idx = 0; idx < N; idx++)
270 			{
271 				PoolItem* pElement = Buffer() + idx;
272 				pElement->id.Store(TaskID::UNUSED);
273 			}
274 		}
275 
~TaskPool()276 		~TaskPool()
277 		{
278 			if (data != nullptr)
279 			{
280 
281 				for(size_t idx = 0; idx < N; idx++)
282 				{
283 					PoolItem* pElement = Buffer() + idx;
284 
285 					int preValue = pElement->id.Exchange(TaskID::UNUSED);
286 					if (preValue != TaskID::UNUSED)
287 					{
288 						pElement->task.~T();
289 					}
290 				}
291 
292 				Memory::Free(data);
293 				data = nullptr;
294 			}
295 		}
296 
TryAlloc(T && task)297 		TaskHandle TryAlloc(T && task)
298 		{
299 			int idx = index.IncFetch() - 1;
300 
301 			int clampedIdx = (idx & MASK);
302 
303 			PoolItem* pElement = Buffer() + clampedIdx;
304 
305 			bool isUnused = ((pElement->id.Load() & 1 ) != 0);
306 
307 			if (isUnused == false)
308 			{
309 				//Can't allocate more, next element in circular buffer is already used
310 				return TaskHandle();
311 			}
312 
313 			//generate next even number for id
314 			int id = idGenerator.AddFetch(2);
315 			MoveCtor( pElement, id, std::move(task) );
316 			return TaskHandle(id, pElement);
317 		}
318 
319 
Alloc(T && task)320 		TaskHandle Alloc(T && task)
321 		{
322 			TaskHandle res = TryAlloc(std::move(task));
323 			MT_ASSERT(res.IsValid(), "Pool allocation failed");
324 			return res;
325 		}
326 
327 	};
328 
329 }
330