Skip to content

Instantly share code, notes, and snippets.

@navaneeth
Created September 2, 2012 06:17
Show Gist options
  • Save navaneeth/3595299 to your computer and use it in GitHub Desktop.
Save navaneeth/3595299 to your computer and use it in GitHub Desktop.
libuv mutex failures
{
"targets": [
{
"target_name": "mutextest",
'type': '<(library)',
"sources": [ "mutex-test.cc" ]
}
]
}
#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)
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