{"id":19665938,"url":"https://github.com/amphp/sync","last_synced_at":"2025-07-18T05:06:29.290Z","repository":{"id":27010418,"uuid":"112144510","full_name":"amphp/sync","owner":"amphp","description":"Non-blocking synchronization primitives for PHP based on Amp and Revolt.","archived":false,"fork":false,"pushed_at":"2024-08-04T17:35:39.000Z","size":269,"stargazers_count":174,"open_issues_count":0,"forks_count":11,"subscribers_count":7,"default_branch":"2.x","last_synced_at":"2025-05-22T21:08:29.189Z","etag":null,"topics":["amphp","async","concurrency","mutex","php","revolt","semaphore","synchronization"],"latest_commit_sha":null,"homepage":"https://amphp.org/sync","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amphp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"amphp"}},"created_at":"2017-11-27T03:51:16.000Z","updated_at":"2025-05-19T06:26:51.000Z","dependencies_parsed_at":"2024-11-11T16:37:05.661Z","dependency_job_id":null,"html_url":"https://github.com/amphp/sync","commit_stats":{"total_commits":171,"total_committers":10,"mean_commits":17.1,"dds":0.5087719298245614,"last_synced_commit":"375ef5b54a0d12c38e12728dde05a55e30f2fbec"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/amphp/sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amphp%2Fsync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amphp%2Fsync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amphp%2Fsync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amphp%2Fsync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amphp","download_url":"https://codeload.github.com/amphp/sync/tar.gz/refs/heads/2.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amphp%2Fsync/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265703491,"owners_count":23814015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amphp","async","concurrency","mutex","php","revolt","semaphore","synchronization"],"created_at":"2024-11-11T16:25:32.914Z","updated_at":"2025-07-18T05:06:29.251Z","avatar_url":"https://github.com/amphp.png","language":"PHP","readme":"# amphp/sync\n\nAMPHP is a collection of event-driven libraries for PHP designed with fibers and concurrency in mind.\n`amphp/sync` specifically provides synchronization primitives such as locks and semaphores for asynchronous and concurrent programming.\n\n[![Latest Release](https://img.shields.io/github/release/amphp/sync.svg?style=flat-square)](https://github.com/amphp/sync/releases)\n[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/amphp/sync/blob/master/LICENSE)\n\n## Installation\n\nThis package can be installed as a [Composer](https://getcomposer.org/) dependency.\n\n```bash\ncomposer require amphp/sync\n```\n\n## Usage\n\nThe weak link when managing concurrency is humans; so `amphp/sync` provides abstractions to hide some complexity.\n\n### Mutex\n\n[Mutual exclusion](https://en.wikipedia.org/wiki/Mutual_exclusion) can be achieved using `Amp\\Sync\\synchronized()` and any `Mutex` implementation, or by manually using the `Mutex` instance to acquire a `Lock`.\n\nAs long as the resulting `Lock` object isn't released using `Lock::release()` or by being garbage collected, the holder of the lock can exclusively run some code as long as all other parties running the same code also acquire a lock before doing so.\n\n```php\nfunction writeExclusively(Amp\\Sync\\Mutex $mutex, string $filePath, string $data) {\n    $lock = $mutex-\u003eacquire();\n    \n    try {\n        Amp\\File\\write($filePath, $data);\n    } finally {\n        $lock-\u003erelease();\n    }\n}\n```\n\n```php\nfunction writeExclusively(Amp\\Sync\\Mutex $mutex, string $filePath, string $data) {\n    Amp\\Sync\\synchronized($mutex, fn () =\u003e Amp\\File\\write($filePath, $data));\n}\n```\n\n### Semaphore\n\n[Semaphores](https://en.wikipedia.org/wiki/Semaphore_%28programming%29) are another synchronization primitive in addition to [mutual exclusion](#mutex).\n\nInstead of providing exclusive access to a single party, they provide access to a limited set of N parties at the same time.\nThis makes them great to control concurrency, e.g. limiting an HTTP client to X concurrent requests, so the HTTP server doesn't get overwhelmed.\n\nSimilar to [`Mutex`](#mutex), `Lock` instances can be acquired using `Semaphore::acquire()`.\nPlease refer to the [`Mutex`](#mutex) documentation for additional usage documentation, as they're basically equivalent except for the fact that `Mutex` is always a `Semaphore` with a count of exactly one party.\n\nIn many cases you can use [`amphp/pipeline`](https://github.com/amphp/pipeline) instead of directly using a `Semaphore`.\n\n### Parcel\n\nA Parcel is used to synchronize access to a value across multiple execution contexts, such as multiple coroutines or multiple processes. The example below demonstrates using a `LocalParcel` to share an integer between two coroutines.\n\n```php\nuse Amp\\Future;\nuse Amp\\Sync\\LocalMutex;\nuse Amp\\Sync\\LocalParcel;\nuse function Amp\\async;\nuse function Amp\\delay;\n\n$parcel = new LocalParcel(new LocalMutex(), 42);\n\n$future1 = async(function () use ($parcel): void {\n    echo \"Coroutine 1 started\\n\";\n\n    $result = $parcel-\u003esynchronized(function (int $value): int {\n        delay(1); // Delay for 1s to simulate I/O.\n        return $value * 2;\n    });\n\n    echo \"Value after access in coroutine 1: \", $result, \"\\n\";\n});\n\n$future2 = async(function () use ($parcel): void {\n    echo \"Coroutine 2 started\\n\";\n\n    $result = $parcel-\u003esynchronized(function (int $value): int {\n        delay(1); // Delay again in this coroutine.\n        return $value + 8;\n    });\n\n    echo \"Value after access in coroutine 2: \", $result, \"\\n\";\n});\n\nFuture\\await([$future1, $future2]); // Wait until both coroutines complete.\n```\n\n### Channels\n\nChannels are used to send data between execution contexts, such as multiple coroutines or multiple processes. The example below shares two `Channel` between two coroutines. These channels are connected. Data sent on a channel is received on the paired channel and vice-versa.\n\n```php\nuse Amp\\Future;\nuse function Amp\\async;\nuse function Amp\\delay;\n\n[$left, $right] = createChannelPair();\n\n$future1 = async(function () use ($left): void {\n    echo \"Coroutine 1 started\\n\";\n    delay(1); // Delay to simulate I/O.\n    $left-\u003esend(42);\n    $received = $left-\u003ereceive();\n    echo \"Received \", $received, \" in coroutine 1\\n\";\n});\n\n$future2 = async(function () use ($right): void {\n    echo \"Coroutine 2 started\\n\";\n    $received = $right-\u003ereceive();\n    echo \"Received \", $received, \" in coroutine 2\\n\";\n    delay(1); // Delay to simulate I/O.\n    $right-\u003esend($received * 2);\n});\n\nFuture\\await([$future1, $future2]); // Wait until both coroutines complete.\n```\n\n### Sharing data between processes\n\nTo share data between processes in PHP, the data must be serializable and use external storage or an IPC (inter-process communication) channel.\n\n#### Parcels in external storage\n\n`SharedMemoryParcel` uses shared memory conjunction with `PosixSemaphore` wrapped in `SemaphoreMutex` (though another cross-context mutex implementation may be used, such as `RedisMutex` in [`amphp/redis`](https://github.com/amphp/redis)).\n\n\u003e **Note**\n\u003e `ext-shmop` and `ext-sysvmsg` are required for `SharedMemoryParcel` and `PosixSemaphore` respectively.\n\n[`amphp/redis`](https://github.com/amphp/redis) provides `RedisParcel` for storing shared data in Redis.\n\n#### Channels over pipes\n\nChannels between processes can be created by layering serialization (native PHP serialization, JSON serialization, etc.) on a pipe between those processes.\n\n`StreamChannel` in [`amphp/byte-stream`](https://github.com/amphp/byte-stream) creates a channel from any `ReadableStream` and `WritableStream`. This allows a channel to be created from a variety of stream sources, such as sockets or process pipes.\n\n`ProcessContext` in [`amphp/parallel`](https://github.com/amphp/parallel) implements `Channel` to send data between parent and child processes.\n\nTask `Execution` objects, also in [`amphp/parallel`](https://github.com/amphp/parallel) contain a `Channel` to send data between the task run and the process which submitted the task.\n\n### Concurrency Approaches\n\nGiven you have a list of URLs you want to crawl, let's discuss a few possible approaches. For simplicity, we will assume a `fetch` function already exists, which takes a URL and returns the HTTP status code (which is everything we want to know for these examples).\n\n#### Approach 1: Sequential\n\nSimple loop using non-blocking I/O, but no concurrency while fetching the individual URLs; starts the second request as soon as the first completed.\n\n```php\n$urls = [...];\n\n$results = [];\n\nforeach ($urls as $url) {\n    $results[$url] = fetch($url);\n}\n\nvar_dump($results);\n```\n\n#### Approach 2: Everything Concurrently\n\nAlmost the same loop, but awaiting all operations at once; starts all requests immediately. Might not be feasible with too many URLs.\n\n```php\n$urls = [...];\n\n$results = [];\n\nforeach ($urls as $url) {\n    $results[$url] = Amp\\async(fetch(...), $url);\n}\n\n$results = Amp\\Future\\await($results);\n\nvar_dump($results);\n```\n\n#### Approach 3: Concurrent Chunks\n\nSplitting the jobs into chunks of ten; all requests within a chunk are made concurrently, but each chunk sequentially, so the timing for each chunk depends on the slowest response; starts the eleventh request as soon as the first ten requests completed.\n\n```php\n$urls = [...];\n\n$results = [];\n\nforeach (\\array_chunk($urls, 10) as $chunk) {\n    $futures = [];\n\n    foreach ($chunk as $url) {\n        $futures[$url] = Amp\\async(fetch(...), $url);\n    }\n\n    $results = \\array_merge($results, Amp\\Future\\await($futures));\n}\n\nvar_dump($results);\n```\n\n#### Approach 4: ConcurrentIterator\n\nThe [`amphp/pipeline`](https://github.com/amphp/pipeline) library provides concurrent iterators which can be used to process and consume data concurrently in multiple fibers.\n\n```php\nuse Amp\\Pipeline\\Pipeline;\nuse function Amp\\delay;\n\n$urls = [...];\n\n$results = Pipeline::fromIterable($urls)\n    -\u003econcurrent(10) // Process up to 10 URLs concurrently\n    -\u003eunordered() // Results may arrive out of order\n    -\u003emap(fetch(...)) // Map each URL to fetch(...)\n    -\u003etoArray();\n\nvar_dump($results);\n```\n\nSee the documentation in [`amphp/pipeline`](https://github.com/amphp/pipeline) for more information on using Pipelines for concurrency.\n\n## Versioning\n\n`amphp/sync` follows the [semver](http://semver.org/) semantic versioning specification like all other `amphp` packages.\n\n## Security\n\nIf you discover any security related issues, please use the private security issue reporter instead of using the public issue tracker.\n\n## License\n\nThe MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information.\n","funding_links":["https://github.com/sponsors/amphp"],"categories":["Synchronisation"],"sub_categories":["Tunnel"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famphp%2Fsync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famphp%2Fsync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famphp%2Fsync/lists"}