Skip to content

Commit e91471e

Browse files
authored
Merge pull request #52 from romgrk/signal-loop-issues
Fix signal errors & loop issues
2 parents 468c7b8 + 2011ea8 commit e91471e

File tree

12 files changed

+182
-49
lines changed

12 files changed

+182
-49
lines changed

doc/overrides.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Implementing overrides
2+
3+
- [Functions that create GMainLoop](#functions-that-create-gmainloop)
4+
5+
### Functions that create GMainLoop
6+
7+
Functions that create a GMainLoop should be wrapped as shown in the snippet below.
8+
The function to quit the created loop must be pushed unto the `loopStack`.
9+
Internally, NodeGTK uses this stack to quit all running loops when an exception occurs.
10+
11+
```javascript
12+
const originalMain = Gtk.main
13+
Gtk.main = function main() {
14+
const loopStack = require('../native.js').GetLoopStack()
15+
16+
loopStack.push(Gtk.mainQuit)
17+
originalMain()
18+
loopStack.pop()
19+
}
20+
```

examples/entry.js

+14-13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ const Gdk = gi.require('Gdk')
1010
gi.startLoop()
1111
Gtk.init()
1212

13+
14+
process.on('uncaughtException', (err) => {
15+
console.log('process.uncaughtException', err)
16+
process.exit(1)
17+
})
18+
process.on('exit', (code) => {
19+
console.log('process.exit', code)
20+
})
21+
process.on('SIGINT', () => {
22+
console.log('process.SIGINT')
23+
process.exit(2)
24+
})
25+
26+
1327
// main program window
1428
const window = new Gtk.Window({
1529
type : Gtk.WindowType.TOPLEVEL
@@ -20,26 +34,13 @@ const entry = new Gtk.Entry()
2034
entry.on('key-press-event', (event) => {
2135
console.log(event)
2236
console.log(event.string)
23-
console.log('')
24-
25-
console.log(event, Object.keys(event))
26-
console.log(event.__proto__, Object.keys(event.__proto__))
27-
28-
const e = new Gdk.EventKey()
29-
console.log(e, Object.keys(e))
30-
console.log(e.__proto__, Object.keys(e.__proto__))
31-
32-
console.log(e.__proto__ === event.__proto__)
33-
console.log(e.__proto__.__proto__ === event.__proto__.__proto__)
3437
})
3538

3639

3740
// configure main window
3841
window.setDefaultSize(200, 50)
3942
window.setResizable(true)
4043
window.connect('show', () => {
41-
// bring it on top in OSX
42-
// window.setKeepAbove(true)
4344
Gtk.main()
4445
})
4546
window.on('destroy', () => Gtk.mainQuit())

lib/index.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@
55
const camelCase = require('lodash.camelcase')
66
const snakeCase = require('lodash.snakecase')
77

8-
let gi;
9-
try {
10-
gi = require('../build/Release/node-gtk');
11-
} catch(e) {
12-
gi = require('../build/Debug/node-gtk');
13-
}
8+
const gi = require('./native.js')
149

1510
// The bootstrap from C here contains functions and methods for each object,
1611
// namespaced with underscores. See gi.cc for more information.

lib/native.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* native.js
3+
*/
4+
5+
let native
6+
7+
try {
8+
native = require('../build/Release/node-gtk');
9+
} catch(e) {
10+
native = require('../build/Debug/node-gtk');
11+
}
12+
13+
module.exports = native

lib/overrides/Gtk-3.0.js

+12
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,17 @@
22
* Gtk-3.0.js
33
*/
44

5+
const internal = require('../native.js')
6+
57
exports.apply = (Gtk) => {
8+
9+
const originalMain = Gtk.main
10+
Gtk.main = function main() {
11+
const loopStack = internal.GetLoopStack()
12+
13+
loopStack.push(Gtk.mainQuit)
14+
originalMain()
15+
loopStack.pop()
16+
}
17+
618
}

src/closure.cc

+8-27
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,16 @@
11
#include <glib.h>
22
#include <nan.h>
33

4+
#include "closure.h"
45
#include "debug.h"
5-
#include "function.h"
6+
#include "loop.h"
67
#include "type.h"
78
#include "value.h"
89

910
using namespace v8;
1011

1112
namespace GNodeJS {
1213

13-
struct Closure {
14-
GClosure base;
15-
Persistent<Function> persistent;
16-
GISignalInfo* info;
17-
18-
~Closure() {
19-
if (info)
20-
g_base_info_unref(info);
21-
}
22-
23-
static void Marshal(GClosure *closure,
24-
GValue *g_return_value,
25-
uint argc, const GValue *g_argv,
26-
gpointer invocation_hint,
27-
gpointer marshal_data);
28-
29-
static void Invalidated(gpointer data, GClosure *closure);
30-
};
31-
3214
void Closure::Marshal(GClosure *base,
3315
GValue *g_return_value,
3416
uint argc, const GValue *g_argv,
@@ -78,12 +60,11 @@ void Closure::Marshal(GClosure *base,
7860
}
7961
}
8062
else {
81-
auto stackTrace = try_catch.StackTrace();
82-
if (!stackTrace.IsEmpty())
83-
printf("%s\n", *Nan::Utf8String(stackTrace.ToLocalChecked()));
84-
else
85-
printf("%s\n", *Nan::Utf8String(try_catch.Exception()));
86-
exit(1);
63+
log("'%s' did throw", g_base_info_get_name (closure->info));
64+
65+
GNodeJS::QuitLoopStack();
66+
67+
try_catch.ReThrow();
8768
}
8869

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

99-
GClosure *MakeClosure(Isolate *isolate, Local<Function> function, GISignalInfo* info) {
80+
GClosure *MakeClosure(Isolate *isolate, Local<Function> function, GIBaseInfo* info) {
10081
Closure *closure = (Closure *) g_closure_new_simple (sizeof (*closure), NULL);
10182
closure->persistent.Reset(isolate, function);
10283
closure->info = info;

src/closure.h

+23
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,31 @@
66
#include <girepository.h>
77
#include <glib-object.h>
88

9+
using v8::Function;
10+
using v8::Persistent;
11+
912
namespace GNodeJS {
1013

14+
struct Closure {
15+
GClosure base;
16+
Persistent<Function> persistent;
17+
GIBaseInfo* info;
18+
19+
~Closure() {
20+
persistent.Reset();
21+
if (info)
22+
g_base_info_unref(info);
23+
}
24+
25+
static void Marshal(GClosure *closure,
26+
GValue *g_return_value,
27+
uint argc, const GValue *g_argv,
28+
gpointer invocation_hint,
29+
gpointer marshal_data);
30+
31+
static void Invalidated(gpointer data, GClosure *closure);
32+
};
33+
1134
GClosure *MakeClosure(v8::Isolate *isolate, v8::Handle<v8::Function> function, GISignalInfo* info);
1235

1336
};

src/debug.h

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
#pragma once
33

4+
#include <string.h>
45
#include <girepository.h>
56

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

21+
#ifdef NDEBUG
22+
#define warn(...)
23+
#else
24+
#define warn(...) \
25+
do { \
26+
printf("\x1b[1;38;5;202m"); \
27+
printf("%s:\x1b[0m\x1b[1m %s: %i: \x1b[0m", FILE_NAME, FUNCTION_NAME, __LINE__); \
28+
printf(__VA_ARGS__); \
29+
printf("\n"); \
30+
} while (0)
31+
#endif
32+
2033
#ifdef NDEBUG
2134
#define log(...)
2235
#else

src/gi.cc

+8-2
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ using GNodeJS::BaseInfo;
1717

1818
namespace GNodeJS {
1919

20-
Nan::Persistent<Object> moduleCache(Nan::New<Object>());
21-
2220
G_DEFINE_QUARK(gnode_js_object, object);
2321
G_DEFINE_QUARK(gnode_js_template, template);
2422

23+
Nan::Persistent<Object> moduleCache(Nan::New<Object>());
24+
2525
Local<Object> GetModuleCache() {
2626
return Nan::New<Object>(GNodeJS::moduleCache);
2727
}
@@ -331,6 +331,11 @@ NAN_METHOD(GetTypeSize) {
331331
info.GetReturnValue().Set(Nan::New<Number>(size));
332332
}
333333

334+
NAN_METHOD(GetLoopStack) {
335+
auto stack = GNodeJS::GetLoopStack();
336+
info.GetReturnValue().Set(stack);
337+
}
338+
334339
void InitModule(Local<Object> exports, Local<Value> module, void *priv) {
335340
NAN_EXPORT(exports, Bootstrap);
336341
NAN_EXPORT(exports, GetModuleCache);
@@ -348,6 +353,7 @@ void InitModule(Local<Object> exports, Local<Value> module, void *priv) {
348353
NAN_EXPORT(exports, InternalFieldCount);
349354
NAN_EXPORT(exports, GetBaseClass);
350355
NAN_EXPORT(exports, GetTypeSize);
356+
NAN_EXPORT(exports, GetLoopStack);
351357
}
352358

353359
NODE_MODULE(gi, InitModule)

src/loop.cc

+27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#include <glib.h>
22
#include <uv.h>
3+
#include <nan.h>
4+
#include <v8.h>
35

6+
#include "debug.h"
7+
#include "gi.h"
48
#include "loop.h"
59
#include "util.h"
610

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

18+
using namespace v8;
19+
1420
namespace GNodeJS {
1521

22+
static Nan::Persistent<Array> loopStack(Nan::New<Array> ());
23+
1624
struct uv_loop_source {
1725
GSource source;
1826
uv_loop_t *loop;
@@ -70,4 +78,23 @@ void StartLoop() {
7078
g_source_attach (source, NULL);
7179
}
7280

81+
Local<Array> GetLoopStack() {
82+
return Nan::New<Array>(loopStack);
83+
}
84+
85+
void QuitLoopStack() {
86+
Local<Array> stack = GetLoopStack();
87+
88+
for (uint32_t i = 0; i < stack->Length(); i++) {
89+
Local<Object> fn = Nan::Get(stack, i).ToLocalChecked()->ToObject();
90+
Local<Object> self = fn;
91+
92+
log("calling %s", *Nan::Utf8String(Nan::Get(fn, UTF8("name")).ToLocalChecked()));
93+
94+
Nan::CallAsFunction(fn, self, 0, nullptr);
95+
}
96+
97+
loopStack.Reset(Nan::New<Array>());
98+
}
99+
73100
};

src/loop.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11

22
#pragma once
33

4+
#include <nan.h>
5+
#include <v8.h>
6+
7+
using namespace v8;
8+
49
namespace GNodeJS {
510

6-
void StartLoop();
11+
void StartLoop();
12+
13+
void QuitLoopStack();
14+
15+
Local<Array> GetLoopStack();
716

817
};

tests/loop.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* loop.js
3+
*/
4+
5+
6+
const gi = require('../lib/')
7+
const Gtk = gi.require('Gtk', '3.0')
8+
9+
gi.startLoop()
10+
Gtk.init()
11+
12+
let didCallTimeout = false
13+
let didCallPromise = false
14+
15+
setTimeout(() => {
16+
console.log('timeout')
17+
didCallTimeout = true
18+
}, 500)
19+
20+
const promise = new Promise(resolve => resolve('promise resolved'))
21+
promise.then(result => {
22+
console.log(result)
23+
didCallPromise = true
24+
})
25+
26+
setTimeout(() => {
27+
Gtk.mainQuit()
28+
console.assert(didCallTimeout, 'did not call timeout')
29+
console.assert(didCallPromise, 'did not call promise')
30+
console.log('done')
31+
}, 1000)
32+
33+
Gtk.main()

0 commit comments

Comments
 (0)