@@ -57,7 +57,7 @@ struct Work: Sendable {
5757 }
5858}
5959
60- func render( scene: Scene , ctx: JSObject , renderTime : JSObject , concurrency: Int , executor: WebWorkerTaskExecutor ) async {
60+ func render( scene: Scene , ctx: JSObject , renderTimeElement : JSObject , concurrency: Int , executor: ( some TaskExecutor ) ? ) async {
6161
6262 let imageBuffer = UnsafeMutableBufferPointer< Color> . allocate( capacity: scene. width * scene. height)
6363 // Initialize the buffer with black color
@@ -67,12 +67,16 @@ func render(scene: Scene, ctx: JSObject, renderTime: JSObject, concurrency: Int,
6767 let clock = ContinuousClock ( )
6868 let start = clock. now
6969
70+ func updateRenderTime( ) {
71+ let renderSceneDuration = clock. now - start
72+ renderTimeElement. textContent = . string( " Render time: \( renderSceneDuration) " )
73+ }
74+
7075 var checkTimer : JSValue ?
7176 checkTimer = JSObject . global. setInterval!( JSClosure { _ in
7277 print ( " Checking thread work... " )
7378 renderInCanvas ( ctx: ctx, image: imageView)
74- let renderSceneDuration = clock. now - start
75- renderTime. textContent = . string( " Render time: \( renderSceneDuration) " )
79+ updateRenderTime ( )
7680 return . undefined
7781 } , 250 )
7882
@@ -94,30 +98,41 @@ func render(scene: Scene, ctx: JSObject, renderTime: JSObject, concurrency: Int,
9498 checkTimer = nil
9599
96100 renderInCanvas ( ctx: ctx, image: imageView)
101+ updateRenderTime ( )
97102 imageBuffer. deallocate ( )
98103 print ( " All work done " )
99104}
100105
106+ func onClick( ) async throws {
107+ let document = JSObject . global. document
108+
109+ let canvasElement = document. getElementById ( " canvas " ) . object!
110+ let renderTimeElement = document. getElementById ( " render-time " ) . object!
111+
112+ let concurrency = max ( Int ( document. getElementById ( " concurrency " ) . object!. value. string!) ?? 1 , 1 )
113+ let background = document. getElementById ( " background " ) . object!. checked. boolean!
114+ let size = Int ( document. getElementById ( " size " ) . object!. value. string ?? " 800 " ) !
115+
116+ let ctx = canvasElement. getContext!( " 2d " ) . object!
117+
118+ let scene = createDemoScene ( size: size)
119+ let executor = background ? try await WebWorkerTaskExecutor ( numberOfThreads: concurrency) : nil
120+ canvasElement. width = . number( Double ( scene. width) )
121+ canvasElement. height = . number( Double ( scene. height) )
122+
123+ await render ( scene: scene, ctx: ctx, renderTimeElement: renderTimeElement, concurrency: concurrency, executor: executor)
124+ executor? . terminate ( )
125+ print ( " Render done " )
126+ }
127+
101128func main( ) async throws {
102- let canvas = JSObject . global. document. getElementById ( " canvas " ) . object!
103- let renderButton = JSObject . global. document. getElementById ( " render-button " ) . object!
104- let concurrency = JSObject . global. document. getElementById ( " concurrency " ) . object!
105- concurrency. value = JSObject . global. navigator. hardwareConcurrency
106- let scene = createDemoScene ( )
107- canvas. width = . number( Double ( scene. width) )
108- canvas. height = . number( Double ( scene. height) )
109-
110- _ = renderButton. addEventListener!( " click " , JSClosure { _ in
129+ let renderButtonElement = JSObject . global. document. getElementById ( " render-button " ) . object!
130+ let concurrencyElement = JSObject . global. document. getElementById ( " concurrency " ) . object!
131+ concurrencyElement. value = JSObject . global. navigator. hardwareConcurrency
132+
133+ _ = renderButtonElement. addEventListener!( " click " , JSClosure { _ in
111134 Task {
112- let canvas = JSObject . global. document. getElementById ( " canvas " ) . object!
113- let renderTime = JSObject . global. document. getElementById ( " render-time " ) . object!
114- let concurrency = JSObject . global. document. getElementById ( " concurrency " ) . object!
115- let concurrencyValue = max ( Int ( concurrency. value. string!) ?? 1 , 1 )
116- let ctx = canvas. getContext!( " 2d " ) . object!
117- let executor = try await WebWorkerTaskExecutor ( numberOfThreads: concurrencyValue)
118- await render ( scene: scene, ctx: ctx, renderTime: renderTime, concurrency: concurrencyValue, executor: executor)
119- executor. terminate ( )
120- print ( " Render done " )
135+ try await onClick ( )
121136 }
122137 return JSValue . undefined
123138 } )
@@ -126,3 +141,21 @@ func main() async throws {
126141Task {
127142 try await main ( )
128143}
144+
145+
146+ #if canImport(wasi_pthread)
147+ import wasi_pthread
148+ import WASILibc
149+
150+ /// Trick to avoid blocking the main thread. pthread_mutex_lock function is used by
151+ /// the Swift concurrency runtime.
152+ @_cdecl ( " pthread_mutex_lock " )
153+ func pthread_mutex_lock( _ mutex: UnsafeMutablePointer < pthread_mutex_t > ) -> Int32 {
154+ // DO NOT BLOCK MAIN THREAD
155+ var ret : Int32
156+ repeat {
157+ ret = pthread_mutex_trylock ( mutex)
158+ } while ret == EBUSY
159+ return ret
160+ }
161+ #endif
0 commit comments