14 Commits
Author SHA1 Message Date
Kenton Varda d8de1b49ad Disambiguate getsockname() call. 2019-11-23 11:19:17 -08:00
Kenton Varda 4df200effa Implement KJ's new whenWriteDisconnected(). 2019-02-09 15:31:44 -08:00
Kenton Varda cbb3ab40fb Fix age-old bug with flag combining.
I guess it never cauased a problem because the result was 1 which just happened to map to TAKE_OWNERSHIP and the other flags are safe to skip (just inefficient)...
2019-02-09 15:29:42 -08:00
Kenton Varda 1abd120c12 Update to C++14 as required by Cap'n Proto 0.7. 2019-01-07 13:40:56 -08:00
Kenton Varda ca30a96c1c Merge pull request #43 from qm3ster/patch-1
update capnp-js link
2018-05-14 15:27:29 -07:00
Mihal Malostanidis d2f4c72d64 update capnp-js link
Reorganization in progress
2018-05-08 00:02:48 +03:00
Kenton Varda 202e13c66f Hack: For Sandstorm build, catch segfaults and print stack traces. 2018-04-15 17:28:00 -07:00
Kenton Varda a3e975d43c Bump version number and publish again because apparently npm went and published a 13MB scratch folder I had in the directory last time, wtf. 2018-02-09 08:43:47 -08:00
Kenton Varda f1e9306cd6 Release 0.3.0 which supports node 8+. 2018-02-04 14:17:42 -08:00
Kenton Varda c6fe3c2624 Revert "Add temporary code to count local capability types and support dumping them to stderr periodically."
This reverts commit 74958750f1.

This is no longer needed.
2018-02-04 14:16:01 -08:00
Kenton Varda 7fb9018515 Correctly handle pipelining on a Capability. 2018-02-04 14:12:01 -08:00
Kenton Varda 742d6b5b79 Fix obvious, glaring memory leak. I dunno what to say. 2018-01-06 12:17:56 -08:00
Kenton Varda ec1721f298 Node 8: Properly support capabilities defined using ES6 classes.
I'm not sure why this worked with Node 4 / Meteor 1.5, but behavior changed in Node 8 / Meteor 1.6.
2017-11-18 16:50:37 -08:00
Kenton Varda 5aeffa328a Node 8: Transition from V8 hidden values to V8 private values. 2017-11-18 16:49:55 -08:00
6 changed files with 130 additions and 85 deletions

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@ messages from complete Javascript objects. This actually makes the library
somewhat nicer syntactically than it would be otherwise, but it is not fast.
A pure-Javascript implementation would likely be much faster. See
[capnp-js](https://github.com/capnp-js/serialization) for such an
[capnp-js](https://github.com/capnp-js) for such an
implementation. Unfortunately, that implementation is incomplete and does not
support [RPC](https://github.com/capnp-js/rpc) just yet. Hence, this hack was
created for short-term use.
+2 -2
View File
@@ -4,12 +4,12 @@
'target_name': 'capnp',
'sources': ['src/node-capnp/capnp.cc'],
'libraries': ['-lkj', '-lkj-async', '-lcapnp', '-lcapnpc', '-lcapnp-rpc'],
'cflags_cc': ['-std=c++11'],
'cflags_cc': ['-std=c++14'],
'cflags_cc!': ['-fno-rtti', '-fno-exceptions'],
'conditions': [
[ 'OS=="mac"', {
'xcode_settings': {
'OTHER_CPLUSPLUSFLAGS' : ['-std=c++11','-stdlib=libc++'],
'OTHER_CPLUSPLUSFLAGS' : ['-std=c++14','-stdlib=libc++'],
'OTHER_LDFLAGS': ['-stdlib=libc++'],
'GCC_ENABLE_CPP_RTTI': 'YES',
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "capnp",
"version": "0.2.0",
"version": "0.3.1",
"description": "A wrapper for the C++ Cap'n Proto library",
"keywords": [ "capnproto", "serialization" ],
"homepage": "http://capnproto.org",
@@ -26,7 +26,7 @@
"url": "git://github.com/kentonv/node-capnp.git"
},
"engines": {
"node": ">=0.12"
"node": ">=8.0"
},
"license": "MIT"
}
+38 -21
View File
@@ -263,31 +263,48 @@ child.stdio[1].once("readable", function() {
closed: false
};
var localCap = new capnp.Capability(pow, Calculator.Function);
assert.equal(9, wait(localCap.call([3, 2])).value);
assert(!pow.closed);
localCap.close();
assert(pow.closed);
function usePow(pow) {
var localCap = new capnp.Capability(pow, Calculator.Function);
assert.equal(9, wait(localCap.call([3, 2])).value);
assert(!pow.closed);
localCap.close();
assert(pow.closed);
var promise = calc.evaluate(
{call: {"function": subtract, params: [
{call: {"function": add, params: [
{literal: 123}, {literal: 456}]}},
{literal: 321}]}});
var promise = calc.evaluate(
{call: {"function": subtract, params: [
{call: {"function": add, params: [
{literal: 123}, {literal: 456}]}},
{literal: 321}]}});
var value = promise.value;
assert.equal(258, wait(value.read()).value);
value.close();
var value = promise.value;
assert.equal(258, wait(value.read()).value);
value.close();
pow.closed = false;
value = calc.evaluate(
{call: {"function": pow, params: [{literal: 2}, {literal: 4}]}}).value;
assert.equal(16, wait(value.read()).value);
pow.closed = false;
value = calc.evaluate(
{call: {"function": pow, params: [{literal: 2}, {literal: 4}]}}).value;
assert.equal(16, wait(value.read()).value);
// Wait a moment to give the capability a chance to be dropped.
wait(new Promise((resolve, reject) => setTimeout(resolve, 10)));
assert(pow.closed);
value.close();
// Wait a moment to give the capability a chance to be dropped.
wait(new Promise((resolve, reject) => setTimeout(resolve, 10)));
assert(pow.closed);
value.close();
}
usePow(pow);
// Try using a class.
class PowClass {
constructor() {
this.closed = false
}
call(params) {
return Math.pow(params[0], params[1]);
}
close() {
this.closed = true;
}
};
usePow(new PowClass());
// Try wrapping a promise as a capability -- calls are queued until resolution.
var resolvePromisedCalc;
+74 -48
View File
@@ -47,7 +47,6 @@
#include <set>
#include <stdlib.h>
#include <sys/uio.h>
#include <algorithm>
#include <typeinfo>
#include <typeindex>
@@ -237,7 +236,7 @@ static int applyFlags(int fd, uint flags) {
static constexpr uint NEW_FD_FLAGS =
#if __linux__
kj::LowLevelAsyncIoProvider::ALREADY_CLOEXEC || kj::LowLevelAsyncIoProvider::ALREADY_NONBLOCK ||
kj::LowLevelAsyncIoProvider::ALREADY_CLOEXEC | kj::LowLevelAsyncIoProvider::ALREADY_NONBLOCK |
#endif
kj::LowLevelAsyncIoProvider::TAKE_OWNERSHIP;
// We always try to open FDs with CLOEXEC and NONBLOCK already set on Linux, but on other platforms
@@ -410,6 +409,13 @@ public:
KJ_SYSCALL(shutdown(fd, SHUT_WR));
}
#if CAPNP_VERSION >= 8000
kj::Promise<void> whenWriteDisconnected() override {
// TODO(someday): Implement using UV_DISCONNECT?
return kj::NEVER_DONE;
}
#endif
private:
kj::Promise<size_t> tryReadInternal(void* buffer, size_t minBytes, size_t maxBytes,
size_t alreadyRead) {
@@ -564,7 +570,7 @@ public:
struct sockaddr_in6 inet6;
} addr;
addrlen = sizeof(addr);
KJ_SYSCALL(getsockname(fd, &addr.generic, &addrlen));
KJ_SYSCALL(::getsockname(fd, &addr.generic, &addrlen));
switch (addr.generic.sa_family) {
case AF_INET: return ntohs(addr.inet4.sin_port);
case AF_INET6: return ntohs(addr.inet6.sin6_port);
@@ -866,6 +872,17 @@ class Wrapper {
handle.SetWeak(this, &deleteCallback, v8::WeakCallbackType::kParameter);
}
~WrappedObject() {
// Since destructors might perform non-trivial operations, including calling back into
// JavaScript, delay the destructor until the next turn of the event loop.
T* ptrCopy = ptr;
kj::evalLater([ptrCopy]() {
delete ptrCopy;
}).detach([](kj::Exception&& exception) {
KJ_LOG(ERROR, exception);
});
}
static void deleteCallback(const v8::WeakCallbackInfo<WrappedObject>& data) {
delete reinterpret_cast<WrappedObject*>(data.GetParameter());
}
@@ -892,11 +909,14 @@ public:
if (getFunctionTemplate<T>()->HasInstance(obj)) {
return *reinterpret_cast<WrappedObject<T>*>(obj->GetAlignedPointerFromInternalField(0))->ptr;
} else {
v8::Handle<v8::Value> native = obj->GetHiddenValue(newSymbol("capnp::native"));
if (native.IsEmpty() || native->IsUndefined()) {
return nullptr;
} else {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Handle<v8::Value> native;
if (obj->GetPrivate(isolate->GetCurrentContext(),
v8::Private::ForApi(isolate, newSymbol("capnp::native"))).ToLocal(&native) &&
!native->IsUndefined()) {
return tryUnwrap<T>(native);
} else {
return nullptr;
}
}
}
@@ -1013,7 +1033,11 @@ void setNative(const v8::FunctionCallbackInfo<v8::Value>& args) {
// passed to fromJs().
if (args[0]->IsObject()) {
v8::Object::Cast(*args[0])->SetHiddenValue(newSymbol("capnp::native"), args[1]);
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Object::Cast(*args[0])->SetPrivate(
isolate->GetCurrentContext(),
v8::Private::ForApi(isolate, newSymbol("capnp::native")),
args[1]);
}
}
@@ -1876,7 +1900,11 @@ void fromBytes(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}
wrapper->SetHiddenValue(newSymbol("buffer"), bufferHandle);
v8::Isolate* isolate = v8::Isolate::GetCurrent();
wrapper->SetPrivate(
isolate->GetCurrentContext(),
v8::Private::ForApi(isolate, newSymbol("capnp::buffer")),
bufferHandle);
return wrapper;
});
}
@@ -2219,11 +2247,18 @@ void pipelineToJs(CapnpContext& context, capnp::DynamicStruct::Pipeline&& pipeli
v8::Handle<v8::Object> fieldValue;
switch (proto.which()) {
case capnp::schema::Field::SLOT:
switch (proto.getSlot().getType().which()) {
case capnp::schema::Field::SLOT: {
auto type = field.getType();
switch (type.which()) {
case capnp::schema::Type::STRUCT:
fieldValue = pipelineStructFieldToJs(context, pipeline, field, capConstructor);
break;
case capnp::schema::Type::ANY_POINTER:
if (type.whichAnyPointerKind() !=
capnp::schema::Type::AnyPointer::Unconstrained::CAPABILITY) {
continue;
}
// fallthrough
case capnp::schema::Type::INTERFACE: {
auto cap = pipeline.get(field).releaseAs<capnp::DynamicCapability>();
capnp::Schema capSchema = cap.getSchema();
@@ -2240,6 +2275,7 @@ void pipelineToJs(CapnpContext& context, capnp::DynamicStruct::Pipeline&& pipeli
continue;
}
break;
}
case capnp::schema::Field::GROUP:
fieldValue = pipelineStructFieldToJs(context, pipeline, field, capConstructor);
@@ -2339,51 +2375,17 @@ void cancel(const v8::FunctionCallbackInfo<v8::Value>& args) {
// -----------------------------------------------------------------------------
// Local caps
struct TypeHash {
inline size_t operator()(const capnp::Type& t) const {
return t.hashCode();
}
};
std::unordered_map<capnp::Type, uint, TypeHash> localCapTypeCounts;
void dumpLocalCapTypeCounts(const v8::FunctionCallbackInfo<v8::Value>& args) {
liftKj(args, [&]() -> void {
typedef std::pair<capnp::Type, uint> E;
kj::Array<E> array = KJ_MAP(e, localCapTypeCounts) { return E(e); };
std::sort(array.begin(), array.end(),
[](const E& a, const E& b) { return a.second > b.second; });
auto lines = KJ_MAP(e, array) {
return kj::str('\n', e.second, " ", e.first.asInterface().getProto().getDisplayName());
};
auto typeStats = kj::strArray(lines, "");
KJ_LOG(WARNING, "dumping types of local capabilities", typeStats);
});
}
class LocalCap final: public capnp::DynamicCapability::Server {
public:
LocalCap(capnp::InterfaceSchema schema, v8::Handle<v8::Object> object,
CapnpContext& capnpContext, v8::Handle<v8::Value> capnpContextHandle)
: capnp::DynamicCapability::Server(schema),
object(object), capnpContext(capnpContext), capnpContextHandle(capnpContextHandle) {
localCapTypeCounts[schema]++;
}
object(object), capnpContext(capnpContext), capnpContextHandle(capnpContextHandle) {}
~LocalCap() {
// Call the object's close() method if it has one, so that it can react to the handle being
// dropped.
auto iter = localCapTypeCounts.find(getSchema());
if (iter == localCapTypeCounts.end()) {
KJ_LOG(ERROR, "inconsistent type counts");
} else if (--iter->second == 0) {
localCapTypeCounts.erase(iter);
}
v8::HandleScope scope(v8::Isolate::GetCurrent());
auto jsMethod = object->Get(newSymbol("close"));
@@ -2796,6 +2798,31 @@ void chacha20(const v8::FunctionCallbackInfo<v8::Value>& args) {
});
}
namespace {
void crashHandler(int signo, siginfo_t* info, void* context) {
void* traceSpace[32];
// ignoreCount = 2 to ignore crashHandler() and signal trampoline.
auto trace = kj::getStackTrace(traceSpace, 2);
auto message = kj::str("*** Received signal #", signo, ": ", strsignal(signo),
"\nstack: ", stringifyStackTraceAddresses(trace), '\n');
kj::FdOutputStream(STDERR_FILENO).write(message.begin(), message.size());
_exit(1);
}
} // namespace
void catchSegfaults() {
struct sigaction action;
memset(&action, 0, sizeof(action));
action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND;
action.sa_sigaction = &crashHandler;
KJ_SYSCALL(sigaction(SIGSEGV, &action, nullptr));
}
#endif // SANDSTORM_BUILD
// -----------------------------------------------------------------------------
@@ -2848,9 +2875,8 @@ void init(v8::Handle<v8::Object> exports) {
#ifdef SANDSTORM_BUILD
mapFunction("matchPowerboxQuery", matchPowerboxQuery);
mapFunction("chacha20", chacha20);
catchSegfaults();
#endif
mapFunction("dumpLocalCapTypeCounts", dumpLocalCapTypeCounts);
}
} // namespace
+13 -11
View File
@@ -192,16 +192,20 @@ function wrapLocalMethod(self, method) {
}
function LocalCapWrapper(obj) {
for (var name in obj) {
if (name === "close") {
// Not an RPC method. Called when the capability has no clients.
this.close = obj.close.bind(obj);
} else {
var method = obj[name];
if (typeof method === "function") {
this[name] = wrapLocalMethod(obj, method);
var type = obj;
while (type && type !== Object.prototype) {
Object.getOwnPropertyNames(type).forEach(name => {
if (name === "close") {
// Not an RPC method. Called when the capability has no clients.
this.close = obj.close.bind(obj);
} else {
var method = obj[name];
if (typeof method === "function") {
this[name] = wrapLocalMethod(obj, method);
}
}
}
})
type = Object.getPrototypeOf(type);
}
}
@@ -298,5 +302,3 @@ exports.bytesToPreorder = function(schema, buf, options) {
if (v8capnp.matchPowerboxQuery) exports.matchPowerboxQuery = v8capnp.matchPowerboxQuery;
if (v8capnp.chacha20) exports.chacha20 = v8capnp.chacha20;
exports.dumpLocalCapTypeCounts = v8capnp.dumpLocalCapTypeCounts;