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

PrimitiveWhat 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_recvGo-style channels — rendezvous or buffered ring; chan_close + drained receives like Go
mutex_new / mutex_lock / mutex_unlockmutual exclusion (with a Mutex::synchronized wrapper class)
atomic_new / atomic_add / atomic_getmutex-backed 64-bit counters, safe across threads
barrier_new($n) / barrier_waitrelease 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.