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 #include <MTColorTable.h> 27 #include <MTTools.h> 28 #include <MTPlatform.h> 29 #include <MTConcurrentQueueLIFO.h> 30 #include <MTStackArray.h> 31 #include <MTArrayView.h> 32 #include <MTThreadContext.h> 33 #include <MTFiberContext.h> 34 #include <MTAppInterop.h> 35 #include <MTTaskPool.h> 36 #include <MTStackRequirements.h> 37 #include <Scopes/MTScopes.h> 38 39 40 namespace MT 41 { 42 43 template<typename CLASS_TYPE, typename MACRO_TYPE> 44 struct CheckType 45 { 46 static_assert(std::is_same<CLASS_TYPE, MACRO_TYPE>::value, "Invalid type in MT_DECLARE_TASK macro. See CheckType template instantiation params to details."); 47 }; 48 49 struct TypeChecker 50 { 51 template <typename T> 52 static T QueryThisType(T thisPtr) 53 { 54 return (T)nullptr; 55 } 56 }; 57 58 59 template <typename T> 60 inline void CallDtor(T* p) 61 { 62 MT_UNUSED(p); 63 p->~T(); 64 } 65 66 } 67 68 #if MT_MSVC_COMPILER_FAMILY 69 70 // Visual Studio compile time check 71 #define MT_COMPILE_TIME_TYPE_CHECK(TYPE) \ 72 void CompileTimeCheckMethod() \ 73 { \ 74 MT::CheckType< typename std::remove_pointer< decltype(MT::TypeChecker::QueryThisType(this)) >::type, typename TYPE > compileTypeTypesCheck; \ 75 compileTypeTypesCheck; \ 76 } 77 78 #elif MT_GCC_COMPILER_FAMILY 79 80 // GCC, Clang and other compilers compile time check 81 #define MT_COMPILE_TIME_TYPE_CHECK(TYPE) \ 82 void CompileTimeCheckMethod() \ 83 { \ 84 /* query this pointer type */ \ 85 typedef decltype(MT::TypeChecker::QueryThisType(this)) THIS_PTR_TYPE; \ 86 /* query class type from this pointer type */ \ 87 typedef typename std::remove_pointer<THIS_PTR_TYPE>::type CPP_TYPE; \ 88 /* define macro type */ \ 89 typedef TYPE MACRO_TYPE; \ 90 /* compile time checking that is same types */ \ 91 MT::CheckType< CPP_TYPE, MACRO_TYPE > compileTypeTypesCheck; \ 92 /* remove unused variable warning */ \ 93 MT_UNUSED(compileTypeTypesCheck); \ 94 } 95 96 #else 97 98 #error Platform is not supported. 99 100 #endif 101 102 103 104 105 #define MT_DECLARE_TASK_IMPL(TYPE, STACK_REQUIREMENTS) \ 106 \ 107 MT_COMPILE_TIME_TYPE_CHECK(TYPE) \ 108 \ 109 static void TaskEntryPoint(MT::FiberContext& fiberContext, const void* userData) \ 110 { \ 111 /* C style cast */ \ 112 TYPE * task = (TYPE *)(userData); \ 113 task->Do(fiberContext); \ 114 } \ 115 \ 116 static void PoolTaskDestroy(const void* userData) \ 117 { \ 118 /* C style cast */ \ 119 TYPE * task = (TYPE *)(userData); \ 120 MT::CallDtor( task ); \ 121 /* Find task pool header */ \ 122 MT::PoolElementHeader * poolHeader = (MT::PoolElementHeader *)((char*)userData - sizeof(MT::PoolElementHeader)); \ 123 /* Fixup pool header, mark task as unused */ \ 124 poolHeader->id.Store(MT::TaskID::UNUSED); \ 125 } \ 126 \ 127 static MT::StackRequirements::Type GetStackRequirements() \ 128 { \ 129 return STACK_REQUIREMENTS; \ 130 } \ 131 132 133 134 #ifdef MT_INSTRUMENTED_BUILD 135 #include <MTProfilerEventListener.h> 136 137 #define MT_DECLARE_TASK(TYPE, STACK_REQUIREMENTS, DEBUG_COLOR) \ 138 static const mt_char* GetDebugID() \ 139 { \ 140 return MT_TEXT( #TYPE ); \ 141 } \ 142 \ 143 static MT::Color::Type GetDebugColor() \ 144 { \ 145 return DEBUG_COLOR; \ 146 } \ 147 \ 148 MT_DECLARE_TASK_IMPL(TYPE, STACK_REQUIREMENTS); 149 150 151 #else 152 153 #define MT_DECLARE_TASK(TYPE, STACK_REQUIREMENTS, DEBUG_COLOR) \ 154 MT_DECLARE_TASK_IMPL(TYPE, STACK_REQUIREMENTS); 155 156 #endif 157 158 159 160 161 162 163 namespace MT 164 { 165 const uint32 MT_MAX_THREAD_COUNT = 64; 166 const uint32 MT_SCHEDULER_STACK_SIZE = 1048576; // 1Mb 167 168 const uint32 MT_MAX_STANDART_FIBERS_COUNT = 256; 169 const uint32 MT_STANDART_FIBER_STACK_SIZE = 32768; //32Kb 170 171 const uint32 MT_MAX_EXTENDED_FIBERS_COUNT = 8; 172 const uint32 MT_EXTENDED_FIBER_STACK_SIZE = 1048576; // 1Mb 173 174 namespace internal 175 { 176 struct ThreadContext; 177 } 178 179 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 180 // Task scheduler 181 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 182 class TaskScheduler 183 { 184 friend class FiberContext; 185 friend struct internal::ThreadContext; 186 187 188 189 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 190 // Task group description 191 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 192 // Application can assign task group to task and later wait until group was finished. 193 class TaskGroupDescription 194 { 195 Atomic32<int32> inProgressTaskCount; 196 Event allDoneEvent; 197 198 //Tasks awaiting group through FiberContext::WaitGroupAndYield call 199 ConcurrentQueueLIFO<FiberContext*> waitTasksQueue; 200 201 bool debugIsFree; 202 203 public: 204 205 MT_NOCOPYABLE(TaskGroupDescription); 206 207 TaskGroupDescription() 208 { 209 inProgressTaskCount.Store(0); 210 allDoneEvent.Create( EventReset::MANUAL, true ); 211 debugIsFree = true; 212 } 213 214 int GetTaskCount() const 215 { 216 return inProgressTaskCount.Load(); 217 } 218 219 ConcurrentQueueLIFO<FiberContext*> & GetWaitQueue() 220 { 221 return waitTasksQueue; 222 } 223 224 int Dec() 225 { 226 return inProgressTaskCount.DecFetch(); 227 } 228 229 int Inc() 230 { 231 return inProgressTaskCount.IncFetch(); 232 } 233 234 int Add(int sum) 235 { 236 return inProgressTaskCount.AddFetch(sum); 237 } 238 239 void Signal() 240 { 241 allDoneEvent.Signal(); 242 } 243 244 void Reset() 245 { 246 allDoneEvent.Reset(); 247 } 248 249 bool Wait(uint32 milliseconds) 250 { 251 return allDoneEvent.Wait(milliseconds); 252 } 253 254 void SetDebugIsFree(bool _debugIsFree) 255 { 256 debugIsFree = _debugIsFree; 257 } 258 259 bool GetDebugIsFree() const 260 { 261 return debugIsFree; 262 } 263 }; 264 265 266 // Thread index for new task 267 Atomic32<int32> roundRobinThreadIndex; 268 269 // Started threads count 270 Atomic32<int32> startedThreadsCount; 271 272 // Threads created by task manager 273 Atomic32<int32> threadsCount; 274 internal::ThreadContext threadContext[MT_MAX_THREAD_COUNT]; 275 276 // All groups task statistic 277 TaskGroupDescription allGroups; 278 279 // Groups pool 280 ConcurrentQueueLIFO<TaskGroup> availableGroups; 281 282 // 283 TaskGroupDescription groupStats[TaskGroup::MT_MAX_GROUPS_COUNT]; 284 285 // Fibers context 286 FiberContext standartFiberContexts[MT_MAX_STANDART_FIBERS_COUNT]; 287 FiberContext extendedFiberContexts[MT_MAX_EXTENDED_FIBERS_COUNT]; 288 289 // Fibers pool 290 ConcurrentQueueLIFO<FiberContext*> standartFibersAvailable; 291 ConcurrentQueueLIFO<FiberContext*> extendedFibersAvailable; 292 293 ConcurrentQueueLIFO<FiberContext*>* GetFibersStorage(MT::StackRequirements::Type stackRequirements); 294 295 #ifdef MT_INSTRUMENTED_BUILD 296 IProfilerEventListener * profilerEventListener; 297 #endif 298 299 FiberContext* RequestFiberContext(internal::GroupedTask& task); 300 void ReleaseFiberContext(FiberContext* fiberExecutionContext); 301 void RunTasksImpl(ArrayView<internal::TaskBucket>& buckets, FiberContext * parentFiber, bool restoredFromAwaitState); 302 TaskGroupDescription & GetGroupDesc(TaskGroup group); 303 304 static void ThreadMain( void* userData ); 305 static void ShedulerFiberMain( void* userData ); 306 static void FiberMain( void* userData ); 307 static bool TryStealTask(internal::ThreadContext& threadContext, internal::GroupedTask & task, uint32 workersCount); 308 309 static FiberContext* ExecuteTask (internal::ThreadContext& threadContext, FiberContext* fiberContext); 310 311 public: 312 313 /// \brief Initializes a new instance of the TaskScheduler class. 314 /// \param workerThreadsCount Worker threads count. Automatically determines the required number of threads if workerThreadsCount set to 0 315 #ifdef MT_INSTRUMENTED_BUILD 316 TaskScheduler(uint32 workerThreadsCount = 0, IProfilerEventListener* listener = nullptr); 317 #else 318 TaskScheduler(uint32 workerThreadsCount = 0); 319 #endif 320 321 322 ~TaskScheduler(); 323 324 template<class TTask> 325 void RunAsync(TaskGroup group, const TTask* taskArray, uint32 taskCount); 326 327 void RunAsync(TaskGroup group, const TaskHandle* taskHandleArray, uint32 taskHandleCount); 328 329 /// \brief Wait while no more tasks in specific group. 330 /// \return true - if no more tasks in specific group. false - if timeout in milliseconds has reached and group still has some tasks. 331 bool WaitGroup(TaskGroup group, uint32 milliseconds); 332 333 bool WaitAll(uint32 milliseconds); 334 335 TaskGroup CreateGroup(); 336 void ReleaseGroup(TaskGroup group); 337 338 bool IsEmpty(); 339 340 int32 GetWorkersCount() const; 341 342 bool IsWorkerThread() const; 343 344 #ifdef MT_INSTRUMENTED_BUILD 345 346 inline IProfilerEventListener* GetProfilerEventListener() 347 { 348 return profilerEventListener; 349 } 350 351 #endif 352 }; 353 } 354 355 #include "MTScheduler.inl" 356 #include "MTFiberContext.inl" 357