Skip to content

Commit 7f882ce

Browse files
author
Minggang Wang
committed
Support non-global init
Fix #437
1 parent 0af883c commit 7f882ce

File tree

6 files changed

+132
-20
lines changed

6 files changed

+132
-20
lines changed

.circleci/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ jobs:
2222
- run: brew install python
2323
- run: brew install python@2
2424
- run: brew install wget cmake cppcheck tinyxml eigen pcre poco
25+
- run: brew install openssl
2526
- run: brew install asio
2627
- run: brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/02f04f84902937422d529945bbabdcb6491b147a/Formula/tinyxml2.rb
2728
- run: python3 -m pip install catkin_pkg empy pyparsing pyyaml setuptools argcomplete colcon-common-extensions
@@ -41,7 +42,7 @@ jobs:
4142
- ./node_modules
4243
# Test
4344
- run: npm install istanbul coveralls
44-
- run: source ~/ros2_install/ros2-osx/local_setup.bash && node scripts/compile_tests.js && node ./node_modules/.bin/istanbul cover ./scripts/run_test.js --report lcovonly && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage
45+
- run: source ~/ros2_install/ros2-osx/local_setup.bash && export OPENSSL_ROOT_DIR="/usr/local/opt/openssl" && node scripts/compile_tests.js && node ./node_modules/.bin/istanbul cover ./scripts/run_test.js --report lcovonly && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage
4546
# Teardown
4647
- run: find $HOME/Library/Developer/Xcode/DerivedData -name '*.xcactivitylog' -exec cp {} $CIRCLE_ARTIFACTS/xcactivitylog \; || true
4748
# Save test results

index.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const Time = require('./lib/time.js');
3030
const TimeSource = require('./lib/time_source.js');
3131
const {Clock, ROSClock} = require('./lib/clock.js');
3232
const Duration = require('./lib/duration.js');
33+
const Context = require('./lib/context.js');
3334

3435
function inherits(target, source) {
3536
let properties = Object.getOwnPropertyNames(source.prototype);
@@ -102,14 +103,15 @@ let rcl = {
102103
* Create a node.
103104
* @param {string} nodeName - The name used to register in ROS.
104105
* @param {string} namespace - The namespace used in ROS, default is an empty string.
106+
* @param {Context} context - The context, default is Context.defaultContext().
105107
* @return {Node} The instance of Node.
106108
*/
107-
createNode(nodeName, namespace = '') {
109+
createNode(nodeName, namespace = '', context = Context.defaultContext()) {
108110
if (typeof (nodeName) !== 'string' || typeof (namespace) !== 'string') {
109111
throw new TypeError('Invalid argument.');
110112
}
111113

112-
let handle = rclnodejs.createNode(nodeName, namespace);
114+
let handle = rclnodejs.createNode(nodeName, namespace, context.handle());
113115
let node = new rclnodejs.ShadowNode();
114116

115117
node.init(nodeName, namespace);
@@ -121,10 +123,10 @@ let rcl = {
121123

122124
/**
123125
* Init the module.
124-
* @param {array} args - The command line arguments to pass to rcl.
126+
* @param {Context} context - The context, default is Context.defaultContext().
125127
* @return {Promise<undefined>} A Promise.
126128
*/
127-
init(...args) {
129+
init(context = Context.defaultContext()) {
128130
return new Promise((resolve, reject) => {
129131
let that = this;
130132
if (!this._initialized) {
@@ -137,8 +139,8 @@ let rcl = {
137139
}
138140

139141
generator.generateAll(forced).then(() => {
140-
rclnodejs.init(args);
141-
debug('Finish initializing rcl with args = %o.', args);
142+
this._context = context;
143+
rclnodejs.init(context.handle());
142144
ActionLib.config({
143145
log: that.logging.getLogger('actionlibjs'),
144146
time: require('./lib/actions/time_utils.js'),
@@ -157,6 +159,7 @@ let rcl = {
157159
});
158160

159161
this._initialized = true;
162+
// this._context = undefined;
160163
resolve();
161164
}).catch(e => {
162165
reject(e);
@@ -186,10 +189,10 @@ let rcl = {
186189
},
187190

188191
/**
189-
* Terminate the node, this will destory all the allocated resources and quit.
192+
* @param {Context} context - The context to be shutdown.
190193
* @return {undefined}
191194
*/
192-
shutdown() {
195+
shutdown(context) {
193196
if (!this._initialized) {
194197
throw new Error('The module rclnodejs has been shut.');
195198
}
@@ -198,8 +201,11 @@ let rcl = {
198201
node.stopSpinning();
199202
node.destroy();
200203
});
201-
202-
rclnodejs.shutdown();
204+
if (!context) {
205+
Context.shutdownDefaultContext();
206+
} else {
207+
context.shutdown();
208+
}
203209
this._nodes = [];
204210
this._initialized = false;
205211
},

lib/context.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright (c) 2019 Intel Corporation. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const rclnodejs = require('bindings')('rclnodejs');
18+
19+
let defaultContext = undefined;
20+
21+
class Context {
22+
constructor() {
23+
this._handle = rclnodejs.createContext();
24+
}
25+
26+
shutdown() {
27+
rclnodejs.shutdown(this._handle);
28+
}
29+
30+
tryShutdown() {
31+
if (rclnodejs.ok(this._handle)) {
32+
rclnodejs.shutdown(this._handle);
33+
}
34+
}
35+
36+
handle() {
37+
return this._handle;
38+
}
39+
40+
static defaultContext() {
41+
if (!defaultContext) {
42+
defaultContext = new Context();
43+
}
44+
return defaultContext;
45+
}
46+
47+
static shutdownDefaultContext() {
48+
defaultContext.shutdown();
49+
defaultContext = undefined;
50+
}
51+
}
52+
53+
module.exports = Context;

lib/node.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const Service = require('./service.js');
2323
const QoS = require('./qos.js');
2424
const debug = require('debug')('rclnodejs:node');
2525
const loader = require('./interface_loader.js');
26-
26+
const Context = require('./context.js');
2727
/**
2828
* @class - Class representing a Node in ROS
2929
* @hideconstructor
@@ -136,9 +136,10 @@ class Node {
136136
* Create a Timer.
137137
* @param {number} period - The number representing period in millisecond.
138138
* @param {function} callback - The callback to be called when timeout.
139+
* @param {Context} context - The context, default is Context.defaultContext().
139140
* @return {Timer} - An instance of Timer.
140141
*/
141-
createTimer(period, callback) {
142+
createTimer(period, callback, context = Context.defaultContext()) {
142143
if (typeof (period) !== 'number' || typeof (callback) !== 'function') {
143144
throw new TypeError('Invalid argument');
144145
}
@@ -151,7 +152,7 @@ class Node {
151152
throw new RangeError('Parameter must be between ' + 0 + ' and ' + MAX_TIMER_PERIOD_IN_MILLISECOND);
152153
}
153154

154-
let timerHandle = rclnodejs.createTimer(period);
155+
let timerHandle = rclnodejs.createTimer(period, context.handle());
155156
let timer = new Timer(timerHandle, period, callback);
156157
debug('Finish creating timer, period = %d.', period);
157158
this._timers.push(timer);

src/rcl_bindings.cpp

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,24 @@ namespace rclnodejs {
4040
std::unique_ptr<rmw_qos_profile_t> GetQoSProfile(v8::Local<v8::Value> qos);
4141

4242
NAN_METHOD(Init) {
43+
rcl_allocator_t allocator = rcl_get_default_allocator();
44+
rcl_init_options_t init_options = rcl_get_zero_initialized_init_options();
45+
rcl_ret_t ret = rcl_init_options_init(&init_options, allocator);
46+
47+
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(info[0]->ToObject());
48+
rcl_context_t* context =
49+
reinterpret_cast<rcl_context_t*>(context_handle->ptr());
4350
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
44-
rcl_init(0, nullptr, rcl_get_default_allocator()),
51+
rcl_init(0, nullptr, &init_options, context),
4552
rcl_get_error_string().str);
4653
}
4754

4855
NAN_METHOD(CreateNode) {
4956
std::string node_name(*v8::String::Utf8Value(info[0]));
5057
std::string name_space(*v8::String::Utf8Value(info[1]));
58+
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(info[2]->ToObject());
59+
rcl_context_t* context =
60+
reinterpret_cast<rcl_context_t*>(context_handle->ptr());
5161

5262
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(malloc(sizeof(rcl_node_t)));
5363

@@ -56,7 +66,8 @@ NAN_METHOD(CreateNode) {
5666

5767
THROW_ERROR_IF_NOT_EQUAL(
5868
RCL_RET_OK,
59-
rcl_node_init(node, node_name.c_str(), name_space.c_str(), &options),
69+
rcl_node_init(node, node_name.c_str(), name_space.c_str(), context,
70+
&options),
6071
rcl_get_error_string().str);
6172

6273
auto handle = RclHandle::NewInstance(node, nullptr,
@@ -66,6 +77,9 @@ NAN_METHOD(CreateNode) {
6677

6778
NAN_METHOD(CreateTimer) {
6879
int64_t period_ms = info[0]->IntegerValue();
80+
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(info[1]->ToObject());
81+
rcl_context_t* context =
82+
reinterpret_cast<rcl_context_t*>(context_handle->ptr());
6983
rcl_timer_t* timer =
7084
reinterpret_cast<rcl_timer_t*>(malloc(sizeof(rcl_timer_t)));
7185
*timer = rcl_get_zero_initialized_timer();
@@ -78,8 +92,9 @@ NAN_METHOD(CreateTimer) {
7892
&allocator),
7993
rcl_get_error_string().str);
8094
THROW_ERROR_IF_NOT_EQUAL(RCL_RET_OK,
81-
rcl_timer_init(timer, clock, RCL_MS_TO_NS(period_ms),
82-
nullptr, rcl_get_default_allocator()),
95+
rcl_timer_init(timer, clock, context,
96+
RCL_MS_TO_NS(period_ms), nullptr,
97+
rcl_get_default_allocator()),
8398
rcl_get_error_string().str);
8499

85100
auto js_obj = RclHandle::NewInstance(
@@ -893,8 +908,24 @@ std::unique_ptr<rmw_qos_profile_t> GetQoSProfile(v8::Local<v8::Value> qos) {
893908
return qos_profile;
894909
}
895910

911+
int DestroyContext(rcl_context_t* context) {
912+
rcl_ret_t ret = RCL_RET_OK;
913+
if (context->impl) {
914+
if (rcl_context_is_valid(context)) {
915+
if (RCL_RET_OK != rcl_shutdown(context)) {
916+
Nan::ThrowError(rcl_get_error_string().str);
917+
}
918+
ret = rcl_context_fini(context);
919+
}
920+
}
921+
return ret;
922+
}
923+
896924
NAN_METHOD(Shutdown) {
897-
THROW_ERROR_IF_NOT_EQUAL(rcl_shutdown(), RCL_RET_OK,
925+
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(info[0]->ToObject());
926+
rcl_context_t* context =
927+
reinterpret_cast<rcl_context_t*>(context_handle->ptr());
928+
THROW_ERROR_IF_NOT_EQUAL(rcl_shutdown(context), RCL_RET_OK,
898929
rcl_get_error_string().str);
899930
info.GetReturnValue().Set(Nan::Undefined());
900931
}
@@ -1002,6 +1033,24 @@ NAN_METHOD(IsEnableFor) {
10021033
info.GetReturnValue().Set(Nan::New(enabled));
10031034
}
10041035

1036+
NAN_METHOD(CreateContext) {
1037+
rcl_context_t* context =
1038+
reinterpret_cast<rcl_context_t*>(malloc(sizeof(rcl_context_t)));
1039+
*context = rcl_get_zero_initialized_context();
1040+
auto js_obj = RclHandle::NewInstance(context, nullptr,
1041+
std::bind(DestroyContext, context));
1042+
1043+
info.GetReturnValue().Set(js_obj);
1044+
}
1045+
1046+
NAN_METHOD(IsContextValid) {
1047+
RclHandle* context_handle = RclHandle::Unwrap<RclHandle>(info[0]->ToObject());
1048+
rcl_context_t* context =
1049+
reinterpret_cast<rcl_context_t*>(context_handle->ptr());
1050+
bool is_valid = rcl_context_is_valid(context);
1051+
info.GetReturnValue().Set(Nan::New(is_valid));
1052+
}
1053+
10051054
uint32_t GetBindingMethodsCount(BindingMethod* methods) {
10061055
uint32_t count = 0;
10071056
while (methods[count].function) {
@@ -1058,6 +1107,8 @@ BindingMethod binding_methods[] = {
10581107
{"getLoggerEffectiveLevel", GetLoggerEffectiveLevel},
10591108
{"log", Log},
10601109
{"isEnableFor", IsEnableFor},
1110+
{"createContext", CreateContext},
1111+
{"ok", IsContextValid},
10611112
{"", nullptr}};
10621113

10631114
} // namespace rclnodejs

test/test-init-shutdown.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,6 @@ describe('Node destroy testing', function() {
9494
it('rclnodejs create node without init', function() {
9595
assert.throws(function() {
9696
rclnodejs.createNode('my_node');
97-
}, /has not been called/, 'Failed to createNode');
97+
});
9898
});
9999
});

0 commit comments

Comments
 (0)