6
6
use Amp \File \FilesystemException ;
7
7
use Amp \File \Internal ;
8
8
use Amp \Future ;
9
+ use Amp \Parallel \Worker \ContextWorkerPool ;
10
+ use Amp \Parallel \Worker \DelegatingWorkerPool ;
11
+ use Amp \Parallel \Worker \LimitedWorkerPool ;
9
12
use Amp \Parallel \Worker \TaskFailureThrowable ;
10
13
use Amp \Parallel \Worker \Worker ;
11
14
use Amp \Parallel \Worker \WorkerException ;
12
15
use Amp \Parallel \Worker \WorkerPool ;
13
16
use function Amp \async ;
14
- use function Amp \Parallel \Worker \workerPool ;
15
17
16
18
final class ParallelFilesystemDriver implements FilesystemDriver
17
19
{
18
20
public const DEFAULT_WORKER_LIMIT = 8 ;
19
21
20
- private WorkerPool $ pool ;
22
+ private readonly WorkerPool $ pool ;
21
23
22
- /** @var int Maximum number of workers to use for open files. */
23
- private int $ workerLimit ;
24
+ /** @var positive- int Maximum number of workers to use for open files. */
25
+ private readonly int $ workerLimit ;
24
26
25
- /** @var \SplObjectStorage <Worker, int> Worker storage. */
26
- private \SplObjectStorage $ workerStorage ;
27
+ /** @var \WeakMap <Worker, int> */
28
+ private \WeakMap $ workerStorage ;
27
29
28
- /** @var Future Pending worker request */
29
- private Future $ pendingWorker ;
30
+ /** @var Future<Worker>|null Pending worker request */
31
+ private ? Future $ pendingWorker = null ;
30
32
31
33
/**
32
- * @param int $workerLimit Maximum number of workers to use from the pool for open files.
34
+ * @param WorkerPool|null $pool Custom worker pool to use for file workers. If null, a new pool is created.
35
+ * @param int|null $workerLimit [Deprecated] Maximum number of workers to use from the pool for open files. Instead
36
+ * of using this parameter, provide a pool with a limited number using an instance of {@see LimitedWorkerPool}
37
+ * such as {@see ContextWorkerPool}.
33
38
*/
34
- public function __construct (?WorkerPool $ pool = null , int $ workerLimit = self ::DEFAULT_WORKER_LIMIT )
35
- {
36
- $ this ->pool = $ pool ?? workerPool ();
39
+ public function __construct (?WorkerPool $ pool = null , ?int $ workerLimit = null )
40
+ {
41
+ /** @var \WeakMap<Worker, int> For Psalm. */
42
+ $ this ->workerStorage = new \WeakMap ();
43
+
44
+ if ($ workerLimit !== null ) {
45
+ \trigger_error (
46
+ 'The $workerLimit parameter is deprecated and will be removed in the next major version. ' .
47
+ ' To limit the number of workers used from the given pool, use an instance of ' .
48
+ LimitedWorkerPool::class . ' instead, such as ' . ContextWorkerPool::class . ' or ' .
49
+ DelegatingWorkerPool::class,
50
+ \E_USER_DEPRECATED ,
51
+ );
52
+ }
53
+
54
+ $ workerLimit ??= $ pool instanceof LimitedWorkerPool ? $ pool ->getWorkerLimit () : self ::DEFAULT_WORKER_LIMIT ;
55
+ if ($ workerLimit <= 0 ) {
56
+ throw new \ValueError ("Worker limit must be a positive integer " );
57
+ }
58
+
59
+ $ this ->pool = $ pool ?? new ContextWorkerPool ($ workerLimit );
37
60
$ this ->workerLimit = $ workerLimit ;
38
- $ this ->workerStorage = new \SplObjectStorage ();
39
- $ this ->pendingWorker = Future::complete ();
40
61
}
41
62
42
63
public function openFile (string $ path , string $ mode ): ParallelFile
@@ -45,12 +66,12 @@ public function openFile(string $path, string $mode): ParallelFile
45
66
46
67
$ workerStorage = $ this ->workerStorage ;
47
68
$ worker = new Internal \FileWorker ($ worker , static function (Worker $ worker ) use ($ workerStorage ): void {
48
- if (!$ workerStorage-> contains ( $ worker )) {
69
+ if (!isset ( $ workerStorage[ $ worker] )) {
49
70
return ;
50
71
}
51
72
52
73
if (($ workerStorage [$ worker ] -= 1 ) === 0 || !$ worker ->isRunning ()) {
53
- $ workerStorage-> detach ( $ worker );
74
+ unset( $ workerStorage[ $ worker] );
54
75
}
55
76
});
56
77
@@ -67,26 +88,19 @@ public function openFile(string $path, string $mode): ParallelFile
67
88
68
89
private function selectWorker (): Worker
69
90
{
70
- $ this ->pendingWorker ->await (); // Wait for any currently pending request for a worker.
91
+ $ this ->pendingWorker ? ->await(); // Wait for any currently pending request for a worker.
71
92
72
93
if ($ this ->workerStorage ->count () < $ this ->workerLimit ) {
73
94
$ this ->pendingWorker = async ($ this ->pool ->getWorker (...));
74
95
$ worker = $ this ->pendingWorker ->await ();
75
96
76
- if ($ this ->workerStorage ->contains ($ worker )) {
77
- // amphp/parallel v1.x may return an already used worker from the pool.
78
- $ this ->workerStorage [$ worker ] += 1 ;
79
- } else {
80
- // amphp/parallel v2.x should always return an unused worker.
81
- $ this ->workerStorage ->attach ($ worker , 1 );
82
- }
97
+ $ this ->workerStorage [$ worker ] = 1 ;
83
98
84
99
return $ worker ;
85
100
}
86
101
87
102
$ max = \PHP_INT_MAX ;
88
- foreach ($ this ->workerStorage as $ storedWorker ) {
89
- $ count = $ this ->workerStorage [$ storedWorker ];
103
+ foreach ($ this ->workerStorage as $ storedWorker => $ count ) {
90
104
if ($ count <= $ max ) {
91
105
$ worker = $ storedWorker ;
92
106
$ max = $ count ;
@@ -96,7 +110,7 @@ private function selectWorker(): Worker
96
110
\assert (isset ($ worker ) && $ worker instanceof Worker);
97
111
98
112
if (!$ worker ->isRunning ()) {
99
- $ this ->workerStorage -> detach ( $ worker );
113
+ unset( $ this ->workerStorage [ $ worker] );
100
114
return $ this ->selectWorker ();
101
115
}
102
116
@@ -172,10 +186,12 @@ public function getLinkStatus(string $path): ?array
172
186
173
187
public function touch (string $ path , ?int $ modificationTime , ?int $ accessTime ): void
174
188
{
175
- $ this ->runFileTask (new Internal \FileTask (
176
- "touch " ,
177
- [$ path , $ modificationTime , $ accessTime ]
178
- ));
189
+ $ this ->runFileTask (
190
+ new Internal \FileTask (
191
+ "touch " ,
192
+ [$ path , $ modificationTime , $ accessTime ]
193
+ )
194
+ );
179
195
}
180
196
181
197
public function read (string $ path ): string
0 commit comments