Skip to content

Commit 623d855

Browse files
improve commit hooks on close. Improve closing and thread management. improve errors for opening a DB.
1 parent d64fafb commit 623d855

File tree

7 files changed

+61
-38
lines changed

7 files changed

+61
-38
lines changed

cpp/ConnectionPool.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ void ConnectionPool::closeContext(ConnectionLockId contextId) {
169169
}
170170

171171
void ConnectionPool::closeAll() {
172+
// Prevent this pointer from being used if closed
173+
rollbackPayload.dbName = NULL;
174+
sqlite3_commit_hook(writeConnection.connection,
175+
NULL, NULL);
176+
sqlite3_rollback_hook(writeConnection.connection,
177+
NULL, NULL);
178+
sqlite3_update_hook(writeConnection.connection,
179+
NULL, NULL);
172180
writeConnection.close();
173181
for (int i = 0; i < maxReads; i++) {
174182
readConnections[i]->close();

cpp/ConnectionPool.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ class ConnectionPool {
6666
std::vector<ConnectionLockId> writeQueue;
6767

6868
// Cached constant payloads for c style commit/rollback callbacks
69-
const TransactionCallbackPayload commitPayload;
70-
const TransactionCallbackPayload rollbackPayload;
69+
TransactionCallbackPayload commitPayload;
70+
TransactionCallbackPayload rollbackPayload;
7171

7272
void (*onContextCallback)(std::string, ConnectionLockId);
7373
void (*onCommitCallback)(const TransactionCallbackPayload *);

cpp/ConnectionState.cpp

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,17 @@ SQLiteOPResult genericSqliteOpenDb(string const dbName, string const docPath,
1010
ConnectionState::ConnectionState(const std::string dbName,
1111
const std::string docPath, int SQLFlags) {
1212
auto result = genericSqliteOpenDb(dbName, docPath, &connection, SQLFlags);
13-
isClosed = false;
14-
15-
this->clearLock();
16-
threadDone = false;
17-
thread = new std::thread(&ConnectionState::doWork, this);
13+
if (result.type != SQLiteOk) {
14+
throw std::runtime_error("Failed to open SQLite database: " + result.errorMessage);
15+
}
16+
thread = std::thread(&ConnectionState::doWork, this);
17+
this->clearLock();
1818
}
1919

2020
ConnectionState::~ConnectionState() {
21-
// So threads know it's time to shut down
22-
threadDone = true;
23-
24-
// Wake up all the threads, so they can finish and be joined
25-
workQueueConditionVariable.notify_all();
26-
if (thread->joinable()) {
27-
thread->join();
21+
if (!isClosed) {
22+
close();
2823
}
29-
30-
delete thread;
3124
}
3225

3326
void ConnectionState::clearLock() {
@@ -65,10 +58,25 @@ std::future<void> ConnectionState::refreshSchema() {
6558
}
6659

6760
void ConnectionState::close() {
61+
// prevent any new work from being queued
6862
isClosed = true;
63+
64+
// Wait for the work queue to empty
6965
waitFinished();
70-
// So that the thread can stop (if not already)
71-
threadDone = true;
66+
67+
{
68+
// Now signal the thread to stop and notify it
69+
std::lock_guard<std::mutex> g(workQueueMutex);
70+
threadDone = true;
71+
workQueueConditionVariable.notify_all();
72+
}
73+
74+
// Join the worker thread
75+
if (thread.joinable()) {
76+
thread.join();
77+
}
78+
79+
// Safely close the SQLite connection
7280
sqlite3_close_v2(connection);
7381
}
7482

@@ -77,14 +85,12 @@ void ConnectionState::queueWork(std::function<void(sqlite3 *)> task) {
7785
throw std::runtime_error("Connection is not open. Connection has been closed before queueing work.");
7886
}
7987

80-
// Grab the mutex
81-
std::lock_guard<std::mutex> g(workQueueMutex);
82-
83-
// Push the request to the queue
84-
workQueue.push(task);
88+
{
89+
std::lock_guard<std::mutex> g(workQueueMutex);
90+
workQueue.push(task);
91+
}
8592

86-
// Notify one thread that there are requests to process
87-
workQueueConditionVariable.notify_all();
93+
workQueueConditionVariable.notify_all();
8894
}
8995

9096
void ConnectionState::doWork() {
@@ -124,11 +130,8 @@ void ConnectionState::doWork() {
124130

125131
void ConnectionState::waitFinished() {
126132
std::unique_lock<std::mutex> g(workQueueMutex);
127-
if (workQueue.empty()) {
128-
return;
129-
}
130133
workQueueConditionVariable.wait(
131-
g, [&] { return workQueue.empty() && (threadBusy == false); });
134+
g, [&] { return workQueue.empty() && !threadBusy; });
132135
}
133136

134137
SQLiteOPResult genericSqliteOpenDb(string const dbName, string const docPath,

cpp/ConnectionState.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ class ConnectionState {
2525
// Mutex to protect workQueue
2626
std::mutex workQueueMutex;
2727
// Store thread in order to stop it gracefully
28-
std::thread *thread;
28+
std::thread thread;
2929
// This condition variable is used for the threads to wait until there is work
3030
// to do
3131
std::condition_variable_any workQueueConditionVariable;
32-
bool threadBusy;
33-
bool threadDone;
32+
std::atomic<bool> threadBusy{false};
33+
std::atomic<bool> threadDone{false};
3434

3535
public:
36-
bool isClosed;
36+
std::atomic<bool> isClosed{false};
3737

3838
ConnectionState(const std::string dbName, const std::string docPath,
3939
int SQLFlags);

cpp/bindings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ void transactionFinalizerHandler(const TransactionCallbackPayload *payload) {
7979
*/
8080
invoker->invokeAsync([payload] {
8181
try {
82+
// Prevent trying to create a JSI string for a potentially closed DB
83+
if (payload == NULL || payload->dbName == NULL) {
84+
return;
85+
}
86+
8287
auto global = runtime->global();
8388
jsi::Function handlerFunction = global.getPropertyAsFunction(
8489
*runtime, "triggerTransactionFinalizerHook");

cpp/sqliteBridge.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,18 @@ sqliteOpenDb(string const dbName, string const docPath,
5050
};
5151
}
5252

53-
dbMap[dbName] = new ConnectionPool(dbName, docPath, numReadConnections);
54-
dbMap[dbName]->setOnContextAvailable(contextAvailableCallback);
55-
dbMap[dbName]->setTableUpdateHandler(updateTableCallback);
56-
dbMap[dbName]->setTransactionFinalizerHandler(onTransactionFinalizedCallback);
53+
try {
54+
// Open the database
55+
dbMap[dbName] = new ConnectionPool(dbName, docPath, numReadConnections);
56+
dbMap[dbName]->setOnContextAvailable(contextAvailableCallback);
57+
dbMap[dbName]->setTableUpdateHandler(updateTableCallback);
58+
dbMap[dbName]->setTransactionFinalizerHandler(onTransactionFinalizedCallback);
59+
} catch (const std::exception &e) {
60+
return SQLiteOPResult{
61+
.type = SQLiteError,
62+
.errorMessage = e.what(),
63+
};
64+
}
5765

5866
return SQLiteOPResult{
5967
.type = SQLiteOk,

tests/tests/sqlite/rawQueries.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ export function registerBaseTests() {
7171

7272
await db.execute('DROP TABLE IF EXISTS User; ');
7373
await db.execute('CREATE TABLE User ( id INT PRIMARY KEY, name TEXT NOT NULL, age INT, networth REAL) STRICT;');
74-
7574
await db.execute('CREATE TABLE IF NOT EXISTS t1(id INTEGER PRIMARY KEY, a INTEGER, b INTEGER, c TEXT)');
7675
} catch (e) {
7776
console.warn('error on before each', e);

0 commit comments

Comments
 (0)