Skip to content

Commit aef839e

Browse files
authored
Merge pull request #109 from async-interop/issue-106
Add warning about locally closed resources being undefined behavior
2 parents 11c04e9 + 9916529 commit aef839e

File tree

2 files changed

+75
-35
lines changed

2 files changed

+75
-35
lines changed

src/Loop.php

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,14 @@ public static function stop()
129129
/**
130130
* Defer the execution of a callback.
131131
*
132-
* @param callable(string $watcherId, mixed $data) $callback The callback to defer.
132+
* The deferred callable MUST be executed in the next tick of the event loop and before any other type of watcher.
133+
* Order of enabling MUST be preserved when executing the callbacks.
134+
*
135+
* @param callable(string $watcherId, mixed $data) $callback The callback to defer. The `$watcherId` will be
136+
* invalidated before the callback call.
133137
* @param mixed $data Arbitrary data given to the callback function as the `$data` parameter.
134138
*
135-
* @return string An identifier that can be used to cancel, enable or disable the watcher.
139+
* @return string An unique identifier that can be used to cancel, enable or disable the watcher.
136140
*/
137141
public static function defer(callable $callback, $data = null)
138142
{
@@ -143,13 +147,15 @@ public static function defer(callable $callback, $data = null)
143147
/**
144148
* Delay the execution of a callback.
145149
*
146-
* The delay is a minimum and approximate, accuracy is not guaranteed.
150+
* The delay is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be determined by which
151+
* timers expire first, but timers with the same expiration time MAY be executed in any order.
147152
*
148-
* @param int $time The amount of time, in milliseconds, to delay the execution for.
149-
* @param callable(string $watcherId, mixed $data) $callback The callback to delay.
153+
* @param int $delay The amount of time, in milliseconds, to delay the execution for.
154+
* @param callable(string $watcherId, mixed $data) $callback The callback to delay. The `$watcherId` will be
155+
* invalidated before the callback call.
150156
* @param mixed $data Arbitrary data given to the callback function as the `$data` parameter.
151157
*
152-
* @return string An identifier that can be used to cancel, enable or disable the watcher.
158+
* @return string An unique identifier that can be used to cancel, enable or disable the watcher.
153159
*/
154160
public static function delay($time, callable $callback, $data = null)
155161
{
@@ -160,14 +166,15 @@ public static function delay($time, callable $callback, $data = null)
160166
/**
161167
* Repeatedly execute a callback.
162168
*
163-
* The interval between executions is a minimum and approximate, accuracy is not guaranteed.
169+
* The interval between executions is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be
170+
* determined by which timers expire first, but timers with the same expiration time MAY be executed in any order.
164171
* The first execution is scheduled after the first interval period.
165172
*
166173
* @param int $interval The time interval, in milliseconds, to wait between executions.
167174
* @param callable(string $watcherId, mixed $data) $callback The callback to repeat.
168175
* @param mixed $data Arbitrary data given to the callback function as the `$data` parameter.
169176
*
170-
* @return string An identifier that can be used to cancel, enable or disable the watcher.
177+
* @return string An unique identifier that can be used to cancel, enable or disable the watcher.
171178
*/
172179
public static function repeat($interval, callable $callback, $data = null)
173180
{
@@ -178,11 +185,18 @@ public static function repeat($interval, callable $callback, $data = null)
178185
/**
179186
* Execute a callback when a stream resource becomes readable or is closed for reading.
180187
*
188+
* Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
189+
* watcher when closing the resource locally. Drivers MAY choose to notify the user if there are watchers on invalid
190+
* resources, but are not required to, due to the high performance impact. Watchers on closed resources are
191+
* therefore undefined behavior.
192+
*
193+
* Multiple watchers on the same stream MAY be executed in any order.
194+
*
181195
* @param resource $stream The stream to monitor.
182196
* @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute.
183197
* @param mixed $data Arbitrary data given to the callback function as the `$data` parameter.
184198
*
185-
* @return string An identifier that can be used to cancel, enable or disable the watcher.
199+
* @return string An unique identifier that can be used to cancel, enable or disable the watcher.
186200
*/
187201
public static function onReadable($stream, callable $callback, $data = null)
188202
{
@@ -193,11 +207,18 @@ public static function onReadable($stream, callable $callback, $data = null)
193207
/**
194208
* Execute a callback when a stream resource becomes writable or is closed for writing.
195209
*
210+
* Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
211+
* watcher when closing the resource locally. Drivers MAY choose to notify the user if there are watchers on invalid
212+
* resources, but are not required to, due to the high performance impact. Watchers on closed resources are
213+
* therefore undefined behavior.
214+
*
215+
* Multiple watchers on the same stream MAY be executed in any order.
216+
*
196217
* @param resource $stream The stream to monitor.
197218
* @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute.
198219
* @param mixed $data Arbitrary data given to the callback function as the `$data` parameter.
199220
*
200-
* @return string An identifier that can be used to cancel, enable or disable the watcher.
221+
* @return string An unique identifier that can be used to cancel, enable or disable the watcher.
201222
*/
202223
public static function onWritable($stream, callable $callback, $data = null)
203224
{
@@ -208,16 +229,19 @@ public static function onWritable($stream, callable $callback, $data = null)
208229
/**
209230
* Execute a callback when a signal is received.
210231
*
211-
* WARNING: Installing a handler on the same signal on different scopes of event loop execution is
212-
* undefined behavior and may break things arbitrarily.
232+
* Warning: Installing the same signal on different instances of this interface is deemed undefined behavior.
233+
* Implementations MAY try to detect this, if possible, but are not required to. This is due to technical
234+
* limitations of the signals being registered globally per process.
235+
*
236+
* Multiple watchers on the same signal MAY be executed in any order.
213237
*
214238
* @param int $signo The signal number to monitor.
215239
* @param callable(string $watcherId, int $signo, mixed $data) $callback The callback to execute.
216-
* @param mixed $data Arbitrary data given to the callback function as the `$data` parameter.
240+
* @param mixed $data Arbitrary data given to the callback function as the $data parameter.
217241
*
218-
* @return string An identifier that can be used to cancel, enable or disable the watcher.
242+
* @return string An unique identifier that can be used to cancel, enable or disable the watcher.
219243
*
220-
* @throws UnsupportedFeatureException Thrown if signal handling is not supported.
244+
* @throws UnsupportedFeatureException If signal handling is not supported.
221245
*/
222246
public static function onSignal($signo, callable $callback, $data = null)
223247
{
@@ -228,11 +252,15 @@ public static function onSignal($signo, callable $callback, $data = null)
228252
/**
229253
* Enable a watcher.
230254
*
255+
* Watchers (enabling or new watchers) MUST immediately be marked as enabled, but only be activated (i.e. callbacks
256+
* can be called) right before the next tick. Callbacks of watchers MUST not be called in the tick they were
257+
* enabled.
258+
*
231259
* @param string $watcherId The watcher identifier.
232260
*
233261
* @return void
234262
*
235-
* @throws InvalidWatcherException Thrown if the watcher identifier is invalid.
263+
* @throws InvalidWatcherException If the watcher identifier is invalid.
236264
*/
237265
public static function enable($watcherId)
238266
{
@@ -243,6 +271,9 @@ public static function enable($watcherId)
243271
/**
244272
* Disable a watcher.
245273
*
274+
* Disabling a watcher MUST NOT invalidate the watcher. Calling this function MUST NOT fail, even if passed an
275+
* invalid watcher.
276+
*
246277
* @param string $watcherId The watcher identifier.
247278
*
248279
* @return void
@@ -256,8 +287,8 @@ public static function disable($watcherId)
256287
/**
257288
* Cancel a watcher.
258289
*
259-
* This will detatch the event loop from all resources that are associated to the watcher. After this
260-
* operation the watcher is permanently invalid.
290+
* This will detatch the event loop from all resources that are associated to the watcher. After this operation the
291+
* watcher is permanently invalid. Calling this function MUST NOT fail, even if passed an invalid watcher.
261292
*
262293
* @param string $watcherId The watcher identifier.
263294
*
@@ -272,8 +303,8 @@ public static function cancel($watcherId)
272303
/**
273304
* Reference a watcher.
274305
*
275-
* This will keep the event loop alive whilst the watcher is still being monitored. Watchers have this state
276-
* by default.
306+
* This will keep the event loop alive whilst the watcher is still being monitored. Watchers have this state by
307+
* default.
277308
*
278309
* @param string $watcherId The watcher identifier.
279310
*
@@ -290,8 +321,8 @@ public static function reference($watcherId)
290321
/**
291322
* Unreference a watcher.
292323
*
293-
* The event loop should exit the run method when only unreferenced watchers are still being monitored.
294-
* Watchers are all referenced by default.
324+
* The event loop should exit the run method when only unreferenced watchers are still being monitored. Watchers
325+
* are all referenced by default.
295326
*
296327
* @param string $watcherId The watcher identifier.
297328
*
@@ -309,8 +340,7 @@ public static function unreference($watcherId)
309340
* Stores information in the loop bound registry.
310341
*
311342
* This can be used to store loop bound information. Stored information is package private. Packages MUST NOT
312-
* retrieve the stored state of other packages. Packages MUST use the following prefix to keys:
313-
* `vendor.package.`
343+
* retrieve the stored state of other packages. Packages MUST use the following prefix for keys: `vendor.package.`
314344
*
315345
* @param string $key The namespaced storage key.
316346
* @param mixed $value The value to be stored.
@@ -326,12 +356,12 @@ public static function setState($key, $value)
326356
/**
327357
* Gets information stored bound to the loop.
328358
*
329-
* Stored information is package private. Packages MUST NOT retrieve the stored state of other packages.
330-
* Packages MUST use the following prefix to keys: `vendor.package.`
359+
* Stored information is package private. Packages MUST NOT retrieve the stored state of other packages. Packages
360+
* MUST use the following prefix for keys: `vendor.package.`
331361
*
332362
* @param string $key The namespaced storage key.
333363
*
334-
* @return mixed previously stored value or null if it doesn't exist
364+
* @return mixed The previously stored value or `null` if it doesn't exist.
335365
*/
336366
public static function getState($key)
337367
{

src/Loop/Driver.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ abstract public function delay($delay, callable $callback, $data = null);
6666
* Repeatedly execute a callback.
6767
*
6868
* The interval between executions is a minimum and approximate, accuracy is not guaranteed. Order of calls MUST be
69-
* determined by which timers expire first, but timers with the same expiration time may be executed in any order.
69+
* determined by which timers expire first, but timers with the same expiration time MAY be executed in any order.
7070
* The first execution is scheduled after the first interval period.
7171
*
7272
* @param int $interval The time interval, in milliseconds, to wait between executions.
@@ -80,7 +80,12 @@ abstract public function repeat($interval, callable $callback, $data = null);
8080
/**
8181
* Execute a callback when a stream resource becomes readable or is closed for reading.
8282
*
83-
* Multiple watchers on the same stream may be executed in any order.
83+
* Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
84+
* watcher when closing the resource locally. Drivers MAY choose to notify the user if there are watchers on invalid
85+
* resources, but are not required to, due to the high performance impact. Watchers on closed resources are
86+
* therefore undefined behavior.
87+
*
88+
* Multiple watchers on the same stream MAY be executed in any order.
8489
*
8590
* @param resource $stream The stream to monitor.
8691
* @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute.
@@ -93,7 +98,12 @@ abstract public function onReadable($stream, callable $callback, $data = null);
9398
/**
9499
* Execute a callback when a stream resource becomes writable or is closed for writing.
95100
*
96-
* Multiple watchers on the same stream may be executed in any order.
101+
* Warning: Closing resources locally, e.g. with `fclose`, might not invoke the callback. Be sure to `cancel` the
102+
* watcher when closing the resource locally. Drivers MAY choose to notify the user if there are watchers on invalid
103+
* resources, but are not required to, due to the high performance impact. Watchers on closed resources are
104+
* therefore undefined behavior.
105+
*
106+
* Multiple watchers on the same stream MAY be executed in any order.
97107
*
98108
* @param resource $stream The stream to monitor.
99109
* @param callable(string $watcherId, resource $stream, mixed $data) $callback The callback to execute.
@@ -106,12 +116,12 @@ abstract public function onWritable($stream, callable $callback, $data = null);
106116
/**
107117
* Execute a callback when a signal is received.
108118
*
109-
* Multiple watchers on the same signal may be executed in any order.
110-
*
111-
* NOTE: Installing the same signal on different instances of this interface is deemed undefined behavior.
112-
* Implementations may try to detect this, if possible, but are not required to. This is due to technical
119+
* Warning: Installing the same signal on different instances of this interface is deemed undefined behavior.
120+
* Implementations MAY try to detect this, if possible, but are not required to. This is due to technical
113121
* limitations of the signals being registered globally per process.
114122
*
123+
* Multiple watchers on the same signal MAY be executed in any order.
124+
*
115125
* @param int $signo The signal number to monitor.
116126
* @param callable(string $watcherId, int $signo, mixed $data) $callback The callback to execute.
117127
* @param mixed $data Arbitrary data given to the callback function as the $data parameter.
@@ -267,7 +277,7 @@ abstract public function info();
267277
*
268278
* Example: the `uv_loop` resource for `libuv` or the `EvLoop` object for `libev` or `null` for a native driver.
269279
*
270-
* NOTE: This function is *not* exposed in the `Loop` class. Users shall access it directly on the respective loop
280+
* Note: This function is *not* exposed in the `Loop` class. Users shall access it directly on the respective loop
271281
* instance.
272282
*
273283
* @return null|object|resource The loop handle the event loop operates on. `null` if there is none.

0 commit comments

Comments
 (0)