Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed the `spawnWith()` function for interaction with the `ScopeProvider` and `SpawnStrategy` interface

### Changed
- **Breaking Change: Function Renaming** - Major API reorganization for better consistency:
- `awaitAllFailFirst()` → `awaitAllOrFail()`
- `awaitAllWithErrors()` → `awaitAll()`
- `awaitAnyOfFailFirst()` → `awaitAnyOfOrFail()`
- `awaitAnyOfWithErrors()` → `awaitAnyOf()`
- **Breaking Change: `awaitAll()` Return Format** - New `awaitAll()` (formerly `awaitAllWithErrors()`) now returns `[results, exceptions]` tuple:
- First element `[0]` contains array of successful results
- Second element `[1]` contains array of exceptions from failed coroutines
- **Migration**: Update from `$results = awaitAll($coroutines)` to `[$results, $exceptions] = awaitAll($coroutines)`
- **LibUV requirement increased to ≥ 1.44.0** - Requires libuv version 1.44.0 or later to ensure proper UV_RUN_ONCE behavior and prevent busy loop issues that could cause high CPU usage
- **Async Iterator API**:
- Proper handling of `REWIND`/`NEXT` states in a concurrent environment.
The iterator code now stops iteration in
coroutines if the iterator is in the process of changing its position.
- Added functionality for proper handling of exceptions from `Zend iterators` (`\Iterator` and `generators`).
An exception that occurs in the iterator can now be handled by the iterators owner.
An exception that occurs in the iterator can now be handled by the iterator's owner.


## [0.2.0] - 2025-07-01
Expand Down
10 changes: 5 additions & 5 deletions async.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ PHP_FUNCTION(Async_await)
zend_async_waker_destroy(coroutine);
}

PHP_FUNCTION(Async_awaitAny)
PHP_FUNCTION(Async_awaitAnyOrFail)
{
zval * futures;
zend_object * cancellation = NULL;
Expand Down Expand Up @@ -397,7 +397,7 @@ PHP_FUNCTION(Async_awaitFirstSuccess)
RETURN_ARR(return_array);
}

PHP_FUNCTION(Async_awaitAll)
PHP_FUNCTION(Async_awaitAllOrFail)
{
zval * futures;
zend_object * cancellation = NULL;
Expand Down Expand Up @@ -437,7 +437,7 @@ PHP_FUNCTION(Async_awaitAll)
RETURN_ARR(results);
}

PHP_FUNCTION(Async_awaitAllWithErrors)
PHP_FUNCTION(Async_awaitAll)
{
zval * futures;
zend_object * cancellation = NULL;
Expand Down Expand Up @@ -488,7 +488,7 @@ PHP_FUNCTION(Async_awaitAllWithErrors)
RETURN_ARR(return_array);
}

PHP_FUNCTION(Async_awaitAnyOf)
PHP_FUNCTION(Async_awaitAnyOfOrFail)
{
zval * futures;
zend_object * cancellation = NULL;
Expand Down Expand Up @@ -532,7 +532,7 @@ PHP_FUNCTION(Async_awaitAnyOf)
RETURN_ARR(results);
}

PHP_FUNCTION(Async_awaitAnyOfWithErrors)
PHP_FUNCTION(Async_awaitAnyOf)
{
zval * futures;
zend_object * cancellation = NULL;
Expand Down
10 changes: 5 additions & 5 deletions async.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ function protect(\Closure $closure): mixed {}

function await(Awaitable $awaitable, ?Awaitable $cancellation = null): mixed {}

function awaitAny(iterable $triggers, ?Awaitable $cancellation = null): mixed {}
function awaitAnyOrFail(iterable $triggers, ?Awaitable $cancellation = null): mixed {}

function awaitFirstSuccess(iterable $triggers, ?Awaitable $cancellation = null): mixed {}

function awaitAll(iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder = true): array {}
function awaitAllOrFail(iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder = true): array {}

function awaitAllWithErrors(iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder = true, bool $fillNull = false): array {}
function awaitAll(iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder = true, bool $fillNull = false): array {}

function awaitAnyOf(int $count, iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder = true): array {}
function awaitAnyOfOrFail(int $count, iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder = true): array {}

function awaitAnyOfWithErrors(int $count, iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder= true, bool $fillNull = false): array {}
function awaitAnyOf(int $count, iterable $triggers, ?Awaitable $cancellation = null, bool $preserveKeyOrder = true, bool $fillNull = false): array {}

function delay(int $ms): void {}

Expand Down
24 changes: 12 additions & 12 deletions async_arginfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,34 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_await, 0, 1, IS_MIXED, 0)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, cancellation, Async\\Awaitable, 1, "null")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAny, 0, 1, IS_MIXED, 0)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAnyOrFail, 0, 1, IS_MIXED, 0)
ZEND_ARG_OBJ_TYPE_MASK(0, triggers, Traversable, MAY_BE_ARRAY, NULL)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, cancellation, Async\\Awaitable, 1, "null")
ZEND_END_ARG_INFO()

#define arginfo_Async_awaitFirstSuccess arginfo_Async_awaitAny
#define arginfo_Async_awaitFirstSuccess arginfo_Async_awaitAnyOrFail

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAll, 0, 1, IS_ARRAY, 0)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAllOrFail, 0, 1, IS_ARRAY, 0)
ZEND_ARG_OBJ_TYPE_MASK(0, triggers, Traversable, MAY_BE_ARRAY, NULL)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, cancellation, Async\\Awaitable, 1, "null")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, preserveKeyOrder, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAllWithErrors, 0, 1, IS_ARRAY, 0)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAll, 0, 1, IS_ARRAY, 0)
ZEND_ARG_OBJ_TYPE_MASK(0, triggers, Traversable, MAY_BE_ARRAY, NULL)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, cancellation, Async\\Awaitable, 1, "null")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, preserveKeyOrder, _IS_BOOL, 0, "true")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, fillNull, _IS_BOOL, 0, "false")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAnyOf, 0, 2, IS_ARRAY, 0)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAnyOfOrFail, 0, 2, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
ZEND_ARG_OBJ_TYPE_MASK(0, triggers, Traversable, MAY_BE_ARRAY, NULL)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, cancellation, Async\\Awaitable, 1, "null")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, preserveKeyOrder, _IS_BOOL, 0, "true")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAnyOfWithErrors, 0, 2, IS_ARRAY, 0)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Async_awaitAnyOf, 0, 2, IS_ARRAY, 0)
ZEND_ARG_TYPE_INFO(0, count, IS_LONG, 0)
ZEND_ARG_OBJ_TYPE_MASK(0, triggers, Traversable, MAY_BE_ARRAY, NULL)
ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, cancellation, Async\\Awaitable, 1, "null")
Expand Down Expand Up @@ -92,12 +92,12 @@ ZEND_FUNCTION(Async_spawnWith);
ZEND_FUNCTION(Async_suspend);
ZEND_FUNCTION(Async_protect);
ZEND_FUNCTION(Async_await);
ZEND_FUNCTION(Async_awaitAny);
ZEND_FUNCTION(Async_awaitAnyOrFail);
ZEND_FUNCTION(Async_awaitFirstSuccess);
ZEND_FUNCTION(Async_awaitAllOrFail);
ZEND_FUNCTION(Async_awaitAll);
ZEND_FUNCTION(Async_awaitAllWithErrors);
ZEND_FUNCTION(Async_awaitAnyOfOrFail);
ZEND_FUNCTION(Async_awaitAnyOf);
ZEND_FUNCTION(Async_awaitAnyOfWithErrors);
ZEND_FUNCTION(Async_delay);
ZEND_FUNCTION(Async_timeout);
ZEND_FUNCTION(Async_currentContext);
Expand All @@ -114,12 +114,12 @@ static const zend_function_entry ext_functions[] = {
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "suspend"), zif_Async_suspend, arginfo_Async_suspend, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "protect"), zif_Async_protect, arginfo_Async_protect, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "await"), zif_Async_await, arginfo_Async_await, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAny"), zif_Async_awaitAny, arginfo_Async_awaitAny, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAnyOrFail"), zif_Async_awaitAnyOrFail, arginfo_Async_awaitAnyOrFail, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitFirstSuccess"), zif_Async_awaitFirstSuccess, arginfo_Async_awaitFirstSuccess, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAllOrFail"), zif_Async_awaitAllOrFail, arginfo_Async_awaitAllOrFail, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAll"), zif_Async_awaitAll, arginfo_Async_awaitAll, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAllWithErrors"), zif_Async_awaitAllWithErrors, arginfo_Async_awaitAllWithErrors, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAnyOfOrFail"), zif_Async_awaitAnyOfOrFail, arginfo_Async_awaitAnyOfOrFail, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAnyOf"), zif_Async_awaitAnyOf, arginfo_Async_awaitAnyOf, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "awaitAnyOfWithErrors"), zif_Async_awaitAnyOfWithErrors, arginfo_Async_awaitAnyOfWithErrors, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "delay"), zif_Async_delay, arginfo_Async_delay, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "timeout"), zif_Async_timeout, arginfo_Async_timeout, 0, NULL, NULL)
ZEND_RAW_FENTRY(ZEND_NS_NAME("Async", "currentContext"), zif_Async_currentContext, arginfo_Async_currentContext, 0, NULL, NULL)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAny() - basic usage with multiple coroutines
awaitAnyOrFail() - basic usage with multiple coroutines
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAny;
use function Async\awaitAnyOrFail;
use function Async\delay;
use function Async\suspend;

Expand All @@ -24,7 +24,7 @@ $coroutines = [
}),
];

$result = awaitAny($coroutines);
$result = awaitAnyOrFail($coroutines);
echo "first completed: $result\n";

echo "end\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
--TEST--
awaitAny() - empty iterable
awaitAnyOrFail() - empty iterable
--FILE--
<?php

use function Async\awaitAny;
use function Async\awaitAnyOrFail;

echo "start\n";

$result = awaitAny([]);
$result = awaitAnyOrFail([]);

$resultCheck = $result === null ? "OK" : "FALSE: " . var_export($result, true);
echo "Result is null: $resultCheck\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAny() - coroutine throws exception
awaitAnyOrFail() - coroutine throws exception
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAny;
use function Async\awaitAnyOrFail;
use function Async\suspend;

echo "start\n";
Expand All @@ -20,7 +20,7 @@ $coroutines = [
];

try {
$result = awaitAny($coroutines);
$result = awaitAnyOrFail($coroutines);
echo "result: $result\n";
} catch (RuntimeException $e) {
echo "caught exception: " . $e->getMessage() . "\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAll() - basic usage with multiple coroutines
awaitAllOrFail() - basic usage with multiple coroutines
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAll;
use function Async\awaitAllOrFail;

echo "start\n";

Expand All @@ -20,7 +20,7 @@ $coroutines = [
}),
];

$results = awaitAll($coroutines);
$results = awaitAllOrFail($coroutines);
var_dump($results);

echo "end\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAll() - one coroutine throws exception
awaitAllOrFail() - one coroutine throws exception
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAll;
use function Async\awaitAllOrFail;

echo "start\n";

Expand All @@ -21,7 +21,7 @@ $coroutines = [
];

try {
$results = awaitAll($coroutines);
$results = awaitAllOrFail($coroutines);
var_dump($results);
} catch (RuntimeException $e) {
echo "caught exception: " . $e->getMessage() . "\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAllWithErrors() - basic usage with mixed success and error
awaitAll() - basic usage with mixed success and error
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAllWithErrors;
use function Async\awaitAll;

echo "start\n";

Expand All @@ -20,7 +20,7 @@ $coroutines = [
}),
];

$result = awaitAllWithErrors($coroutines);
$result = awaitAll($coroutines);
var_dump($result);

echo "end\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAllWithErrors() - all coroutines succeed
awaitAll() - all coroutines succeed
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAllWithErrors;
use function Async\awaitAll;

echo "start\n";

Expand All @@ -20,7 +20,7 @@ $coroutines = [
}),
];

$result = awaitAllWithErrors($coroutines);
$result = awaitAll($coroutines);
var_dump($result);

echo "end\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAnyOf() - basic usage with count parameter
awaitAnyOfOrFail() - basic usage with count parameter
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAnyOf;
use function Async\awaitAnyOfOrFail;

echo "start\n";

Expand All @@ -23,7 +23,7 @@ $coroutines = [
}),
];

$results = awaitAnyOf(2, $coroutines);
$results = awaitAnyOfOrFail(2, $coroutines);

$countOfResults = count($results) >= 2 ? "OK" : "FALSE: ".count($results);
echo "Count of results: $countOfResults\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAnyOf() - count is zero
awaitAnyOfOrFail() - count is zero
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAnyOf;
use function Async\awaitAnyOfOrFail;

echo "start\n";

Expand All @@ -17,7 +17,7 @@ $coroutines = [
}),
];

$results = awaitAnyOf(0, $coroutines);
$results = awaitAnyOfOrFail(0, $coroutines);
var_dump($results);

echo "end\n";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
--TEST--
awaitAnyOfWithErrors() - basic usage with mixed success and error
awaitAnyOf() - basic usage with mixed success and error
--FILE--
<?php

use function Async\spawn;
use function Async\awaitAnyOfWithErrors;
use function Async\awaitAnyOf;
use function Async\suspend;

echo "start\n";
Expand All @@ -25,7 +25,7 @@ $coroutines = [
}),
];

$result = awaitAnyOfWithErrors(2, $coroutines);
$result = awaitAnyOf(2, $coroutines);

$countOfResults = count($result[0]) >= 2 ? "OK" : "FALSE: ".count($result[0]);
$countOfErrors = count($result[1]) == 1 ? "OK" : "FALSE: ".count($result[1]);
Expand Down
Loading