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 
26 
27 namespace MT
28 {
29 
30 	namespace TaskID
31 	{
32 		//unused_id is any odd number, valid_id should be always only even numbers
33 		static const int UNUSED = 1;
34 	}
35 
36 
37 	//forward declaration
38 	class TaskHandle;
39 
40 
41 	/// \class PoolElementHeader
42 	/// \brief
43 	//////////////////////////////////////////////////////////////////////////
44 	struct PoolElementHeader
45 	{
46 		//Task id (timestamp)
47 		AtomicInt32 id;
48 
49 		internal::TaskDesc desc;
50 
51 	public:
52 
53 		PoolElementHeader(int _id)
54 			: id(_id)
55 		{
56 		}
57 
58 		static bool DestoryByHandle(const MT::TaskHandle & handle);
59 	};
60 
61 
62 	/// \class TaskPoolElement
63 	/// \brief
64 	//////////////////////////////////////////////////////////////////////////
65 	template<typename T>
66 	class PoolElement : public PoolElementHeader
67 	{
68 	public:
69 
70 		// Storage for task
71 		T task;
72 
73 		PoolElement(int _id, T && _task)
74 			: PoolElementHeader(_id)
75 			, task( std::move(_task) )
76 		{
77 			MT_ASSERT( offsetof(PoolElement<T>, task) == sizeof(PoolElementHeader), "Invalid offset for task in PoolElement");
78 
79 			desc.poolDestroyFunc = T::PoolTaskDestroy;
80 			desc.taskFunc = T::TaskEntryPoint;
81 			desc.stackRequirements = T::GetStackRequirements();
82 			desc.userData = &task;
83 
84 #ifdef MT_INSTRUMENTED_BUILD
85 			desc.debugID = T::GetDebugID();
86 			desc.debugColor = T::GetDebugColor();
87 #endif
88 		}
89 
90 	};
91 
92 
93 	/// \class TaskHandle
94 	/// \brief
95 	//////////////////////////////////////////////////////////////////////////
96 	class TaskHandle
97 	{
98 		int32 check_id;
99 
100 	protected:
101 
102 		friend struct PoolElementHeader;
103 
104 		PoolElementHeader* task;
105 
106 	public:
107 
108 		//default ctor
109 		TaskHandle()
110 			: check_id(TaskID::UNUSED)
111 			, task(nullptr)
112 		{
113 
114 		}
115 
116 		//ctor
117 		TaskHandle(int _id, PoolElementHeader* _task)
118 			: check_id(_id)
119 			, task(_task)
120 		{
121 		}
122 
123 		//copy ctor
124 		TaskHandle(const TaskHandle & other)
125 			: check_id(other.check_id)
126 			, task(other.task)
127 		{
128 		}
129 
130 		//move ctor
131 		TaskHandle(TaskHandle && other)
132 			: check_id(other.check_id)
133 			, task(other.task)
134 		{
135 			other.check_id = TaskID::UNUSED;
136 			other.task = nullptr;
137 		}
138 
139 		~TaskHandle()
140 		{
141 		}
142 
143 		bool IsValid() const
144 		{
145 			if (task == nullptr)
146 			{
147 				return false;
148 			}
149 
150 			if (check_id != task->id.Load())
151 			{
152 				return false;
153 			}
154 
155 			return true;
156 		}
157 
158 
159 		// assignment operator
160 		TaskHandle & operator= (const TaskHandle & other)
161 		{
162 			check_id = other.check_id;
163 			task = other.task;
164 
165 			return *this;
166 		}
167 
168 		// move assignment operator
169 		TaskHandle & operator= (TaskHandle && other)
170 		{
171 			check_id = other.check_id;
172 			task = other.task;
173 
174 			other.check_id = TaskID::UNUSED;
175 			other.task = nullptr;
176 
177 			return *this;
178 		}
179 
180 		const internal::TaskDesc & GetDesc() const
181 		{
182 			MT_ASSERT(IsValid(), "Task handle is invalid");
183 			return task->desc;
184 		}
185 
186 
187 	};
188 
189 
190 
191 
192 	//////////////////////////////////////////////////////////////////////////
193 	inline bool PoolElementHeader::DestoryByHandle(const MT::TaskHandle & handle)
194 	{
195 		if (!handle.IsValid())
196 		{
197 			return false;
198 		}
199 
200 		if (handle.task->desc.poolDestroyFunc == nullptr)
201 		{
202 			return false;
203 		}
204 
205 		if (handle.task->desc.userData == nullptr)
206 		{
207 			return false;
208 		}
209 
210 		//call destroy func
211 		handle.task->desc.poolDestroyFunc(handle.task->desc.userData);
212 		return true;
213 	}
214 
215 
216 
217 
218 
219 	/// \class TaskPool
220 	/// \brief
221 	//////////////////////////////////////////////////////////////////////////
222 	template<typename T, size_t N>
223 	class TaskPool
224 	{
225 		typedef PoolElement<T> PoolItem;
226 
227 		//
228 		static const size_t MASK = (N - 1);
229 
230 		void* data;
231 		AtomicInt32 idGenerator;
232 		AtomicInt32 index;
233 
234 		inline PoolItem* Buffer()
235 		{
236 			return (PoolItem*)(data);
237 		}
238 
239 		inline void MoveCtor(PoolItem* element, int id, T && val)
240 		{
241 			new(element) PoolItem(id, std::move(val));
242 		}
243 
244 	public:
245 
246 		MT_NOCOPYABLE(TaskPool);
247 
248 		TaskPool()
249 			: idGenerator(0)
250 			, index(0)
251 		{
252 			static_assert( MT::StaticIsPow2<N>::result, "Task pool capacity must be power of 2");
253 
254 			size_t bytesCount = sizeof(PoolItem) * N;
255 			data = Memory::Alloc(bytesCount);
256 
257 			for(size_t idx = 0; idx < N; idx++)
258 			{
259 				PoolItem* pElement = Buffer() + idx;
260 				pElement->id.Store(TaskID::UNUSED);
261 			}
262 		}
263 
264 		~TaskPool()
265 		{
266 			if (data != nullptr)
267 			{
268 
269 				for(size_t idx = 0; idx < N; idx++)
270 				{
271 					PoolItem* pElement = Buffer() + idx;
272 
273 					int preValue = pElement->id.Store(TaskID::UNUSED);
274 					if (preValue != TaskID::UNUSED)
275 					{
276 						pElement->task.~T();
277 					}
278 				}
279 
280 				Memory::Free(data);
281 				data = nullptr;
282 			}
283 		}
284 
285 		TaskHandle TryAlloc(T && task)
286 		{
287 			int idx = index.IncFetch() - 1;
288 
289 			int clampedIdx = (idx & MASK);
290 
291 			PoolItem* pElement = Buffer() + clampedIdx;
292 
293 			bool isUnused = ((pElement->id.Load() & 1 ) != 0);
294 
295 			if (isUnused == false)
296 			{
297 				//Can't allocate more, next element in circular buffer is already used
298 				return TaskHandle();
299 			}
300 
301 			//generate next even number for id
302 			int id = idGenerator.AddFetch(2);
303 			MoveCtor( pElement, id, std::move(task) );
304 			return TaskHandle(id, pElement);
305 		}
306 
307 
308 		TaskHandle Alloc(T && task)
309 		{
310 			TaskHandle res = TryAlloc(std::move(task));
311 			MT_ASSERT(res.IsValid(), "Pool allocation failed");
312 			return res;
313 		}
314 
315 	};
316 
317 }
318