Skip to content

Commit

Permalink
Merge pull request #52 from romgrk/signal-loop-issues
Browse files Browse the repository at this point in the history
Fix signal errors & loop issues
  • Loading branch information
romgrk authored Jul 18, 2018
2 parents 468c7b8 + 2011ea8 commit e91471e
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 49 deletions.
20 changes: 20 additions & 0 deletions doc/overrides.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Implementing overrides

- [Functions that create GMainLoop](#functions-that-create-gmainloop)

### Functions that create GMainLoop

Functions that create a GMainLoop should be wrapped as shown in the snippet below.
The function to quit the created loop must be pushed unto the `loopStack`.
Internally, NodeGTK uses this stack to quit all running loops when an exception occurs.

```javascript
const originalMain = Gtk.main
Gtk.main = function main() {
const loopStack = require('../native.js').GetLoopStack()

loopStack.push(Gtk.mainQuit)
originalMain()
loopStack.pop()
}
```
27 changes: 14 additions & 13 deletions examples/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ const Gdk = gi.require('Gdk')
gi.startLoop()
Gtk.init()


process.on('uncaughtException', (err) => {
console.log('process.uncaughtException', err)
process.exit(1)
})
process.on('exit', (code) => {
console.log('process.exit', code)
})
process.on('SIGINT', () => {
console.log('process.SIGINT')
process.exit(2)
})


// main program window
const window = new Gtk.Window({
type : Gtk.WindowType.TOPLEVEL
Expand All @@ -20,26 +34,13 @@ const entry = new Gtk.Entry()
entry.on('key-press-event', (event) => {
console.log(event)
console.log(event.string)
console.log('')

console.log(event, Object.keys(event))
console.log(event.__proto__, Object.keys(event.__proto__))

const e = new Gdk.EventKey()
console.log(e, Object.keys(e))
console.log(e.__proto__, Object.keys(e.__proto__))

console.log(e.__proto__ === event.__proto__)
console.log(e.__proto__.__proto__ === event.__proto__.__proto__)
})


// configure main window
window.setDefaultSize(200, 50)
window.setResizable(true)
window.connect('show', () => {
// bring it on top in OSX
// window.setKeepAbove(true)
Gtk.main()
})
window.on('destroy', () => Gtk.mainQuit())
Expand Down
7 changes: 1 addition & 6 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@
const camelCase = require('lodash.camelcase')
const snakeCase = require('lodash.snakecase')

let gi;
try {
gi = require('../build/Release/node-gtk');
} catch(e) {
gi = require('../build/Debug/node-gtk');
}
const gi = require('./native.js')

// The bootstrap from C here contains functions and methods for each object,
// namespaced with underscores. See gi.cc for more information.
Expand Down
13 changes: 13 additions & 0 deletions lib/native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* native.js
*/

let native

try {
native = require('../build/Release/node-gtk');
} catch(e) {
native = require('../build/Debug/node-gtk');
}

module.exports = native
12 changes: 12 additions & 0 deletions lib/overrides/Gtk-3.0.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,17 @@
* Gtk-3.0.js
*/

const internal = require('../native.js')

exports.apply = (Gtk) => {

const originalMain = Gtk.main
Gtk.main = function main() {
const loopStack = internal.GetLoopStack()

loopStack.push(Gtk.mainQuit)
originalMain()
loopStack.pop()
}

}
35 changes: 8 additions & 27 deletions src/closure.cc
Original file line number Diff line number Diff line change
@@ -1,34 +1,16 @@
#include <glib.h>
#include <nan.h>

#include "closure.h"
#include "debug.h"
#include "function.h"
#include "loop.h"
#include "type.h"
#include "value.h"

using namespace v8;

namespace GNodeJS {

struct Closure {
GClosure base;
Persistent<Function> persistent;
GISignalInfo* info;

~Closure() {
if (info)
g_base_info_unref(info);
}

static void Marshal(GClosure *closure,
GValue *g_return_value,
uint argc, const GValue *g_argv,
gpointer invocation_hint,
gpointer marshal_data);

static void Invalidated(gpointer data, GClosure *closure);
};

void Closure::Marshal(GClosure *base,
GValue *g_return_value,
uint argc, const GValue *g_argv,
Expand Down Expand Up @@ -78,12 +60,11 @@ void Closure::Marshal(GClosure *base,
}
}
else {
auto stackTrace = try_catch.StackTrace();
if (!stackTrace.IsEmpty())
printf("%s\n", *Nan::Utf8String(stackTrace.ToLocalChecked()));
else
printf("%s\n", *Nan::Utf8String(try_catch.Exception()));
exit(1);
log("'%s' did throw", g_base_info_get_name (closure->info));

GNodeJS::QuitLoopStack();

try_catch.ReThrow();
}

#ifndef __linux__
Expand All @@ -96,7 +77,7 @@ void Closure::Invalidated(gpointer data, GClosure *base) {
closure->~Closure();
}

GClosure *MakeClosure(Isolate *isolate, Local<Function> function, GISignalInfo* info) {
GClosure *MakeClosure(Isolate *isolate, Local<Function> function, GIBaseInfo* info) {
Closure *closure = (Closure *) g_closure_new_simple (sizeof (*closure), NULL);
closure->persistent.Reset(isolate, function);
closure->info = info;
Expand Down
23 changes: 23 additions & 0 deletions src/closure.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,31 @@
#include <girepository.h>
#include <glib-object.h>

using v8::Function;
using v8::Persistent;

namespace GNodeJS {

struct Closure {
GClosure base;
Persistent<Function> persistent;
GIBaseInfo* info;

~Closure() {
persistent.Reset();
if (info)
g_base_info_unref(info);
}

static void Marshal(GClosure *closure,
GValue *g_return_value,
uint argc, const GValue *g_argv,
gpointer invocation_hint,
gpointer marshal_data);

static void Invalidated(gpointer data, GClosure *closure);
};

GClosure *MakeClosure(v8::Isolate *isolate, v8::Handle<v8::Function> function, GISignalInfo* info);

};
13 changes: 13 additions & 0 deletions src/debug.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#pragma once

#include <string.h>
#include <girepository.h>

#define FILE_NAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
Expand All @@ -17,6 +18,18 @@
printf("\x1b[0m\n"); } \
while (0)

#ifdef NDEBUG
#define warn(...)
#else
#define warn(...) \
do { \
printf("\x1b[1;38;5;202m"); \
printf("%s:\x1b[0m\x1b[1m %s: %i: \x1b[0m", FILE_NAME, FUNCTION_NAME, __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
} while (0)
#endif

#ifdef NDEBUG
#define log(...)
#else
Expand Down
10 changes: 8 additions & 2 deletions src/gi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ using GNodeJS::BaseInfo;

namespace GNodeJS {

Nan::Persistent<Object> moduleCache(Nan::New<Object>());

G_DEFINE_QUARK(gnode_js_object, object);
G_DEFINE_QUARK(gnode_js_template, template);

Nan::Persistent<Object> moduleCache(Nan::New<Object>());

Local<Object> GetModuleCache() {
return Nan::New<Object>(GNodeJS::moduleCache);
}
Expand Down Expand Up @@ -331,6 +331,11 @@ NAN_METHOD(GetTypeSize) {
info.GetReturnValue().Set(Nan::New<Number>(size));
}

NAN_METHOD(GetLoopStack) {
auto stack = GNodeJS::GetLoopStack();
info.GetReturnValue().Set(stack);
}

void InitModule(Local<Object> exports, Local<Value> module, void *priv) {
NAN_EXPORT(exports, Bootstrap);
NAN_EXPORT(exports, GetModuleCache);
Expand All @@ -348,6 +353,7 @@ void InitModule(Local<Object> exports, Local<Value> module, void *priv) {
NAN_EXPORT(exports, InternalFieldCount);
NAN_EXPORT(exports, GetBaseClass);
NAN_EXPORT(exports, GetTypeSize);
NAN_EXPORT(exports, GetLoopStack);
}

NODE_MODULE(gi, InitModule)
27 changes: 27 additions & 0 deletions src/loop.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include <glib.h>
#include <uv.h>
#include <nan.h>
#include <v8.h>

#include "debug.h"
#include "gi.h"
#include "loop.h"
#include "util.h"

Expand All @@ -11,8 +15,12 @@
* either uv allows external sources to drive prepare/check, or until GLib
* exposes an epoll fd to wait on... */

using namespace v8;

namespace GNodeJS {

static Nan::Persistent<Array> loopStack(Nan::New<Array> ());

struct uv_loop_source {
GSource source;
uv_loop_t *loop;
Expand Down Expand Up @@ -70,4 +78,23 @@ void StartLoop() {
g_source_attach (source, NULL);
}

Local<Array> GetLoopStack() {
return Nan::New<Array>(loopStack);
}

void QuitLoopStack() {
Local<Array> stack = GetLoopStack();

for (uint32_t i = 0; i < stack->Length(); i++) {
Local<Object> fn = Nan::Get(stack, i).ToLocalChecked()->ToObject();
Local<Object> self = fn;

log("calling %s", *Nan::Utf8String(Nan::Get(fn, UTF8("name")).ToLocalChecked()));

Nan::CallAsFunction(fn, self, 0, nullptr);
}

loopStack.Reset(Nan::New<Array>());
}

};
11 changes: 10 additions & 1 deletion src/loop.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@

#pragma once

#include <nan.h>
#include <v8.h>

using namespace v8;

namespace GNodeJS {

void StartLoop();
void StartLoop();

void QuitLoopStack();

Local<Array> GetLoopStack();

};
33 changes: 33 additions & 0 deletions tests/loop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* loop.js
*/


const gi = require('../lib/')
const Gtk = gi.require('Gtk', '3.0')

gi.startLoop()
Gtk.init()

let didCallTimeout = false
let didCallPromise = false

setTimeout(() => {
console.log('timeout')
didCallTimeout = true
}, 500)

const promise = new Promise(resolve => resolve('promise resolved'))
promise.then(result => {
console.log(result)
didCallPromise = true
})

setTimeout(() => {
Gtk.mainQuit()
console.assert(didCallTimeout, 'did not call timeout')
console.assert(didCallPromise, 'did not call promise')
console.log('done')
}, 1000)

Gtk.main()

0 comments on commit e91471e

Please sign in to comment.