Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Support for node v0.11.x

Simple wrappers that prefer the new v8 API but support the legacy API as
well. Also improves the build process by adding support for Debug builds
and detects binaries that are broken somehow.
  • Loading branch information...
commit b5d5ec33c134e359d54782962d7c5bbefe1da349 1 parent 3dfb2ac
@laverdet authored
View
27 binding.gyp
@@ -1,4 +1,22 @@
{
+ 'target_defaults': {
+ 'default_configuration': 'Release',
+ 'configurations': {
+ 'Release': {
+ 'cflags': [ '-O3' ],
+ 'xcode_settings': {
+ 'GCC_OPTIMIZATION_LEVEL': '3',
+ 'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO',
+ },
+ 'msvs_settings': {
+ 'VCCLCompilerTool': {
+ 'Optimization': 3,
+ 'FavorSizeOrSpeed': 1,
+ },
+ },
+ }
+ },
+ },
'targets': [
{
'target_name': 'fibers',
@@ -16,19 +34,12 @@
{'defines': ['CORO_FIBER', 'WINDOWS']},
# else
{
- 'cflags': ['-Wno-deprecated-declarations'],
'defines': ['USE_CORO', 'CORO_GUARDPAGES=1'],
'ldflags': ['-pthread'],
}
],
['OS == "linux" or OS == "solaris" or OS == "sunos" or OS == "freebsd"', {'defines': ['CORO_UCONTEXT']}],
- ['OS == "mac"', {
- 'defines': ['CORO_SJLJ'],
- 'xcode_settings': {
- 'GCC_OPTIMIZATION_LEVEL': '3',
- 'GCC_GENERATE_DEBUGGING_SYMBOLS': 'NO',
- },
- }],
+ ['OS == "mac"', {'defines': ['CORO_SJLJ']}],
['OS == "openbsd"', {'defines': ['CORO_ASM']}],
['target_arch == "arm"',
{
View
62 build.js
@@ -1,10 +1,10 @@
#!/usr/bin/env node
-var spawn = require('child_process').spawn,
+var cp = require('child_process'),
fs = require('fs'),
path = require('path');
// Parse args
-var force = false;
+var force = false, debug = false;
var
arch = process.arch,
platform = process.platform,
@@ -15,6 +15,8 @@ var args = process.argv.slice(2).filter(function(arg) {
return false;
} else if (arg.substring(0, 13) === '--target_arch') {
arch = arg.substring(14);
+ } else if (arg === '--debug') {
+ debug = true;
}
return true;
});
@@ -28,34 +30,48 @@ var modPath = platform+ '-'+ arch+ '-v8-'+ v8;
if (!force) {
try {
fs.statSync(path.join(__dirname, 'bin', modPath, 'fibers.node'));
- console.log('`'+ modPath+ '` exists; skipping build');
- return process.exit();
- } catch (ex) {}
+ console.log('`'+ modPath+ '` exists; testing');
+ cp.execFile(process.execPath, ['quick-test'], function(err, stdout, stderr) {
+ if (err || stdout !== 'pass' || stderr) {
+ console.log('Problem with the binary; manual build incoming');
+ build();
+ } else {
+ console.log('Binary is fine; exiting');
+ }
+ });
+ } catch (ex) {
+ // Stat failed
+ build();
+ }
+} else {
+ build();
}
// Build it
-spawn(
- process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp',
- ['rebuild'].concat(args),
- {customFds: [0, 1, 2]})
-.on('exit', function(err) {
- if (err) {
- if (err === 127) {
- console.error(
- 'node-gyp not found! Please upgrade your install of npm! You need at least 1.1.5 (I think) '+
- 'and preferably 1.1.30.'
- );
- } else {
- console.error('Build failed');
+function build() {
+ cp.spawn(
+ process.platform === 'win32' ? 'node-gyp.cmd' : 'node-gyp',
+ ['rebuild'].concat(args),
+ {customFds: [0, 1, 2]})
+ .on('exit', function(err) {
+ if (err) {
+ if (err === 127) {
+ console.error(
+ 'node-gyp not found! Please upgrade your install of npm! You need at least 1.1.5 (I think) '+
+ 'and preferably 1.1.30.'
+ );
+ } else {
+ console.error('Build failed');
+ }
+ return process.exit(err);
}
- return process.exit(err);
- }
- afterBuild();
-});
+ afterBuild();
+ });
+}
// Move it to expected location
function afterBuild() {
- var targetPath = path.join(__dirname, 'build', 'Release', 'fibers.node');
+ var targetPath = path.join(__dirname, 'build', debug ? 'Debug' : 'Release', 'fibers.node');
var installPath = path.join(__dirname, 'bin', modPath, 'fibers.node');
try {
View
7 quick-test.js
@@ -0,0 +1,7 @@
+"use strict"
+var Fiber = require('./fibers');
+var fiber = Fiber(function() {
+ process.stdout.write(Fiber.yield());
+});
+fiber.run();
+fiber.run('pass');
View
13 src/coroutine.cc
@@ -1,13 +1,12 @@
#include "coroutine.h"
#include <assert.h>
-#include <node.h>
#ifndef WINDOWS
#include <pthread.h>
#else
#include <windows.h>
// Stub pthreads into Windows approximations
#define pthread_t HANDLE
-#define pthread_create(thread, attr, fn, arg) !((*thread)=CreateThread(NULL, 0, &(fn), NULL, 0, NULL))
+#define pthread_create(thread, attr, fn, arg) !((*thread)=CreateThread(NULL, 0, &(fn), arg, 0, NULL))
#define pthread_join(thread, arg) WaitForSingleObject((thread), INFINITE)
#define pthread_key_t DWORD
#define pthread_key_create(key, dtor) (*key)=TlsAlloc()
@@ -37,8 +36,8 @@ static void* find_thread_id_key(void* arg)
static DWORD __stdcall find_thread_id_key(LPVOID arg)
#endif
{
- v8::Locker locker;
- v8::Isolate* isolate = v8::Isolate::GetCurrent();
+ v8::Isolate* isolate = static_cast<v8::Isolate*>(arg);
+ v8::Locker locker(isolate);
assert(isolate != NULL);
floor_thread_key = 0;
for (pthread_key_t ii = coro_thread_key - 1; ii > (coro_thread_key >= 20 ? coro_thread_key - 20 : 0); --ii) {
@@ -55,12 +54,12 @@ static DWORD __stdcall find_thread_id_key(LPVOID arg)
/**
* Coroutine class definition
*/
-void Coroutine::init() {
- v8::Unlocker unlocker;
+void Coroutine::init(v8::Isolate* isolate) {
+ v8::Unlocker unlocker(isolate);
pthread_key_create(&coro_thread_key, NULL);
pthread_setspecific(coro_thread_key, &current());
pthread_t thread;
- pthread_create(&thread, NULL, find_thread_id_key, NULL);
+ pthread_create(&thread, NULL, find_thread_id_key, isolate);
pthread_join(thread, NULL);
}
View
3  src/coroutine.h
@@ -1,3 +1,4 @@
+#include <node.h>
#include <stdlib.h>
#include <vector>
#include "libcoro/coro.h"
@@ -55,7 +56,7 @@ class Coroutine {
/**
* Initialize the library.
*/
- static void init();
+ static void init(v8::Isolate* isolate);
/**
* Set the size of coroutines created by this library. Since coroutines are pooled the stack
View
146 src/fibers.cc
@@ -17,26 +17,93 @@
using namespace std;
using namespace v8;
+#if NODE_MODULE_VERSION > 1
+#define USE_GLOBAL_LOCKER
+#endif
+
+// Handle legacy V8 API
+namespace uni {
+#if NODE_MODULE_VERSION >= 0x000C
+ // Node v0.11+
+ template <class T>
+ Persistent<T> New(Isolate* isolate, Handle<T> handle) {
+ return Persistent<T>::New(isolate, handle);
+ }
+ template <class T>
+ void Dispose(Isolate* isolate, Persistent<T>& handle) {
+ handle.Dispose(isolate);
+ }
+
+ template <void (*F)(Isolate*, Persistent<Value>, void*), class T, typename P>
+ void MakeWeak(Isolate* isolate, Persistent<T>& handle, P* val) {
+ handle.MakeWeak(isolate, val, F);
+ }
+ template <class T>
+ void ClearWeak(Isolate* isolate, Persistent<T>& handle) {
+ handle.ClearWeak(isolate);
+ }
+
+ template <class T>
+ void SetInternalPointer(Handle<T> handle, int index, void* val) {
+ handle->SetAlignedPointerInInternalField(index, val);
+ }
+ template <class T>
+ void* GetInternalPointer(Handle<T> handle, int index) {
+ return handle->GetAlignedPointerFromInternalField(index);
+ }
+#else
+ // Node v0.10.x and lower
+ template <class T>
+ Persistent<T> New(Isolate* isolate, Handle<T> handle) {
+ return Persistent<T>::New(handle);
+ }
+ template <class T>
+ void Dispose(Isolate* isolate, Persistent<T>& handle) {
+ handle.Dispose();
+ }
+
+ template <void (*F)(Isolate*, Persistent<Value>, void*)>
+ void WeakCallbackShim(Persistent<Value> value, void* data) {
+ F(NULL, value, data);
+ }
+ template <void (*F)(Isolate*, Persistent<Value>, void*), class T, typename P>
+ void MakeWeak(Isolate* isolate, Persistent<T>& handle, P* val) {
+ handle.MakeWeak(val, WeakCallbackShim<F>);
+ }
+ template <class T>
+ void ClearWeak(Isolate* isolate, Persistent<T>& handle) {
+ handle.ClearWeak();
+ }
+
+ template <class T>
+ void SetInternalPointer(Handle<T> handle, int index, void* val) {
+ handle->SetPointerInInternalField(index, val);
+ }
+ template <class T>
+ void* GetInternalPointer(Handle<T> handle, int index) {
+ return handle->GetPointerFromInternalField(index);
+ }
+#endif
+}
+
class Fiber {
-#define Unwrap(target, handle) \
- assert(!handle.IsEmpty()); \
- assert(handle->InternalFieldCount() == 1); \
- target = *static_cast<Fiber*>(handle->GetPointerFromInternalField(0));
private:
+#ifdef USE_GLOBAL_LOCKER
static Locker* global_locker; // Node does not use locks or threads, so we need a global lock
+#endif
static Persistent<FunctionTemplate> tmpl;
static Persistent<Function> fiber_object;
static Fiber* current;
static vector<Fiber*> orphaned_fibers;
static Persistent<Value> fatal_stack;
+ Isolate* isolate;
Persistent<Object> handle;
Persistent<Function> cb;
Persistent<Context> v8_context;
Persistent<Value> zombie_exception;
Persistent<Value> yielded;
- Isolate* isolate;
bool yielded_exception;
Coroutine* entry_fiber;
Coroutine* this_fiber;
@@ -45,24 +112,30 @@ class Fiber {
bool zombie;
bool resetting;
+ static Fiber& Unwrap(Handle<Object> handle) {
+ assert(!handle.IsEmpty()); \
+ assert(handle->InternalFieldCount() == 1); \
+ return *static_cast<Fiber*>(uni::GetInternalPointer(handle, 0));
+ }
+
Fiber(Handle<Object> handle, Handle<Function> cb, Handle<Context> v8_context) :
- handle(Persistent<Object>::New(handle)),
- cb(Persistent<Function>::New(cb)),
- v8_context(Persistent<Context>::New(v8_context)),
isolate(Isolate::GetCurrent()),
+ handle(uni::New(isolate, handle)),
+ cb(uni::New(isolate, cb)),
+ v8_context(uni::New(isolate, v8_context)),
started(false),
yielding(false),
zombie(false),
resetting(false) {
MakeWeak();
- handle->SetPointerInInternalField(0, this);
+ uni::SetInternalPointer(handle, 0, this);
}
virtual ~Fiber() {
assert(!this->started);
- handle.Dispose();
- cb.Dispose();
- v8_context.Dispose();
+ uni::Dispose(isolate, handle);
+ uni::Dispose(isolate, cb);
+ uni::Dispose(isolate, v8_context);
}
/**
@@ -70,7 +143,7 @@ class Fiber {
* i.e. After fiber completes, while yielded, or before started
*/
void MakeWeak() {
- handle.MakeWeak(this, WeakCallback);
+ uni::MakeWeak<WeakCallback>(isolate, handle, this);
}
/**
@@ -86,7 +159,7 @@ class Fiber {
* the fiber is currently suspended we'll unwind the fiber's stack by throwing exceptions in
* order to clear all references.
*/
- static void WeakCallback(Persistent<Value> value, void* data) {
+ static void WeakCallback(Isolate* isolate, Persistent<Value> value, void* data) {
Fiber& that = *static_cast<Fiber*>(data);
assert(that.handle == value);
assert(value.IsNearDeath());
@@ -166,7 +239,7 @@ class Fiber {
* be created and the callback will start. Otherwise we switch back into the exist context.
*/
static Handle<Value> Run(const Arguments& args) {
- Unwrap(Fiber& that, args.Holder());
+ Fiber& that = Unwrap(args.Holder());
// There seems to be no better place to put this check..
DestroyOrphans();
@@ -191,9 +264,9 @@ class Fiber {
// misnomer, we're just reusing the same handle.
that.yielded_exception = false;
if (args.Length()) {
- that.yielded = Persistent<Value>::New(args[0]);
+ that.yielded = uni::New(that.isolate, args[0]);
} else {
- that.yielded = Persistent<Value>::New(Undefined());
+ that.yielded = uni::New(that.isolate, Undefined());
}
}
that.SwapContext();
@@ -204,14 +277,14 @@ class Fiber {
* Throw an exception into a currently yielding fiber.
*/
static Handle<Value> ThrowInto(const Arguments& args) {
- Unwrap(Fiber& that, args.Holder());
+ Fiber& that = Unwrap(args.Holder());
if (!that.yielding) {
THROW(Exception::Error, "This Fiber is not yielding");
} else if (args.Length() == 0) {
- that.yielded = Persistent<Value>::New(Undefined());
+ that.yielded = uni::New(that.isolate, Undefined());
} else if (args.Length() == 1) {
- that.yielded = Persistent<Value>::New(args[0]);
+ that.yielded = uni::New(that.isolate, args[0]);
} else {
THROW(Exception::TypeError, "throwInto() expects 1 or no arguments");
}
@@ -225,7 +298,7 @@ class Fiber {
* effect.
*/
static Handle<Value> Reset(const Arguments& args) {
- Unwrap(Fiber& that, args.Holder());
+ Fiber& that = Unwrap(args.Holder());
if (!that.started) {
return Undefined();
@@ -263,8 +336,8 @@ class Fiber {
// Setup an exception which will be thrown and rethrown from Fiber::Yield()
Local<Value> zombie_exception = Exception::Error(String::New("This Fiber is a zombie"));
- this->zombie_exception = Persistent<Value>::New(zombie_exception);
- yielded = Persistent<Value>::New(zombie_exception);
+ this->zombie_exception = uni::New(isolate, zombie_exception);
+ yielded = uni::New(isolate, zombie_exception);
yielded_exception = true;
// Swap context back to Fiber::Yield() which will throw an exception to unwind the stack.
@@ -277,7 +350,7 @@ class Fiber {
if (yielded_exception && yielded == zombie_exception) {
yielded_exception = false;
yielded.Dispose();
- yielded = Persistent<Value>::New(Undefined());
+ yielded = uni::New(isolate, Undefined());
}
this->zombie_exception.Dispose();
}
@@ -357,14 +430,14 @@ class Fiber {
}
if (try_catch.HasCaught()) {
- that.yielded = Persistent<Value>::New(try_catch.Exception());
+ that.yielded = uni::New(that.isolate, try_catch.Exception());
that.yielded_exception = true;
if (that.zombie && !that.resetting && that.yielded != that.zombie_exception) {
// Throwing an exception from a garbage sweep
- fatal_stack = Persistent<Value>::New(try_catch.StackTrace());
+ fatal_stack = uni::New(that.isolate, try_catch.StackTrace());
}
} else {
- that.yielded = Persistent<Value>::New(yielded);
+ that.yielded = uni::New(that.isolate, yielded);
that.yielded_exception = false;
}
@@ -402,9 +475,9 @@ class Fiber {
if (that.zombie) {
return ThrowException(that.zombie_exception);
} else if (args.Length() == 0) {
- that.yielded = Persistent<Value>::New(Undefined());
+ that.yielded = uni::New(that.isolate, Undefined());
} else if (args.Length() == 1) {
- that.yielded = Persistent<Value>::New(args[0]);
+ that.yielded = uni::New(that.isolate, args[0]);
} else {
THROW(Exception::TypeError, "yield() expects 1 or no arguments");
}
@@ -438,7 +511,7 @@ class Fiber {
if (info.This().IsEmpty() || info.This()->InternalFieldCount() != 1) {
return Undefined();
}
- Unwrap(Fiber& that, info.This());
+ Fiber& that = Unwrap(info.This());
return Boolean::New(that.started);
}
@@ -478,11 +551,14 @@ class Fiber {
// shutting down. TODO: There's likely a better way to accomplish this, but since the
// application is going down lost memory isn't the end of the world. But with a regular lock
// there's seg faults when node shuts down.
- global_locker = new Locker(Isolate::GetCurrent());
+ Isolate* isolate = Isolate::GetCurrent();
+#ifdef USE_GLOBAL_LOCKER
+ global_locker = new Locker(isolate);
+#endif
current = NULL;
// Fiber constructor
- tmpl = Persistent<FunctionTemplate>::New(FunctionTemplate::New(New));
+ tmpl = uni::New(isolate, FunctionTemplate::New(New));
tmpl->SetClassName(String::NewSymbol("Fiber"));
// Guard which only allows these methods to be called on a fiber; prevents
@@ -514,13 +590,15 @@ class Fiber {
// Global Fiber
target->Set(String::NewSymbol("Fiber"), fn, ReadOnly);
- fiber_object = Persistent<Function>::New(fn);
+ fiber_object = uni::New(isolate, fn);
}
};
Persistent<FunctionTemplate> Fiber::tmpl;
Persistent<Function> Fiber::fiber_object;
+#ifdef USE_GLOBAL_LOCKER
Locker* Fiber::global_locker;
+#endif
Fiber* Fiber::current = NULL;
vector<Fiber*> Fiber::orphaned_fibers;
Persistent<Value> Fiber::fatal_stack;
@@ -534,7 +612,7 @@ extern "C" void init(Handle<Object> target) {
}
did_init = true;
HandleScope scope;
- Coroutine::init();
+ Coroutine::init(Isolate::GetCurrent());
Fiber::Init(target);
// Default stack size of either 512k or 1M. Perhaps make this configurable by the run time?
Coroutine::set_stack_size(128 * 1024);
Please sign in to comment.
Something went wrong with that request. Please try again.