@@ -11,6 +11,9 @@ import {
1111import { StockfishModelStorage } from './stockfishStorage'
1212
1313const DEFAULT_NNUE_FETCH_TIMEOUT_MS = 30000
14+ const DEFAULT_STOCKFISH_MODULE_INIT_TIMEOUT_MS = 15000
15+ const STOCKFISH_CACHE_LOOKUP_TIMEOUT_MS = 1500
16+ const STOCKFISH_CACHE_WRITE_TIMEOUT_MS = 5000
1417type StockfishInitPhase =
1518 | 'idle'
1619 | 'loading-module'
@@ -1784,6 +1787,39 @@ const fetchWithTimeout = async (
17841787 }
17851788}
17861789
1790+ const withTimeout = async < T > (
1791+ promise : Promise < T > ,
1792+ timeoutMs : number ,
1793+ message : string ,
1794+ ) : Promise < T > => {
1795+ if ( timeoutMs <= 0 ) {
1796+ return promise
1797+ }
1798+
1799+ let timeoutId : ReturnType < typeof setTimeout > | null = null
1800+
1801+ try {
1802+ return await Promise . race ( [
1803+ promise ,
1804+ new Promise < T > ( ( _ , reject ) => {
1805+ timeoutId = setTimeout ( ( ) => reject ( new Error ( message ) ) , timeoutMs )
1806+ } ) ,
1807+ ] )
1808+ } finally {
1809+ if ( timeoutId ) {
1810+ clearTimeout ( timeoutId )
1811+ }
1812+ }
1813+ }
1814+
1815+ const getStockfishModuleInitTimeoutMs = ( ) : number => {
1816+ const raw = process . env . NEXT_PUBLIC_STOCKFISH_MODULE_INIT_TIMEOUT_MS
1817+ const parsed = raw ? parseInt ( raw , 10 ) : NaN
1818+ return Number . isFinite ( parsed ) && parsed > 0
1819+ ? parsed
1820+ : DEFAULT_STOCKFISH_MODULE_INIT_TIMEOUT_MS
1821+ }
1822+
17871823const loadNnueModel = async (
17881824 modelUrl : string ,
17891825 storage : StockfishModelStorage ,
@@ -1792,7 +1828,14 @@ const loadNnueModel = async (
17921828 forceRefresh = false ,
17931829) : Promise < ArrayBuffer > => {
17941830 if ( ! forceRefresh ) {
1795- const cachedModel = await storage . getModel ( modelUrl )
1831+ const cachedModel = await withTimeout (
1832+ storage . getModel ( modelUrl ) ,
1833+ STOCKFISH_CACHE_LOOKUP_TIMEOUT_MS ,
1834+ `Timed out while checking cached Stockfish model: ${ modelUrl } ` ,
1835+ ) . catch ( ( error ) => {
1836+ console . warn ( error )
1837+ return null
1838+ } )
17961839 if ( cachedModel ) {
17971840 return cachedModel
17981841 }
@@ -1807,7 +1850,13 @@ const loadNnueModel = async (
18071850 }
18081851
18091852 const buffer = await response . arrayBuffer ( )
1810- await storage . storeModel ( modelUrl , buffer )
1853+ void withTimeout (
1854+ storage . storeModel ( modelUrl , buffer ) ,
1855+ STOCKFISH_CACHE_WRITE_TIMEOUT_MS ,
1856+ `Timed out while caching Stockfish model: ${ modelUrl } ` ,
1857+ ) . catch ( ( error ) => {
1858+ console . warn ( error )
1859+ } )
18111860 return buffer
18121861}
18131862
@@ -1839,16 +1888,24 @@ const setupStockfish = async (
18391888 process . env . NEXT_PUBLIC_STOCKFISH_NNUE_BASE_URL ??
18401889 'https://raw.githubusercontent.com/CSSLab/maia-platform-frontend/e23a50e/public/stockfish'
18411890 const storage = new StockfishModelStorage ( )
1842- await storage . requestPersistentStorage ( )
18431891 const timeoutMs = getNnueFetchTimeoutMs ( )
1892+ const moduleInitTimeoutMs = getStockfishModuleInitTimeoutMs ( )
18441893 let nnueUrls : [ string , string ] | null = null
18451894
1895+ void storage . requestPersistentStorage ( ) . catch ( ( error ) => {
1896+ console . warn ( 'Failed to request persistent storage for Stockfish:' , error )
1897+ } )
1898+
18461899 const createInstance = async ( ) : Promise < StockfishWeb > => {
18471900 onPhaseChange ?.( 'loading-module' )
1848- return makeModule . default ( {
1849- wasmMemory : sharedWasmMemory ( 2560 ) ,
1850- locateFile : ( name : string ) => `/stockfish/${ name } ` ,
1851- } )
1901+ return withTimeout (
1902+ makeModule . default ( {
1903+ wasmMemory : sharedWasmMemory ( 2560 ) ,
1904+ locateFile : ( name : string ) => `/stockfish/${ name } ` ,
1905+ } ) ,
1906+ moduleInitTimeoutMs ,
1907+ `Stockfish module initialization timed out after ${ moduleInitTimeoutMs } ms` ,
1908+ )
18521909 }
18531910
18541911 const loadWeightsIntoInstance = async (
0 commit comments