@@ -36,8 +36,10 @@ export class Aoc {
3636 #instance;
3737 /** @type {WebAssembly.Memory } */
3838 #memory;
39- /** @type {Worker[] } */
39+ /** @type {Worker[]|undefined } */
4040 #workers;
41+ /** @type {number|undefined } */
42+ #workerCount;
4143
4244 /**
4345 * @param {WebAssembly.Module } module
@@ -101,38 +103,33 @@ export class Aoc {
101103
102104 /**
103105 * @param {WebAssembly.Module } module
104- * @param {WebAssembly.Instance } [instance ]
106+ * @param {{instance?: WebAssembly.Instance, workerCount?: number} } [options ]
105107 */
106- constructor ( module , instance ) {
108+ constructor ( module , options ) {
109+ options ??= { } ;
107110 const imports = WebAssembly . Module . imports ( module ) ;
108111 if ( imports . length === 0 ) {
109112 this . #multithreaded = false ;
110113 this . #module = module ;
111- this . #instance = instance ?? new WebAssembly . Instance ( module ) ;
112- this . #memory = this . #exports. memory ;
114+ this . #instance = options . instance ;
115+ if ( this . #instance) this . #memory = this . #exports. memory ;
116+ if ( options . workerCount !== undefined ) throw new Error ( "workerCount can only be provided for multithreaded modules" ) ;
113117 } else if ( imports . length === 1 && imports [ 0 ] . module === "env" && imports [ 0 ] . name === "memory" && imports [ 0 ] . kind === "memory" ) {
114118 this . #multithreaded = true ;
115119 this . #module = module ;
116- if ( instance ) throw new Error ( "Instance cannot be provided for multithreaded modules" ) ;
117- this . newInstance ( ) ;
120+ this . #workers = [ ] ;
121+ this . #workerCount = options . workerCount ?? navigator . hardwareConcurrency ;
122+ if ( options . instance ) throw new Error ( "instance cannot be provided for multithreaded modules" ) ;
118123 } else {
119124 throw new Error ( "Unsupported module" ) ;
120125 }
121126 }
122127
123- /** @param {number } [numWorkers] */
124- newInstance ( numWorkers ) {
125- if ( this . #multithreaded) {
126- if ( this . #workers?. length > 0 ) {
127- // Stop existing workers
128- for ( const worker of this . #workers) {
129- worker . terminate ( ) ;
130- }
131- numWorkers ??= this . #workers. length ;
132- this . #workers = [ ] ;
133- }
134- numWorkers ??= navigator . hardwareConcurrency ;
128+ newInstance ( ) {
129+ this . stopInstance ( ) ;
135130
131+ if ( this . #multithreaded) {
132+ console . debug ( "creating multithreaded instance" ) ;
136133 this . #memory = new WebAssembly . Memory ( { initial : 96 , maximum : 2048 , shared : true } ) ;
137134 this . #instance = new WebAssembly . Instance ( this . #module, { env : { memory : this . #memory} } ) ;
138135
@@ -151,21 +148,45 @@ export class Aoc {
151148 //
152149 // Allocate all the stacks at once to avoid memory growing as workers start, which seems to cause problems.
153150 const stacks = [ ] ;
154- for ( let i = 0 ; i < numWorkers ; i ++ ) {
151+ for ( let i = 0 ; i < this . #workerCount ; i ++ ) {
155152 stacks . push ( this . #exports. allocate_stack ( stackSize + tlsSize , align ) ) ;
156153 }
157154
158155 this . #workers = [ ] ;
159- for ( let i = 0 ; i < numWorkers ; i ++ ) {
156+ for ( let i = 0 ; i < this . #workerCount ; i ++ ) {
160157 const worker = new Worker ( "./worker.mjs" , { type : "module" } ) ;
161158 worker . postMessage ( [ "thread" , this . #module, this . #memory, stacks [ i ] + stackSize ] ) ;
162159 this . #workers. push ( worker ) ;
163160 }
164161 } else {
162+ console . debug ( "creating single-threaded instance" ) ;
165163 this . #instance = new WebAssembly . Instance ( this . #module) ;
164+ this . #memory = this . #exports. memory ;
165+ }
166+ }
167+
168+ ensureInstance ( ) {
169+ if ( this . #instance === undefined ) {
170+ this . newInstance ( ) ;
166171 }
167172 }
168173
174+ stopInstance ( ) {
175+ if ( this . #multithreaded && this . #workers !== undefined ) {
176+ for ( const worker of this . #workers) {
177+ try {
178+ worker . terminate ( ) ;
179+ } catch ( e ) {
180+ console . warn ( e ) ;
181+ }
182+ }
183+ this . #workers = [ ] ;
184+ }
185+
186+ this . #instance = undefined ;
187+ this . #memory = undefined ;
188+ }
189+
169190 /**
170191 * @param {number } year
171192 * @param {number } day
@@ -176,6 +197,8 @@ export class Aoc {
176197 * @return {{success: true, part1: string, part2: string} | {success: false, error: string, stack?: string, panic_location?: string} }
177198 */
178199 run ( year , day , input , isExample = false , part1 = true , part2 = true ) {
200+ this . ensureInstance ( ) ;
201+
179202 let success ;
180203 try {
181204 this . #write( input ) ;
@@ -191,7 +214,7 @@ export class Aoc {
191214 console . warn ( e2 ) ;
192215 }
193216
194- this . newInstance ( )
217+ this . stopInstance ( ) ;
195218
196219 if ( panic_payload . length > 0 ) {
197220 return {
0 commit comments