@@ -22,6 +22,7 @@ static sqlite3_mem_methods default_alloc_methods = {0};
2222typedef struct connection
2323{
2424 sqlite3 * db ;
25+ ErlNifMutex * mutex ;
2526} connection_t ;
2627
2728typedef struct statement
@@ -219,6 +220,12 @@ exqlite_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
219220 return make_error_tuple (env , "database_open_failed" );
220221 }
221222
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+
222229 sqlite3_busy_timeout (conn -> db , 2000 );
223230
224231 result = enif_make_resource (env , conn );
@@ -256,17 +263,24 @@ exqlite_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
256263 }
257264 }
258265
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+
259271 // note: _v2 may not fully close the connection, hence why we check if
260272 // any transaction is open above, to make sure other connections aren't blocked.
261273 // v1 is guaranteed to close or error, but will return error if any
262274 // unfinalized statements, which we likely have, as we rely on the destructors
263275 // to later run to clean those up
264276 rc = sqlite3_close_v2 (conn -> db );
265277 if (rc != SQLITE_OK ) {
278+ enif_mutex_unlock (conn -> mutex );
266279 return make_sqlite3_error_tuple (env , rc , conn -> db );
267280 }
268281
269282 conn -> db = NULL ;
283+ enif_mutex_unlock (conn -> mutex );
270284
271285 return make_atom (env , "ok" );
272286}
@@ -357,11 +371,21 @@ exqlite_prepare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
357371 if (!statement ) {
358372 return make_error_tuple (env , "out_of_memory" );
359373 }
374+ statement -> statement = NULL ;
360375
361376 enif_keep_resource (conn );
362377 statement -> conn = conn ;
363378
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+ }
364386 rc = sqlite3_prepare_v3 (conn -> db , (char * )bin .data , bin .size , 0 , & statement -> statement , NULL );
387+ enif_mutex_unlock (conn -> mutex );
388+
365389 if (rc != SQLITE_OK ) {
366390 enif_release_resource (statement );
367391 return make_sqlite3_error_tuple (env , rc , conn -> db );
@@ -857,6 +881,7 @@ connection_type_destructor(ErlNifEnv* env, void* arg)
857881 if (conn -> db ) {
858882 sqlite3_close_v2 (conn -> db );
859883 conn -> db = NULL ;
884+ enif_mutex_destroy (conn -> mutex );
860885 }
861886}
862887
0 commit comments