--- layout: docs title: Event Loop API permalink: /event-loop/api --- This document describes the [`Amp\Loop`](https://github.com/amphp/amp/blob/master/lib/Loop.php) accessor. You might want to also read the documentation contained in the source file, it's extensively documented and doesn't contain much distracting code. ## `run()` The primary way an application interacts with the event loop is to schedule events for execution and then simply let the program run. Once `Loop::run()` is invoked the event loop will run indefinitely until there are no watchable timer events, IO streams or signals remaining to watch. Long-running programs generally execute entirely inside the confines of a single `Loop::run()` call. `Loop::run()` accepts an optional callback as first parameter. Passing such a callback is equivalent to calling `Loop::defer($callback)` and `Loop::run()` afterwards. ## `stop()` The event loop can be stopped at any time while running. When `Loop::stop()` is invoked the event loop will return control to the userland script at the end of the current tick of the event loop. This method may be used to yield control from the event loop even if events or watchable IO streams are still pending. ## `now()` Returns the current 'loop time' in millisecond increments. The value returned by this method does not necessarily correlate to wall-clock time, rather the value is meant to be used in relative comparisons to prior values returned by this method (e.g.: interval calculations, expiration times, etc.). The value returned by this method is only updated once per loop tick. ## Timer Watchers Amp exposes several ways to schedule timer watchers. Let's look at some details for each function. ### `defer()` - Schedules a callback to execute in the next iteration of the event loop - This method guarantees a clean call stack to avoid starvation of other events in the current iteration of the loop. An `defer` callback is *always* executed in the next tick of the event loop. - After an `defer` timer watcher executes it is automatically garbage collected by the event loop so there is no need for applications to manually cancel the associated watcher. - Like all watchers, `defer` timers may be disabled and re-enabled. If you disable this watcher between the time you schedule it and the time that it actually runs the event loop *will not* be able to garbage collect it until it executes. Therefore you must manually cancel an `defer` watcher yourself if it never actually executes to free any associated resources. **Example** ```php This is an advanced low-level API. Most users should use [`amphp/byte-stream`](https://github.com/amphp/byte-stream) instead. Watchers registered via `Loop::onReadable()` trigger their callbacks in the following situations: - When data is available to read on the stream under observation - When the stream is at EOF (for sockets, this means the connection is broken) A common usage pattern for reacting to readable data looks something like this example: ```php You should always read a multiple of the configured chunk size (default: 8192), otherwise your code might not work as expected with loop backends other than `stream_select()`, see [amphp/amp#65](https://github.com/amphp/amp/issues/65) for more information. ### `onWritable()` {:.note} > This is an advanced low-level API. Most users should use [`amphp/byte-stream`](https://github.com/amphp/byte-stream) instead. - Streams are essentially *"always"* writable. The only time they aren't is when their respective write buffers are full. A common usage pattern for reacting to writability involves initializing a writability watcher without enabling it when a client first connects to a server. Once incomplete writes occur we're then able to "unpause" the write watcher using `Loop::enable()` until data is fully sent without having to create and cancel new watcher resources on the same stream multiple times. ## Pausing, Resuming and Canceling Watchers All watchers, regardless of type, can be temporarily disabled and enabled in addition to being cleared via `Loop::cancel()`. This allows for advanced capabilities such as disabling the acceptance of new socket clients in server applications when simultaneity limits are reached. In general, the performance characteristics of watcher reuse via pause/resume are favorable by comparison to repeatedly canceling and re-registering watchers. ### `disable()` A simple disable example: ```php socket = $sock; $readWatcher = Loop::onReadable($sock, function () use ($client) { $this->onReadable($client); }); $writeWatcher = Loop::onWritable($sock, function () use ($client) { $this->doWrite($client); }); Loop::disable($writeWatcher); // <-- let's initialize the watcher as "disabled" $client->readWatcher = $readWatcher; $client->writeWatcher = $writeWatcher; $this->clients[$socketId] = $client; } // ... other class implementation details here ... private function writeToClient($client, $data) { $client->writeBuffer .= $data; $this->doWrite($client); } private function doWrite(ClientStruct $client) { $bytesToWrite = strlen($client->writeBuffer); $bytesWritten = @fwrite($client->socket, $client->writeBuffer); if ($bytesToWrite === $bytesWritten) { Loop::disable($client->writeWatcher); } elseif ($bytesWritten >= 0) { $client->writeBuffer = substr($client->writeBuffer, $bytesWritten); Loop::enable($client->writeWatcher); } elseif ($this->isSocketDead($client->socket)) { $this->unloadClient($client); } } // ... other class implementation details here ... } ``` ### `cancel()` It's important to *always* cancel persistent watchers once you're finished with them or you'll create memory leaks in your application. This functionality works in exactly the same way as the above `enable` / `disable` examples: ```php = 3) { Loop::cancel($watcherId); // <-- cancel myself! } }); ``` It is also always safe to cancel a watcher from multiple places. A double-cancel will simply be ignored. ### An Important Note on Writability Because streams are essentially *"always"* writable you should only enable writability watchers while you have data to send. If you leave these watchers enabled when your application doesn't have anything to write the watcher will trigger endlessly until disabled or canceled. This will max out your CPU. If you're seeing inexplicably high CPU usage in your application it's a good bet you've got a writability watcher that you failed to disable or cancel after you were finished with it. A standard pattern in this area is to initialize writability watchers in a disabled state before subsequently enabling them at a later time as shown here: ```php