TestGame/addons/reactivex/internal/conditionalvariable.gd
2024-12-27 21:00:07 +01:00

73 lines
1.7 KiB
GDScript

class_name ConditionalVariable
## Naive implementation of a conditional variable for multi-threading
var _mutex : Mutex
var _waiting_queue : Array[Lock]
func _init():
self._mutex = Mutex.new()
self._waiting_queue = []
func wait(lock : LockBase):
var thread_lock : Lock = Lock.new()
thread_lock.lock()
self._mutex.lock()
self._waiting_queue.push_back(thread_lock)
self._mutex.unlock()
lock._unlock_and_store_recursion_depth()
thread_lock.lock()
lock._lock_and_restore_recursion_depth()
thread_lock.unlock()
func wait_pred(lock : LockBase, stop_waiting : Callable):
while !stop_waiting.call():
self.wait(lock)
func wait_for(lock : LockBase, time_sec : float) -> bool:
var thread_lock : Lock = Lock.new()
thread_lock.lock()
self._mutex.lock()
self._waiting_queue.push_back(thread_lock)
self._mutex.unlock()
var notified = RefValue.Set(true)
var on_timeout = func():
notified.v = false
thread_lock.unlock()
self._mutex.lock()
self._waiting_queue.erase(thread_lock)
self._mutex.unlock()
var timer = GDRx.get_tree().create_timer(time_sec)
timer.connect("timeout", on_timeout)
lock._unlock_and_store_recursion_depth()
thread_lock.lock()
lock._lock_and_restore_recursion_depth()
if notified.v:
timer.disconnect("timeout", on_timeout)
thread_lock.unlock()
return notified.v
func notify(n = 1):
self._mutex.lock()
for __ in range(n):
if self._waiting_queue.is_empty():
break
var next : Lock = self._waiting_queue.pop_front()
next.unlock()
self._mutex.unlock()
func notify_all():
self._mutex.lock()
while not self._waiting_queue.is_empty():
var next : Lock = self._waiting_queue.pop_front()
next.unlock()
self._mutex.unlock()