Files
XC_VM/tests/Unit/MultithreadPoolTest.php
T
Divarion_D c4ec65217c feat(vod): run Movies/Series imports as one bounded batch
An M3U can list tens of thousands of items, and the import started one
background vod_import_item per item from the web request: that many
processes and TMDb requests at once.

The request now writes the items to a batch file (one JSON payload per
line) and starts a single `vod_import_batch`, which feeds them through
Multithread::pool(): at most thread_count imports at a time, commands
read from the file only as slots free up, output discarded, polled every
100 ms. The payload no longer carries the whole genre map; the importer
reads it from core. The batch writes a .wpid, so Kill Running stops it.

thread_count (parallel imports) moves to Settings -> VOD Import, since
core now uses it; VodItemImporter::importThreads() is the one reader for
the import and the watch module's scans. saveGenreMap is simplified.

Multithread::pool() is new beside the existing run(), which holds every
command up front, spins without sleeping and keeps each finished process'
pipes open; the watch cron moves to pool() too.
2026-10-03 23:02:35 +03:00

64 lines
1.9 KiB
PHP

<?php
use XcVm\Core\Process\Multithread;
use PHPUnit\Framework\TestCase;
/**
* Multithread::pool() runs every command, never more than the pool size at
* once, and pulls commands from a generator only as slots free up. Each
* command logs its start and end; the log shows the overlap.
*/
final class MultithreadPoolTest extends TestCase {
private string $log;
protected function setUp(): void {
$this->log = tempnam(sys_get_temp_dir(), 'pool');
}
protected function tearDown(): void {
@unlink($this->log);
}
private function command(int $rID): string {
return 'echo s >> ' . escapeshellarg($this->log) . '; sleep 0.2; echo e' . $rID . ' >> ' . escapeshellarg($this->log);
}
/** Highest number of commands running at the same time, from the log. */
private function maxConcurrent(): int {
$rRunning = $rMax = 0;
foreach (file($this->log, FILE_IGNORE_NEW_LINES) as $rLine) {
$rRunning += ($rLine === 's') ? 1 : -1;
$rMax = max($rMax, $rRunning);
}
return $rMax;
}
public function testRunsEveryCommandWithinThePoolSize(): void {
$this->assertSame(6, Multithread::pool(array_map([$this, 'command'], range(1, 6)), 2, 10000));
$this->assertCount(12, file($this->log, FILE_IGNORE_NEW_LINES), 'every command started and finished');
$this->assertSame(2, $this->maxConcurrent());
}
public function testTakesCommandsFromAGeneratorOnlyAsSlotsFree(): void {
$rTaken = 0;
$rCommands = (function () use (&$rTaken) {
foreach (range(1, 3) as $rID) {
$rTaken++;
yield $this->command($rID);
}
})();
$this->assertSame(3, Multithread::pool($rCommands, 1, 10000));
$this->assertSame(3, $rTaken);
$this->assertSame(1, $this->maxConcurrent());
}
public function testANonPositiveSizeStillRunsOneAtATime(): void {
Multithread::pool([$this->command(1), $this->command(2)], 0, 10000);
$this->assertSame(1, $this->maxConcurrent());
}
}