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 TaskPoolBase
42 	/// \brief
43 	//////////////////////////////////////////////////////////////////////////
44 	struct PoolElementHeader
45 	{
46 		AtomicInt id;
47 
48 	protected:
49 
50 		virtual void Destroy()
51 		{
52 			id.Set(TaskID::UNUSED);
53 		}
54 
55 	public:
56 
57 		PoolElementHeader(int _id)
58 			: id(_id)
59 		{
60 		}
61 
62 		static bool DestoryByHandle(const MT::TaskHandle & handle);
63 	};
64 
65 
66 	/// \class TaskPoolElement
67 	/// \brief
68 	//////////////////////////////////////////////////////////////////////////
69 	template<typename T>
70 	class PoolElement : public PoolElementHeader
71 	{
72 	protected:
73 
74 		virtual void Destroy()
75 		{
76 			PoolElementHeader::Destroy();
77 			//call dtor
78 			task.~T();
79 		}
80 
81 	public:
82 
83 		T task;
84 
85 		PoolElement(int _id, T && _task)
86 			: PoolElementHeader(_id)
87 			, task( std::move(_task) )
88 		{
89 		}
90 
91 	};
92 
93 
94 	/// \class TaskHandle
95 	/// \brief
96 	//////////////////////////////////////////////////////////////////////////
97 	class TaskHandle
98 	{
99 
100 		int check_id;
101 
102 	protected:
103 
104 		friend struct PoolElementHeader;
105 		PoolElementHeader * task;
106 
107 	public:
108 
109 		//default ctor
110 		TaskHandle()
111 			: check_id(TaskID::UNUSED)
112 			, task(nullptr)
113 		{
114 
115 		}
116 
117 		//ctor
118 		TaskHandle(int _id, PoolElementHeader* _task)
119 			: check_id(_id)
120 			, task(_task)
121 		{
122 		}
123 
124 		//copy ctor
125 		TaskHandle(const TaskHandle & other)
126 			: check_id(other.check_id)
127 			, task(other.task)
128 		{
129 		}
130 
131 		//move ctor
132 		TaskHandle(TaskHandle && other)
133 			: check_id(other.check_id)
134 			, task(other.task)
135 		{
136 			other.check_id = TaskID::UNUSED;
137 			other.task = nullptr;
138 		}
139 
140 		~TaskHandle()
141 		{
142 		}
143 
144 		bool IsValid() const
145 		{
146 			if (task == nullptr)
147 			{
148 				return false;
149 			}
150 
151 			if (check_id != task->id.Get())
152 			{
153 				return false;
154 			}
155 
156 			return true;
157 		}
158 
159 	};
160 
161 
162 
163 
164 	//////////////////////////////////////////////////////////////////////////
165 	inline bool PoolElementHeader::DestoryByHandle(const MT::TaskHandle & handle)
166 	{
167 		if (!handle.IsValid())
168 		{
169 			return false;
170 		}
171 
172 		handle.task->Destroy();
173 		return true;
174 	}
175 
176 
177 
178 
179 
180 	/// \class TaskPool
181 	/// \brief
182 	//////////////////////////////////////////////////////////////////////////
183 	template<typename T, size_t N>
184 	class TaskPool
185 	{
186 		typedef PoolElement<T> PoolItem;
187 
188 		//
189 		static const size_t MASK = (N - 1);
190 
191 		void* data;
192 		AtomicInt idGenerator;
193 		AtomicInt index;
194 
195 		inline PoolItem* Buffer()
196 		{
197 			return (PoolItem*)(data);
198 		}
199 
200 		inline void MoveCtor(PoolItem* element, int id, T && val)
201 		{
202 			new(element) PoolItem(id, std::move(val));
203 		}
204 
205 	private:
206 
207 		TaskPool(const TaskPool &) {}
208 		void operator=(const TaskPool &) {}
209 
210 	public:
211 
212 		TaskPool()
213 			: idGenerator(0)
214 			, index(0)
215 		{
216 			static_assert( MT::StaticIsPow2<N>::result, "Task pool capacity must be power of 2");
217 
218 			size_t bytesCount = sizeof(PoolItem) * N;
219 			data = Memory::Alloc(bytesCount);
220 
221 			for(size_t idx = 0; idx < N; idx++)
222 			{
223 				PoolItem* pElement = Buffer() + idx;
224 				pElement->id.Set(TaskID::UNUSED);
225 			}
226 		}
227 
228 		~TaskPool()
229 		{
230 			if (data != nullptr)
231 			{
232 
233 				for(size_t idx = 0; idx < N; idx++)
234 				{
235 					PoolItem* pElement = Buffer() + idx;
236 
237 					int preValue = pElement->id.Set(TaskID::UNUSED);
238 					if (preValue != TaskID::UNUSED)
239 					{
240 						pElement->task.~T();
241 					}
242 				}
243 
244 				Memory::Free(data);
245 				data = nullptr;
246 			}
247 		}
248 
249 
250 		TaskHandle Alloc(T && task)
251 		{
252 			int idx = index.Inc() - 1;
253 
254 			int clampedIdx = (idx & MASK);
255 
256 			PoolItem* pElement = Buffer() + clampedIdx;
257 
258 			bool isUnused = ((pElement->id.Get() & 1 ) != 0);
259 			if (isUnused == false)
260 			{
261 				//Can't allocate more, next element in circular buffer is already used
262 				return TaskHandle();
263 			}
264 
265 			//generate next even number for id
266 			int id = idGenerator.Add(2);
267 			MoveCtor( pElement, id, std::move(task) );
268 			return TaskHandle(id, pElement);
269 		}
270 
271 	};
272 
273 }
274