-
Notifications
You must be signed in to change notification settings - Fork 14
Description
For large scale use and production readiness however, it really wants plexing. You sort of achieve that with the mutxes, but only within a limited scope.
If I want to use this for battle ready scenarios and large scale, there are areas where it can block where it doesn't have to leading to things like hanging, lost performance gains, etc.
My particular use case is that I want to use this buried between layers of abstraction that let you program asyncronously but very easily. The result of that can be a surprising amount of different scenarios and consider and use cases and need to be handled out of site.
I think a really quick fix might be something like:
Thread {
public static multiJoin(int $timeout, Thread ...$threads):?Thread
}
Though that still leaves a lot of questions unanswered. I may want to wait for joins and locks at the same time. Same issue with the mutexes, you might want to wait for the first to unlock. You don't really want things like interrupted exceptions everywhere either.
You could consider a Selectable interface for anything blocking (locks, join, etc).
If you consider stability, there's a lot to think about such as what happens when one thing putting on the queue dies?
The mutexes do have a limitation over sync. I can't see an obvious way to wait for data efficiently. In thread communication there is a need to syncronise as well as exchange data safely. In this case I think only the latter case is really directly supported.
Unless I'm not thinking straight it requires some odd roundabout ways to do some things.
$q = new Queue();
function producer() use($q) {
for($i = 0; $i < 10**6; $i++) {
$q->lock();
$q->push($i);
$q->unlock();
}
}
function consumer() use($q) {
while(true) {
$q->lock();
$items = $q->shiftAll();
$q->unlock();
foreach($items as $i)
echo "$i\n";
}
}
thread producer();
thread consumer();
It's subtle but if you look closely you'll notice that the consumer will use 100% CPU, doing nothing while the producer is doing something other than working on the queue. The obvious way to see that is to comment out starting the producer and watch the CPU go whoooop.
$q = new Queue();
// Wrong thread lock ownership!
$q->lock();
function producer() use($q) {
for($i = 0; $i < 10**6; $i++) {
$q->push($i);
$q->unlock();
if(rand(0, 10**5) === 0)
throw new Exception();
$q->lock();
}
$q->unlock();
}
$active = true;
function consumer() use($q, &$active) {
// When does it end?
while(true) {
$q->lock();
$items = $q->shiftAll();
if(!$active && count($items) === 0)
break;
$q->unlock();
foreach($items as $i)
echo "$i\n";
}
}
$c = thread consumer();
join thread producer();
$active = false;
join $c;
I'm not really sure if there are any data exchange cases mutexes can't handle and at this point I'll skip the cognitive effort of determining that as at this point I think there's already enough to generate questions and to give food for thought. This is also just based on a very quick glance of the library so I might have tripped up on something somewhere.
I believe you can use the mutex to achieve sync but really that implies pretty much that you actually want standalone mutexs. Appart from being ugly the main limitation seems to be the lack of a select on locks as well as the question if a thread can unlock someone else's lock.