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