threads.bmx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718
  1. SuperStrict
  2. Rem
  3. bbdoc: System/Threads
  4. End Rem
  5. Module BRL.Threads
  6. ModuleInfo "Version: 1.03"
  7. ModuleInfo "License: zlib/libpng"
  8. ModuleInfo "Copyright: Blitz Research Ltd"
  9. ModuleInfo "History: 1.03"
  10. ModuleInfo "History: Added TFuture type."
  11. ModuleInfo "History: 1.02"
  12. ModuleInfo "History: Changed to use macOS dispatch semphores."
  13. ModuleInfo "History: 1.01"
  14. ModuleInfo "History: Use Byte Ptr instead of Int handles."
  15. ModuleInfo "History: 1.00"
  16. ModuleInfo "History: Initial release."
  17. ?Threaded And macos
  18. Import "threads_mac.m"
  19. ?Threaded
  20. Import Pub.Stdc
  21. Import BRL.Time
  22. Import "threads.c"
  23. Private
  24. Extern
  25. Function bbThreadAllocData:Int()
  26. Function bbThreadSetData( index:Int,data:Object )
  27. Function bbThreadGetData:Object( index:Int )
  28. Function bbAtomicCAS:Int( target:Int Ptr,old_value:Int,new_value:Int )="int bbAtomicCAS( int *,int ,int )!"
  29. Function bbAtomicAdd:Int( target:Int Ptr,value:Int )="int bbAtomicAdd( int *,int )!"
  30. Function threads_CreateThread:Byte Ptr( entry:Object( data:Object ),data:Object )
  31. Function threads_DetachThread( thread:Byte Ptr )
  32. Function threads_WaitThread:Object( thread:Byte Ptr )
  33. Function threads_CreateMutex:Byte Ptr()
  34. Function threads_CloseMutex( mutex:Byte Ptr )
  35. Function threads_LockMutex( mutex:Byte Ptr )
  36. Function threads_TryLockMutex:Int( mutex:Byte Ptr )
  37. Function threads_UnlockMutex( mutex:Byte Ptr )
  38. Function threads_CreateSemaphore:Byte Ptr( count:Int )
  39. Function threads_CloseSemaphore( sema:Byte Ptr )
  40. Function threads_WaitSemaphore( sema:Byte Ptr )
  41. Function threads_PostSemaphore( sema:Byte Ptr )
  42. Function threads_TimedWaitSemaphore:Int( sema:Byte Ptr, millis:Int )
  43. Function threads_CreateCond:Byte Ptr()
  44. Function threads_WaitCond( cond:Byte Ptr,mutex:Byte Ptr )
  45. Function threads_SignalCond( cond:Byte Ptr )
  46. Function threads_BroadcastCond( cond:Byte Ptr )
  47. Function threads_CloseCond( cond:Byte Ptr )
  48. Function threads_TimedWaitCond:Int( cond:Byte Ptr,mutex:Byte Ptr, millis:Int )
  49. End Extern
  50. Global _mainThread:TThread=New TThread
  51. Global _curThread:TThreadData=TThreadData.Create()
  52. _curThread.SetValue _mainThread
  53. Public
  54. Rem
  55. bbdoc: Thread type
  56. End Rem
  57. Type TThread
  58. Rem
  59. bbdoc: Detach this thread
  60. End Rem
  61. Method Detach()
  62. If Not _handle Return 'don't Assert, as Detach is a form of Close() which always works...
  63. threads_DetachThread _handle
  64. _handle=0
  65. End Method
  66. Rem
  67. bbdoc: Wait for this thread to finish
  68. returns: The object returned by the thread.
  69. End Rem
  70. Method Wait:Object()
  71. If Not _handle Return _result 'don't Assert, as Wait is a form of Close() which always works...
  72. threads_WaitThread _handle
  73. _handle=0
  74. Return _result
  75. End Method
  76. Rem
  77. bbdoc: Check if this thread is running
  78. End Rem
  79. Method Running:Int()
  80. Return _running
  81. End Method
  82. Rem
  83. bbdoc: Create a new thread
  84. End Rem
  85. Function Create:TThread( entry:Object( data:Object),data:Object )
  86. Local thread:TThread=New TThread
  87. thread._entry=entry
  88. thread._data=data
  89. thread._running=True
  90. thread._handle=threads_CreateThread( _EntryStub,thread )
  91. Return thread
  92. End Function
  93. Rem
  94. bbdoc: Get main thread
  95. returns: A thread object representing the main application thread.
  96. End Rem
  97. Function Main:TThread()
  98. Return _mainThread
  99. End Function
  100. Rem
  101. bbdoc: Get current thread
  102. returns: A thread object representing the current thread.
  103. End Rem
  104. Function Current:TThread()
  105. Return TThread( _curThread.GetValue() )
  106. End Function
  107. Function _EntryStub:Object( data:Object )
  108. Local thread:TThread=TThread( data )
  109. _curThread.SetValue thread
  110. thread._result=thread._entry( thread._data )
  111. thread._running=False
  112. End Function
  113. Method Delete()
  114. If _handle threads_DetachThread _handle
  115. End Method
  116. Field _running:Int
  117. Field _handle:Byte Ptr
  118. Field _result:Object
  119. Field _entry:Object( data:Object )
  120. Field _data:Object
  121. End Type
  122. Rem
  123. bbdoc: ThreadData type
  124. End Rem
  125. Type TThreadData
  126. Rem
  127. bbdoc: Set thread data value
  128. End Rem
  129. Method SetValue( value:Object )
  130. bbThreadSetData _handle,value
  131. End Method
  132. Rem
  133. bbdoc: Get thread data value
  134. End Rem
  135. Method GetValue:Object()
  136. Return bbThreadGetData( _handle )
  137. End Method
  138. Rem
  139. bbdoc: Create thread data
  140. End Rem
  141. Function Create:TThreadData()
  142. Local handle:Int=bbThreadAllocData()
  143. If Not handle Return Null
  144. Local data:TThreadData=New TThreadData
  145. data._handle=handle
  146. Return data
  147. End Function
  148. Field _handle:Int
  149. End Type
  150. Rem
  151. bbdoc: Mutex type
  152. End Rem
  153. Type TMutex
  154. Rem
  155. bbdoc: Close the mutex
  156. End Rem
  157. Method Close()
  158. If Not _handle Return
  159. threads_CloseMutex _handle
  160. _handle=0
  161. End Method
  162. Rem
  163. bbdoc: Lock the mutex
  164. End Rem
  165. Method Lock()
  166. Assert _handle
  167. threads_LockMutex _handle
  168. End Method
  169. Rem
  170. bbdoc: Try to lock the mutex
  171. returns: #True if mutex was successfully locked; #False if mutex was already locked by another thread.
  172. End Rem
  173. Method TryLock:Int()
  174. Assert _handle
  175. Return threads_TryLockMutex( _handle )
  176. End Method
  177. Rem
  178. bbdoc: Unlock the mutex
  179. End Rem
  180. Method Unlock()
  181. Assert _handle
  182. threads_UnlockMutex _handle
  183. End Method
  184. Rem
  185. bbdoc: Create a new mutex
  186. End Rem
  187. Function Create:TMutex()
  188. Local handle:Byte Ptr=threads_CreateMutex()
  189. If Not handle Return Null
  190. Local mutex:TMutex=New TMutex
  191. mutex._handle=handle
  192. Return mutex
  193. End Function
  194. Method Delete()
  195. If _handle threads_CloseMutex _handle
  196. End Method
  197. Field _handle:Byte Ptr
  198. End Type
  199. Rem
  200. bbdoc: Semaphore type
  201. End Rem
  202. Type TSemaphore
  203. Rem
  204. bbdoc: Close the semaphore
  205. End Rem
  206. Method Close()
  207. If Not _handle Return
  208. threads_CloseSemaphore _handle
  209. _handle=0
  210. End Method
  211. Rem
  212. bbdoc: Wait for the semaphore
  213. End Rem
  214. Method Wait()
  215. Assert _handle
  216. threads_WaitSemaphore _handle
  217. End Method
  218. Rem
  219. bbdoc: Wait for the semaphore
  220. End Rem
  221. Method TimedWait:Int(millis:Int)
  222. Assert _handle
  223. Return threads_TimedWaitSemaphore(_handle, millis)
  224. End Method
  225. Rem
  226. bbdoc: Post the semaphore
  227. End Rem
  228. Method Post()
  229. Assert _handle
  230. threads_PostSemaphore _handle
  231. End Method
  232. Rem
  233. bbdoc: Create a new semaphore
  234. End Rem
  235. Function Create:TSemaphore( count:Int )
  236. Local handle:Byte Ptr=threads_CreateSemaphore( count )
  237. If Not handle Return Null
  238. Local semaphore:TSemaphore=New TSemaphore
  239. semaphore._handle=handle
  240. Return semaphore
  241. End Function
  242. Method Delete()
  243. If _handle threads_CloseSemaphore _handle
  244. End Method
  245. Field _handle:Byte Ptr
  246. End Type
  247. Rem
  248. bbdoc: CondVar type
  249. End Rem
  250. Type TCondVar
  251. Rem
  252. bbdoc: Close the condvar
  253. End Rem
  254. Method Close()
  255. If Not _handle Return
  256. threads_CloseCond _handle
  257. _handle=0
  258. End Method
  259. Rem
  260. bbdoc: Wait for the condvar
  261. End Rem
  262. Method Wait( mutex:TMutex )
  263. Assert _handle
  264. threads_WaitCond _handle,mutex._handle
  265. End Method
  266. Rem
  267. bbdoc: Wait for the condvar
  268. End Rem
  269. Method TimedWait:Int( mutex:TMutex, millis:Int )
  270. Assert _handle
  271. Return threads_TimedWaitCond(_handle,mutex._handle, millis)
  272. End Method
  273. Rem
  274. bbdoc: Signal the condvar
  275. End Rem
  276. Method Signal()
  277. Assert _handle
  278. threads_SignalCond _handle
  279. End Method
  280. Rem
  281. bbdoc: Broadcast the condvar
  282. End Rem
  283. Method Broadcast()
  284. Assert _handle
  285. threads_BroadcastCond _handle
  286. End Method
  287. Rem
  288. bbdoc: Create a new condvar
  289. End Rem
  290. Function Create:TCondVar()
  291. Local handle:Byte Ptr=threads_CreateCond()
  292. If Not handle Return Null
  293. Local condvar:TCondVar=New TCondVar
  294. condvar._handle=handle
  295. Return condvar
  296. End Function
  297. Method Delete()
  298. If _handle threads_CloseCond _handle
  299. End Method
  300. Field _handle:Byte Ptr
  301. End Type
  302. Rem
  303. bbdoc: A generic type for asynchronous result handling, allowing threads to wait for and retrieve results safely.
  304. about: It provides a mechanism for one thread to produce a result that another thread can wait for and retrieve
  305. at a later time. This is particularly useful for tasks that are executed in parallel, where the completion
  306. time may vary, and the consumer needs to wait for the result before proceeding.
  307. End Rem
  308. Type TFuture<V>
  309. Private
  310. Field value:V
  311. Field ready:Int
  312. Field condvar:TCondVar=CreateCondVar()
  313. Field mutex:TMutex=CreateMutex()
  314. Public
  315. Rem
  316. bbdoc: Waits for the result to become available and then returns it.
  317. about: This method blocks the calling thread until result is available.
  318. End Rem
  319. Method GetResult:V()
  320. mutex.Lock()
  321. While Not ready
  322. condvar.Wait( mutex )
  323. Wend
  324. Local result:V = value
  325. mutex.Unlock()
  326. Return result
  327. End Method
  328. Rem
  329. bbdoc: Sets the result of the asynchronous operation and signals any waiting threads.
  330. End Rem
  331. Method SetResult( value:V )
  332. mutex.Lock()
  333. Self.value=value
  334. Self.ready=True
  335. condvar.Signal()
  336. mutex.Unlock()
  337. End Method
  338. End Type
  339. Rem
  340. bbdoc: A thread event object.
  341. about: A basic synchronization object that allows one thread to signal an event to other threads.
  342. It manages an internal flag that can be set or cleared, and provides methods to wait for the event to be set.
  343. End rem
  344. Type TThreadEvent
  345. Private
  346. Field lock:TMutex
  347. Field condition:TCondVar
  348. Field _isSet:Int
  349. Public
  350. Method New()
  351. lock = TMutex.Create()
  352. condition = TCondVar.Create()
  353. _isSet = False
  354. End Method
  355. Rem
  356. bboc: Sets the internal flag to #True and signals any waiting threads.
  357. about: All threads waiting for it to become #True are awakened. Threads that call #Wait once the flag is true will not block at all.
  358. End Rem
  359. Method Set()
  360. lock.Lock()
  361. _isSet = True
  362. condition.Broadcast()
  363. lock.Unlock()
  364. End Method
  365. Rem
  366. bbdoc: Resets the internal flag to false.
  367. about: After clearing, threads calling #Wait will block until #Set is called to set the internal flag to #True again.
  368. End Rem
  369. Method Clear()
  370. lock.Lock()
  371. _isSet = False
  372. lock.Unlock()
  373. End Method
  374. Rem
  375. bbdoc: Waits for the event to be set.
  376. about: This method could block indefinitely if the event is never set.
  377. If the event is already set, the method returns immediately.
  378. End Rem
  379. Method Wait()
  380. lock.Lock()
  381. While Not _isSet
  382. condition.Wait(lock)
  383. Wend
  384. lock.Unlock()
  385. End Method
  386. Rem
  387. bbdoc: Waits for the event to be set, with a timeout.
  388. about: If the timeout is reached before the event is set, the method returns #False.
  389. End Rem
  390. Method Wait:Int(timeout:ULong, unit:ETimeUnit = ETimeUnit.Milliseconds)
  391. lock.Lock()
  392. Local timeoutMs:ULong = TimeUnitToMillis(timeout, unit)
  393. Local endTime:ULong = CurrentUnixTime() + timeoutMs
  394. While Not _isSet
  395. Local now:ULong = CurrentUnixTime()
  396. If now >= timeoutMs Then
  397. lock.Unlock()
  398. Return False
  399. End If
  400. condition.TimedWait(lock, Int(timeoutMs - now))
  401. Wend
  402. lock.Unlock()
  403. Return True
  404. End Method
  405. Rem
  406. bbdoc: Returns whether the event is set or not.
  407. End Rem
  408. Method IsSet:Int()
  409. lock.Lock()
  410. Local result:Int = _isSet
  411. lock.Unlock()
  412. Return result
  413. End Method
  414. End Type
  415. Rem
  416. bbdoc: Create a thread
  417. returns: A new thread object.
  418. about:
  419. Creates a thread and returns a thread object.
  420. The value returned by the thread @entry routine can be later retrieved using #WaitThread.
  421. To 'close' a thread, call either #DetachThread or #WaitThread. This isn't strictly
  422. necessary as the thread will eventually be closed when it is garbage collected, however, it
  423. may be a good idea if you are creating many threads very often, as some operating systems have
  424. a limit on the number of threads that can be allocated at once.
  425. End Rem
  426. Function CreateThread:TThread( entry:Object( data:Object ),data:Object )
  427. Return TThread.Create( entry,data )
  428. End Function
  429. Rem
  430. bbdoc: Get main thread
  431. returns: A thread object representing the main application thread.
  432. End Rem
  433. Function MainThread:TThread()
  434. Return _mainThread
  435. End Function
  436. Rem
  437. bbdoc: Get current thread
  438. returns: A thread object representing the current thread.
  439. End Rem
  440. Function CurrentThread:TThread()
  441. Return TThread( _curThread.GetValue() )
  442. End Function
  443. Rem
  444. bbdoc: Detach a thread
  445. about:
  446. #DetachThread closes a thread's handle, but does not halt or otherwise affect the target thread.
  447. Once one a thread has been detached, it wil no longer be possible to use #WaitThread to get its return value.
  448. This allows the thread to run without your program having to continually check whether it has completedin order to close it.
  449. End Rem
  450. Function DetachThread( thread:TThread )
  451. thread.Detach()
  452. End Function
  453. Rem
  454. bbdoc: Wait for a thread to finish
  455. returns: The object returned by the thread entry routine.
  456. about:
  457. #WaitThread causes the calling thread to block until the target thread has completed execution.
  458. If the target thread has already completed execution, #WaitThread returns immediately.
  459. The returned object is the object returned by the thread's entry routine, as passed to #CreateThread.
  460. End Rem
  461. Function WaitThread:Object( thread:TThread )
  462. Return thread.Wait()
  463. End Function
  464. Rem
  465. bbdoc: Check if a thread is running
  466. returns: #True if @thread is still running, otherwise #False.
  467. End Rem
  468. Function ThreadRunning:Int( thread:TThread )
  469. Return thread.Running()
  470. End Function
  471. Rem
  472. bbdoc: Create thread data
  473. returns: A new thread data object.
  474. End Rem
  475. Function CreateThreadData:TThreadData()
  476. Return TThreadData.Create()
  477. End Function
  478. Rem
  479. bbdoc: Set thread data value
  480. End Rem
  481. Function SetThreadDataValue( data:TThreadData,value:Object )
  482. data.SetValue value
  483. End Function
  484. Rem
  485. bbdoc: Get thread data value
  486. End Rem
  487. Function GetThreadDataValue:Object( data:TThreadData )
  488. Return data.Getvalue()
  489. End Function
  490. Rem
  491. bbdoc: Create a mutex
  492. returns: A new mutex object
  493. End Rem
  494. Function CreateMutex:TMutex()
  495. Return TMutex.Create()
  496. End Function
  497. Rem
  498. bbdoc: Close a mutex
  499. End Rem
  500. Function CloseMutex( mutex:TMutex )
  501. mutex.Close
  502. End Function
  503. Rem
  504. bbdoc: Lock a mutex
  505. End Rem
  506. Function LockMutex( mutex:TMutex )
  507. mutex.Lock
  508. End Function
  509. Rem
  510. bbdoc: Try to lock a mutex
  511. returns: #True if @mutex was successfully locked; #False if @mutex was already locked by another thread.
  512. End Rem
  513. Function TryLockMutex:Int( mutex:TMutex )
  514. Return mutex.TryLock()
  515. End Function
  516. Rem
  517. bbdoc: Unlock a mutex
  518. End Rem
  519. Function UnlockMutex( mutex:TMutex )
  520. mutex.Unlock
  521. End Function
  522. Rem
  523. bbdoc: Create a semaphore
  524. returns: A new semaphore object
  525. End Rem
  526. Function CreateSemaphore:TSemaphore( count:Int )
  527. Return TSemaphore.Create( count )
  528. End Function
  529. Rem
  530. bbdoc: Close a semaphore
  531. End Rem
  532. Function CloseSemaphore( semaphore:TSemaphore )
  533. semaphore.Close
  534. End Function
  535. Rem
  536. bbdoc: Wait for a semaphore
  537. End Rem
  538. Function WaitSemaphore( semaphore:TSemaphore )
  539. semaphore.Wait
  540. End Function
  541. Rem
  542. bbdoc: Post a semaphore
  543. End Rem
  544. Function PostSemaphore( semaphore:TSemaphore )
  545. semaphore.Post
  546. End Function
  547. Rem
  548. bbdoc: Create a condvar
  549. returns: A new condvar object
  550. End Rem
  551. Function CreateCondVar:TCondVar()
  552. Return TCondVar.Create()
  553. End Function
  554. Rem
  555. bbdoc: Close a condvar
  556. End Rem
  557. Function CloseCondVar( condvar:TCondVar )
  558. condvar.Close
  559. End Function
  560. Rem
  561. bbdoc: Wait for a condvar
  562. End Rem
  563. Function WaitCondVar( condvar:TCondVar,mutex:TMutex )
  564. condvar.Wait mutex
  565. End Function
  566. Rem
  567. bbdoc: Signal a condvar
  568. End Rem
  569. Function SignalCondVar( condvar:TCondVar )
  570. condvar.Signal
  571. End Function
  572. Rem
  573. bbdoc: Broadcast a condvar
  574. End Rem
  575. Function BroadcastCondVar( condvar:TCondVar )
  576. condvar.Broadcast
  577. End Function
  578. Rem
  579. bbdoc: Compare and swap
  580. returns: @True if target was updated
  581. about:
  582. Atomically replace @target with @new_value if @target equals @old_value.
  583. End Rem
  584. Function CompareAndSwap:Int( target:Int Var,oldValue:Int,newValue:Int )
  585. Return bbAtomicCAS( Varptr target,oldValue,newValue )
  586. End Function
  587. Rem
  588. bbdoc: Atomic add
  589. returns: Previuous value of target
  590. about:
  591. Atomically add @value to @target.
  592. End Rem
  593. Function AtomicAdd:Int( target:Int Var,value:Int )
  594. Return bbAtomicAdd( Varptr target,value )
  595. End Function
  596. Rem
  597. bbdoc: Atomically swap values
  598. returns: The old value of @target
  599. End Rem
  600. Function AtomicSwap:Int( target:Int Var,value:Int )
  601. Repeat
  602. Local oldval:Int=target
  603. If CompareAndSwap( Varptr target,oldval,value ) Return oldval
  604. Forever
  605. End Function
  606. ?