thread
Real OS threads for HolyPHP with Go-style channels, mutexes, atomics, barriers and a worker Pool. Shared state is discouraged by design — communicate by sending messages over channels.
stdlib — embedded
real OS threads
Go-style channels
parallel_map
no data races by design
Primitives at a glance
| Primitive | What it does |
|---|---|
thr_spawn($fn) | run a closure on a new OS thread |
thr_join($t) | wait for a thread to finish |
thr_parallel_map($fn, $arr) | map a closure over an array on all cores, order preserved |
chan_new($cap) / chan_send / chan_recv | Go-style channels — rendezvous or buffered ring; chan_close + drained receives like Go |
mutex_new / mutex_lock / mutex_unlock | mutual exclusion (with a Mutex::synchronized wrapper class) |
atomic_new / atomic_add / atomic_get | mutex-backed 64-bit counters, safe across threads |
barrier_new($n) / barrier_wait | release N threads together, arrival indexes like pthreads |
thr_cpu_count() | number of usable cores |
Channels: the preferred style
import "thread";
$ch = chan_new(4); // buffered channel
thr_spawn(function() use ($ch) {
foreach (["scan", "parse", "index"] as $step) {
chan_send($ch, $step . " done");
}
chan_close($ch); // no more sends
});
while (true) {
$msg = chan_recv($ch);
if ($msg === false) { break; } // closed and drained
echo $msg;
}
Rule: captured arrays and objects are COW copies — each
thread sees its own snapshot. Never write one captured array from several
threads; send results back through a channel instead. That single rule is why
there are no data races to debug.
Parallel map, one call
import "thread";
$squares = thr_parallel_map(function($x) { return $x * $x; }, [1,2,3,4,5]);
// [1, 4, 9, 16, 25] — input order kept regardless of finish order
// chunked variant for big inputs: strided workers + channel collection
$out = thr_map_chunked(function($x) { return heavy($x); }, $bigArray);
The Pool class
import "thread";
$pool = new Pool(4); // 4 strided workers
$pool->start(function($items, $wid) {
foreach ($items as $x) { process($x); }
});
foreach ($urls as $u) { $pool->submit($u); }
$pool->collect($results);
$pool->waitOne();
$pool->shutdown();
Concurrency + event loop
Threads and async compose: run a blocking fetch or a private loop on a worker thread, deliver the outcome through a channel, and the main loop never stalls.
import "async";
import "thread";
$ch = chan_new(1);
thr_spawn(function() use ($ch) {
async_get("http://slow.service/", function($r) use ($ch) {
chan_send($ch, strlen($r));
});
});
$lp = new Loop();
$lp->every(100, function() use ($ch, $lp) {
$n = chan_try_recv($ch);
if ($n !== false) { echo "fetched $n bytes"; $lp->stop(); }
});
$lp->run();
Try it
hphp pkg install thread
hphp run examples/thr_demo.hphp
Ships embedded in the compiler — import "thread"
works offline. Source: lib/thread/thread.hphp.