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 
54 		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 
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.userData = &task;
92 
93 #ifdef MT_INSTRUMENTED_BUILD
94 			desc.debugID = T::GetDebugID();
95 			desc.debugColor = T::GetDebugColor();
96 #endif
97 		}
98 
99 	};
100 
101 
102 	/// \class TaskHandle
103 	/// \brief
104 	//////////////////////////////////////////////////////////////////////////
105 	class TaskHandle
106 	{
107 		int32 check_id;
108 
109 	protected:
110 
111 		friend struct PoolElementHeader;
112 
113 		PoolElementHeader* task;
114 
115 	public:
116 
117 		//default ctor
118 		TaskHandle()
119 			: check_id(TaskID::UNUSED)
120 			, task(nullptr)
121 		{
122 
123 		}
124 
125 		//ctor
126 		TaskHandle(int _id, PoolElementHeader* _task)
127 			: check_id(_id)
128 			, task(_task)
129 		{
130 		}
131 
132 		//copy ctor
133 		TaskHandle(const TaskHandle & other)
134 			: check_id(other.check_id)
135 			, task(other.task)
136 		{
137 		}
138 
139 		//move ctor
140 		TaskHandle(TaskHandle && other)
141 			: check_id(other.check_id)
142 			, task(other.task)
143 		{
144 			other.check_id = TaskID::UNUSED;
145 			other.task = nullptr;
146 		}
147 
148 		~TaskHandle()
149 		{
150 		}
151 
152 		bool IsValid() const
153 		{
154 			if (task == nullptr)
155 			{
156 				return false;
157 			}
158 
159 			if (check_id != task->id.Load())
160 			{
161 				return false;
162 			}
163 
164 			return true;
165 		}
166 
167 
168 		// assignment operator
169 		TaskHandle & operator= (const TaskHandle & other)
170 		{
171 			check_id = other.check_id;
172 			task = other.task;
173 
174 			return *this;
175 		}
176 
177 		// move assignment operator
178 		TaskHandle & operator= (TaskHandle && other)
179 		{
180 			check_id = other.check_id;
181 			task = other.task;
182 
183 			other.check_id = TaskID::UNUSED;
184 			other.task = nullptr;
185 
186 			return *this;
187 		}
188 
189 		const internal::TaskDesc & GetDesc() const
190 		{
191 			MT_ASSERT(IsValid(), "Task handle is invalid");
192 			return task->desc;
193 		}
194 
195 
196 	};
197 
198 
199 
200 
201 	//////////////////////////////////////////////////////////////////////////
202 	inline bool PoolElementHeader::DestoryByHandle(const MT::TaskHandle & handle)
203 	{
204 		if (!handle.IsValid())
205 		{
206 			return false;
207 		}
208 
209 		if (handle.task->desc.poolDestroyFunc == nullptr)
210 		{
211 			return false;
212 		}
213 
214 		if (handle.task->desc.userData == nullptr)
215 		{
216 			return false;
217 		}
218 
219 		//call destroy func
220 		handle.task->desc.poolDestroyFunc(handle.task->desc.userData);
221 		return true;
222 	}
223 
224 
225 
226 
227 
228 	/// \class TaskPool
229 	/// \brief
230 	//////////////////////////////////////////////////////////////////////////
231 	template<typename T, size_t N>
232 	class TaskPool
233 	{
234 		static const int32 ALIGNMENT = 16;
235 
236 		typedef PoolElement<T> PoolItem;
237 
238 		//
239 		static const size_t MASK = (N - 1);
240 
241 		void* data;
242 		Atomic32<int32> idGenerator;
243 		Atomic32<int32> index;
244 
245 		inline PoolItem* Buffer()
246 		{
247 			return (PoolItem*)(data);
248 		}
249 
250 		inline void MoveCtor(PoolItem* element, int id, T && val)
251 		{
252 			new(element) PoolItem(id, std::move(val));
253 		}
254 
255 	public:
256 
257 		MT_NOCOPYABLE(TaskPool);
258 
259 		TaskPool()
260 			: idGenerator(0)
261 			, index(0)
262 		{
263 			static_assert( MT::StaticIsPow2<N>::result, "Task pool capacity must be power of 2");
264 
265 			size_t bytesCount = sizeof(PoolItem) * N;
266 			data = Memory::Alloc(bytesCount, ALIGNMENT);
267 
268 			for(size_t idx = 0; idx < N; idx++)
269 			{
270 				PoolItem* pElement = Buffer() + idx;
271 				pElement->id.Store(TaskID::UNUSED);
272 			}
273 		}
274 
275 		~TaskPool()
276 		{
277 			if (data != nullptr)
278 			{
279 
280 				for(size_t idx = 0; idx < N; idx++)
281 				{
282 					PoolItem* pElement = Buffer() + idx;
283 
284 					int preValue = pElement->id.Exchange(TaskID::UNUSED);
285 					if (preValue != TaskID::UNUSED)
286 					{
287 						pElement->task.~T();
288 					}
289 				}
290 
291 				Memory::Free(data);
292 				data = nullptr;
293 			}
294 		}
295 
296 		TaskHandle TryAlloc(T && task)
297 		{
298 			int idx = index.IncFetch() - 1;
299 
300 			int clampedIdx = (idx & MASK);
301 
302 			PoolItem* pElement = Buffer() + clampedIdx;
303 
304 			bool isUnused = ((pElement->id.Load() & 1 ) != 0);
305 
306 			if (isUnused == false)
307 			{
308 				//Can't allocate more, next element in circular buffer is already used
309 				return TaskHandle();
310 			}
311 
312 			//generate next even number for id
313 			int id = idGenerator.AddFetch(2);
314 			MoveCtor( pElement, id, std::move(task) );
315 			return TaskHandle(id, pElement);
316 		}
317 
318 
319 		TaskHandle Alloc(T && task)
320 		{
321 			TaskHandle res = TryAlloc(std::move(task));
322 			MT_ASSERT(res.IsValid(), "Pool allocation failed");
323 			return res;
324 		}
325 
326 	};
327 
328 }
329