@@ -22,6 +22,7 @@ static sqlite3_mem_methods default_alloc_methods = {0};
22
22
typedef struct connection
23
23
{
24
24
sqlite3 * db ;
25
+ ErlNifMutex * mutex ;
25
26
} connection_t ;
26
27
27
28
typedef struct statement
@@ -219,6 +220,12 @@ exqlite_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
219
220
return make_error_tuple (env , "database_open_failed" );
220
221
}
221
222
223
+ conn -> mutex = enif_mutex_create ("exqlite:connection" );
224
+ if (conn -> mutex == NULL ) {
225
+ enif_release_resource (conn );
226
+ return make_error_tuple (env , "failed_to_create_mutex" );
227
+ }
228
+
222
229
sqlite3_busy_timeout (conn -> db , 2000 );
223
230
224
231
result = enif_make_resource (env , conn );
@@ -256,17 +263,24 @@ exqlite_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
256
263
}
257
264
}
258
265
266
+ // close connection in critical section to avoid race-condition
267
+ // cases. Cases such as query timeout and connection pooling
268
+ // attempting to close the connection
269
+ enif_mutex_lock (conn -> mutex );
270
+
259
271
// note: _v2 may not fully close the connection, hence why we check if
260
272
// any transaction is open above, to make sure other connections aren't blocked.
261
273
// v1 is guaranteed to close or error, but will return error if any
262
274
// unfinalized statements, which we likely have, as we rely on the destructors
263
275
// to later run to clean those up
264
276
rc = sqlite3_close_v2 (conn -> db );
265
277
if (rc != SQLITE_OK ) {
278
+ enif_mutex_unlock (conn -> mutex );
266
279
return make_sqlite3_error_tuple (env , rc , conn -> db );
267
280
}
268
281
269
282
conn -> db = NULL ;
283
+ enif_mutex_unlock (conn -> mutex );
270
284
271
285
return make_atom (env , "ok" );
272
286
}
@@ -357,11 +371,21 @@ exqlite_prepare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
357
371
if (!statement ) {
358
372
return make_error_tuple (env , "out_of_memory" );
359
373
}
374
+ statement -> statement = NULL ;
360
375
361
376
enif_keep_resource (conn );
362
377
statement -> conn = conn ;
363
378
379
+ // ensure connection is not getting closed by parallel thread
380
+ enif_mutex_lock (conn -> mutex );
381
+ if (conn -> db == NULL ) {
382
+ enif_mutex_unlock (conn -> mutex );
383
+ enif_release_resource (statement );
384
+ return make_error_tuple (env , "connection closed" );
385
+ }
364
386
rc = sqlite3_prepare_v3 (conn -> db , (char * )bin .data , bin .size , 0 , & statement -> statement , NULL );
387
+ enif_mutex_unlock (conn -> mutex );
388
+
365
389
if (rc != SQLITE_OK ) {
366
390
enif_release_resource (statement );
367
391
return make_sqlite3_error_tuple (env , rc , conn -> db );
@@ -857,6 +881,7 @@ connection_type_destructor(ErlNifEnv* env, void* arg)
857
881
if (conn -> db ) {
858
882
sqlite3_close_v2 (conn -> db );
859
883
conn -> db = NULL ;
884
+ enif_mutex_destroy (conn -> mutex );
860
885
}
861
886
}
862
887
0 commit comments