Thread.h 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. /******************************************************************************
  2. Use 'SyncLock' for multi-threaded synchronization
  3. Use 'SyncLocker' for simplified usage of the 'SyncLock'
  4. Use 'SyncEvent' for multi-threaded synchronization
  5. Use 'ReadWriteSync' to handle synchronization between multiple "reader" and "writer" threads, where multiple "readers" can access a resource at once, while only 1 "writer" (and no other "readers" or "writers") can access a resource at once
  6. Use 'Thread' to run functions simultaneously on the Cpu
  7. Use 'MultiThreadedCall' to process data in multi-threaded mode by splitting the work into multiple threads
  8. Use 'Proc' functions for OS processes management.
  9. /******************************************************************************/
  10. #if EE_PRIVATE
  11. #define SYNC_LOCK_SAFE 0 // if 'SyncLock' methods should check if it was initialized first, 0=faster and less safe, 1=slower and more safe
  12. #endif
  13. struct SyncLock // Synchronization Lock (multi-threaded safe)
  14. {
  15. Bool created()C; // if lock is still created and not yet deleted in the constructor
  16. Bool owned ()C; // if lock is owned by current thread
  17. Bool tryOn ()C; // try entering lock, false on fail (after receiving 'true' you must call the 'off' method, while after receiving 'false' you may not call the 'off' method)
  18. void on ()C; // enter lock
  19. void off ()C; // leave lock
  20. ~SyncLock();
  21. SyncLock();
  22. private:
  23. #if EE_PRIVATE
  24. union
  25. {
  26. #if WINDOWS
  27. struct // total 40 bytes
  28. {
  29. mutable CRITICAL_SECTION _lock; // SIZE(_lock)==40 on x64
  30. };
  31. ULong _b[5]; // 5 ULong = 40 bytes = max possible size of union
  32. #else
  33. struct // total 80 bytes
  34. {
  35. mutable pthread_mutex_t _lock ; // SIZE(_lock)==64 on x64
  36. mutable Int _lock_count; // SIZE( )== 4
  37. mutable UIntPtr _owner ; // SIZE( )== 8 on x64
  38. mutable UInt _is ; // SIZE(_is )== 4
  39. };
  40. ULong _b[10]; // 10 ULong = 80 bytes = max possible size of union
  41. #endif
  42. };
  43. // !! use ULong above and below to force ULong alignment, if Byte[] is set, it could cause crash !! "struct A {Bool b; SyncLock lock;}" would have different alignments
  44. #else
  45. ULong _lock[PLATFORM(5, 10)];
  46. #endif
  47. NO_COPY_CONSTRUCTOR(SyncLock);
  48. };
  49. #if EE_PRIVATE
  50. ASSERT(SIZE(SyncLock)==SIZE(ULong)*PLATFORM(5, 10));
  51. #endif
  52. struct SyncLocker // Synchronization Locker (automatically locks and unlocks the Synchronization Lock at object creation and destruction)
  53. {
  54. explicit SyncLocker(C SyncLock &lock) : _lock(lock) {_lock.on ();}
  55. ~SyncLocker( ) {_lock.off();}
  56. private:
  57. C SyncLock &_lock;
  58. NO_COPY_CONSTRUCTOR(SyncLocker);
  59. };
  60. struct SyncLockerEx // Synchronization Locker Extended
  61. {
  62. void on () {if(!_on){_on=true ; _lock.on ();}} // manually lock
  63. void off() {if( _on){_on=false; _lock.off();}} // manually unlock
  64. void set(Bool on) {if(on!=_on){if(_on=on)_lock.on();else _lock.off();}} // set if lock should be enabled
  65. Bool tryOn() {if(!_on && _lock.tryOn())_on=true; return _on;} // try entering locking, false on fail
  66. explicit SyncLockerEx(C SyncLock &lock, Bool on=true) : _lock(lock) {if(_on=on)lock.on();}
  67. ~SyncLockerEx( ) {off();}
  68. private:
  69. Bool _on;
  70. C SyncLock &_lock;
  71. NO_COPY_CONSTRUCTOR(SyncLockerEx);
  72. };
  73. #if EE_PRIVATE
  74. #if SYNC_LOCK_SAFE
  75. typedef SyncLocker SafeSyncLocker;
  76. typedef SyncLockerEx SafeSyncLockerEx;
  77. #else
  78. struct SafeSyncLocker
  79. {
  80. explicit SafeSyncLocker(C SyncLock &lock) {if(lock.created()){_lock=&lock; lock. on ();}else _lock=null;}
  81. ~SafeSyncLocker( ) { if(_lock)_lock->off();}
  82. private:
  83. C SyncLock *_lock;
  84. NO_COPY_CONSTRUCTOR(SafeSyncLocker);
  85. };
  86. struct SafeSyncLockerEx
  87. {
  88. void on () {if(!_on && _lock){_on=true ; _lock->on ();}} // manually lock
  89. void off() {if( _on && _lock){_on=false; _lock->off();}} // manually unlock
  90. void set(Bool on) {if(on!=_on && _lock){if(_on=on)_lock->on();else _lock->off();}} // set if lock should be enabled
  91. Bool tryOn() {if(!_on && _lock && _lock->tryOn())_on=true; return _on;} // try entering locking, false on fail
  92. explicit SafeSyncLockerEx(C SyncLock &lock, Bool on=true) {if(lock.created()){_lock=&lock; if(_on=on)lock.on();}else{_lock=null; _on=false;}}
  93. ~SafeSyncLockerEx( ) {off();}
  94. private:
  95. Bool _on;
  96. C SyncLock *_lock;
  97. NO_COPY_CONSTRUCTOR(SafeSyncLockerEx);
  98. };
  99. #endif
  100. #define SYNC_UNLOCK_SINGLE 0 // can't use single because locks can be locked multiple times, and we need to unlock all
  101. #if SYNC_UNLOCK_SINGLE // single
  102. struct SyncUnlocker
  103. {
  104. explicit SyncUnlocker(C SyncLock &lock) : _lock(lock) {if(_owned=lock.owned())_lock.off();}
  105. ~SyncUnlocker( ) {if(_owned )_lock.on ();}
  106. private:
  107. Bool _owned;
  108. C SyncLock &_lock;
  109. NO_COPY_CONSTRUCTOR(SyncUnlocker);
  110. };
  111. #else // multi
  112. struct SyncUnlocker
  113. {
  114. explicit SyncUnlocker(C SyncLock &lock) : _lock(lock) {for(_owned=0; lock.owned(); _owned++)_lock.off();}
  115. ~SyncUnlocker( ) {REP(_owned )_lock.on ();}
  116. private:
  117. Int _owned;
  118. C SyncLock &_lock;
  119. NO_COPY_CONSTRUCTOR(SyncUnlocker);
  120. };
  121. #endif
  122. #endif
  123. /******************************************************************************/
  124. struct SyncEvent // Synchronization Event (multi-threaded safe)
  125. {
  126. void on ( )C; // activate
  127. void off ( )C; // deactivate
  128. Bool wait( )C; // wait until activated , false on timeout
  129. Bool wait(Int milliseconds)C; // wait 'milliseconds' until activated (<0 = infinite wait), false on timeout
  130. ~SyncEvent();
  131. explicit SyncEvent(Bool auto_off=true); // 'auto_off'=if automatically call 'off' upon a successful 'wait'
  132. #if EE_PRIVATE
  133. Bool is()C {return _handle!=null;} // if created
  134. #if WINDOWS
  135. HANDLE handle()C {return _handle;}
  136. #endif
  137. #endif
  138. private:
  139. #if EE_PRIVATE
  140. PLATFORM(HANDLE, pthread_cond_t*) _handle;
  141. #else
  142. Ptr _handle;
  143. #endif
  144. #if !WINDOWS
  145. #if EE_PRIVATE
  146. PLATFORM(Ptr, pthread_mutex_t*) _mutex;
  147. #else
  148. Ptr _mutex;
  149. #endif
  150. mutable Bool _condition, _auto_off;
  151. #endif
  152. NO_COPY_CONSTRUCTOR(SyncEvent);
  153. };
  154. /******************************************************************************/
  155. struct SyncCounter // Synchronization Counter (multi-threaded safe)
  156. {
  157. void operator+=(Int count)C; // activate
  158. void operator++(int )C {T+=1;} // activate 1
  159. Bool wait( )C; // wait until activated , false on timeout
  160. Bool wait(Int milliseconds)C; // wait 'milliseconds' until activated (<0 = infinite wait), false on timeout
  161. ~SyncCounter();
  162. SyncCounter();
  163. #if EE_PRIVATE
  164. Bool is()C {return _handle!=null;} // if created
  165. #if WINDOWS
  166. HANDLE handle()C {return _handle;}
  167. #endif
  168. #endif
  169. private:
  170. #if EE_PRIVATE
  171. PLATFORM(HANDLE, pthread_cond_t*) _handle;
  172. #else
  173. Ptr _handle;
  174. #endif
  175. #if !WINDOWS
  176. #if EE_PRIVATE
  177. PLATFORM(Ptr, pthread_mutex_t*) _mutex;
  178. #else
  179. Ptr _mutex;
  180. #endif
  181. mutable Int _counter;
  182. #endif
  183. NO_COPY_CONSTRUCTOR(SyncCounter);
  184. };
  185. /******************************************************************************/
  186. struct ReadWriteSync // allows multiple "readers/writers" synchronization (multi-threaded safe)
  187. {
  188. void enterRead(); // enter reading (shared mode)
  189. void leaveRead(); // leave reading (shared mode)
  190. void enterWrite(); // enter writing (exclusive mode)
  191. void leaveWrite(); // leave writing (exclusive mode)
  192. Bool ownedRead (); // if read lock is owned by current thread
  193. Bool ownedWrite()C {return _write_lock.owned();} // if write lock is owned by current thread
  194. Bool created()C {return _locks_lock.created();} // if lock is still created and not yet deleted in the constructor
  195. private:
  196. struct Lock
  197. {
  198. UIntPtr thread_id;
  199. Int locks;
  200. };
  201. SyncLock _locks_lock, _write_lock;
  202. SyncEvent _left_reading;
  203. Memc<Lock> _locks;
  204. };
  205. struct ReadLock // 'ReadWriteSync' Read Lock (automatically locks and unlocks for reading the 'ReadWriteSync' at object creation and destruction)
  206. {
  207. explicit ReadLock(ReadWriteSync &lock) : _lock(lock) {_lock.enterRead();}
  208. ~ReadLock( ) {_lock.leaveRead();}
  209. private:
  210. ReadWriteSync &_lock;
  211. NO_COPY_CONSTRUCTOR(ReadLock);
  212. };
  213. struct WriteLock // 'ReadWriteSync' Write Lock (automatically locks and unlocks for writing the 'ReadWriteSync' at object creation and destruction)
  214. {
  215. explicit WriteLock(ReadWriteSync &lock) : _lock(lock) {_lock.enterWrite();}
  216. ~WriteLock( ) {_lock.leaveWrite();}
  217. private:
  218. ReadWriteSync &_lock;
  219. NO_COPY_CONSTRUCTOR(WriteLock);
  220. };
  221. struct WriteLockEx // 'ReadWriteSync' Write Lock Extended
  222. {
  223. void on () {if(!_on){_on=true ; _lock.enterWrite();}} // manually lock
  224. void off() {if( _on){_on=false; _lock.leaveWrite();}} // manually unlock
  225. void set(Bool on) {if(on!=_on){if(_on=on)_lock.enterWrite();else _lock.leaveWrite();}} // set if lock should be enabled
  226. explicit WriteLockEx(ReadWriteSync &lock, Bool on=true) : _lock(lock) {if(_on=on)lock.enterWrite();}
  227. ~WriteLockEx( ) {off();}
  228. private:
  229. Bool _on;
  230. ReadWriteSync &_lock;
  231. NO_COPY_CONSTRUCTOR(WriteLockEx);
  232. };
  233. #if EE_PRIVATE
  234. #if SYNC_LOCK_SAFE
  235. typedef WriteLock SafeWriteLock;
  236. #else
  237. struct SafeWriteLock
  238. {
  239. explicit SafeWriteLock(ReadWriteSync &lock) {if(lock.created()){_lock=&lock; lock. enterWrite();}else _lock=null;}
  240. ~SafeWriteLock( ) { if(_lock)_lock->leaveWrite();}
  241. private:
  242. ReadWriteSync *_lock;
  243. NO_COPY_CONSTRUCTOR(SafeWriteLock);
  244. };
  245. #endif
  246. #endif
  247. /******************************************************************************/
  248. struct SimpleReadWriteSync // !! NOT REENTRANT - does not support 'enterWrite' if already 'enterRead' was called on the same thread - deadlock will occur !!
  249. {
  250. void enterRead()C; // enter reading (shared mode)
  251. void leaveRead()C; // leave reading (shared mode)
  252. void enterWrite()C; // enter writing (exclusive mode)
  253. void leaveWrite()C; // leave writing (exclusive mode)
  254. private:
  255. SyncLock _lock;
  256. SyncEvent _finished;
  257. mutable Int _readers;
  258. };
  259. struct SimpleReadLock // SimpleReadWriteSync Read Lock (automatically locks and unlocks for reading the 'SimpleReadWriteSync' at object creation and destruction)
  260. {
  261. explicit SimpleReadLock(C SimpleReadWriteSync &lock) : _lock(lock) {_lock.enterRead();}
  262. ~SimpleReadLock( ) {_lock.leaveRead();}
  263. private:
  264. C SimpleReadWriteSync &_lock;
  265. NO_COPY_CONSTRUCTOR(SimpleReadLock);
  266. };
  267. struct SimpleWriteLock // SimpleReadWriteSync Write Lock (automatically locks and unlocks for writing the 'SimpleReadWriteSync' at object creation and destruction)
  268. {
  269. explicit SimpleWriteLock(C SimpleReadWriteSync &lock) : _lock(lock) {_lock.enterWrite();}
  270. ~SimpleWriteLock( ) {_lock.leaveWrite();}
  271. private:
  272. C SimpleReadWriteSync &_lock;
  273. NO_COPY_CONSTRUCTOR(SimpleWriteLock);
  274. };
  275. struct SimpleReadLockEx // SimpleReadWriteSync Read Lock Extended
  276. {
  277. void on () {if(!_on){_on=true ; _lock.enterRead();}} // manually lock
  278. void off() {if( _on){_on=false; _lock.leaveRead();}} // manually unlock
  279. void set(Bool on) {if(on!=_on){if(_on=on)_lock.enterRead();else _lock.leaveRead();}} // set if lock should be enabled
  280. explicit SimpleReadLockEx(C SimpleReadWriteSync &lock, Bool on=true) : _lock(lock) {if(_on=on)lock.enterRead();}
  281. ~SimpleReadLockEx( ) {off();}
  282. private:
  283. Bool _on;
  284. C SimpleReadWriteSync &_lock;
  285. NO_COPY_CONSTRUCTOR(SimpleReadLockEx);
  286. };
  287. struct SimpleWriteLockEx // SimpleReadWriteSync Write Lock Extended
  288. {
  289. void on () {if(!_on){_on=true ; _lock.enterWrite();}} // manually lock
  290. void off() {if( _on){_on=false; _lock.leaveWrite();}} // manually unlock
  291. void set(Bool on) {if(on!=_on){if(_on=on)_lock.enterWrite();else _lock.leaveWrite();}} // set if lock should be enabled
  292. explicit SimpleWriteLockEx(C SimpleReadWriteSync &lock, Bool on=true) : _lock(lock) {if(_on=on)lock.enterWrite();}
  293. ~SimpleWriteLockEx( ) {off();}
  294. private:
  295. Bool _on;
  296. C SimpleReadWriteSync &_lock;
  297. NO_COPY_CONSTRUCTOR(SimpleWriteLockEx);
  298. };
  299. /******************************************************************************/
  300. const_mem_addr struct Thread // Thread !! must be stored in constant memory address !!
  301. {
  302. Ptr user; // user data
  303. // get
  304. Bool created ()C {return _handle!=NULL;} // if the thread is created (this will be false only if the thread hasn't yet been created, or if it has been manually deleted, if the thread was created but stopped running, the return value will be still true)
  305. Bool active ()C {return _active ;} // if the thread is created and running (this will be false if the thread hasn't yet been created, or if it has been manually deleted, or it stopped running )
  306. Bool wantStop ()C {return _want_stop ;} // if the thread is requested to be stopped by the 'stop' method
  307. Bool wantPause()C {return _want_pause ;} // if the thread is requested to be paused by the 'pause' method
  308. Bool paused ()C {return _paused ;} // if the thread is currently paused
  309. Int priority ()C {return _priority ;} // get thread priority (-3..3)
  310. UIntPtr id()C; // get thread ID, 0 on fail
  311. // manage
  312. Bool create (Bool func(Thread &thread), Ptr user=null, Int priority=0, Bool paused=false, C Str8 &name=S8); // create, 'func'=function which will be called in the created thread, 'user'=custom user data, 'priority'=thread priority (-3..3), 'paused'=if start the thread in paused mode, 'name'=thread name, threads have a default stack size of 1MB, if the thread will require performing operations on GPU data, then you must call 'ThreadMayUseGPUData' inside the thread, please check that function comments for more info
  313. void del (Int milliseconds=-1); // delete thread, if it's active then wait 'milliseconds' until finishes (<0 = infinite wait), if after the waiting time it's still active then the thread will be killed
  314. void stop( ); // stop thread, notifies that the thread should stop calling 'func' and exit, this method doesn't however wait until the thread exits
  315. void cancelStop( ); // cancel thread stopping, this method has no effect if the thread has already been stopped
  316. void pause ( ); // pause thread, 'func' will no longer be called until the thread is resumed
  317. void resume ( ); // resume thread from paused state
  318. void priority (Int priority ); // set thread priority, 'priority'=-3..3
  319. void kill ( ); // kill thread, immediately shut down the thread, usage of this method is not recommended because it may cause memory leaks
  320. Bool wait (Int milliseconds=-1); // wait until the thread finishes processing (<0 = infinite wait), false on timeout
  321. #if EE_PRIVATE
  322. void zero ();
  323. void func ();
  324. Int sleep()C {return 0;} // get thread sleeping (0..Inf), amount of time (in milliseconds) to sleep between function calls
  325. #endif
  326. ~Thread() {del();}
  327. Thread();
  328. explicit Thread(Bool func(Thread &thread), Ptr user=null, Int priority=0, Bool paused=false); // create, 'func'=function which will be called in the created thread, 'user'=custom user data, 'priority'=thread priority (-3..3), 'paused'=if start the thread in paused mode, threads have a default stack size of 1MB, if the thread will require performing operations on GPU data, then you must call 'ThreadMayUseGPUData' inside the thread, please check that function comments for more info
  329. #if !EE_PRIVATE
  330. private:
  331. #endif
  332. Bool _want_stop, _want_pause, _paused, _active;
  333. SByte _priority;
  334. #if EE_PRIVATE
  335. PLATFORM(HANDLE, pthread_t) _handle;
  336. #else
  337. Ptr _handle;
  338. #endif
  339. Bool (*_func)(Thread &thread);
  340. SyncEvent _resume;
  341. #if !WINDOWS
  342. SyncEvent _finished;
  343. #endif
  344. #if APPLE
  345. Str8 _name;
  346. #endif
  347. NO_COPY_CONSTRUCTOR(Thread);
  348. };
  349. /******************************************************************************/
  350. const_mem_addr struct Threads // Worker Threads, allow to process data on multiple threads !! must be stored in constant memory address !!
  351. {
  352. struct Call
  353. {
  354. Ptr data, user;
  355. void (*func)(Ptr data, Ptr user, Int thread_index);
  356. T1(DATA ) void set(DATA &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null) {_set((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  357. T2(DATA, USER_DATA) void set(DATA &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null) {_set((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  358. T2(DATA, USER_DATA) void set(DATA &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user ) {_set((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  359. void _set(Ptr data, void func(Ptr data, Ptr user, Int thread_index), Ptr user=null) {T.data=data; T.user=user; T.func=func;}
  360. void clear() {_set(null, null);}
  361. #if EE_PRIVATE
  362. void call (Int thread_index)C {func(data, user, thread_index);}
  363. Bool is ( )C {return func!=null;}
  364. Bool operator==(C Call &c)C {return data==c.data && user==c.user && func==c.func;}
  365. Bool isFuncUser(void func(Ptr data, Ptr user, Int thread_index), Ptr user)C {return T.func==func && T.user==user;}
  366. Call() {}
  367. Call(Ptr data, void func(Ptr data, Ptr user, Int thread_index), Ptr user=null) {_set(data, func, user);}
  368. #endif
  369. };
  370. void del ( ); // delete the threads without finishing all queued work
  371. void create(Bool ordered, Int threads=Cpu.threads(), Int priority=0, C Str8 &name=S8); // 'ordered'=if process queued calls in the order as they were given (this will be a bit slower), 'priority'=threads priority (-3..3), 'name'=Threads name
  372. // perform multi-threaded call on 'func' function, by giving a unique 'elm_index' from the "0 .. elms-1" range as the function parameter, this function will return only after all calls have been processed, 'max_threads'=max threads to wake up and perform processing (threads that are already awake may also do processing) -1=default value which is 'threads', 'thread_index' will always be 0..'threads'-1 (or 0 if there are no threads), consider using 'process1' for better performance
  373. void process(Int elms, void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, user, max_threads, false);}
  374. T1(USER_DATA) void process(Int elms, void func(IntPtr elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, user, max_threads, false);}
  375. T1(USER_DATA) void process(Int elms, void func(IntPtr elm_index, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, &user, max_threads, false);}
  376. // perform multi-threaded call on 'func' function, by giving a unique 'data' element from the memory container as the function parameter, this function will return only after all calls have been processed, 'max_threads'=max threads to wake up and perform processing (threads that are already awake may also do processing) -1=default value which is 'threads', 'thread_index' will always be 0..'threads'-1 (or 0 if there are no threads), consider using 'process1' for better performance
  377. T1(DATA ) void process(Mems<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, false);}
  378. T1(DATA ) void process(Mems<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, true );}
  379. T1(DATA ) void process(Memc<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, false);}
  380. T1(DATA ) void process(Memc<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, true );}
  381. T1(DATA ) void process(Memt<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, false);}
  382. T1(DATA ) void process(Memt<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, true );}
  383. T1(DATA ) void process(Memb<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false);}
  384. T1(DATA ) void process(Memx<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false);}
  385. T2(DATA, USER_DATA) void process(Mems<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, false);}
  386. T2(DATA, USER_DATA) void process(Mems<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, true );}
  387. T2(DATA, USER_DATA) void process(Memc<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, false);}
  388. T2(DATA, USER_DATA) void process(Memc<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, true );}
  389. T2(DATA, USER_DATA) void process(Memt<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, false);}
  390. T2(DATA, USER_DATA) void process(Memt<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false, true );}
  391. T2(DATA, USER_DATA) void process(Memb<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false);}
  392. T2(DATA, USER_DATA) void process(Memx<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, false);}
  393. T2(DATA, USER_DATA) void process(Mems<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false, false);}
  394. T2(DATA, USER_DATA) void process(Mems<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false, true );}
  395. T2(DATA, USER_DATA) void process(Memc<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false, false);}
  396. T2(DATA, USER_DATA) void process(Memc<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false, true );}
  397. T2(DATA, USER_DATA) void process(Memt<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false, false);}
  398. T2(DATA, USER_DATA) void process(Memt<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false, true );}
  399. T2(DATA, USER_DATA) void process(Memb<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false);}
  400. T2(DATA, USER_DATA) void process(Memx<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, false);}
  401. // perform multi-threaded call on 'func' function, by giving a unique 'elm_index' from the "0 .. elms-1" range as the function parameter, this function will return only after all calls have been processed, 'max_threads'=max threads to wake up and perform processing (threads that are already awake may also do processing) -1=default value which is 'threads', 'thread_index' will always be 0..'threads' (inclusive), which means if you're using per-thread data, then make sure to allocate 'threads'+1 per-thread data (use 'threads1' method to get the number of elements), this method is preferred instead of 'process' offering better performance, however caution must be taken to allocate 1 extra per-thread data
  402. void process1(Int elms, void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, user, max_threads, true);}
  403. T1(USER_DATA) void process1(Int elms, void func(IntPtr elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, user, max_threads, true);}
  404. T1(USER_DATA) void process1(Int elms, void func(IntPtr elm_index, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, &user, max_threads, true);}
  405. // perform multi-threaded call on 'func' function, by giving a unique 'data' element from the memory container as the function parameter, this function will return only after all calls have been processed, 'max_threads'=max threads to wake up and perform processing (threads that are already awake may also do processing) -1=default value which is 'threads', 'thread_index' will always be 0..'threads' (inclusive), which means if you're using per-thread data, then make sure to allocate 'threads'+1 per-thread data (use 'threads1' method to get the number of elements), this method is preferred instead of 'process' offering better performance, however caution must be taken to allocate 1 extra per-thread data
  406. T1(DATA ) void process1(Mems<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, false);}
  407. T1(DATA ) void process1(Mems<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, true );}
  408. T1(DATA ) void process1(Memc<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, false);}
  409. T1(DATA ) void process1(Memc<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, true );}
  410. T1(DATA ) void process1(Memt<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, false);}
  411. T1(DATA ) void process1(Memt<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, true );}
  412. T1(DATA ) void process1(Memb<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true);}
  413. T1(DATA ) void process1(Memx<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true);}
  414. T2(DATA, USER_DATA) void process1(Mems<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, false);}
  415. T2(DATA, USER_DATA) void process1(Mems<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, true );}
  416. T2(DATA, USER_DATA) void process1(Memc<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, false);}
  417. T2(DATA, USER_DATA) void process1(Memc<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, true );}
  418. T2(DATA, USER_DATA) void process1(Memt<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, false);}
  419. T2(DATA, USER_DATA) void process1(Memt<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true, true );}
  420. T2(DATA, USER_DATA) void process1(Memb<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true);}
  421. T2(DATA, USER_DATA) void process1(Memx<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, max_threads, true);}
  422. T2(DATA, USER_DATA) void process1(Mems<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true, false);}
  423. T2(DATA, USER_DATA) void process1(Mems<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true, true );}
  424. T2(DATA, USER_DATA) void process1(Memc<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true, false);}
  425. T2(DATA, USER_DATA) void process1(Memc<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true, true );}
  426. T2(DATA, USER_DATA) void process1(Memt<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true, false);}
  427. T2(DATA, USER_DATA) void process1(Memt<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true, true );}
  428. T2(DATA, USER_DATA) void process1(Memb<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true);}
  429. T2(DATA, USER_DATA) void process1(Memx<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int max_threads=-1) {_process(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, max_threads, true);}
  430. // queue calls on 'func' function, by giving a unique 'elm_index' from the "0 .. elms-1" range as the function parameter, this function will queue the calls for processing and return immediately without waiting for them to be processed
  431. void queue(Int elms, void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user=null) {_queue(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, user);}
  432. T1(USER_DATA) void queue(Int elms, void func(IntPtr elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null) {_queue(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, user);}
  433. T1(USER_DATA) void queue(Int elms, void func(IntPtr elm_index, USER_DATA &user, Int thread_index), USER_DATA &user ) {_queue(elms, (void (*)(IntPtr elm_index, Ptr user, Int thread_index))func, &user);}
  434. // queue call on 'func' function with 'data' and 'user' parameters to be performed on one of the worker threads, this function will queue the object for processing and return immediately without waiting for it to be processed
  435. T1(DATA ) void queue(DATA &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null) {_queue((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  436. T2(DATA, USER_DATA) void queue(DATA &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null) {_queue((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  437. T2(DATA, USER_DATA) void queue(DATA &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user ) {_queue((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  438. Threads& queue(C MemPtr<Call> &calls); // queue multiple calls
  439. // cancel queued calls on 'func' function with 'data' and 'user' parameters, returns the number of canceled calls, please note that call that's already in progress can't be canceled
  440. Int cancelFuncUser(void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user=null) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  441. T1(USER_DATA) Int cancelFuncUser(void func(IntPtr elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  442. T1(USER_DATA) Int cancelFuncUser(void func(IntPtr elm_index, USER_DATA &user, Int thread_index), USER_DATA &user ) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  443. Int cancelFunc(void func(IntPtr elm_index, Ptr user, Int thread_index)) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  444. T1(USER_DATA) Int cancelFunc(void func(IntPtr elm_index, USER_DATA *user, Int thread_index)) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  445. T1(USER_DATA) Int cancelFunc(void func(IntPtr elm_index, USER_DATA &user, Int thread_index)) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  446. T1(DATA ) Int cancel(DATA &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null) {return _cancel((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  447. T2(DATA, USER_DATA) Int cancel(DATA &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null) {return _cancel((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  448. T2(DATA, USER_DATA) Int cancel(DATA &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user ) {return _cancel((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  449. T1(DATA ) Int cancelFuncUser(void func(DATA &data, Ptr user, Int thread_index), Ptr user=null) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  450. T2(DATA, USER_DATA) Int cancelFuncUser(void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  451. T2(DATA, USER_DATA) Int cancelFuncUser(void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user ) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  452. T1(DATA ) Int cancelFunc(void func(DATA &data, Ptr user, Int thread_index)) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  453. T2(DATA, USER_DATA) Int cancelFunc(void func(DATA &data, USER_DATA *user, Int thread_index)) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  454. T2(DATA, USER_DATA) Int cancelFunc(void func(DATA &data, USER_DATA &user, Int thread_index)) {return _cancel((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  455. Threads& cancel(); // cancel all queued calls
  456. // wait until queued calls on 'func' function with 'data' and 'user' parameters have finished processing
  457. void waitFuncUser(void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user=null) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  458. T1(USER_DATA) void waitFuncUser(void func(IntPtr elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  459. T1(USER_DATA) void waitFuncUser(void func(IntPtr elm_index, USER_DATA &user, Int thread_index), USER_DATA &user ) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  460. void waitFunc(void func(IntPtr elm_index, Ptr user, Int thread_index)) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  461. T1(USER_DATA) void waitFunc(void func(IntPtr elm_index, USER_DATA *user, Int thread_index)) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  462. T1(USER_DATA) void waitFunc(void func(IntPtr elm_index, USER_DATA &user, Int thread_index)) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  463. T1(DATA ) void wait(DATA &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null) {_wait((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  464. T2(DATA, USER_DATA) void wait(DATA &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null) {_wait((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  465. T2(DATA, USER_DATA) void wait(DATA &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user ) {_wait((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  466. T1(DATA ) void waitFuncUser(void func(DATA &data, Ptr user, Int thread_index), Ptr user=null) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  467. T2(DATA, USER_DATA) void waitFuncUser(void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  468. T2(DATA, USER_DATA) void waitFuncUser(void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user ) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  469. T1(DATA ) void waitFunc(void func(DATA &data, Ptr user, Int thread_index)) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  470. T2(DATA, USER_DATA) void waitFunc(void func(DATA &data, USER_DATA *user, Int thread_index)) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  471. T2(DATA, USER_DATA) void waitFunc(void func(DATA &data, USER_DATA &user, Int thread_index)) {_wait((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  472. Threads& wait(); // wait until all queued calls have finished processing
  473. // get number of queued calls on 'func' function with 'data' and 'user' parameters still waiting in the queue for processing (this includes calls being currently processed)
  474. Int queuedFuncUser(void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user=null) {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  475. T1(USER_DATA) Int queuedFuncUser(void func(IntPtr elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null) {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  476. T1(USER_DATA) Int queuedFuncUser(void func(IntPtr elm_index, USER_DATA &user, Int thread_index), USER_DATA &user ) {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  477. Int queuedFunc(void func(IntPtr elm_index, Ptr user, Int thread_index)) {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  478. T1(USER_DATA) Int queuedFunc(void func(IntPtr elm_index, USER_DATA *user, Int thread_index)) {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  479. T1(USER_DATA) Int queuedFunc(void func(IntPtr elm_index, USER_DATA &user, Int thread_index)) {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  480. T1(DATA ) Int queued(DATA &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null)C {return _queued((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  481. T2(DATA, USER_DATA) Int queued(DATA &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null)C {return _queued((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  482. T2(DATA, USER_DATA) Int queued(DATA &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user )C {return _queued((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  483. T1(DATA ) Int queuedFuncUser(void func(DATA &data, Ptr user, Int thread_index), Ptr user=null)C {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  484. T2(DATA, USER_DATA) Int queuedFuncUser(void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null)C {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  485. T2(DATA, USER_DATA) Int queuedFuncUser(void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user )C {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  486. T1(DATA ) Int queuedFunc(void func(DATA &data, Ptr user, Int thread_index))C {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  487. T2(DATA, USER_DATA) Int queuedFunc(void func(DATA &data, USER_DATA *user, Int thread_index))C {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  488. T2(DATA, USER_DATA) Int queuedFunc(void func(DATA &data, USER_DATA &user, Int thread_index))C {return _queued((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  489. Int queued()C; // get number of all calls still waiting in the queue for processing (this includes calls being currently processed)
  490. // check if there are any queued calls on 'func' function with 'data' and 'user' parameters still waiting in the queue for processing (this includes calls being currently processed)
  491. Bool busyFuncUser(void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user=null) {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  492. T1(USER_DATA) Bool busyFuncUser(void func(IntPtr elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null) {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  493. T1(USER_DATA) Bool busyFuncUser(void func(IntPtr elm_index, USER_DATA &user, Int thread_index), USER_DATA &user ) {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  494. Bool busyFunc(void func(IntPtr elm_index, Ptr user, Int thread_index)) {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  495. T1(USER_DATA) Bool busyFunc(void func(IntPtr elm_index, USER_DATA *user, Int thread_index)) {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  496. T1(USER_DATA) Bool busyFunc(void func(IntPtr elm_index, USER_DATA &user, Int thread_index)) {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  497. T1(DATA ) Bool busy(DATA &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null)C {return _busy((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  498. T2(DATA, USER_DATA) Bool busy(DATA &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null)C {return _busy((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  499. T2(DATA, USER_DATA) Bool busy(DATA &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user )C {return _busy((Ptr)&data, (void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  500. T1(DATA ) Bool busyFuncUser(void func(DATA &data, Ptr user, Int thread_index), Ptr user=null)C {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  501. T2(DATA, USER_DATA) Bool busyFuncUser(void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null)C {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func, user);}
  502. T2(DATA, USER_DATA) Bool busyFuncUser(void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user )C {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func, &user);}
  503. T1(DATA ) Bool busyFunc(void func(DATA &data, Ptr user, Int thread_index))C {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  504. T2(DATA, USER_DATA) Bool busyFunc(void func(DATA &data, USER_DATA *user, Int thread_index))C {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  505. T2(DATA, USER_DATA) Bool busyFunc(void func(DATA &data, USER_DATA &user, Int thread_index))C {return _busy((void (*)(Ptr data, Ptr user, Int thread_index))func);}
  506. Bool busy()C; // if there's something being processed right now
  507. Bool wantStop()C; // get if threads were requested to be stopped by the 'del' method
  508. Int threads ()C {return _threads.elms() ;} // get how many threads were created for this object
  509. Int threads1()C {return _threads.elms()+1;} // get how many threads were created for this object + 1, use this method when allocating per-thread data to be used for 'process1' methods
  510. Int activeThreads()C; Threads& activeThreads(Int active ); // get/set how many threads should be active (remaining threads will be paused)
  511. Int priority()C; Threads& priority (Int priority); // get/set threads priority, 'priority'=-3..3
  512. ~Threads() {del();}
  513. #if !EE_PRIVATE
  514. private:
  515. #endif
  516. struct ThreadEx : Thread
  517. {
  518. Call call;
  519. ThreadEx() {call.clear();}
  520. };
  521. Mems<ThreadEx> _threads;
  522. Memc<Call > _calls;
  523. SyncCounter _wake_threads, _queued_finished;
  524. SyncEvent _finished;
  525. SyncLock _lock_calls, _lock_process;
  526. void (*_func)(Ptr data, Ptr user, Int thread_index);
  527. union
  528. {
  529. Ptr _func_data;
  530. _Memb *_func_memb;
  531. _Memx *_func_memx;
  532. };
  533. Ptr _func_user;
  534. Byte _func_mode;
  535. Bool _ordered;
  536. Int _left, _processed, _elms, _elm_size, _calls_pos, _waiting;
  537. #if EE_PRIVATE
  538. Bool callsLeft();
  539. void free ();
  540. void checkEnd ();
  541. #endif
  542. #if EE_PRIVATE // "if" on purpose because there's already 'private' above, but we want to set it as private even in engine mode
  543. private:
  544. #endif
  545. void _process( Int elms, void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user, Int max_threads, Bool allow_processing_on_this_thread);
  546. void _process( Ptr data, Int elms, Int elm_size, void func(Ptr data , Ptr user, Int thread_index), Ptr user, Int max_threads, Bool allow_processing_on_this_thread, Bool data_ptr);
  547. void _process(_Memb &data, void func(Ptr data , Ptr user, Int thread_index), Ptr user, Int max_threads, Bool allow_processing_on_this_thread);
  548. void _process(_Memx &data, void func(Ptr data , Ptr user, Int thread_index), Ptr user, Int max_threads, Bool allow_processing_on_this_thread);
  549. void _queue (Int elms, void func(IntPtr elm_index, Ptr user, Int thread_index), Ptr user);
  550. void _queue (Ptr data, void func(Ptr data , Ptr user, Int thread_index), Ptr user);
  551. Int _cancel(Ptr data, void func(Ptr data , Ptr user, Int thread_index), Ptr user);
  552. Int _cancel( void func(Ptr data , Ptr user, Int thread_index), Ptr user);
  553. Int _cancel( void func(Ptr data , Ptr user, Int thread_index) );
  554. void _wait (Ptr data, void func(Ptr data , Ptr user, Int thread_index), Ptr user);
  555. void _wait ( void func(Ptr data , Ptr user, Int thread_index), Ptr user);
  556. void _wait ( void func(Ptr data , Ptr user, Int thread_index) );
  557. Int _queued(Ptr data, void func(Ptr data , Ptr user, Int thread_index), Ptr user)C;
  558. Int _queued( void func(Ptr data , Ptr user, Int thread_index), Ptr user)C;
  559. Int _queued( void func(Ptr data , Ptr user, Int thread_index) )C;
  560. Bool _busy (Ptr data, void func(Ptr data , Ptr user, Int thread_index), Ptr user)C;
  561. Bool _busy ( void func(Ptr data , Ptr user, Int thread_index), Ptr user)C;
  562. Bool _busy ( void func(Ptr data , Ptr user, Int thread_index) )C;
  563. };
  564. /******************************************************************************/
  565. struct ConsoleProcess // allows running console processes and reading their output
  566. {
  567. // get
  568. Bool created ()C; // if the process is created (this will be false only if the process hasn't yet been created, or if it has been manually deleted, if the process was created but stopped running, the return value will be still true)
  569. Bool active ()C; // if the process is created and running (this will be false if the process hasn't yet been created, or if it has been manually deleted, or it stopped running )
  570. Int exitCode()C {return _exit_code;} // get exit code of the process
  571. // manage
  572. void del (); // request process to be closed and close all handles without killing it
  573. Bool create (C Str &name , C Str &params=S, Bool hidden=true, Bool binary=false); // 'name'=process file to execute, 'params'=custom params to be passed to the process, 'hidden'=if start the process as hidden, 'binary'=if treat the output as binary data
  574. Bool createMem(C Str &script, C Str &cur_dir , Bool hidden=true, Bool binary=false); // 'script'=list of commands to be executed, multiple commands should be separated with new line '\n' character, commands will be executed by cmd.exe on Windows (they should be BAT compatible) and bash on Unix (they should be SH compatible), 'cur_dir'=directory at which the commands should be executed, 'hidden'=if start the process as hidden, 'binary'=if treat the output as binary data
  575. // operations
  576. Bool wait(Int milliseconds=-1); // wait 'milliseconds' for process to close (-1 for infinite), false on fail
  577. void stop(); // request process to be closed
  578. void kill(); // kill the process
  579. // io
  580. Str get(); // read new data that the console did output
  581. ~ConsoleProcess() {del();}
  582. ConsoleProcess() {_binary=false; _exit_code=-1; _proc_id=0; _proc=_out_read=_in_write=PLATFORM(null, 0);}
  583. #if !EE_PRIVATE
  584. private:
  585. #endif
  586. Bool _binary;
  587. Int _exit_code;
  588. UInt _proc_id;
  589. #if EE_PRIVATE
  590. PLATFORM(HANDLE, Int) _proc, _out_read, _in_write;
  591. #else
  592. PLATFORM(Ptr, Int) _proc, _out_read, _in_write;
  593. #endif
  594. Str8 _data;
  595. SyncLock _lock;
  596. Thread _thread;
  597. NO_COPY_CONSTRUCTOR(ConsoleProcess);
  598. };
  599. /******************************************************************************/
  600. // Atomic operations
  601. Int AtomicInc(Int &x); // increase value of 'x' by 1 in an atomic operation and return its previous value, this is a thread-safe version of function "return x++;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  602. Int AtomicDec(Int &x); // decrease value of 'x' by 1 in an atomic operation and return its previous value, this is a thread-safe version of function "return x--;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  603. Int AtomicAdd(Int &x, Int y); // increase value of 'x' by 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Int old=x; x+=y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  604. Long AtomicAdd(Long &x, Long y); // increase value of 'x' by 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Long old=x; x+=y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  605. Int AtomicSub(Int &x, Int y); // decrease value of 'x' by 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Int old=x; x-=y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  606. Long AtomicSub(Long &x, Long y); // decrease value of 'x' by 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Long old=x; x-=y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  607. Int AtomicAnd (Int &x, Int y); // and value of 'x' by 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Int old=x; x&= y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  608. Int AtomicDisable(Int &x, Int y); // and value of 'x' by '~y' in an atomic operation and return its previous value, this is a thread-safe version of function "Int old=x; x&=~y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  609. Int AtomicOr (Int &x, Int y); // or value of 'x' by 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Int old=x; x|= y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  610. Int AtomicXor (Int &x, Int y); // xor value of 'x' by 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Int old=x; x^= y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  611. Int AtomicGet(C Int &x ); // get value of 'x' in an atomic operation, this is a thread-safe version of function "return x;" (this allows to access the value across multiple threads without usage of Synchronization Locks), this method
  612. Long AtomicGet(C Long &x ); // get value of 'x' in an atomic operation, this is a thread-safe version of function "return x;" (this allows to access the value across multiple threads without usage of Synchronization Locks), this method
  613. Flt AtomicGet(C Flt &x ); // get value of 'x' in an atomic operation, this is a thread-safe version of function "return x;" (this allows to access the value across multiple threads without usage of Synchronization Locks)
  614. void AtomicSet( Int &x, Int y); // set value of 'x' to 'y' in an atomic operation, this is a thread-safe version of function "x=y; " (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  615. void AtomicSet( Long &x, Long y); // set value of 'x' to 'y' in an atomic operation, this is a thread-safe version of function "x=y; " (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  616. void AtomicSet( Flt &x, Flt y); // set value of 'x' to 'y' in an atomic operation, this is a thread-safe version of function "x=y; " (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  617. Int AtomicGetSet(Int &x, Int y); // set value of 'x' to 'y' in an atomic operation and return its previous value, this is a thread-safe version of function "Int old=x; x=y; return old;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  618. Bool AtomicCAS(Int &x, Int compare, Int new_value); // set value of 'x' to 'new_value' if 'x' is equal to 'compare' in an atomic operation, this is a thread-safe version of function "if(x==compare){x=new_value; return true;} return false;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  619. Bool AtomicCAS(Long &x, Long compare, Long new_value); // set value of 'x' to 'new_value' if 'x' is equal to 'compare' in an atomic operation, this is a thread-safe version of function "if(x==compare){x=new_value; return true;} return false;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  620. Bool AtomicCAS(Flt &x, Flt compare, Flt new_value); // set value of 'x' to 'new_value' if 'x' is equal to 'compare' in an atomic operation, this is a thread-safe version of function "if(x==compare){x=new_value; return true;} return false;" (this allows to modify the value across multiple threads without usage of Synchronization Locks)
  621. // Thread functions
  622. UIntPtr GetThreadId ( ); // get current thread id
  623. UIntPtr GetThreadIdFromWindow(Ptr hwnd ); // get id of the thread which owns the OS window handle
  624. void SetThreadName (C Str8 &name, UIntPtr thread_id=GetThreadId()); // set custom thread name for debugging purpose
  625. void ThreadMayUseGPUData (); // call this from a secondary thread if you expect the thread to perform any operations on GPU data (like Mesh, Material, Image, Shaders, ..., this includes any operation like creating, editing, loading, saving, deleting, ...). This function is best called at the start of the thread, it needs to be called at least once, further calls are ignored. Once the function is called, the thread locks a secondary OpenGL context (if no context is available, then the function waits until other threads finish processing and release their context lock, amount of OpenGL contexts is specified in 'D.secondaryOpenGLContexts'). Context lock is automatically released once the thread exits. This call is required only for OpenGL renderer.
  626. void ThreadFinishedUsingGPUData(); // calling this function is optional (it does not need to be called manually), call it if you wish to manually release a thread from locking an OpenGL context. Threads automatically call this function at end of their life, which means that they automatically release any locked contexts, however in some scenarios you may want to manually release any locked context if you wish to provide more contexts for background processing on other threads. This call is used only for OpenGL renderer.
  627. // Multi-threaded calls
  628. // base functions, do not use
  629. void _MultiThreadedCall( Ptr data, Int elms, Int elm_size, void func(Ptr data, Ptr user, Int thread_index), Ptr user, Int threads, Bool data_ptr);
  630. void _MultiThreadedCall(_Memb &data, void func(Ptr data, Ptr user, Int thread_index), Ptr user, Int threads, Bool data_ptr);
  631. void _MultiThreadedCall(_Memx &data, void func(Ptr data, Ptr user, Int thread_index), Ptr user, Int threads, Bool data_ptr);
  632. // perform multi-threaded call on 'func' function, by giving a unique 'elm_index' from the "0 .. elms-1" range as the function parameter
  633. void MultiThreadedCall(Int elms, void func(Int elm_index, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads());
  634. T1(USER_DATA) void MultiThreadedCall(Int elms, void func(Int elm_index, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {MultiThreadedCall(elms, (void (*)(Int elm_index, Ptr user, Int thread_index))func, user, threads);}
  635. T1(USER_DATA) void MultiThreadedCall(Int elms, void func(Int elm_index, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {MultiThreadedCall(elms, (void (*)(Int elm_index, Ptr user, Int thread_index))func, &user, threads);}
  636. // perform multi-threaded call on 'func' function, by giving a unique 'data' element from the memory container as the function parameter
  637. T1(DATA ) void MultiThreadedCall(Mems<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  638. T1(DATA ) void MultiThreadedCall(Mems<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  639. T1(DATA ) void MultiThreadedCall(Memc<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  640. T1(DATA ) void MultiThreadedCall(Memc<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  641. T1(DATA ) void MultiThreadedCall(Memt<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  642. T1(DATA ) void MultiThreadedCall(Memt<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  643. T1(DATA ) void MultiThreadedCall(Memb<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  644. T1(DATA ) void MultiThreadedCall(Memb<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  645. T1(DATA ) void MultiThreadedCall(Memx<DATA > &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  646. T1(DATA ) void MultiThreadedCall(Memx<DATA*> &data, void func(DATA &data, Ptr user, Int thread_index), Ptr user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  647. T2(DATA, USER_DATA) void MultiThreadedCall(Mems<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  648. T2(DATA, USER_DATA) void MultiThreadedCall(Mems<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  649. T2(DATA, USER_DATA) void MultiThreadedCall(Memc<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  650. T2(DATA, USER_DATA) void MultiThreadedCall(Memc<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  651. T2(DATA, USER_DATA) void MultiThreadedCall(Memt<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  652. T2(DATA, USER_DATA) void MultiThreadedCall(Memt<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  653. T2(DATA, USER_DATA) void MultiThreadedCall(Memb<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  654. T2(DATA, USER_DATA) void MultiThreadedCall(Memb<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  655. T2(DATA, USER_DATA) void MultiThreadedCall(Memx<DATA > &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, false);}
  656. T2(DATA, USER_DATA) void MultiThreadedCall(Memx<DATA*> &data, void func(DATA &data, USER_DATA *user, Int thread_index), USER_DATA *user=null, Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, user, threads, true );}
  657. T2(DATA, USER_DATA) void MultiThreadedCall(Mems<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, false);}
  658. T2(DATA, USER_DATA) void MultiThreadedCall(Mems<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, true );}
  659. T2(DATA, USER_DATA) void MultiThreadedCall(Memc<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, false);}
  660. T2(DATA, USER_DATA) void MultiThreadedCall(Memc<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, true );}
  661. T2(DATA, USER_DATA) void MultiThreadedCall(Memt<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, false);}
  662. T2(DATA, USER_DATA) void MultiThreadedCall(Memt<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data.data(), data.elms(), data.elmSize(), (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, true );}
  663. T2(DATA, USER_DATA) void MultiThreadedCall(Memb<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, false);}
  664. T2(DATA, USER_DATA) void MultiThreadedCall(Memb<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, true );}
  665. T2(DATA, USER_DATA) void MultiThreadedCall(Memx<DATA > &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, false);}
  666. T2(DATA, USER_DATA) void MultiThreadedCall(Memx<DATA*> &data, void func(DATA &data, USER_DATA &user, Int thread_index), USER_DATA &user , Int threads=Cpu.threads()) {_MultiThreadedCall(data , (void (*)(Ptr data, Ptr user, Int thread_index))func, &user, threads, true );}
  667. // Process functions
  668. void ProcPriority( Int priority ); // set process priority (-2..2)
  669. void ProcClose ( UInt id ); // close process
  670. void ProcClose (C Str &name ); // close process
  671. Bool ProcKill ( UInt id ); // kill process, false on fail
  672. Bool ProcKill (C Str &name ); // kill process, false on fail
  673. Bool ProcWait ( UInt id, Int milliseconds=-1); // wait for process to exit (<0 = infinite wait), false on timeout
  674. Ptr ProcWindow ( UInt id ); // get OS window handle of process
  675. Str ProcName ( UInt id ); // get process name
  676. UInt ProcFind (C Str &name ); // find process ID based on its executable name, name can be either a full path or just the base name
  677. void ProcList ( MemPtr<UInt> id ); // get list of process ID's , after calling this function the 'id' will contain a list of process ID's
  678. void ProcModules (UInt id, MemPtr<Str > modules ); // get list of process modules, after calling this function the 'modules' will contain a list of process modules
  679. struct ProcessAccess
  680. {
  681. UInt proc_id; // Process ID that has a handle opened to our process
  682. Bool write ; // if the handle has write permission
  683. };
  684. Bool GetProcessesAccessingThisProcess(MemPtr<ProcessAccess> proc, Bool write_only=false, Mems<Byte> *temp=null); // get a list of processes that are accessing this process (you may need to run with admin rights to detect all processes), 'write_only'=if only detect processes that have write permission, 'temp'=optional container for temporary memory allocation that can be reused instead of allocating new memory each time, false on fail
  685. /******************************************************************************/
  686. #if EE_PRIVATE
  687. struct ThreadEmulation
  688. {
  689. void include(Thread &thread);
  690. void exclude(Thread &thread);
  691. void update();
  692. ThreadEmulation();
  693. private:
  694. struct DelayedThread
  695. {
  696. Int waited;
  697. Thread *thread;
  698. DelayedThread() {waited=0;}
  699. };
  700. Int _process_left;
  701. UInt _process_type, _time;
  702. Memc< Thread*> _rt_threads;
  703. Memc<DelayedThread > _delayed_threads;
  704. }extern
  705. EmulatedThreads;
  706. #if HAS_THREADS
  707. INLINE void UpdateThreads() {}
  708. #else
  709. INLINE void UpdateThreads() {EmulatedThreads.update();}
  710. #endif
  711. INLINE UIntPtr _GetThreadId() {return PLATFORM(GetCurrentThreadId(), (UIntPtr)pthread_self());}
  712. #define GetThreadId _GetThreadId // use this macro so all engine functions access '_GetThreadId' directly
  713. #endif
  714. /******************************************************************************/