Initial Commit

This commit is contained in:
David Stone
2024-11-30 18:24:12 -07:00
commit e8f7955c1c
5432 changed files with 1397750 additions and 0 deletions

View File

@ -0,0 +1,146 @@
<?php
namespace WP_Ultimo\Dependencies\Amp\Sync\ConcurrentIterator;
use WP_Ultimo\Dependencies\Amp\CancelledException;
use WP_Ultimo\Dependencies\Amp\Iterator;
use WP_Ultimo\Dependencies\Amp\Producer;
use WP_Ultimo\Dependencies\Amp\Promise;
use WP_Ultimo\Dependencies\Amp\Sync\Barrier;
use WP_Ultimo\Dependencies\Amp\Sync\Lock;
use WP_Ultimo\Dependencies\Amp\Sync\Semaphore;
use function WP_Ultimo\Dependencies\Amp\asyncCall;
use function WP_Ultimo\Dependencies\Amp\call;
use function WP_Ultimo\Dependencies\Amp\coroutine;
/**
* Concurrently act on iterator values using {@code $processor}.
*
* @param Iterator $iterator Values to process.
* @param Semaphore $semaphore Semaphore limiting the concurrency, e.g. {@code LocalSemaphore}
* @param callable $processor Processing callable, which is run as coroutine. It should not throw any errors,
* otherwise the entire operation is aborted.
*
* @return Iterator Result values.
*/
function transform(Iterator $iterator, Semaphore $semaphore, callable $processor) : Iterator
{
return new Producer(static function (callable $emit) use($iterator, $semaphore, $processor) {
// one dummy item, because we can't start the barrier with a count of zero
$barrier = new Barrier(1);
/** @var \Throwable|null $error */
$error = null;
$locks = [];
$gc = \false;
$processor = coroutine($processor);
$processor = static function (Lock $lock, $currentElement) use($processor, $emit, $barrier, &$locks, &$error, &$gc) {
$done = \false;
try {
(yield $processor($currentElement, $emit));
$done = \true;
} catch (\Throwable $e) {
$error = $error ?? $e;
$done = \true;
} finally {
if (!$done) {
$gc = \true;
}
unset($locks[$lock->getId()]);
$lock->release();
$barrier->arrive();
}
};
while ((yield $iterator->advance())) {
if ($error) {
break;
}
/** @var Lock $lock */
$lock = (yield $semaphore->acquire());
if ($gc || isset($locks[$lock->getId()])) {
// Throwing here causes a segfault on PHP 7.3
return;
// throw new CancelledException; // producer and locks have been GCed
}
$locks[$lock->getId()] = \true;
$barrier->register();
asyncCall($processor, $lock, $iterator->getCurrent());
}
$barrier->arrive();
// remove dummy item
(yield $barrier->await());
if ($error) {
throw $error;
}
});
}
/**
* Concurrently map all iterator values using {@code $processor}.
*
* The order of the items in the resulting iterator is not guaranteed in any way.
*
* @param Iterator $iterator Values to map.
* @param Semaphore $semaphore Semaphore limiting the concurrency, e.g. {@code LocalSemaphore}
* @param callable $processor Processing callable, which is run as coroutine. It should not throw any errors,
* otherwise the entire operation is aborted.
*
* @return Iterator Mapped values.
*/
function map(Iterator $iterator, Semaphore $semaphore, callable $processor) : Iterator
{
$processor = coroutine($processor);
return transform($iterator, $semaphore, static function ($value, callable $emit) use($processor) {
$value = (yield $processor($value));
(yield $emit($value));
});
}
/**
* Concurrently filter all iterator values using {@code $filter}.
*
* The order of the items in the resulting iterator is not guaranteed in any way.
*
* @param Iterator $iterator Values to map.
* @param Semaphore $semaphore Semaphore limiting the concurrency, e.g. {@code LocalSemaphore}
* @param callable $filter Processing callable, which is run as coroutine. It should not throw any errors,
* otherwise the entire operation is aborted. Must resolve to a boolean, true to keep values in the resulting
* iterator.
*
* @return Iterator Values, where {@code $filter} resolved to {@code true}.
*/
function filter(Iterator $iterator, Semaphore $semaphore, callable $filter) : Iterator
{
$filter = coroutine($filter);
return transform($iterator, $semaphore, static function ($value, callable $emit) use($filter) {
$keep = (yield $filter($value));
if (!\is_bool($keep)) {
throw new \TypeError(__NAMESPACE__ . '\\filter\'s callable must resolve to a boolean value, got ' . \gettype($keep));
}
if ($keep) {
(yield $emit($value));
}
});
}
/**
* Concurrently invoke a callback on all iterator values using {@code $processor}.
*
* @param Iterator $iterator Values to act on.
* @param Semaphore $semaphore Semaphore limiting the concurrency, e.g. {@code LocalSemaphore}
* @param callable $processor Processing callable, which is run as coroutine. It should not throw any errors,
* otherwise the entire operation is aborted.
*
* @return Promise
*/
function each(Iterator $iterator, Semaphore $semaphore, callable $processor) : Promise
{
$processor = coroutine($processor);
$iterator = transform($iterator, $semaphore, static function ($value, callable $emit) use($processor) {
(yield $processor($value));
(yield $emit(null));
});
// Use Amp\Iterator\discard in the future
return call(static function () use($iterator) {
$count = 0;
while ((yield $iterator->advance())) {
$count++;
}
return $count;
});
}