Created
September 2, 2012 06:17
-
-
Save navaneeth/3595299 to your computer and use it in GitHub Desktop.
libuv mutex failures
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"targets": [ | |
{ | |
"target_name": "mutextest", | |
'type': '<(library)', | |
"sources": [ "mutex-test.cc" ] | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include <iostream> | |
#include <queue> | |
#include <node.h> | |
using namespace v8; | |
#ifdef _WIN32 | |
#include <windows.h> | |
void vsleep(unsigned milliseconds) | |
{ | |
Sleep(milliseconds); | |
} | |
#else | |
#include <unistd.h> | |
void vsleep(unsigned milliseconds) | |
{ | |
usleep(milliseconds * 1000); // takes microseconds | |
} | |
#endif | |
class NativeLibrary | |
{ | |
public: | |
NativeLibrary() {} | |
void DoWork() | |
{ | |
std::cout << "Doing work" << std::endl; | |
} | |
}; | |
class Foo : public node::ObjectWrap | |
{ | |
public: | |
static void Init(v8::Handle<v8::Object> target); | |
NativeLibrary* GetHandle(); | |
void ReturnHandle(NativeLibrary* handle); | |
private: | |
Foo() | |
{ | |
uv_mutex_init (&mutex); | |
} | |
~Foo() {} | |
static v8::Handle<v8::Value> New(const v8::Arguments& args); | |
static v8::Handle<v8::Value> Process(const v8::Arguments& args); | |
static v8::Handle<v8::Value> GetHandleCallCount(const v8::Arguments& args); | |
std::queue<NativeLibrary*> handles_available; // Contains handles which are ready to do work | |
uv_mutex_t mutex; | |
}; | |
struct WorkerData | |
{ | |
uv_work_t request; | |
Foo* clazz; | |
bool errored; | |
std::string error_message; | |
v8::Persistent<v8::Function> callback; | |
}; | |
static int get_handle_call_count = 0; | |
NativeLibrary* Foo::GetHandle() | |
{ | |
NativeLibrary* handle = NULL; | |
uv_mutex_lock (&mutex); | |
if (!handles_available.empty()) | |
{ | |
handle = handles_available.front(); | |
} | |
else | |
{ | |
handle = new NativeLibrary; | |
assert(handle); | |
handles_available.push (handle); | |
} | |
handles_available.pop(); | |
++get_handle_call_count; | |
uv_mutex_unlock (&mutex); | |
return handle; | |
} | |
void Foo::ReturnHandle(NativeLibrary* handle) | |
{ | |
uv_mutex_lock (&mutex); | |
handles_available.push (handle); | |
uv_mutex_unlock (&mutex); | |
} | |
void perform_work_async(uv_work_t *req) | |
{ | |
WorkerData *data = static_cast<WorkerData*>(req->data); | |
int tries = 0; | |
int maxtries = 10; | |
NativeLibrary* handle = NULL; | |
while (++tries < maxtries) | |
{ | |
handle = data->clazz->GetHandle(); | |
if (handle != NULL) | |
break; | |
vsleep (500); | |
} | |
if (handle == NULL) | |
{ | |
// We couldn't acquire a handle after 10 tries. | |
data->errored = true; | |
data->error_message = "Couldn't acquire a native library handle"; | |
return; | |
} | |
handle->DoWork(); | |
data->clazz->ReturnHandle(handle); | |
} | |
void after_work(uv_work_t *req) | |
{ | |
WorkerData *data = static_cast<WorkerData*>(req->data); | |
if (data->errored) | |
{ | |
Local<Object> error = Object::New(); | |
error->Set(String::New("message"), String::New(data->error_message.c_str())); | |
Handle<Value> argv[] = { error, Null() }; | |
data->callback->Call(Context::GetCurrent()->Global(), 2, argv); | |
} | |
else | |
{ | |
Handle<Value> argv[] = { Null(), String::New("success") }; | |
data->callback->Call(Context::GetCurrent()->Global(), 2, argv); | |
} | |
data->callback.Dispose(); | |
delete data; | |
data = NULL; | |
} | |
void Foo::Init(Handle<Object> target) | |
{ | |
Local<FunctionTemplate> tpl = FunctionTemplate::New(New); | |
tpl->SetClassName(String::NewSymbol("Foo")); | |
tpl->InstanceTemplate()->SetInternalFieldCount(1); | |
// Prototype | |
tpl->PrototypeTemplate()->Set(String::NewSymbol("process"), | |
FunctionTemplate::New(Process)->GetFunction()); | |
// Prototype | |
tpl->PrototypeTemplate()->Set(String::NewSymbol("getHandleCallCount"), | |
FunctionTemplate::New(GetHandleCallCount)->GetFunction()); | |
Persistent<Function> constructor = Persistent<Function>::New(tpl->GetFunction()); | |
target->Set(String::NewSymbol("Foo"), constructor); | |
} | |
Handle<Value> Foo::New(const Arguments& args) | |
{ | |
HandleScope scope; | |
Foo* obj = new Foo(); | |
obj->Wrap(args.This()); | |
return args.This(); | |
} | |
Handle<Value> Foo::Process(const Arguments& args) | |
{ | |
HandleScope scope; | |
WorkerData* data = new WorkerData; | |
data->errored = false; | |
data->error_message = ""; | |
data->request.data = data; | |
data->callback = Persistent<Function>::New(Local<Function>::Cast(args[1])); | |
data->clazz = ObjectWrap::Unwrap<Foo>(args.This()); | |
uv_queue_work(uv_default_loop(), &data->request, perform_work_async, after_work); | |
return scope.Close(Undefined()); | |
} | |
Handle<Value> Foo::GetHandleCallCount(const Arguments& args) | |
{ | |
HandleScope scope; | |
return scope.Close(Number::New(get_handle_call_count)); | |
} | |
void InitAll(Handle<Object> target) | |
{ | |
Foo::Init (target); | |
} | |
NODE_MODULE(mutextest, InitAll) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var lib = require('./mutextest') | |
var foo = new lib.Foo(); | |
var looptill = 1000 | |
function sleep(ms) | |
{ | |
var now = new Date().getTime(); | |
while(new Date().getTime() < now + ms) { | |
// do nothing | |
} | |
} | |
process.on('exit', function () { | |
// Uncommenting the below line also fixes the problem. | |
// But sleeping here doesn't help. | |
// console.log(foo.getHandleCallCount()); | |
}); | |
for (var i = 0; i < looptill; i++) { | |
foo.process(function(err, result) { | |
console.log(result); | |
}); | |
// Uncomment the below line and segfaults/double free errors will go away | |
// sleep (1); | |
// Uncommenting the below line also fixes the problem somehow.. | |
// console.log(foo.getHandleCallCount()); | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment