Skip to content
This repository was archived by the owner on Oct 1, 2023. It is now read-only.

Commit 35a7464

Browse files
fredemmottfacebook-github-bot
authored andcommitted
Add CloseableHandle::closeWhenDisposed() (#159)
Summary: If not used and `close()` is not called, handles will be closed at some undefined later time, e.g. end of request or when garabage collected. No current implementation is refcount-based. Pull Request resolved: #159 Reviewed By: DavidSnider Differential Revision: D22418834 Pulled By: fredemmott fbshipit-source-id: 0bcee26d7d3357cafefd796944cf0172448361b6
1 parent f131390 commit 35a7464

File tree

7 files changed

+67
-3
lines changed

7 files changed

+67
-3
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
"hhvm/hsl": "^4.15"
1818
},
1919
"provide": {
20-
"hhvm/hsl-io": "0.2.0"
20+
"hhvm/hsl-io": "0.2.1"
2121
}
2222
}

src/io/CloseableHandle.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,11 @@
1919
interface CloseableHandle extends Handle {
2020
/** Close the handle */
2121
public function close(): void;
22+
23+
/** Close the handle when the returned disposable is disposed.
24+
*
25+
* Usage: `using $handle->closeWhenDisposed();`
26+
*/
27+
<<__ReturnDisposable>>
28+
public function closeWhenDisposed(): \IDisposable;
2229
}

src/io/MemoryHandle.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace HH\Lib\IO;
1212

1313
use namespace HH\Lib\{Math, OS, Str};
14-
use namespace HH\Lib\_Private\_OS;
14+
use namespace HH\Lib\_Private\{_IO, _OS};
1515

1616
enum MemoryHandleWriteMode: int {
1717
OVERWRITE = 0;
@@ -42,6 +42,11 @@ public function close(): void {
4242
$this->offset = -1;
4343
}
4444

45+
<<__ReturnDisposable>>
46+
public function closeWhenDisposed(): \IDisposable {
47+
return new _IO\CloseWhenDisposed($this);
48+
}
49+
4550
public async function readAsync(
4651
?int $max_bytes = null,
4752
?int $_timeout_nanos = null,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?hh
2+
/*
3+
* Copyright (c) 2004-present, Facebook, Inc.
4+
* All rights reserved.
5+
*
6+
* This source code is licensed under the MIT license found in the
7+
* LICENSE file in the root directory of this source tree.
8+
*
9+
*/
10+
11+
namespace HH\Lib\_Private\_IO;
12+
13+
use namespace HH\Lib\IO;
14+
15+
final class CloseWhenDisposed implements \IDisposable {
16+
public function __construct(
17+
private IO\CloseableHandle $handle,
18+
) {
19+
}
20+
21+
public function __dispose(): void {
22+
$this->handle->close();
23+
}
24+
}

src/io/_Private/FileDescriptorHandle.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace HH\Lib\_Private\_IO;
1212

1313
use namespace HH\Lib\{IO, OS, Str};
14-
use namespace HH\Lib\_Private\_OS;
14+
use namespace HH\Lib\_Private\{_IO, _OS};
1515

1616
abstract class FileDescriptorHandle implements IO\CloseableHandle, IO\FDHandle {
1717
protected bool $isAwaitable = true;
@@ -52,4 +52,9 @@ final public function getFileDescriptor(): OS\FileDescriptor {
5252
final public function close(): void {
5353
OS\close($this->impl);
5454
}
55+
56+
<<__ReturnDisposable>>
57+
final public function closeWhenDisposed(): \IDisposable {
58+
return new _IO\CloseWhenDisposed($this);
59+
}
5560
}

tests/io/MemoryHandleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ final class MemoryHandleTest extends HackTest {
3030
expect(await $h->readAllAsync())->toEqual('derp');
3131
}
3232

33+
public function testCloseWhenDisposed(): void {
34+
$h = new IO\MemoryHandle('foobar');
35+
using ($h->closeWhenDisposed()) {
36+
expect($h->read(3))->toEqual('foo');
37+
}
38+
$ex = expect(() ==> $h->read(3))->toThrow(OS\ErrnoException::class);
39+
expect($ex->getErrno())->toEqual(OS\Errno::EBADF);
40+
}
41+
3342
public async function testReadAtInvalidOffset(): Awaitable<void> {
3443
$h = new IO\MemoryHandle('herpderp');
3544
$h->seek(99999);

tests/io/PipeTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ final class PipeTest extends HackTest {
8181
}
8282
}
8383

84+
public async function testCloseWhenDisposed(): Awaitable<void> {
85+
list($r, $w) = IO\pipe();
86+
using ($w->closeWhenDisposed()) {
87+
$w->write("Hello, world\n");
88+
}
89+
expect(await $r->readAsync())->toEqual("Hello, world\n");
90+
// - does not block forever
91+
// - does not fail, just succeeds with no data
92+
expect(await $r->readAsync())->toEqual('');
93+
94+
$ex = expect(() ==> $w->write('foo'))->toThrow(OS\ErrnoException::class);
95+
expect($ex->getErrno())->toEqual(OS\Errno::EBADF);
96+
}
97+
8498
public async function testReadFromClosedPipe(): Awaitable<void> {
8599
// Intent is to:
86100
// - make sure we throw the expected errno

0 commit comments

Comments
 (0)