summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorxant <xant@xant.net>2013-09-23 17:18:51 (GMT)
committer xant <xant@xant.net>2013-09-23 17:18:51 (GMT)
commit223789e04b8d5549faebbf14b40ed7b4bda115dc (patch)
tree54fee2bcf860a49677d311e92173c46ab6cd489e
parent0027f1ab54f177ccf69c1e37569a9472123cdbe3 (diff)
handle internal timers directly instead of using NSRunloop ...it's much faster
also ensure cleaning globals before disposing a js context wrapped xml nodes are not retained anymore .... since they are supposed to live longer than the script which may refer to them
-rw-r--r--core/JMXCanvasElement.mm8
-rw-r--r--core/JMXScript.mm121
-rw-r--r--core/JMXThreadedEntity.mm67
-rw-r--r--core/NSXMLNode+V8.mm28
-rw-r--r--entities/JMXDrawEntity.mm10
-rw-r--r--node.js/src/node.js4
6 files changed, 119 insertions, 119 deletions
diff --git a/core/JMXCanvasElement.mm b/core/JMXCanvasElement.mm
index d76bf38..59736ea 100644
--- a/core/JMXCanvasElement.mm
+++ b/core/JMXCanvasElement.mm
@@ -72,14 +72,6 @@ JMXV8_EXPORT_NODE_CLASS(JMXCanvasElement);
}
}
-- (oneway void) release
-{
- return [super release];
-}
-- (id)retain
-{
- return [super retain];
-}
- (void)dealloc
{
[drawPath release];
diff --git a/core/JMXScript.mm b/core/JMXScript.mm
index 106913e..0c55d06 100644
--- a/core/JMXScript.mm
+++ b/core/JMXScript.mm
@@ -60,16 +60,7 @@
#include "node_string.h"
using namespace v8;
-using namespace std;
using namespace node;
-/*
-typedef std::map<id, v8::Persistent<v8::Object> > InstMap;
-*/
-
-typedef std::pair< JMXScript *, Handle<Context> >CtxPair;
-typedef std::map< JMXScript *, Handle<Context> > CtxMap;
-
-CtxMap contextes;
typedef struct __JMXPersistantInstance {
id obj;
@@ -113,8 +104,6 @@ static JMXV8ClassDescriptor mappedClasses[] = {
{ NULL, NULL, NULL }
};
-static NSThread *nodeJSTimersThread = nil;
-
void JSExit(int code)
{
v8::Locker locker;
@@ -455,7 +444,6 @@ static v8::Handle<Value> AddToRunLoop(const Arguments& args)
foo.function = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
foo.function->SetHiddenValue(String::New("lastUpdate"), v8::Number::New([[NSDate date] timeIntervalSince1970]));
foo.function->SetHiddenValue(String::New("interval"), args[1]);
- [[NSRunLoop currentRunLoop] addTimer:foo.timer forMode:NSRunLoopCommonModes];
[scriptContext addRunloopTimer:foo];
return handleScope.Close([foo jsObj]);
}
@@ -507,7 +495,6 @@ static v8::Handle<Value> SetInterval(const Arguments& args)
foo.function->SetHiddenValue(String::New("lastUpdate"), v8::Number::New([[NSDate date] timeIntervalSince1970]));
foo.function->SetHiddenValue(String::New("interval"), args[1]);
}
- [[NSRunLoop currentRunLoop] addTimer:foo.timer forMode:NSRunLoopCommonModes];
[scriptContext addRunloopTimer:foo];
return handleScope.Close([foo jsObj]);
}
@@ -540,7 +527,6 @@ static v8::Handle<Value> SetTimeout(const Arguments& args)
foo.function->SetHiddenValue(String::New("lastUpdate"), v8::Number::New([[NSDate date] timeIntervalSince1970]));
foo.function->SetHiddenValue(String::New("interval"), args[1]);
}
- [[NSRunLoop currentRunLoop] addTimer:foo.timer forMode:NSRunLoopCommonModes];
[scriptContext addRunloopTimer:foo];
return handleScope.Close([foo jsObj]);
}
@@ -556,15 +542,22 @@ static v8::Handle<Value> ClearTimeout(const Arguments& args)
v8::Local<v8::Object> obj = globalObject->Get(String::New("scriptEntity"))->ToObject();
JMXScriptEntity *entity = (JMXScriptEntity *)obj->GetPointerFromInternalField(0);
JMXScript *scriptContext = entity.jsContext;
- JMXScriptTimer *foo = (JMXScriptTimer *)Local<Object>::Cast(args[0])->GetPointerFromInternalField(0);
- if (foo && [scriptContext.runloopTimers containsObject:foo]) {
- [foo.timer invalidate];
- [scriptContext removeRunloopTimer:foo];
- return handleScope.Close(v8::Boolean::New(1));
+ if (args[0]->IsObject() && Local<Object>::Cast(args[0])->InternalFieldCount() > 0) {
+ JMXScriptTimer *foo = (JMXScriptTimer *)Local<Object>::Cast(args[0])->GetPointerFromInternalField(0);
+ if (foo && [scriptContext.runloopTimers containsObject:foo]) {
+ [foo.timer invalidate];
+ [scriptContext removeRunloopTimer:foo];
+ return handleScope.Close(v8::Boolean::New(1));
+ }
}
return handleScope.Close(v8::Boolean::New(0));
}
+@interface JMXScript ()
+{
+ NSThread *nodeJSTimersThread;
+}
+@end
@implementation JMXScript
@synthesize scriptEntity, runloopTimers, eventListeners, ctx;
@@ -609,17 +602,36 @@ static char *argv[2] = { (char *)"JMX", NULL };
return self;
}
-- (void)nodejsRun
+- (void)jsRunTimers
{
uint64_t maxDelta = 1e9 / 120.0; // max 120 ticks per seconds
while (![[NSThread currentThread] isCancelled]) {
@try {
- v8::Locker locker;
- v8::HandleScope handle_scope;
- v8::Context::Scope context_scope(ctx);
+ //v8::Context::Scope context_scope(ctx);
uint64_t timeStamp = CVGetCurrentHostTime();
+
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- uv_run(uv_default_loop(), (uv_run_mode)(UV_RUN_ONCE | UV_RUN_NOWAIT));
+ //uv_run(uv_default_loop(), (uv_run_mode)(UV_RUN_ONCE | UV_RUN_NOWAIT));
+ NSMutableArray *toRemove = [NSMutableArray array];
+ for (JMXScriptTimer *scriptTimer in runloopTimers) {
+ if (!scriptTimer.timer.isValid) {
+ [toRemove addObject:scriptTimer];
+ } else if ([scriptTimer.timer.fireDate
+ compare:[NSDate dateWithTimeInterval:0.001
+ sinceDate:[NSDate date]]] == NSOrderedAscending)
+ {
+ [scriptTimer.timer fire];
+ if (scriptTimer.repeats) {
+ scriptTimer.timer.fireDate = [NSDate dateWithTimeInterval:scriptTimer.timer.timeInterval sinceDate:[NSDate date]];
+ }
+ }
+ }
+
+
+ for (JMXScriptTimer *scriptTimer in toRemove) {
+ [self removeRunloopTimer:scriptTimer];
+ }
+
uint64_t now = CVGetCurrentHostTime();
uint64_t delta = now - timeStamp;
uint64_t sleepTime = (delta && delta < maxDelta) ? maxDelta - delta : 0;
@@ -710,6 +722,7 @@ static char *argv[2] = { (char *)"JMX", NULL };
HandleScope handleScope;
v8::TryCatch try_catch;
v8::Handle<Value> ret = function->Call(function, (int)count, argv);
+
if (ret.IsEmpty()) {
ReportException(&try_catch);
return Undefined();
@@ -724,6 +737,7 @@ static char *argv[2] = { (char *)"JMX", NULL };
v8::TryCatch try_catch;
v8::Handle<Value> ret = function->Call(function, 0, nil);
+
if (ret.IsEmpty()) {
ReportException(&try_catch);
@@ -783,11 +797,10 @@ static char *argv[2] = { (char *)"JMX", NULL };
operationQueue = [[NSOperationQueue alloc] init];
-
// second part of node initialization
Handle<Object> process = node::SetupProcessObject(1, argv);
v8_typed_array::AttachBindings(ctx->Global());
-
+
// Create all the objects, load modules, do everything.
node::Load(process);
@@ -810,12 +823,10 @@ static char *argv[2] = { (char *)"JMX", NULL };
//exit(4);
}
}
- if (!nodeJSTimersThread) {
- nodeJSTimersThread = [[NSThread alloc] initWithTarget:self
- selector:@selector(nodejsRun)
- object:nil];
- [nodeJSTimersThread start];
- }
+ nodeJSTimersThread = [[NSThread alloc] initWithTarget:self
+ selector:@selector(jsRunTimers)
+ object:self];
+ [nodeJSTimersThread start];
}
- (void)clearPersistentInstances
@@ -833,7 +844,7 @@ static char *argv[2] = { (char *)"JMX", NULL };
[t invalidate];
}
[runloopTimers removeAllObjects];
- [self execCode:@"clearAllTimers()"];
+ //[self execCode:@"clearAllTimers()"];
}
- (void)stop
@@ -841,10 +852,12 @@ static char *argv[2] = { (char *)"JMX", NULL };
v8::Locker locker;
v8::HandleScope handle_scope;
v8::Context::Scope context_scope(ctx);
+
+
+ [nodeJSTimersThread cancel];
+ [nodeJSTimersThread release];
[self clearTimers];
- // and tell V8 we want to release anything possible (by notifying a low memory condition
- V8::LowMemoryNotification();
if (scriptEntity) {
if ([scriptEntity conformsToProtocol:@protocol(JMXRunLoop)])
@@ -852,27 +865,32 @@ static char *argv[2] = { (char *)"JMX", NULL };
scriptEntity = nil;
}
[self clearPersistentInstances];
-}
-
-- (void)dealloc
-{
- v8::Locker locker;
- v8::HandleScope handle_scope;
- v8::Context::Scope context_scope(ctx);
-
- // and tell V8 we want to release anything possible (by notifying a low memory condition
- V8::LowMemoryNotification();
-
- ctx->Global()->Delete(String::New("scriptEntity"));
+
+ Local<Array> properties = ctx->Global()->GetPropertyNames();
+ for (int i = 0; i < properties->Length(); i++) {
+ Local<String> str = properties->Get(i)->ToString();
+ v8::String::Utf8Value cstr(str);
+ if (strcmp("process", *cstr) == 0)
+ continue;
+ ctx->Global()->Delete(str);
+ }
ctx->Global().Clear();
- //contextes.erase(self);
+
ctx.Dispose();
ctx.Clear();
+
+ // and tell V8 we want to release anything possible (by notifying a low memory condition
+ V8::LowMemoryNotification();
+
// notify that we have disposed the context
- V8::ContextDisposedNotification();
+ V8::ContextDisposedNotification();
+
while( !V8::IdleNotification() )
;
-
+}
+
+- (void)dealloc
+{
[persistentInstances release];
[runloopTimers release];
[eventListeners release];
@@ -882,7 +900,8 @@ static char *argv[2] = { (char *)"JMX", NULL };
- (BOOL)execCode:(NSString *)code
{
- return ExecJSCode((const char *)[code UTF8String], (uint32_t)[code length], "code");
+ BOOL ret = ExecJSCode((const char *)[code UTF8String], (uint32_t)[code length], "code");
+ return ret;
}
- (BOOL)runScript:(NSString *)script withArgs:(NSArray *)args
diff --git a/core/JMXThreadedEntity.mm b/core/JMXThreadedEntity.mm
index f74b361..71ffaf6 100644
--- a/core/JMXThreadedEntity.mm
+++ b/core/JMXThreadedEntity.mm
@@ -236,55 +236,42 @@
[pool release];
}
-- (void)_do_tick
-{
-// NSThread *currentThread = [NSThread currentThread];
- uint64_t timeStamp = CVGetCurrentHostTime();
- [self tick:timeStamp];
- previousTimeStamp = timeStamp;
-// uint64_t now = CVGetCurrentHostTime();
-// // Check if tick() has returned earlier and we still have time before next tick.
-// // If the current delta is smaller than our frequency, we will wait the difference
-// // between maxDelta and delta to honor the configured frequency.
-// // Otherwise, since we would be already late, we just skip the sleep time and
-// // go for the next frame.
-// uint64_t delta = now - timeStamp;
-// uint64_t sleepTime = (delta && delta < maxDelta) ? maxDelta - delta : 0;
- //return sleepTime;
-}
-
- (void)run
{
NSThread *currentThread = [NSThread currentThread];
realEntity.active = YES;
- [[NSOperationQueue mainQueue] addOperation:[NSO]]
while (![currentThread isCancelled]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- //uint64_t sleepTime = [self _do_tick];
uint64_t maxDelta = 1e9 / [self.frequency doubleValue];
uint64_t timeStamp = CVGetCurrentHostTime();
- uint64_t delta = timeStamp - previousTimeStamp;
+ [self tick:timeStamp];
+ previousTimeStamp = timeStamp;
+ uint64_t now = CVGetCurrentHostTime();
+ // Check if tick() has returned earlier and we still have time before next tick.
+ // If the current delta is smaller than our frequency, we will wait the difference
+ // between maxDelta and delta to honor the configured frequency.
+ // Otherwise, since we would be already late, we just skip the sleep time and
+ // go for the next frame.
+ uint64_t delta = now - timeStamp;
uint64_t sleepTime = (delta && delta < maxDelta) ? maxDelta - delta : 0;
- [[NSRunLoop currentRunLoop] addTimer:[NSTimer timerWithTimeInterval:(double)(sleepTime/1e9) target:self selector:@selector(_do_tick) userInfo:nil repeats:NO] forMode:NSRunLoopCommonModes];
- [[NSRunLoop currentRunLoop] run];
-
-// if (sleepTime) {
-// // using nanosleep is a good portable way, but since we are running
-// // on OSX only, we should try relying on the NSThread API.
-// // We will switch back to nanosleep if we notice that 'sleepForTimeInterval'
-// // is not precise enough.
-// struct timespec time = { 0, 0 };
-// struct timespec remainder = { 0, static_cast<long>(sleepTime) };
-// do {
-// time.tv_sec = remainder.tv_sec;
-// time.tv_nsec = remainder.tv_nsec;
-// remainder.tv_nsec = 0;
-// nanosleep(&time, &remainder);
-// } while (remainder.tv_sec || remainder.tv_nsec);
-// } else {
-// // mmm ... no sleep time ... perhaps we are out of resources and slowing down mixing
-// // TODO - produce a warning in this case
-// }
+
+ if (sleepTime) {
+ // using nanosleep is a good portable way, but since we are running
+ // on OSX only, we should try relying on the NSThread API.
+ // We will switch back to nanosleep if we notice that 'sleepForTimeInterval'
+ // is not precise enough.
+ struct timespec time = { 0, 0 };
+ struct timespec remainder = { 0, static_cast<long>(sleepTime) };
+ do {
+ time.tv_sec = remainder.tv_sec;
+ time.tv_nsec = remainder.tv_nsec;
+ remainder.tv_nsec = 0;
+ nanosleep(&time, &remainder);
+ } while (remainder.tv_sec || remainder.tv_nsec);
+ } else {
+ // mmm ... no sleep time ... perhaps we are out of resources and slowing down mixing
+ // TODO - produce a warning in this case
+ }
[pool drain];
}
realEntity.active = NO;
diff --git a/core/NSXMLNode+V8.mm b/core/NSXMLNode+V8.mm
index 1554b03..8f854a1 100644
--- a/core/NSXMLNode+V8.mm
+++ b/core/NSXMLNode+V8.mm
@@ -697,19 +697,19 @@ static v8::Handle<Value> DispatchEvent(const Arguments& args)
return objectTemplate;
}
-static void JMXNodeJSDestructor(Persistent<Value> object, void *parameter)
-{
- HandleScope handle_scope;
- v8::Locker lock;
- id obj = static_cast<id>(parameter);
- NSDebug(@"V8 WeakCallback (%@) called ", obj);
- [obj release];
- if (!object.IsEmpty()) {
- object.ClearWeak();
- object.Dispose();
- object.Clear();
- }
-}
+//static void JMXNodeJSDestructor(Persistent<Value> object, void *parameter)
+//{
+// HandleScope handle_scope;
+// v8::Locker lock;
+// id obj = static_cast<id>(parameter);
+// NSDebug(@"V8 WeakCallback (%@) called ", obj);
+// [obj release];
+// if (!object.IsEmpty()) {
+// object.ClearWeak();
+// object.Dispose();
+// object.Clear();
+// }
+//}
- (v8::Handle<v8::Object>)jsObj
{
@@ -717,7 +717,7 @@ static void JMXNodeJSDestructor(Persistent<Value> object, void *parameter)
HandleScope handle_scope;
v8::Handle<FunctionTemplate> objectTemplate = [[self class] jsObjectTemplate];
v8::Persistent<Object> jsInstance = Persistent<Object>::New(objectTemplate->InstanceTemplate()->NewInstance());
- jsInstance.MakeWeak([self retain], JMXNodeJSDestructor);
+ //jsInstance.MakeWeak([self retain], JMXNodeJSDestructor);
jsInstance->SetPointerInInternalField(0, self);
//[ctx addPersistentInstance:jsInstance obj:self];
return handle_scope.Close(jsInstance);
diff --git a/entities/JMXDrawEntity.mm b/entities/JMXDrawEntity.mm
index 00096fe..4c153c5 100644
--- a/entities/JMXDrawEntity.mm
+++ b/entities/JMXDrawEntity.mm
@@ -23,7 +23,7 @@ JMXV8_EXPORT_NODE_CLASS(JMXDrawEntity);
{
self = [super init];
if (self) {
- canvas = [[JMXCanvasElement alloc] init];
+ canvas = [[[JMXCanvasElement alloc] init] autorelease];
JMXThreadedEntity *threadedEntity = [[JMXThreadedEntity threadedEntity:self] retain];
self.label = @"DrawPath";
drawPath = canvas.drawPath; // NOTE : weak reference (it's owned by the canvas)
@@ -37,14 +37,16 @@ JMXV8_EXPORT_NODE_CLASS(JMXDrawEntity);
- (void)setSize:(JMXSize *)aSize
{
[super setSize:aSize];
- canvas.width = aSize.width;
- canvas.height = aSize.height;
+ if (aSize) {
+ canvas.width = aSize.width;
+ canvas.height = aSize.height;
+ }
}
- (void)dealloc
{
[canvas detach];
- [canvas release];
+ //[canvas release];
[super dealloc];
}
diff --git a/node.js/src/node.js b/node.js/src/node.js
index 270965f..4d76bd6 100644
--- a/node.js/src/node.js
+++ b/node.js/src/node.js
@@ -172,7 +172,7 @@
_timeouts = new Array();
_intervals = new Array();
-/*
+ /*
global.setTimeout = function() {
var t = NativeModule.require('timers');
timer = t.setTimeout.apply(this, arguments);
@@ -204,7 +204,6 @@
}
return t.clearInterval.apply(this, arguments);
};
-*/
global.clearAllTimers = function() {
for (i in _intervals)
@@ -212,6 +211,7 @@
for (i in _timeouts)
global.clearTimeout(_timeouts[i]);
}
+ */
global.setImmediate = function() {
var t = NativeModule.require('timers');