Unverified Commit e87dfa47 by Mohamed Akram Committed by GitHub

Fix segfaults (#1368)

* Ensure JavaScript exceptions bubble up

* Fix segfault

* Fix mutex segfault

* Fix segfault caused by memory leak

If the database was being closed, and non-exclusive work was scheduled,
it overrode the lock flag such that the state became open=false locked=false
instead of open=false locked=true. This caused queued work to not be
processed, leaking memory, which causes a segfault during napi cleanup.

Make the same changes to other methods for safe measure.
parent c9caae47
...@@ -31,11 +31,10 @@ void Backup::Process() { ...@@ -31,11 +31,10 @@ void Backup::Process() {
} }
while (inited && !locked && !queue.empty()) { while (inited && !locked && !queue.empty()) {
Call* call = queue.front(); std::unique_ptr<Call> call(queue.front());
queue.pop(); queue.pop();
call->callback(call->baton); call->callback(call->baton);
delete call;
} }
} }
...@@ -86,21 +85,17 @@ void Backup::CleanQueue() { ...@@ -86,21 +85,17 @@ void Backup::CleanQueue() {
// Clear out the queue so that this object can get GC'ed. // Clear out the queue so that this object can get GC'ed.
while (!queue.empty()) { while (!queue.empty()) {
Call* call = queue.front(); std::unique_ptr<Call> call(queue.front());
queue.pop(); queue.pop();
Napi::Function cb = call->baton->callback.Value(); std::unique_ptr<Baton> baton(call->baton);
Napi::Function cb = baton->callback.Value();
if (inited && !cb.IsEmpty() && if (inited && !cb.IsEmpty() &&
cb.IsFunction()) { cb.IsFunction()) {
TRY_CATCH_CALL(Value(), cb, 1, argv); TRY_CATCH_CALL(Value(), cb, 1, argv);
called = true; called = true;
} }
// We don't call the actual callback, so we have to make sure that
// the baton gets destroyed.
delete call->baton;
delete call;
} }
// When we couldn't call a callback function, emit an error on the // When we couldn't call a callback function, emit an error on the
...@@ -113,13 +108,12 @@ void Backup::CleanQueue() { ...@@ -113,13 +108,12 @@ void Backup::CleanQueue() {
else while (!queue.empty()) { else while (!queue.empty()) {
// Just delete all items in the queue; we already fired an event when // Just delete all items in the queue; we already fired an event when
// initializing the backup failed. // initializing the backup failed.
Call* call = queue.front(); std::unique_ptr<Call> call(queue.front());
queue.pop(); queue.pop();
// We don't call the actual callback, so we have to make sure that // We don't call the actual callback, so we have to make sure that
// the baton gets destroyed. // the baton gets destroyed.
delete call->baton; delete call->baton;
delete call;
} }
} }
...@@ -220,13 +214,14 @@ void Backup::Work_Initialize(napi_env e, void* data) { ...@@ -220,13 +214,14 @@ void Backup::Work_Initialize(napi_env e, void* data) {
} }
void Backup::Work_AfterInitialize(napi_env e, napi_status status, void* data) { void Backup::Work_AfterInitialize(napi_env e, napi_status status, void* data) {
BACKUP_INIT(InitializeBaton); std::unique_ptr<InitializeBaton> baton(static_cast<InitializeBaton*>(data));
Backup* backup = baton->backup;
Napi::Env env = backup->Env(); Napi::Env env = backup->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
if (backup->status != SQLITE_OK) { if (backup->status != SQLITE_OK) {
Error(baton); Error(baton.get());
backup->FinishAll(); backup->FinishAll();
} }
else { else {
...@@ -282,7 +277,8 @@ void Backup::Work_Step(napi_env e, void* data) { ...@@ -282,7 +277,8 @@ void Backup::Work_Step(napi_env e, void* data) {
} }
void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) { void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) {
BACKUP_INIT(StepBaton); std::unique_ptr<StepBaton> baton(static_cast<StepBaton*>(data));
Backup* backup = baton->backup;
Napi::Env env = backup->Env(); Napi::Env env = backup->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -294,7 +290,7 @@ void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) { ...@@ -294,7 +290,7 @@ void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) {
} }
if (backup->status != SQLITE_OK && backup->status != SQLITE_DONE) { if (backup->status != SQLITE_OK && backup->status != SQLITE_DONE) {
Error(baton); Error(baton.get());
} }
else { else {
// Fire callbacks. // Fire callbacks.
...@@ -329,7 +325,8 @@ void Backup::Work_Finish(napi_env e, void* data) { ...@@ -329,7 +325,8 @@ void Backup::Work_Finish(napi_env e, void* data) {
} }
void Backup::Work_AfterFinish(napi_env e, napi_status status, void* data) { void Backup::Work_AfterFinish(napi_env e, napi_status status, void* data) {
BACKUP_INIT(Baton); std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
Backup* backup = baton->backup;
Napi::Env env = backup->Env(); Napi::Env env = backup->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
......
...@@ -96,7 +96,7 @@ public: ...@@ -96,7 +96,7 @@ public:
static Napi::Object Init(Napi::Env env, Napi::Object exports); static Napi::Object Init(Napi::Env env, Napi::Object exports);
struct Baton { struct Baton {
napi_async_work request; napi_async_work request = NULL;
Backup* backup; Backup* backup;
Napi::FunctionReference callback; Napi::FunctionReference callback;
...@@ -105,6 +105,7 @@ public: ...@@ -105,6 +105,7 @@ public:
callback.Reset(cb_, 1); callback.Reset(cb_, 1);
} }
virtual ~Baton() { virtual ~Baton() {
if (request) napi_delete_async_work(backup->Env(), request);
backup->Unref(); backup->Unref();
callback.Reset(); callback.Reset();
} }
......
...@@ -50,17 +50,14 @@ void Database::Process() { ...@@ -50,17 +50,14 @@ void Database::Process() {
// Call all callbacks with the error object. // Call all callbacks with the error object.
while (!queue.empty()) { while (!queue.empty()) {
Call* call = queue.front(); std::unique_ptr<Call> call(queue.front());
Napi::Function cb = call->baton->callback.Value(); queue.pop();
std::unique_ptr<Baton> baton(call->baton);
Napi::Function cb = baton->callback.Value();
if (!cb.IsUndefined() && cb.IsFunction()) { if (!cb.IsUndefined() && cb.IsFunction()) {
TRY_CATCH_CALL(this->Value(), cb, 1, argv); TRY_CATCH_CALL(this->Value(), cb, 1, argv);
called = true; called = true;
} }
queue.pop();
// We don't call the actual callback, so we have to make sure that
// the baton gets destroyed.
delete call->baton;
delete call;
} }
// When we couldn't call a callback function, emit an error on the // When we couldn't call a callback function, emit an error on the
...@@ -73,16 +70,16 @@ void Database::Process() { ...@@ -73,16 +70,16 @@ void Database::Process() {
} }
while (open && (!locked || pending == 0) && !queue.empty()) { while (open && (!locked || pending == 0) && !queue.empty()) {
Call* call = queue.front(); Call *c = queue.front();
if (call->exclusive && pending > 0) { if (c->exclusive && pending > 0) {
break; break;
} }
queue.pop(); queue.pop();
std::unique_ptr<Call> call(c);
locked = call->exclusive; locked = call->exclusive;
call->callback(call->baton); call->callback(call->baton);
delete call;
if (locked) break; if (locked) break;
} }
...@@ -95,6 +92,9 @@ void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { ...@@ -95,6 +92,9 @@ void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) {
if (!open && locked) { if (!open && locked) {
EXCEPTION(Napi::String::New(env, "Database is closed"), SQLITE_MISUSE, exception); EXCEPTION(Napi::String::New(env, "Database is closed"), SQLITE_MISUSE, exception);
Napi::Function cb = baton->callback.Value(); Napi::Function cb = baton->callback.Value();
// We don't call the actual callback, so we have to make sure that
// the baton gets destroyed.
delete baton;
if (!cb.IsUndefined() && cb.IsFunction()) { if (!cb.IsUndefined() && cb.IsFunction()) {
Napi::Value argv[] = { exception }; Napi::Value argv[] = { exception };
TRY_CATCH_CALL(Value(), cb, 1, argv); TRY_CATCH_CALL(Value(), cb, 1, argv);
...@@ -181,7 +181,7 @@ void Database::Work_Open(napi_env e, void* data) { ...@@ -181,7 +181,7 @@ void Database::Work_Open(napi_env e, void* data) {
} }
void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) { void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) {
OpenBaton* baton = static_cast<OpenBaton*>(data); std::unique_ptr<OpenBaton> baton(static_cast<OpenBaton*>(data));
Database* db = baton->db; Database* db = baton->db;
...@@ -213,9 +213,6 @@ void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) { ...@@ -213,9 +213,6 @@ void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) {
EMIT_EVENT(db->Value(), 1, info); EMIT_EVENT(db->Value(), 1, info);
db->Process(); db->Process();
} }
napi_delete_async_work(e, baton->request);
delete baton;
} }
Napi::Value Database::OpenGetter(const Napi::CallbackInfo& info) { Napi::Value Database::OpenGetter(const Napi::CallbackInfo& info) {
...@@ -241,6 +238,7 @@ void Database::Work_BeginClose(Baton* baton) { ...@@ -241,6 +238,7 @@ void Database::Work_BeginClose(Baton* baton) {
assert(baton->db->_handle); assert(baton->db->_handle);
assert(baton->db->pending == 0); assert(baton->db->pending == 0);
baton->db->pending++;
baton->db->RemoveCallbacks(); baton->db->RemoveCallbacks();
baton->db->closing = true; baton->db->closing = true;
...@@ -269,13 +267,14 @@ void Database::Work_Close(napi_env e, void* data) { ...@@ -269,13 +267,14 @@ void Database::Work_Close(napi_env e, void* data) {
} }
void Database::Work_AfterClose(napi_env e, napi_status status, void* data) { void Database::Work_AfterClose(napi_env e, napi_status status, void* data) {
Baton* baton = static_cast<Baton*>(data); std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
Database* db = baton->db; Database* db = baton->db;
Napi::Env env = db->Env(); Napi::Env env = db->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
db->pending--;
db->closing = false; db->closing = false;
Napi::Value argv[1]; Napi::Value argv[1];
...@@ -306,9 +305,6 @@ void Database::Work_AfterClose(napi_env e, napi_status status, void* data) { ...@@ -306,9 +305,6 @@ void Database::Work_AfterClose(napi_env e, napi_status status, void* data) {
EMIT_EVENT(db->Value(), 1, info); EMIT_EVENT(db->Value(), 1, info);
db->Process(); db->Process();
} }
napi_delete_async_work(e, baton->request);
delete baton;
} }
Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { Napi::Value Database::Serialize(const Napi::CallbackInfo& info) {
...@@ -320,7 +316,7 @@ Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { ...@@ -320,7 +316,7 @@ Napi::Value Database::Serialize(const Napi::CallbackInfo& info) {
db->serialize = true; db->serialize = true;
if (!callback.IsEmpty() && callback.IsFunction()) { if (!callback.IsEmpty() && callback.IsFunction()) {
TRY_CATCH_CALL(info.This(), callback, 0, NULL); TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This());
db->serialize = before; db->serialize = before;
} }
...@@ -338,7 +334,7 @@ Napi::Value Database::Parallelize(const Napi::CallbackInfo& info) { ...@@ -338,7 +334,7 @@ Napi::Value Database::Parallelize(const Napi::CallbackInfo& info) {
db->serialize = false; db->serialize = false;
if (!callback.IsEmpty() && callback.IsFunction()) { if (!callback.IsEmpty() && callback.IsFunction()) {
TRY_CATCH_CALL(info.This(), callback, 0, NULL); TRY_CATCH_CALL(info.This(), callback, 0, NULL, info.This());
db->serialize = before; db->serialize = before;
} }
...@@ -407,17 +403,18 @@ Napi::Value Database::Interrupt(const Napi::CallbackInfo& info) { ...@@ -407,17 +403,18 @@ Napi::Value Database::Interrupt(const Napi::CallbackInfo& info) {
return info.This(); return info.This();
} }
void Database::SetBusyTimeout(Baton* baton) { void Database::SetBusyTimeout(Baton* b) {
std::unique_ptr<Baton> baton(b);
assert(baton->db->open); assert(baton->db->open);
assert(baton->db->_handle); assert(baton->db->_handle);
// Abuse the status field for passing the timeout. // Abuse the status field for passing the timeout.
sqlite3_busy_timeout(baton->db->_handle, baton->status); sqlite3_busy_timeout(baton->db->_handle, baton->status);
delete baton;
} }
void Database::RegisterTraceCallback(Baton* baton) { void Database::RegisterTraceCallback(Baton* b) {
std::unique_ptr<Baton> baton(b);
assert(baton->db->open); assert(baton->db->open);
assert(baton->db->_handle); assert(baton->db->_handle);
Database* db = baton->db; Database* db = baton->db;
...@@ -433,8 +430,6 @@ void Database::RegisterTraceCallback(Baton* baton) { ...@@ -433,8 +430,6 @@ void Database::RegisterTraceCallback(Baton* baton) {
db->debug_trace->finish(); db->debug_trace->finish();
db->debug_trace = NULL; db->debug_trace = NULL;
} }
delete baton;
} }
void Database::TraceCallback(void* db, const char* sql) { void Database::TraceCallback(void* db, const char* sql) {
...@@ -443,7 +438,8 @@ void Database::TraceCallback(void* db, const char* sql) { ...@@ -443,7 +438,8 @@ void Database::TraceCallback(void* db, const char* sql) {
static_cast<Database*>(db)->debug_trace->send(new std::string(sql)); static_cast<Database*>(db)->debug_trace->send(new std::string(sql));
} }
void Database::TraceCallback(Database* db, std::string* sql) { void Database::TraceCallback(Database* db, std::string* s) {
std::unique_ptr<std::string> sql(s);
// Note: This function is called in the main V8 thread. // Note: This function is called in the main V8 thread.
Napi::Env env = db->Env(); Napi::Env env = db->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -453,10 +449,10 @@ void Database::TraceCallback(Database* db, std::string* sql) { ...@@ -453,10 +449,10 @@ void Database::TraceCallback(Database* db, std::string* sql) {
Napi::String::New(env, sql->c_str()) Napi::String::New(env, sql->c_str())
}; };
EMIT_EVENT(db->Value(), 2, argv); EMIT_EVENT(db->Value(), 2, argv);
delete sql;
} }
void Database::RegisterProfileCallback(Baton* baton) { void Database::RegisterProfileCallback(Baton* b) {
std::unique_ptr<Baton> baton(b);
assert(baton->db->open); assert(baton->db->open);
assert(baton->db->_handle); assert(baton->db->_handle);
Database* db = baton->db; Database* db = baton->db;
...@@ -472,8 +468,6 @@ void Database::RegisterProfileCallback(Baton* baton) { ...@@ -472,8 +468,6 @@ void Database::RegisterProfileCallback(Baton* baton) {
db->debug_profile->finish(); db->debug_profile->finish();
db->debug_profile = NULL; db->debug_profile = NULL;
} }
delete baton;
} }
void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) { void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) {
...@@ -485,7 +479,8 @@ void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) ...@@ -485,7 +479,8 @@ void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs)
static_cast<Database*>(db)->debug_profile->send(info); static_cast<Database*>(db)->debug_profile->send(info);
} }
void Database::ProfileCallback(Database *db, ProfileInfo* info) { void Database::ProfileCallback(Database *db, ProfileInfo* i) {
std::unique_ptr<ProfileInfo> info(i);
Napi::Env env = db->Env(); Napi::Env env = db->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -495,10 +490,10 @@ void Database::ProfileCallback(Database *db, ProfileInfo* info) { ...@@ -495,10 +490,10 @@ void Database::ProfileCallback(Database *db, ProfileInfo* info) {
Napi::Number::New(env, (double)info->nsecs / 1000000.0) Napi::Number::New(env, (double)info->nsecs / 1000000.0)
}; };
EMIT_EVENT(db->Value(), 3, argv); EMIT_EVENT(db->Value(), 3, argv);
delete info;
} }
void Database::RegisterUpdateCallback(Baton* baton) { void Database::RegisterUpdateCallback(Baton* b) {
std::unique_ptr<Baton> baton(b);
assert(baton->db->open); assert(baton->db->open);
assert(baton->db->_handle); assert(baton->db->_handle);
Database* db = baton->db; Database* db = baton->db;
...@@ -514,8 +509,6 @@ void Database::RegisterUpdateCallback(Baton* baton) { ...@@ -514,8 +509,6 @@ void Database::RegisterUpdateCallback(Baton* baton) {
db->update_event->finish(); db->update_event->finish();
db->update_event = NULL; db->update_event = NULL;
} }
delete baton;
} }
void Database::UpdateCallback(void* db, int type, const char* database, void Database::UpdateCallback(void* db, int type, const char* database,
...@@ -530,7 +523,8 @@ void Database::UpdateCallback(void* db, int type, const char* database, ...@@ -530,7 +523,8 @@ void Database::UpdateCallback(void* db, int type, const char* database,
static_cast<Database*>(db)->update_event->send(info); static_cast<Database*>(db)->update_event->send(info);
} }
void Database::UpdateCallback(Database *db, UpdateInfo* info) { void Database::UpdateCallback(Database *db, UpdateInfo* i) {
std::unique_ptr<UpdateInfo> info(i);
Napi::Env env = db->Env(); Napi::Env env = db->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -541,7 +535,6 @@ void Database::UpdateCallback(Database *db, UpdateInfo* info) { ...@@ -541,7 +535,6 @@ void Database::UpdateCallback(Database *db, UpdateInfo* info) {
Napi::Number::New(env, info->rowid), Napi::Number::New(env, info->rowid),
}; };
EMIT_EVENT(db->Value(), 4, argv); EMIT_EVENT(db->Value(), 4, argv);
delete info;
} }
Napi::Value Database::Exec(const Napi::CallbackInfo& info) { Napi::Value Database::Exec(const Napi::CallbackInfo& info) {
...@@ -562,6 +555,7 @@ void Database::Work_BeginExec(Baton* baton) { ...@@ -562,6 +555,7 @@ void Database::Work_BeginExec(Baton* baton) {
assert(baton->db->open); assert(baton->db->open);
assert(baton->db->_handle); assert(baton->db->_handle);
assert(baton->db->pending == 0); assert(baton->db->pending == 0);
baton->db->pending++;
Napi::Env env = baton->db->Env(); Napi::Env env = baton->db->Env();
int status = napi_create_async_work( int status = napi_create_async_work(
env, NULL, Napi::String::New(env, "sqlite3.Database.Exec"), env, NULL, Napi::String::New(env, "sqlite3.Database.Exec"),
...@@ -590,9 +584,10 @@ void Database::Work_Exec(napi_env e, void* data) { ...@@ -590,9 +584,10 @@ void Database::Work_Exec(napi_env e, void* data) {
} }
void Database::Work_AfterExec(napi_env e, napi_status status, void* data) { void Database::Work_AfterExec(napi_env e, napi_status status, void* data) {
ExecBaton* baton = static_cast<ExecBaton*>(data); std::unique_ptr<ExecBaton> baton(static_cast<ExecBaton*>(data));
Database* db = baton->db; Database* db = baton->db;
db->pending--;
Napi::Env env = db->Env(); Napi::Env env = db->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -617,9 +612,6 @@ void Database::Work_AfterExec(napi_env e, napi_status status, void* data) { ...@@ -617,9 +612,6 @@ void Database::Work_AfterExec(napi_env e, napi_status status, void* data) {
} }
db->Process(); db->Process();
napi_delete_async_work(e, baton->request);
delete baton;
} }
Napi::Value Database::Wait(const Napi::CallbackInfo& info) { Napi::Value Database::Wait(const Napi::CallbackInfo& info) {
...@@ -634,7 +626,9 @@ Napi::Value Database::Wait(const Napi::CallbackInfo& info) { ...@@ -634,7 +626,9 @@ Napi::Value Database::Wait(const Napi::CallbackInfo& info) {
return info.This(); return info.This();
} }
void Database::Work_Wait(Baton* baton) { void Database::Work_Wait(Baton* b) {
std::unique_ptr<Baton> baton(b);
Napi::Env env = baton->db->Env(); Napi::Env env = baton->db->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -650,8 +644,6 @@ void Database::Work_Wait(Baton* baton) { ...@@ -650,8 +644,6 @@ void Database::Work_Wait(Baton* baton) {
} }
baton->db->Process(); baton->db->Process();
delete baton;
} }
Napi::Value Database::LoadExtension(const Napi::CallbackInfo& info) { Napi::Value Database::LoadExtension(const Napi::CallbackInfo& info) {
...@@ -672,6 +664,7 @@ void Database::Work_BeginLoadExtension(Baton* baton) { ...@@ -672,6 +664,7 @@ void Database::Work_BeginLoadExtension(Baton* baton) {
assert(baton->db->open); assert(baton->db->open);
assert(baton->db->_handle); assert(baton->db->_handle);
assert(baton->db->pending == 0); assert(baton->db->pending == 0);
baton->db->pending++;
Napi::Env env = baton->db->Env(); Napi::Env env = baton->db->Env();
int status = napi_create_async_work( int status = napi_create_async_work(
env, NULL, Napi::String::New(env, "sqlite3.Database.LoadExtension"), env, NULL, Napi::String::New(env, "sqlite3.Database.LoadExtension"),
...@@ -703,9 +696,10 @@ void Database::Work_LoadExtension(napi_env e, void* data) { ...@@ -703,9 +696,10 @@ void Database::Work_LoadExtension(napi_env e, void* data) {
} }
void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* data) { void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* data) {
LoadExtensionBaton* baton = static_cast<LoadExtensionBaton*>(data); std::unique_ptr<LoadExtensionBaton> baton(static_cast<LoadExtensionBaton*>(data));
Database* db = baton->db; Database* db = baton->db;
db->pending--;
Napi::Env env = db->Env(); Napi::Env env = db->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -730,9 +724,6 @@ void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* dat ...@@ -730,9 +724,6 @@ void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* dat
} }
db->Process(); db->Process();
napi_delete_async_work(e, baton->request);
delete baton;
} }
void Database::RemoveCallbacks() { void Database::RemoveCallbacks() {
......
...@@ -41,7 +41,7 @@ public: ...@@ -41,7 +41,7 @@ public:
} }
struct Baton { struct Baton {
napi_async_work request; napi_async_work request = NULL;
Database* db; Database* db;
Napi::FunctionReference callback; Napi::FunctionReference callback;
int status; int status;
...@@ -55,6 +55,7 @@ public: ...@@ -55,6 +55,7 @@ public:
} }
} }
virtual ~Baton() { virtual ~Baton() {
if (request) napi_delete_async_work(db->Env(), request);
db->Unref(); db->Unref();
callback.Reset(); callback.Reset();
} }
......
...@@ -119,13 +119,14 @@ inline bool OtherIsInt(Napi::Number source) { ...@@ -119,13 +119,14 @@ inline bool OtherIsInt(Napi::Number source) {
// The Mac OS compiler complains when argv is NULL unless we // The Mac OS compiler complains when argv is NULL unless we
// first assign it to a locally defined variable. // first assign it to a locally defined variable.
#define TRY_CATCH_CALL(context, callback, argc, argv) \ #define TRY_CATCH_CALL(context, callback, argc, argv, ...) \
Napi::Value* passed_argv = argv;\ Napi::Value* passed_argv = argv;\
std::vector<napi_value> args;\ std::vector<napi_value> args;\
if ((argc != 0) && (passed_argv != NULL)) {\ if ((argc != 0) && (passed_argv != NULL)) {\
args.assign(passed_argv, passed_argv + argc);\ args.assign(passed_argv, passed_argv + argc);\
}\ }\
(callback).MakeCallback(Napi::Value(context), args); Napi::Value res = (callback).MakeCallback(Napi::Value(context), args); \
if (res.IsEmpty()) return __VA_ARGS__;
#define WORK_DEFINITION(name) \ #define WORK_DEFINITION(name) \
Napi::Value name(const Napi::CallbackInfo& info); \ Napi::Value name(const Napi::CallbackInfo& info); \
...@@ -153,15 +154,21 @@ inline bool OtherIsInt(Napi::Number source) { ...@@ -153,15 +154,21 @@ inline bool OtherIsInt(Napi::Number source) {
type* baton = static_cast<type*>(data); \ type* baton = static_cast<type*>(data); \
Statement* stmt = baton->stmt; Statement* stmt = baton->stmt;
#define STATEMENT_MUTEX(name) \
if (!stmt->db->_handle) { \
stmt->status = SQLITE_MISUSE; \
stmt->message = "Database handle is closed"; \
return; \
} \
sqlite3_mutex* name = sqlite3_db_mutex(stmt->db->_handle);
#define STATEMENT_END() \ #define STATEMENT_END() \
assert(stmt->locked); \ assert(stmt->locked); \
assert(stmt->db->pending); \ assert(stmt->db->pending); \
stmt->locked = false; \ stmt->locked = false; \
stmt->db->pending--; \ stmt->db->pending--; \
stmt->Process(); \ stmt->Process(); \
stmt->db->Process(); \ stmt->db->Process();
napi_delete_async_work(e, baton->request); \
delete baton;
#define BACKUP_BEGIN(type) \ #define BACKUP_BEGIN(type) \
assert(baton); \ assert(baton); \
...@@ -189,9 +196,7 @@ inline bool OtherIsInt(Napi::Number source) { ...@@ -189,9 +196,7 @@ inline bool OtherIsInt(Napi::Number source) {
backup->locked = false; \ backup->locked = false; \
backup->db->pending--; \ backup->db->pending--; \
backup->Process(); \ backup->Process(); \
backup->db->Process(); \ backup->db->Process();
napi_delete_async_work(e, baton->request); \
delete baton;
#define DELETE_FIELD(field) \ #define DELETE_FIELD(field) \
if (field != NULL) { \ if (field != NULL) { \
......
...@@ -42,11 +42,10 @@ void Statement::Process() { ...@@ -42,11 +42,10 @@ void Statement::Process() {
} }
while (prepared && !locked && !queue.empty()) { while (prepared && !locked && !queue.empty()) {
Call* call = queue.front(); std::unique_ptr<Call> call(queue.front());
queue.pop(); queue.pop();
call->callback(call->baton); call->callback(call->baton);
delete call;
} }
} }
...@@ -133,7 +132,7 @@ void Statement::Work_Prepare(napi_env e, void* data) { ...@@ -133,7 +132,7 @@ void Statement::Work_Prepare(napi_env e, void* data) {
// In case preparing fails, we use a mutex to make sure we get the associated // In case preparing fails, we use a mutex to make sure we get the associated
// error message. // error message.
sqlite3_mutex* mtx = sqlite3_db_mutex(baton->db->_handle); STATEMENT_MUTEX(mtx);
sqlite3_mutex_enter(mtx); sqlite3_mutex_enter(mtx);
stmt->status = sqlite3_prepare_v2( stmt->status = sqlite3_prepare_v2(
...@@ -153,13 +152,14 @@ void Statement::Work_Prepare(napi_env e, void* data) { ...@@ -153,13 +152,14 @@ void Statement::Work_Prepare(napi_env e, void* data) {
} }
void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) { void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) {
STATEMENT_INIT(PrepareBaton); std::unique_ptr<PrepareBaton> baton(static_cast<PrepareBaton*>(data));
Statement* stmt = baton->stmt;
Napi::Env env = stmt->Env(); Napi::Env env = stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
if (stmt->status != SQLITE_OK) { if (stmt->status != SQLITE_OK) {
Error(baton); Error(baton.get());
stmt->Finalize_(); stmt->Finalize_();
} }
else { else {
...@@ -347,20 +347,21 @@ void Statement::Work_BeginBind(Baton* baton) { ...@@ -347,20 +347,21 @@ void Statement::Work_BeginBind(Baton* baton) {
void Statement::Work_Bind(napi_env e, void* data) { void Statement::Work_Bind(napi_env e, void* data) {
STATEMENT_INIT(Baton); STATEMENT_INIT(Baton);
sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); STATEMENT_MUTEX(mtx);
sqlite3_mutex_enter(mtx); sqlite3_mutex_enter(mtx);
stmt->Bind(baton->parameters); stmt->Bind(baton->parameters);
sqlite3_mutex_leave(mtx); sqlite3_mutex_leave(mtx);
} }
void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) { void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) {
STATEMENT_INIT(Baton); std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
Statement* stmt = baton->stmt;
Napi::Env env = stmt->Env(); Napi::Env env = stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
if (stmt->status != SQLITE_OK) { if (stmt->status != SQLITE_OK) {
Error(baton); Error(baton.get());
} }
else { else {
// Fire callbacks. // Fire callbacks.
...@@ -399,7 +400,7 @@ void Statement::Work_Get(napi_env e, void* data) { ...@@ -399,7 +400,7 @@ void Statement::Work_Get(napi_env e, void* data) {
STATEMENT_INIT(RowBaton); STATEMENT_INIT(RowBaton);
if (stmt->status != SQLITE_DONE || baton->parameters.size()) { if (stmt->status != SQLITE_DONE || baton->parameters.size()) {
sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); STATEMENT_MUTEX(mtx);
sqlite3_mutex_enter(mtx); sqlite3_mutex_enter(mtx);
if (stmt->Bind(baton->parameters)) { if (stmt->Bind(baton->parameters)) {
...@@ -420,13 +421,14 @@ void Statement::Work_Get(napi_env e, void* data) { ...@@ -420,13 +421,14 @@ void Statement::Work_Get(napi_env e, void* data) {
} }
void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) { void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) {
STATEMENT_INIT(RowBaton); std::unique_ptr<RowBaton> baton(static_cast<RowBaton*>(data));
Statement* stmt = baton->stmt;
Napi::Env env = stmt->Env(); Napi::Env env = stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) {
Error(baton); Error(baton.get());
} }
else { else {
// Fire callbacks. // Fire callbacks.
...@@ -469,7 +471,7 @@ void Statement::Work_BeginRun(Baton* baton) { ...@@ -469,7 +471,7 @@ void Statement::Work_BeginRun(Baton* baton) {
void Statement::Work_Run(napi_env e, void* data) { void Statement::Work_Run(napi_env e, void* data) {
STATEMENT_INIT(RunBaton); STATEMENT_INIT(RunBaton);
sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); STATEMENT_MUTEX(mtx);
sqlite3_mutex_enter(mtx); sqlite3_mutex_enter(mtx);
// Make sure that we also reset when there are no parameters. // Make sure that we also reset when there are no parameters.
...@@ -493,13 +495,14 @@ void Statement::Work_Run(napi_env e, void* data) { ...@@ -493,13 +495,14 @@ void Statement::Work_Run(napi_env e, void* data) {
} }
void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) { void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) {
STATEMENT_INIT(RunBaton); std::unique_ptr<RunBaton> baton(static_cast<RunBaton*>(data));
Statement* stmt = baton->stmt;
Napi::Env env = stmt->Env(); Napi::Env env = stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) {
Error(baton); Error(baton.get());
} }
else { else {
// Fire callbacks. // Fire callbacks.
...@@ -538,7 +541,7 @@ void Statement::Work_BeginAll(Baton* baton) { ...@@ -538,7 +541,7 @@ void Statement::Work_BeginAll(Baton* baton) {
void Statement::Work_All(napi_env e, void* data) { void Statement::Work_All(napi_env e, void* data) {
STATEMENT_INIT(RowsBaton); STATEMENT_INIT(RowsBaton);
sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); STATEMENT_MUTEX(mtx);
sqlite3_mutex_enter(mtx); sqlite3_mutex_enter(mtx);
// Make sure that we also reset when there are no parameters. // Make sure that we also reset when there are no parameters.
...@@ -562,13 +565,14 @@ void Statement::Work_All(napi_env e, void* data) { ...@@ -562,13 +565,14 @@ void Statement::Work_All(napi_env e, void* data) {
} }
void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) { void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) {
STATEMENT_INIT(RowsBaton); std::unique_ptr<RowsBaton> baton(static_cast<RowsBaton*>(data));
Statement* stmt = baton->stmt;
Napi::Env env = stmt->Env(); Napi::Env env = stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
if (stmt->status != SQLITE_DONE) { if (stmt->status != SQLITE_DONE) {
Error(baton); Error(baton.get());
} }
else { else {
// Fire callbacks. // Fire callbacks.
...@@ -580,8 +584,8 @@ void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) { ...@@ -580,8 +584,8 @@ void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) {
Rows::const_iterator it = baton->rows.begin(); Rows::const_iterator it = baton->rows.begin();
Rows::const_iterator end = baton->rows.end(); Rows::const_iterator end = baton->rows.end();
for (int i = 0; it < end; ++it, i++) { for (int i = 0; it < end; ++it, i++) {
(result).Set(i, RowToJS(env,*it)); std::unique_ptr<Row> row(*it);
delete *it; (result).Set(i, RowToJS(env,row.get()));
} }
Napi::Value argv[] = { env.Null(), result }; Napi::Value argv[] = { env.Null(), result };
...@@ -640,7 +644,7 @@ void Statement::Work_Each(napi_env e, void* data) { ...@@ -640,7 +644,7 @@ void Statement::Work_Each(napi_env e, void* data) {
Async* async = baton->async; Async* async = baton->async;
sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); STATEMENT_MUTEX(mtx);
int retrieved = 0; int retrieved = 0;
...@@ -710,10 +714,10 @@ void Statement::AsyncEach(uv_async_t* handle) { ...@@ -710,10 +714,10 @@ void Statement::AsyncEach(uv_async_t* handle) {
Rows::const_iterator it = rows.begin(); Rows::const_iterator it = rows.begin();
Rows::const_iterator end = rows.end(); Rows::const_iterator end = rows.end();
for (int i = 0; it < end; ++it, i++) { for (int i = 0; it < end; ++it, i++) {
argv[1] = RowToJS(env,*it); std::unique_ptr<Row> row(*it);
argv[1] = RowToJS(env,row.get());
async->retrieved++; async->retrieved++;
TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv);
delete *it;
} }
} }
} }
...@@ -733,13 +737,14 @@ void Statement::AsyncEach(uv_async_t* handle) { ...@@ -733,13 +737,14 @@ void Statement::AsyncEach(uv_async_t* handle) {
} }
void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) { void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) {
STATEMENT_INIT(EachBaton); std::unique_ptr<EachBaton> baton(static_cast<EachBaton*>(data));
Statement* stmt = baton->stmt;
Napi::Env env = stmt->Env(); Napi::Env env = stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
if (stmt->status != SQLITE_DONE) { if (stmt->status != SQLITE_DONE) {
Error(baton); Error(baton.get());
} }
STATEMENT_END(); STATEMENT_END();
...@@ -769,7 +774,8 @@ void Statement::Work_Reset(napi_env e, void* data) { ...@@ -769,7 +774,8 @@ void Statement::Work_Reset(napi_env e, void* data) {
} }
void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) { void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) {
STATEMENT_INIT(Baton); std::unique_ptr<Baton> baton(static_cast<Baton*>(data));
Statement* stmt = baton->stmt;
Napi::Env env = stmt->Env(); Napi::Env env = stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -865,7 +871,8 @@ Napi::Value Statement::Finalize_(const Napi::CallbackInfo& info) { ...@@ -865,7 +871,8 @@ Napi::Value Statement::Finalize_(const Napi::CallbackInfo& info) {
return stmt->db->Value(); return stmt->db->Value();
} }
void Statement::Finalize_(Baton* baton) { void Statement::Finalize_(Baton* b) {
std::unique_ptr<Baton> baton(b);
Napi::Env env = baton->stmt->Env(); Napi::Env env = baton->stmt->Env();
Napi::HandleScope scope(env); Napi::HandleScope scope(env);
...@@ -876,8 +883,6 @@ void Statement::Finalize_(Baton* baton) { ...@@ -876,8 +883,6 @@ void Statement::Finalize_(Baton* baton) {
if (!cb.IsUndefined() && cb.IsFunction()) { if (!cb.IsUndefined() && cb.IsFunction()) {
TRY_CATCH_CALL(baton->stmt->Value(), cb, 0, NULL); TRY_CATCH_CALL(baton->stmt->Value(), cb, 0, NULL);
} }
delete baton;
} }
void Statement::Finalize_() { void Statement::Finalize_() {
...@@ -904,21 +909,17 @@ void Statement::CleanQueue() { ...@@ -904,21 +909,17 @@ void Statement::CleanQueue() {
// Clear out the queue so that this object can get GC'ed. // Clear out the queue so that this object can get GC'ed.
while (!queue.empty()) { while (!queue.empty()) {
Call* call = queue.front(); std::unique_ptr<Call> call(queue.front());
queue.pop(); queue.pop();
Napi::Function cb = call->baton->callback.Value(); std::unique_ptr<Baton> baton(call->baton);
Napi::Function cb = baton->callback.Value();
if (prepared && !cb.IsEmpty() && if (prepared && !cb.IsEmpty() &&
cb.IsFunction()) { cb.IsFunction()) {
TRY_CATCH_CALL(Value(), cb, 1, argv); TRY_CATCH_CALL(Value(), cb, 1, argv);
called = true; called = true;
} }
// We don't call the actual callback, so we have to make sure that
// the baton gets destroyed.
delete call->baton;
delete call;
} }
// When we couldn't call a callback function, emit an error on the // When we couldn't call a callback function, emit an error on the
...@@ -931,12 +932,10 @@ void Statement::CleanQueue() { ...@@ -931,12 +932,10 @@ void Statement::CleanQueue() {
else while (!queue.empty()) { else while (!queue.empty()) {
// Just delete all items in the queue; we already fired an event when // Just delete all items in the queue; we already fired an event when
// preparing the statement failed. // preparing the statement failed.
Call* call = queue.front(); std::unique_ptr<Call> call(queue.front());
queue.pop(); queue.pop();
// We don't call the actual callback, so we have to make sure that // We don't call the actual callback, so we have to make sure that
// the baton gets destroyed. // the baton gets destroyed.
delete call->baton; delete call->baton;
delete call;
} }
} }
...@@ -76,7 +76,7 @@ public: ...@@ -76,7 +76,7 @@ public:
static Napi::Value New(const Napi::CallbackInfo& info); static Napi::Value New(const Napi::CallbackInfo& info);
struct Baton { struct Baton {
napi_async_work request; napi_async_work request = NULL;
Statement* stmt; Statement* stmt;
Napi::FunctionReference callback; Napi::FunctionReference callback;
Parameters parameters; Parameters parameters;
...@@ -90,6 +90,7 @@ public: ...@@ -90,6 +90,7 @@ public:
Values::Field* field = parameters[i]; Values::Field* field = parameters[i];
DELETE_FIELD(field); DELETE_FIELD(field);
} }
if (request) napi_delete_async_work(stmt->Env(), request);
stmt->Unref(); stmt->Unref();
callback.Reset(); callback.Reset();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment