11import {
2- ISQLite ,
32 ConcurrentLockType ,
4- QuickSQLiteConnection ,
53 ContextLockID ,
4+ ISQLite ,
65 LockContext ,
76 LockOptions ,
8- TransactionContext ,
9- UpdateCallback ,
10- SQLBatchTuple ,
117 OpenOptions ,
12- QueryResult
8+ QueryResult ,
9+ QuickSQLiteConnection ,
10+ SQLBatchTuple ,
11+ TransactionContext ,
12+ UpdateCallback
1313} from './types' ;
1414
15- import { enhanceQueryResult } from './utils' ;
1615import { DBListenerManagerInternal } from './DBListenerManager' ;
1716import { LockHooks } from './lock-hooks' ;
17+ import { enhanceQueryResult } from './utils' ;
1818
1919type LockCallbackRecord = {
2020 callback : ( context : LockContext ) => Promise < any > ;
@@ -39,11 +39,16 @@ const getRequestId = () => {
3939const LockCallbacks : Record < ContextLockID , LockCallbackRecord > = { } ;
4040let proxy : ISQLite ;
4141
42+ /**
43+ * Creates a unique identifier for all a database's lock requests
44+ */
45+ const lockKey = ( dbName : string , id : ContextLockID ) => `${ dbName } :${ id } ` ;
46+
4247/**
4348 * Closes the context in JS and C++
4449 */
4550function closeContextLock ( dbName : string , id : ContextLockID ) {
46- delete LockCallbacks [ id ] ;
51+ delete LockCallbacks [ lockKey ( dbName , id ) ] ;
4752
4853 // This is configured by the setupOpen function
4954 proxy . releaseLock ( dbName , id ) ;
@@ -59,7 +64,11 @@ global.onLockContextIsAvailable = async (dbName: string, lockId: ContextLockID)
5964 // Don't hold C++ bridge side up waiting to complete
6065 setImmediate ( async ( ) => {
6166 try {
62- const record = LockCallbacks [ lockId ] ;
67+ const key = lockKey ( dbName , lockId ) ;
68+ const record = LockCallbacks [ key ] ;
69+ // clear record after fetching, the hash should only contain pending requests
70+ delete LockCallbacks [ key ] ;
71+
6372 if ( record ?. timeout ) {
6473 clearTimeout ( record . timeout ) ;
6574 }
@@ -116,12 +125,12 @@ export function setupOpen(QuickSQLite: ISQLite) {
116125 // Wrap the callback in a promise that will resolve to the callback result
117126 return new Promise < T > ( ( resolve , reject ) => {
118127 // Add callback to the queue for timing
119- const record = ( LockCallbacks [ id ] = {
128+ const key = lockKey ( dbName , id ) ;
129+ const record = ( LockCallbacks [ key ] = {
120130 callback : async ( context : LockContext ) => {
121131 try {
122132 await hooks ?. lockAcquired ?.( ) ;
123133 const res = await callback ( context ) ;
124-
125134 closeContextLock ( dbName , id ) ;
126135 resolve ( res ) ;
127136 } catch ( ex ) {
@@ -134,18 +143,19 @@ export function setupOpen(QuickSQLite: ISQLite) {
134143 } as LockCallbackRecord ) ;
135144
136145 try {
146+ // throws if lock could not be requested
137147 QuickSQLite . requestLock ( dbName , id , type ) ;
138148 const timeout = options ?. timeoutMs ;
139149 if ( timeout ) {
140150 record . timeout = setTimeout ( ( ) => {
141151 // The callback won't be executed
142- delete LockCallbacks [ id ] ;
152+ delete LockCallbacks [ key ] ;
143153 reject ( new Error ( `Lock request timed out after ${ timeout } ms` ) ) ;
144154 } , timeout ) ;
145155 }
146156 } catch ( ex ) {
147157 // Remove callback from the queue
148- delete LockCallbacks [ id ] ;
158+ delete LockCallbacks [ key ] ;
149159 reject ( ex ) ;
150160 }
151161 } ) ;
@@ -224,7 +234,26 @@ export function setupOpen(QuickSQLite: ISQLite) {
224234
225235 // Return the concurrent connection object
226236 return {
227- close : ( ) => QuickSQLite . close ( dbName ) ,
237+ close : ( ) => {
238+ QuickSQLite . close ( dbName ) ;
239+ // Reject any pending lock requests
240+ Object . entries ( LockCallbacks ) . forEach ( ( [ key , record ] ) => {
241+ const recordDBName = key . split ( ':' ) [ 0 ] ;
242+ if ( dbName !== recordDBName ) {
243+ return ;
244+ }
245+ // A bit of a hack, let the callbacks run with an execute method that will fail
246+ record
247+ . callback ( {
248+ execute : async ( ) => {
249+ throw new Error ( 'Connection is closed' ) ;
250+ }
251+ } )
252+ . catch ( ( ) => { } ) ;
253+
254+ delete LockCallbacks [ key ] ;
255+ } ) ;
256+ } ,
228257 refreshSchema : ( ) => QuickSQLite . refreshSchema ( dbName ) ,
229258 execute : ( sql : string , args ?: any [ ] ) => writeLock ( ( context ) => context . execute ( sql , args ) ) ,
230259 readLock,
0 commit comments