Skip to content

Commit 25388bc

Browse files
committed
fix race condition when baton was deleted to early
1 parent 1b0cf90 commit 25388bc

2 files changed

Lines changed: 24 additions & 12 deletions

File tree

src/statement.cc

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -593,13 +593,19 @@ Handle<Value> Statement::Each(const Arguments& args) {
593593
}
594594
else {
595595
baton->completed = Persistent<Function>::New(completed);
596-
baton->async = new Async(stmt, baton, AsyncEach);
597596
stmt->Schedule(EIO_BeginEach, baton);
598597
return args.This();
599598
}
600599
}
601600

602601
void Statement::EIO_BeginEach(Baton* baton) {
602+
// Only create the Async object when we're actually going into
603+
// the event loop. This prevents dangling events.
604+
EachBaton* each_baton = static_cast<EachBaton*>(baton);
605+
each_baton->async = new Async(each_baton->stmt, AsyncEach);
606+
each_baton->async->item_cb = Persistent<Function>::New(each_baton->callback);
607+
each_baton->async->completed_cb = Persistent<Function>::New(each_baton->completed);
608+
603609
STATEMENT_BEGIN(Each);
604610
}
605611

@@ -649,6 +655,7 @@ void Statement::EIO_Each(eio_req *req) {
649655

650656
void Statement::CloseCallback(uv_handle_t* handle) {
651657
assert(handle != NULL);
658+
assert(handle->data != NULL);
652659
Async* async = static_cast<Async*>(handle->data);
653660
delete async;
654661
handle->data = NULL;
@@ -657,7 +664,6 @@ void Statement::CloseCallback(uv_handle_t* handle) {
657664
void Statement::AsyncEach(uv_async_t* handle, int status) {
658665
HandleScope scope;
659666
Async* async = static_cast<Async*>(handle->data);
660-
EachBaton* baton = async->baton;
661667

662668
while (true) {
663669
// Get the contents out of the data cache for us to process in the JS callback.
@@ -670,7 +676,7 @@ void Statement::AsyncEach(uv_async_t* handle, int status) {
670676
break;
671677
}
672678

673-
if (!baton->callback.IsEmpty() && baton->callback->IsFunction()) {
679+
if (!async->item_cb.IsEmpty() && async->item_cb->IsFunction()) {
674680
Local<Value> argv[2];
675681
argv[0] = Local<Value>::New(Null());
676682

@@ -679,20 +685,20 @@ void Statement::AsyncEach(uv_async_t* handle, int status) {
679685
for (int i = 0; it < end; it++, i++) {
680686
argv[1] = RowToJS(*it);
681687
async->retrieved++;
682-
TRY_CATCH_CALL(async->stmt->handle_, baton->callback, 2, argv);
688+
TRY_CATCH_CALL(async->stmt->handle_, async->item_cb, 2, argv);
683689
delete *it;
684690
}
685691
}
686692
}
687693

688694
if (async->completed) {
689-
if (!baton->completed.IsEmpty() &&
690-
baton->completed->IsFunction()) {
695+
if (!async->completed_cb.IsEmpty() &&
696+
async->completed_cb->IsFunction()) {
691697
Local<Value> argv[] = {
692698
Local<Value>::New(Null()),
693699
Integer::New(async->retrieved)
694700
};
695-
TRY_CATCH_CALL(async->stmt->handle_, baton->completed, 2, argv);
701+
TRY_CATCH_CALL(async->stmt->handle_, async->completed_cb, 2, argv);
696702
}
697703
uv_close((uv_handle_t*)handle, CloseCallback);
698704
}

src/statement.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class Statement : public ObjectWrap {
8888
ev_ref(EV_DEFAULT_UC);
8989
callback = Persistent<Function>::New(cb_);
9090
}
91-
~Baton() {
91+
virtual ~Baton() {
9292
for (unsigned int i = 0; i < parameters.size(); i++) {
9393
Values::Field* field = parameters[i];
9494
DELETE_FIELD(field);
@@ -124,7 +124,7 @@ class Statement : public ObjectWrap {
124124
EachBaton(Statement* stmt_, Handle<Function> cb_) :
125125
Baton(stmt_, cb_) {}
126126
Persistent<Function> completed;
127-
Async* async;
127+
Async* async; // Isn't deleted when the baton is deleted.
128128
};
129129

130130
struct PrepareBaton : Database::Baton {
@@ -155,14 +155,18 @@ class Statement : public ObjectWrap {
155155
struct Async {
156156
uv_async_t watcher;
157157
Statement* stmt;
158-
EachBaton* baton;
159158
Rows data;
160159
pthread_mutex_t mutex;
161160
bool completed;
162161
int retrieved;
163162

164-
Async(Statement* st, EachBaton* eb, uv_async_cb async_cb) :
165-
stmt(st), baton(eb), completed(false), retrieved(0) {
163+
// Store the callbacks here because we don't have
164+
// access to the baton in the async callback.
165+
Persistent<Function> item_cb;
166+
Persistent<Function> completed_cb;
167+
168+
Async(Statement* st, uv_async_cb async_cb) :
169+
stmt(st), completed(false), retrieved(0) {
166170
watcher.data = this;
167171
pthread_mutex_init(&mutex, NULL);
168172
stmt->Ref();
@@ -171,6 +175,8 @@ class Statement : public ObjectWrap {
171175

172176
~Async() {
173177
stmt->Unref();
178+
item_cb.Dispose();
179+
completed_cb.Dispose();
174180
pthread_mutex_destroy(&mutex);
175181
}
176182
};

0 commit comments

Comments
 (0)