@@ -58,6 +58,12 @@ export class IDBBatchAtomicVFS extends VFS.Base {
5858 #taskTimestamp = performance . now ( ) ;
5959 #pendingAsync = new Set ( ) ;
6060
61+ // Asyncify can grow WebAssembly memory during an asynchronous call.
62+ // If this happens, then any array buffer arguments will be detached.
63+ // The workaround is when finding a detached buffer, set this handler
64+ // function to process the new buffer outside handlerAsync().
65+ #growthHandler = null ;
66+
6167 constructor ( idbDatabaseName = 'wa-sqlite' , options = DEFAULT_OPTIONS ) {
6268 super ( ) ;
6369 this . name = idbDatabaseName ;
@@ -84,7 +90,7 @@ export class IDBBatchAtomicVFS extends VFS.Base {
8490 * @returns {number }
8591 */
8692 xOpen ( name , fileId , flags , pOutFlags ) {
87- return this . handleAsync ( async ( ) => {
93+ const result = this . handleAsync ( async ( ) => {
8894 if ( name === null ) name = `null_${ fileId } ` ;
8995 log ( `xOpen ${ name } 0x${ fileId . toString ( 16 ) } 0x${ flags . toString ( 16 ) } ` ) ;
9096
@@ -118,13 +124,25 @@ export class IDBBatchAtomicVFS extends VFS.Base {
118124 }
119125 }
120126 } ) ;
127+
128+ // @ts -ignore
129+ if ( pOutFlags . buffer . detached ) {
130+ pOutFlags = new DataView ( new ArrayBuffer ( 4 ) ) ;
131+ this . #growthHandler = ( pOutFlagsNew ) => {
132+ pOutFlagsNew . setInt32 ( 0 , pOutFlags . getInt32 ( 0 , true ) , true ) ;
133+ } ;
134+ }
121135 pOutFlags . setInt32 ( 0 , flags & VFS . SQLITE_OPEN_READONLY , true ) ;
122136 return VFS . SQLITE_OK ;
123137 } catch ( e ) {
124138 console . error ( e ) ;
125139 return VFS . SQLITE_CANTOPEN ;
126140 }
127141 } ) ;
142+
143+ this . #growthHandler?. ( pOutFlags ) ;
144+ this . #growthHandler = null ;
145+ return result ;
128146 }
129147
130148 /**
@@ -160,7 +178,8 @@ export class IDBBatchAtomicVFS extends VFS.Base {
160178 * @returns {number }
161179 */
162180 xRead ( fileId , pData , iOffset ) {
163- return this . handleAsync ( async ( ) => {
181+ const byteLength = pData . byteLength ;
182+ const result = this . handleAsync ( async ( ) => {
164183 const file = this . #mapIdToFile. get ( fileId ) ;
165184 log ( `xRead ${ file . path } ${ pData . byteLength } ${ iOffset } ` ) ;
166185
@@ -170,6 +189,15 @@ export class IDBBatchAtomicVFS extends VFS.Base {
170189 // one case - rollback after journal spill - where reads cross
171190 // write boundaries so we have to allow for that.
172191 const result = await this . #idb. run ( 'readonly' , async ( { blocks} ) => {
192+ // @ts -ignore
193+ if ( pData . buffer . detached ) {
194+ // WebAssembly memory has grown, invalidating our buffer. Use
195+ // a temporary buffer and copy after this asynchronous call
196+ // completes.
197+ pData = new Uint8Array ( byteLength ) ;
198+ this . #growthHandler = ( pDataNew ) => pDataNew . set ( pData ) ;
199+ }
200+
173201 let pDataOffset = 0 ;
174202 while ( pDataOffset < pData . byteLength ) {
175203 // Fetch the IndexedDB block for this file location.
@@ -200,6 +228,10 @@ export class IDBBatchAtomicVFS extends VFS.Base {
200228 return VFS . SQLITE_IOERR ;
201229 }
202230 } ) ;
231+
232+ this . #growthHandler?. ( pData ) ;
233+ this . #growthHandler = null ;
234+ return result ;
203235 }
204236
205237 /**
@@ -221,7 +253,7 @@ export class IDBBatchAtomicVFS extends VFS.Base {
221253 }
222254 await new Promise ( resolve => setTimeout ( resolve ) ) ;
223255
224- const result = this . #xWriteHelper( fileId , pData , iOffset ) ;
256+ const result = this . #xWriteHelper( fileId , pData . slice ( ) , iOffset ) ;
225257 this . #taskTimestamp = performance . now ( ) ;
226258 return result ;
227259 } ) ;
@@ -436,14 +468,28 @@ export class IDBBatchAtomicVFS extends VFS.Base {
436468 * @returns {number }
437469 */
438470 xCheckReservedLock ( fileId , pResOut ) {
439- return this . handleAsync ( async ( ) => {
471+ const result = this . handleAsync ( async ( ) => {
440472 const file = this . #mapIdToFile. get ( fileId ) ;
441473 log ( `xCheckReservedLock ${ file . path } ` ) ;
442474
443475 const isReserved = await file . locks . isSomewhereReserved ( ) ;
476+ function setOutput ( pResOut ) {
477+ } ;
478+
479+ // @ts -ignore
480+ if ( pResOut . buffer . detached ) {
481+ pResOut = new DataView ( new ArrayBuffer ( 4 ) ) ;
482+ this . #growthHandler = ( pResOutNew ) => {
483+ pResOutNew . setInt32 ( 0 , pResOut . getInt32 ( 0 , true ) , true ) ;
484+ } ;
485+ }
444486 pResOut . setInt32 ( 0 , isReserved ? 1 : 0 , true ) ;
445487 return VFS . SQLITE_OK ;
446488 } ) ;
489+
490+ this . #growthHandler?. ( pResOut ) ;
491+ this . #growthHandler = null ;
492+ return result ;
447493 }
448494
449495 /**
@@ -611,7 +657,7 @@ export class IDBBatchAtomicVFS extends VFS.Base {
611657 * @returns {number }
612658 */
613659 xAccess ( name , flags , pResOut ) {
614- return this . handleAsync ( async ( ) => {
660+ const result = this . handleAsync ( async ( ) => {
615661 try {
616662 const path = new URL ( name , 'file://localhost/' ) . pathname ;
617663 log ( `xAccess ${ path } ${ flags } ` ) ;
@@ -620,13 +666,25 @@ export class IDBBatchAtomicVFS extends VFS.Base {
620666 const key = await this . #idb. run ( 'readonly' , ( { blocks} ) => {
621667 return blocks . getKey ( this . #bound( { path} , 0 ) ) ;
622668 } ) ;
669+
670+ // @ts -ignore
671+ if ( pResOut . buffer . detached ) {
672+ pResOut = new DataView ( new ArrayBuffer ( 4 ) ) ;
673+ this . #growthHandler = ( pResOutNew ) => {
674+ pResOutNew . setInt32 ( 0 , pResOut . getInt32 ( 0 , true ) , true ) ;
675+ }
676+ }
623677 pResOut . setInt32 ( 0 , key ? 1 : 0 , true ) ;
624678 return VFS . SQLITE_OK ;
625679 } catch ( e ) {
626680 console . error ( e ) ;
627681 return VFS . SQLITE_IOERR ;
628682 }
629683 } ) ;
684+
685+ this . #growthHandler?. ( pResOut ) ;
686+ this . #growthHandler = null ;
687+ return result ;
630688 }
631689
632690 /**
0 commit comments