8
8
9
9
import { Importer , ImporterReturnType , Options , Result , SassException } from 'sass' ;
10
10
import { MessageChannel , Worker } from 'worker_threads' ;
11
+ import { maxWorkers } from '../utils/environment-options' ;
12
+
13
+ /**
14
+ * The maximum number of Workers that will be created to execute render requests.
15
+ */
16
+ const MAX_RENDER_WORKERS = maxWorkers ;
11
17
12
18
/**
13
19
* The callback type for the `dart-sass` asynchronous render function.
@@ -19,6 +25,7 @@ type RenderCallback = (error?: SassException, result?: Result) => void;
19
25
*/
20
26
interface RenderRequest {
21
27
id : number ;
28
+ workerIndex : number ;
22
29
callback : RenderCallback ;
23
30
importers ?: Importer [ ] ;
24
31
}
@@ -39,9 +46,11 @@ interface RenderResponseMessage {
39
46
* the worker which can be up to two times faster than the asynchronous variant.
40
47
*/
41
48
export class SassWorkerImplementation {
42
- private worker ?: Worker ;
49
+ private readonly workers : Worker [ ] = [ ] ;
50
+ private readonly availableWorkers : number [ ] = [ ] ;
43
51
private readonly requests = new Map < number , RenderRequest > ( ) ;
44
52
private idCounter = 1 ;
53
+ private nextWorkerIndex = 0 ;
45
54
46
55
/**
47
56
* Provides information about the Sass implementation.
@@ -74,14 +83,23 @@ export class SassWorkerImplementation {
74
83
throw new Error ( 'Sass custom functions are not supported.' ) ;
75
84
}
76
85
77
- if ( ! this . worker ) {
78
- this . worker = this . createWorker ( ) ;
86
+ let workerIndex = this . availableWorkers . pop ( ) ;
87
+ if ( workerIndex === undefined ) {
88
+ if ( this . workers . length < MAX_RENDER_WORKERS ) {
89
+ workerIndex = this . workers . length ;
90
+ this . workers . push ( this . createWorker ( ) ) ;
91
+ } else {
92
+ workerIndex = this . nextWorkerIndex ++ ;
93
+ if ( this . nextWorkerIndex >= this . workers . length ) {
94
+ this . nextWorkerIndex = 0 ;
95
+ }
96
+ }
79
97
}
80
98
81
- const request = this . createRequest ( callback , importer ) ;
99
+ const request = this . createRequest ( workerIndex , callback , importer ) ;
82
100
this . requests . set ( request . id , request ) ;
83
101
84
- this . worker . postMessage ( {
102
+ this . workers [ workerIndex ] . postMessage ( {
85
103
id : request . id ,
86
104
hasImporter : ! ! importer ,
87
105
options : serializableOptions ,
@@ -96,7 +114,9 @@ export class SassWorkerImplementation {
96
114
* is only needed if early cleanup is needed.
97
115
*/
98
116
close ( ) : void {
99
- this . worker ?. terminate ( ) ;
117
+ for ( const worker of this . workers ) {
118
+ void worker . terminate ( ) ;
119
+ }
100
120
this . requests . clear ( ) ;
101
121
}
102
122
@@ -117,6 +137,7 @@ export class SassWorkerImplementation {
117
137
}
118
138
119
139
this . requests . delete ( response . id ) ;
140
+ this . availableWorkers . push ( request . workerIndex ) ;
120
141
121
142
if ( response . result ) {
122
143
// The results are expected to be Node.js `Buffer` objects but will each be transferred as
@@ -193,11 +214,13 @@ export class SassWorkerImplementation {
193
214
}
194
215
195
216
private createRequest (
217
+ workerIndex : number ,
196
218
callback : RenderCallback ,
197
219
importer : Importer | Importer [ ] | undefined ,
198
220
) : RenderRequest {
199
221
return {
200
222
id : this . idCounter ++ ,
223
+ workerIndex,
201
224
callback,
202
225
importers : ! importer || Array . isArray ( importer ) ? importer : [ importer ] ,
203
226
} ;
0 commit comments