Commit 4ee25e43 authored by Kenton Varda's avatar Kenton Varda

Simplify exceptions. Eliminate Durability. Rename Nature to Type and simplify it.

Distinguishing between "local bugs" and "preconditions" was proving difficult in practice, because a precondition failure in one function may very well indicate a bug in a calling function, but the exception may be thrown through that function, thus when caught the classification is nonsensical. The distinction also was not as useful as imagined. So, I eliminated this distinction.
parent 428f1313
...@@ -575,8 +575,7 @@ class BrokenClient final: public ClientHook, public kj::Refcounted { ...@@ -575,8 +575,7 @@ class BrokenClient final: public ClientHook, public kj::Refcounted {
public: public:
BrokenClient(const kj::Exception& exception): exception(exception) {} BrokenClient(const kj::Exception& exception): exception(exception) {}
BrokenClient(const kj::StringPtr description) BrokenClient(const kj::StringPtr description)
: exception(kj::Exception::Nature::PRECONDITION, kj::Exception::Durability::PERMANENT, : exception(kj::Exception::Type::FAILED, "", 0, kj::str(description)) {}
"", 0, kj::str(description)) {}
Request<AnyPointer, AnyPointer> newCall( Request<AnyPointer, AnyPointer> newCall(
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) override { uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) override {
......
...@@ -646,17 +646,10 @@ public: ...@@ -646,17 +646,10 @@ public:
private: private:
struct ParseErrorCatcher: public kj::ExceptionCallback { struct ParseErrorCatcher: public kj::ExceptionCallback {
void onRecoverableException(kj::Exception&& e) { void onRecoverableException(kj::Exception&& e) {
if (e.getNature() == kj::Exception::Nature::PRECONDITION) { // Only capture the first exception, on the assumption that later exceptions are probably
// This is probably a problem with the input. Let's try to report it more nicely. // just cascading problems.
if (exception == nullptr) {
// Only capture the first exception, on the assumption that later exceptions are probably exception = kj::mv(e);
// just cascading problems.
if (exception == nullptr) {
exception = kj::mv(e);
}
} else {
// This is probably a bug, not a problem with the input.
ExceptionCallback::onRecoverableException(kj::mv(e));
} }
} }
......
...@@ -190,9 +190,7 @@ public: ...@@ -190,9 +190,7 @@ public:
TestNetworkAdapter(TestNetwork& network): network(network) {} TestNetworkAdapter(TestNetwork& network): network(network) {}
~TestNetworkAdapter() { ~TestNetworkAdapter() {
kj::Exception exception( kj::Exception exception = KJ_EXCEPTION(FAILED, "Network was destroyed.");
kj::Exception::Nature::PRECONDITION, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__, kj::str("Network was destroyed."));
for (auto& entry: connections) { for (auto& entry: connections) {
entry.second->disconnect(kj::cp(exception)); entry.second->disconnect(kj::cp(exception));
} }
......
...@@ -107,45 +107,15 @@ Orphan<List<rpc::PromisedAnswer::Op>> fromPipelineOps( ...@@ -107,45 +107,15 @@ Orphan<List<rpc::PromisedAnswer::Op>> fromPipelineOps(
} }
kj::Exception toException(const rpc::Exception::Reader& exception) { kj::Exception toException(const rpc::Exception::Reader& exception) {
kj::Exception::Nature nature = return kj::Exception(static_cast<kj::Exception::Type>(exception.getType()),
exception.getIsCallersFault() "(remote)", 0, kj::str("remote exception: ", exception.getReason()));
? kj::Exception::Nature::PRECONDITION
: kj::Exception::Nature::LOCAL_BUG;
kj::Exception::Durability durability;
switch (exception.getDurability()) {
default:
case rpc::Exception::Durability::PERMANENT:
durability = kj::Exception::Durability::PERMANENT;
break;
case rpc::Exception::Durability::TEMPORARY:
durability = kj::Exception::Durability::TEMPORARY;
break;
case rpc::Exception::Durability::OVERLOADED:
durability = kj::Exception::Durability::OVERLOADED;
break;
}
return kj::Exception(nature, durability, "(remote)", 0,
kj::str("remote exception: ", exception.getReason()));
} }
void fromException(const kj::Exception& exception, rpc::Exception::Builder builder) { void fromException(const kj::Exception& exception, rpc::Exception::Builder builder) {
// TODO(someday): Indicate the remote server name as part of the stack trace. Maybe even // TODO(someday): Indicate the remote server name as part of the stack trace. Maybe even
// transmit stack traces? // transmit stack traces?
builder.setReason(exception.getDescription()); builder.setReason(exception.getDescription());
builder.setIsCallersFault(exception.getNature() == kj::Exception::Nature::PRECONDITION); builder.setType(static_cast<rpc::Exception::Type>(exception.getType()));
switch (exception.getDurability()) {
case kj::Exception::Durability::PERMANENT:
builder.setDurability(rpc::Exception::Durability::PERMANENT);
break;
case kj::Exception::Durability::TEMPORARY:
builder.setDurability(rpc::Exception::Durability::TEMPORARY);
break;
case kj::Exception::Durability::OVERLOADED:
builder.setDurability(rpc::Exception::Durability::OVERLOADED);
break;
}
} }
uint exceptionSizeHint(const kj::Exception& exception) { uint exceptionSizeHint(const kj::Exception& exception) {
...@@ -318,9 +288,8 @@ public: ...@@ -318,9 +288,8 @@ public:
return; return;
} }
kj::Exception networkException( kj::Exception networkException(kj::Exception::Type::DISCONNECTED,
kj::Exception::Nature::NETWORK_FAILURE, kj::Exception::Durability::PERMANENT, exception.getFile(), exception.getLine(), kj::heapString(exception.getDescription()));
__FILE__, __LINE__, kj::str("Disconnected: ", exception.getDescription()));
KJ_IF_MAYBE(newException, kj::runCatchingExceptions([&]() { KJ_IF_MAYBE(newException, kj::runCatchingExceptions([&]() {
// Carefully pull all the objects out of the tables prior to releasing them because their // Carefully pull all the objects out of the tables prior to releasing them because their
...@@ -1970,9 +1939,7 @@ private: ...@@ -1970,9 +1939,7 @@ private:
handleMessage(kj::mv(*m)); handleMessage(kj::mv(*m));
return true; return true;
} else { } else {
disconnect(kj::Exception( disconnect(KJ_EXCEPTION(DISCONNECTED, "Peer disconnected."));
kj::Exception::Nature::PRECONDITION, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__, kj::str("Peer disconnected.")));
return false; return false;
} }
}).then([this](bool keepGoing) { }).then([this](bool keepGoing) {
...@@ -2626,9 +2593,7 @@ public: ...@@ -2626,9 +2593,7 @@ public:
// disassemble it. // disassemble it.
if (!connections.empty()) { if (!connections.empty()) {
kj::Vector<kj::Own<RpcConnectionState>> deleteMe(connections.size()); kj::Vector<kj::Own<RpcConnectionState>> deleteMe(connections.size());
kj::Exception shutdownException( kj::Exception shutdownException = KJ_EXCEPTION(FAILED, "RpcSystem was destroyed.");
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__, kj::str("RpcSystem was destroyed."));
for (auto& entry: connections) { for (auto& entry: connections) {
entry.second->disconnect(kj::cp(shutdownException)); entry.second->disconnect(kj::cp(shutdownException));
deleteMe.add(kj::mv(entry.second)); deleteMe.add(kj::mv(entry.second));
......
...@@ -1053,29 +1053,86 @@ struct Exception { ...@@ -1053,29 +1053,86 @@ struct Exception {
# **(level 0)** # **(level 0)**
# #
# Describes an arbitrary error that prevented an operation (e.g. a call) from completing. # Describes an arbitrary error that prevented an operation (e.g. a call) from completing.
#
# Cap'n Proto exceptions always indicate that something went wrong. In other words, in a fantasy
# world where everything always works as expected, no exceptions would ever be thrown. Clients
# should only ever catch exceptions as a means to implement fault-tolerance, where "fault" can
# mean:
# - Bugs.
# - Invalid input.
# - Configuration errors.
# - Network probles.
# - Insufficient resources.
# - Version skew (unimplemented functionality).
# - Other logistical problems.
#
# Exceptions should NOT be used to flag application-specific conditions that a client is expected
# to handle in an application-specific way. Put another way, in the Cap'n Proto world,
# "checked exceptions" (where an interface explicitly defines the exceptions it throws and
# clients are forced by the type system to handle those exceptions) do NOT make sense.
reason @0 :Text; reason @0 :Text;
# Human-readable failure description. # Human-readable failure description.
isCallersFault @1 :Bool; type @3 :Type;
# In the best estimate of the error source, is it the caller's fault that this error occurred # The type of the error. The purpose of this enum is not to describe the error itself, but
# (like HTTP 400), or is it the callee's fault (like HTTP 500)? Or, put another way, if an # rather to describe how the client might want to respond to the error.
# automated bug report were to be generated for this error, should it be initially filed on the
# caller's code or the callee's? This is a guess. Generally guesses should err towards blaming enum Type {
# the callee -- at the very least, the callee should be on the hook for improving their error failed @0;
# handling to be more confident in assigning blame. # A generic problem occurred, and it is believed that if the operation were repeated without
# any change in the state of the world, the problem would occur again.
durability @2 :Durability; #
# In the best estimate of the error source, is this error likely to repeat if the same call is # A client might respond to this error by logging it for investigation by the developer and/or
# executed again? Callers might use this to decide when to retry a request. # displaying it to the user.
enum Durability { overloaded @1;
permanent @0; # Retrying the exact same operation will fail in the same way. # The request was rejected due to a temporary lack of resources.
temporary @1; # Retrying the exact same operation might succeed. #
overloaded @2; # The error may be due to the system being overloaded. Retrying may work # Examples include:
# later on, but for now the caller should not retry right away as this will # - There's not enough CPU time to keep up with incoming requests, so some are rejected.
# likely exacerbate the problem. # - The server ran out of RAM or disk space during the request.
# - The operation timed out (took significantly longer than it should have).
#
# A client might respond to this error by scheduling to retry the operation much later. The
# client should NOT retry again immediately since this would likely exacerbate the problem.
disconnected @2;
# The method failed because a connection to some necessary capability was lost.
#
# Examples include:
# - The client introduced the server to a third-party capability, the connection to that third
# party was subsequently lost, and then the client requested that the server use the dead
# capability for something.
# - The client previously requested that the server obtain a capability from some third party.
# The server returned a capability to an object wrapping the third-party capability. Later,
# the server's connection to the third party was lost.
# - The capability has been revoked. Revocation does not necessarily mean that the client is
# no longer authorized to use the capability; it is often used simply as a way to force the
# client to repeat the setup process, perhaps to efficiently move them to a new back-end or
# get them to recognize some other change that has occurred.
#
# A client should normally respond to this error by releasing all capabilities it is currently
# holding related to the one it called and then re-creating them by restoring SturdyRefs and/or
# repeating the method calls used to create them originally. In other words, disconnect and
# start over. This should in turn cause the server to obtain a new copy of the capability that
# it lost, thus making everything work.
#
# If the client receives another `disconnencted` error in the process of rebuilding the
# capability and retrying the call, it should treat this as an `overloaded` error: the network
# is currently unreliable, possibly due to load or other temporary issues.
unimplemented @3;
# The server doesn't implement the requested method. If there is some other method that the
# client could call (perhaps an older and/or slower interface), it should try that instead.
# Otherwise, this should be treated like `serverError`.
} }
obsoleteIsCallersFault @1 :Bool;
# OBSOLETE. Ignore.
obsoleteDurability @2 :UInt16;
# OBSOLETE. See `type` instead.
} }
# ======================================================================================== # ========================================================================================
......
...@@ -1734,7 +1734,7 @@ const ::capnp::_::RawSchema s_d37007fde1f0027d = { ...@@ -1734,7 +1734,7 @@ const ::capnp::_::RawSchema s_d37007fde1f0027d = {
0, 2, i_d37007fde1f0027d, nullptr, nullptr, { &s_d37007fde1f0027d, nullptr, nullptr, 0, 0, nullptr } 0, 2, i_d37007fde1f0027d, nullptr, nullptr, { &s_d37007fde1f0027d, nullptr, nullptr, 0, 0, nullptr }
}; };
#endif // !CAPNP_LITE #endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = { static const ::capnp::_::AlignedData<85> b_d625b7063acf691a = {
{ 0, 0, 0, 0, 5, 0, 6, 0, { 0, 0, 0, 0, 5, 0, 6, 0,
26, 105, 207, 58, 6, 183, 37, 214, 26, 105, 207, 58, 6, 183, 37, 214,
16, 0, 0, 0, 1, 0, 1, 0, 16, 0, 0, 0, 1, 0, 1, 0,
...@@ -1744,7 +1744,7 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = { ...@@ -1744,7 +1744,7 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
21, 0, 0, 0, 210, 0, 0, 0, 21, 0, 0, 0, 210, 0, 0, 0,
33, 0, 0, 0, 23, 0, 0, 0, 33, 0, 0, 0, 23, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45, 0, 0, 0, 175, 0, 0, 0, 41, 0, 0, 0, 231, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99, 97, 112, 110, 112, 47, 114, 112, 99, 97, 112, 110, 112, 47, 114, 112,
...@@ -1752,32 +1752,38 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = { ...@@ -1752,32 +1752,38 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
69, 120, 99, 101, 112, 116, 105, 111, 69, 120, 99, 101, 112, 116, 105, 111,
110, 0, 0, 0, 0, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 1, 0, 1, 0, 4, 0, 0, 0, 1, 0, 1, 0,
88, 249, 182, 7, 38, 218, 174, 187, 88, 189, 76, 63, 226, 150, 140, 178,
1, 0, 0, 0, 90, 0, 0, 0, 1, 0, 0, 0, 42, 0, 0, 0,
68, 117, 114, 97, 98, 105, 108, 105, 84, 121, 112, 101, 0, 0, 0, 0,
116, 121, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 3, 0, 4, 0,
12, 0, 0, 0, 3, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69, 0, 0, 0, 58, 0, 0, 0, 97, 0, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64, 0, 0, 0, 3, 0, 1, 0, 92, 0, 0, 0, 3, 0, 1, 0,
76, 0, 0, 0, 2, 0, 1, 0, 104, 0, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73, 0, 0, 0, 122, 0, 0, 0, 101, 0, 0, 0, 186, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72, 0, 0, 0, 3, 0, 1, 0, 104, 0, 0, 0, 3, 0, 1, 0,
84, 0, 0, 0, 2, 0, 1, 0, 116, 0, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
81, 0, 0, 0, 90, 0, 0, 0, 113, 0, 0, 0, 154, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
80, 0, 0, 0, 3, 0, 1, 0, 116, 0, 0, 0, 3, 0, 1, 0,
92, 0, 0, 0, 2, 0, 1, 0, 128, 0, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 2, 0, 0, 0,
0, 0, 1, 0, 3, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
125, 0, 0, 0, 42, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
120, 0, 0, 0, 3, 0, 1, 0,
132, 0, 0, 0, 2, 0, 1, 0,
114, 101, 97, 115, 111, 110, 0, 0, 114, 101, 97, 115, 111, 110, 0, 0,
12, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
...@@ -1786,7 +1792,8 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = { ...@@ -1786,7 +1792,8 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
12, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105, 115, 67, 97, 108, 108, 101, 114, 111, 98, 115, 111, 108, 101, 116, 101,
73, 115, 67, 97, 108, 108, 101, 114,
115, 70, 97, 117, 108, 116, 0, 0, 115, 70, 97, 117, 108, 116, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
...@@ -1795,10 +1802,19 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = { ...@@ -1795,10 +1802,19 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
100, 117, 114, 97, 98, 105, 108, 105, 111, 98, 115, 111, 108, 101, 116, 101,
68, 117, 114, 97, 98, 105, 108, 105,
116, 121, 0, 0, 0, 0, 0, 0, 116, 121, 0, 0, 0, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
116, 121, 112, 101, 0, 0, 0, 0,
15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0,
88, 249, 182, 7, 38, 218, 174, 187, 88, 189, 76, 63, 226, 150, 140, 178,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
15, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0,
...@@ -1808,60 +1824,63 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = { ...@@ -1808,60 +1824,63 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
::capnp::word const* const bp_d625b7063acf691a = b_d625b7063acf691a.words; ::capnp::word const* const bp_d625b7063acf691a = b_d625b7063acf691a.words;
#if !CAPNP_LITE #if !CAPNP_LITE
static const ::capnp::_::RawSchema* const d_d625b7063acf691a[] = { static const ::capnp::_::RawSchema* const d_d625b7063acf691a[] = {
&s_bbaeda2607b6f958, &s_b28c96e23f4cbd58,
}; };
static const uint16_t m_d625b7063acf691a[] = {2, 1, 0}; static const uint16_t m_d625b7063acf691a[] = {2, 1, 0, 3};
static const uint16_t i_d625b7063acf691a[] = {0, 1, 2}; static const uint16_t i_d625b7063acf691a[] = {0, 1, 2, 3};
const ::capnp::_::RawSchema s_d625b7063acf691a = { const ::capnp::_::RawSchema s_d625b7063acf691a = {
0xd625b7063acf691a, b_d625b7063acf691a.words, 69, d_d625b7063acf691a, m_d625b7063acf691a, 0xd625b7063acf691a, b_d625b7063acf691a.words, 85, d_d625b7063acf691a, m_d625b7063acf691a,
1, 3, i_d625b7063acf691a, nullptr, nullptr, { &s_d625b7063acf691a, nullptr, nullptr, 0, 0, nullptr } 1, 4, i_d625b7063acf691a, nullptr, nullptr, { &s_d625b7063acf691a, nullptr, nullptr, 0, 0, nullptr }
}; };
#endif // !CAPNP_LITE #endif // !CAPNP_LITE
static const ::capnp::_::AlignedData<34> b_bbaeda2607b6f958 = { static const ::capnp::_::AlignedData<37> b_b28c96e23f4cbd58 = {
{ 0, 0, 0, 0, 5, 0, 6, 0, { 0, 0, 0, 0, 5, 0, 6, 0,
88, 249, 182, 7, 38, 218, 174, 187, 88, 189, 76, 63, 226, 150, 140, 178,
26, 0, 0, 0, 2, 0, 0, 0, 26, 0, 0, 0, 2, 0, 0, 0,
26, 105, 207, 58, 6, 183, 37, 214, 26, 105, 207, 58, 6, 183, 37, 214,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 42, 1, 0, 0, 21, 0, 0, 0, 250, 0, 0, 0,
37, 0, 0, 0, 7, 0, 0, 0, 33, 0, 0, 0, 7, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33, 0, 0, 0, 79, 0, 0, 0, 29, 0, 0, 0, 103, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
99, 97, 112, 110, 112, 47, 114, 112, 99, 97, 112, 110, 112, 47, 114, 112,
99, 46, 99, 97, 112, 110, 112, 58, 99, 46, 99, 97, 112, 110, 112, 58,
69, 120, 99, 101, 112, 116, 105, 111, 69, 120, 99, 101, 112, 116, 105, 111,
110, 46, 68, 117, 114, 97, 98, 105, 110, 46, 84, 121, 112, 101, 0, 0,
108, 105, 116, 121, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0,
12, 0, 0, 0, 1, 0, 2, 0, 16, 0, 0, 0, 1, 0, 2, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 0, 0, 0, 82, 0, 0, 0, 41, 0, 0, 0, 58, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
25, 0, 0, 0, 82, 0, 0, 0, 33, 0, 0, 0, 90, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 90, 0, 0, 0, 29, 0, 0, 0, 106, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112, 101, 114, 109, 97, 110, 101, 110, 3, 0, 0, 0, 0, 0, 0, 0,
116, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 114, 0, 0, 0,
116, 101, 109, 112, 111, 114, 97, 114, 0, 0, 0, 0, 0, 0, 0, 0,
121, 0, 0, 0, 0, 0, 0, 0, 102, 97, 105, 108, 101, 100, 0, 0,
111, 118, 101, 114, 108, 111, 97, 100, 111, 118, 101, 114, 108, 111, 97, 100,
101, 100, 0, 0, 0, 0, 0, 0, } 101, 100, 0, 0, 0, 0, 0, 0,
100, 105, 115, 99, 111, 110, 110, 101,
99, 116, 101, 100, 0, 0, 0, 0,
117, 110, 105, 109, 112, 108, 101, 109,
101, 110, 116, 101, 100, 0, 0, 0, }
}; };
::capnp::word const* const bp_bbaeda2607b6f958 = b_bbaeda2607b6f958.words; ::capnp::word const* const bp_b28c96e23f4cbd58 = b_b28c96e23f4cbd58.words;
#if !CAPNP_LITE #if !CAPNP_LITE
static const uint16_t m_bbaeda2607b6f958[] = {2, 0, 1}; static const uint16_t m_b28c96e23f4cbd58[] = {2, 0, 1, 3};
const ::capnp::_::RawSchema s_bbaeda2607b6f958 = { const ::capnp::_::RawSchema s_b28c96e23f4cbd58 = {
0xbbaeda2607b6f958, b_bbaeda2607b6f958.words, 34, nullptr, m_bbaeda2607b6f958, 0xb28c96e23f4cbd58, b_b28c96e23f4cbd58.words, 37, nullptr, m_b28c96e23f4cbd58,
0, 3, nullptr, nullptr, nullptr, { &s_bbaeda2607b6f958, nullptr, nullptr, 0, 0, nullptr } 0, 4, nullptr, nullptr, nullptr, { &s_b28c96e23f4cbd58, nullptr, nullptr, 0, 0, nullptr }
}; };
#endif // !CAPNP_LITE #endif // !CAPNP_LITE
CAPNP_DEFINE_ENUM(Durability_bbaeda2607b6f958, bbaeda2607b6f958); CAPNP_DEFINE_ENUM(Type_b28c96e23f4cbd58, b28c96e23f4cbd58);
} // namespace schemas } // namespace schemas
} // namespace capnp } // namespace capnp
......
...@@ -34,13 +34,14 @@ CAPNP_DECLARE_SCHEMA(d800b1d6cd6f1ca0); ...@@ -34,13 +34,14 @@ CAPNP_DECLARE_SCHEMA(d800b1d6cd6f1ca0);
CAPNP_DECLARE_SCHEMA(f316944415569081); CAPNP_DECLARE_SCHEMA(f316944415569081);
CAPNP_DECLARE_SCHEMA(d37007fde1f0027d); CAPNP_DECLARE_SCHEMA(d37007fde1f0027d);
CAPNP_DECLARE_SCHEMA(d625b7063acf691a); CAPNP_DECLARE_SCHEMA(d625b7063acf691a);
CAPNP_DECLARE_SCHEMA(bbaeda2607b6f958); CAPNP_DECLARE_SCHEMA(b28c96e23f4cbd58);
enum class Durability_bbaeda2607b6f958: uint16_t { enum class Type_b28c96e23f4cbd58: uint16_t {
PERMANENT, FAILED,
TEMPORARY,
OVERLOADED, OVERLOADED,
DISCONNECTED,
UNIMPLEMENTED,
}; };
CAPNP_DECLARE_ENUM(Durability, bbaeda2607b6f958); CAPNP_DECLARE_ENUM(Type, b28c96e23f4cbd58);
} // namespace schemas } // namespace schemas
} // namespace capnp } // namespace capnp
...@@ -397,7 +398,7 @@ struct Exception { ...@@ -397,7 +398,7 @@ struct Exception {
class Reader; class Reader;
class Builder; class Builder;
class Pipeline; class Pipeline;
typedef ::capnp::schemas::Durability_bbaeda2607b6f958 Durability; typedef ::capnp::schemas::Type_b28c96e23f4cbd58 Type;
struct _capnpPrivate { struct _capnpPrivate {
...@@ -2390,9 +2391,11 @@ public: ...@@ -2390,9 +2391,11 @@ public:
inline bool hasReason() const; inline bool hasReason() const;
inline ::capnp::Text::Reader getReason() const; inline ::capnp::Text::Reader getReason() const;
inline bool getIsCallersFault() const; inline bool getObsoleteIsCallersFault() const;
inline ::capnp::rpc::Exception::Durability getDurability() const; inline ::uint16_t getObsoleteDurability() const;
inline ::capnp::rpc::Exception::Type getType() const;
private: private:
::capnp::_::StructReader _reader; ::capnp::_::StructReader _reader;
...@@ -2429,11 +2432,14 @@ public: ...@@ -2429,11 +2432,14 @@ public:
inline void adoptReason(::capnp::Orphan< ::capnp::Text>&& value); inline void adoptReason(::capnp::Orphan< ::capnp::Text>&& value);
inline ::capnp::Orphan< ::capnp::Text> disownReason(); inline ::capnp::Orphan< ::capnp::Text> disownReason();
inline bool getIsCallersFault(); inline bool getObsoleteIsCallersFault();
inline void setIsCallersFault(bool value); inline void setObsoleteIsCallersFault(bool value);
inline ::uint16_t getObsoleteDurability();
inline void setObsoleteDurability( ::uint16_t value);
inline ::capnp::rpc::Exception::Durability getDurability(); inline ::capnp::rpc::Exception::Type getType();
inline void setDurability( ::capnp::rpc::Exception::Durability value); inline void setType( ::capnp::rpc::Exception::Type value);
private: private:
::capnp::_::StructBuilder _builder; ::capnp::_::StructBuilder _builder;
...@@ -4754,34 +4760,48 @@ inline ::capnp::Orphan< ::capnp::Text> Exception::Builder::disownReason() { ...@@ -4754,34 +4760,48 @@ inline ::capnp::Orphan< ::capnp::Text> Exception::Builder::disownReason() {
_builder.getPointerField(0 * ::capnp::POINTERS)); _builder.getPointerField(0 * ::capnp::POINTERS));
} }
inline bool Exception::Reader::getIsCallersFault() const { inline bool Exception::Reader::getObsoleteIsCallersFault() const {
return _reader.getDataField<bool>( return _reader.getDataField<bool>(
0 * ::capnp::ELEMENTS); 0 * ::capnp::ELEMENTS);
} }
inline bool Exception::Builder::getIsCallersFault() { inline bool Exception::Builder::getObsoleteIsCallersFault() {
return _builder.getDataField<bool>( return _builder.getDataField<bool>(
0 * ::capnp::ELEMENTS); 0 * ::capnp::ELEMENTS);
} }
inline void Exception::Builder::setIsCallersFault(bool value) { inline void Exception::Builder::setObsoleteIsCallersFault(bool value) {
_builder.setDataField<bool>( _builder.setDataField<bool>(
0 * ::capnp::ELEMENTS, value); 0 * ::capnp::ELEMENTS, value);
} }
inline ::capnp::rpc::Exception::Durability Exception::Reader::getDurability() const { inline ::uint16_t Exception::Reader::getObsoleteDurability() const {
return _reader.getDataField< ::capnp::rpc::Exception::Durability>( return _reader.getDataField< ::uint16_t>(
1 * ::capnp::ELEMENTS); 1 * ::capnp::ELEMENTS);
} }
inline ::capnp::rpc::Exception::Durability Exception::Builder::getDurability() { inline ::uint16_t Exception::Builder::getObsoleteDurability() {
return _builder.getDataField< ::capnp::rpc::Exception::Durability>( return _builder.getDataField< ::uint16_t>(
1 * ::capnp::ELEMENTS); 1 * ::capnp::ELEMENTS);
} }
inline void Exception::Builder::setDurability( ::capnp::rpc::Exception::Durability value) { inline void Exception::Builder::setObsoleteDurability( ::uint16_t value) {
_builder.setDataField< ::capnp::rpc::Exception::Durability>( _builder.setDataField< ::uint16_t>(
1 * ::capnp::ELEMENTS, value); 1 * ::capnp::ELEMENTS, value);
} }
inline ::capnp::rpc::Exception::Type Exception::Reader::getType() const {
return _reader.getDataField< ::capnp::rpc::Exception::Type>(
2 * ::capnp::ELEMENTS);
}
inline ::capnp::rpc::Exception::Type Exception::Builder::getType() {
return _builder.getDataField< ::capnp::rpc::Exception::Type>(
2 * ::capnp::ELEMENTS);
}
inline void Exception::Builder::setType( ::capnp::rpc::Exception::Type value) {
_builder.setDataField< ::capnp::rpc::Exception::Type>(
2 * ::capnp::ELEMENTS, value);
}
} // namespace } // namespace
} // namespace } // namespace
......
...@@ -462,8 +462,8 @@ public: ...@@ -462,8 +462,8 @@ public:
void reportError(SourcePos start, SourcePos end, kj::StringPtr message) const override { void reportError(SourcePos start, SourcePos end, kj::StringPtr message) const override {
kj::getExceptionCallback().onRecoverableException(kj::Exception( kj::getExceptionCallback().onRecoverableException(kj::Exception(
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::PERMANENT, kj::Exception::Type::FAILED, kj::heapString(diskPath), start.line,
kj::heapString(diskPath), start.line, kj::heapString(message))); kj::heapString(message)));
} }
private: private:
......
...@@ -828,9 +828,7 @@ private: ...@@ -828,9 +828,7 @@ private:
delete this; delete this;
} else { } else {
if (inner->isWaiting()) { if (inner->isWaiting()) {
inner->reject(kj::Exception( inner->reject(kj::Exception(kj::Exception::Type::FAILED, __FILE__, __LINE__,
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__,
kj::heapString("PromiseFulfiller was destroyed without fulfilling the promise."))); kj::heapString("PromiseFulfiller was destroyed without fulfilling the promise.")));
} }
inner = nullptr; inner = nullptr;
......
...@@ -29,21 +29,10 @@ namespace _ { // private ...@@ -29,21 +29,10 @@ namespace _ { // private
void inlineRequireFailure(const char* file, int line, const char* expectation, void inlineRequireFailure(const char* file, int line, const char* expectation,
const char* macroArgs, const char* message) { const char* macroArgs, const char* message) {
if (message == nullptr) { if (message == nullptr) {
Debug::Fault f(file, line, Exception::Nature::PRECONDITION, 0, expectation, macroArgs); Debug::Fault f(file, line, 0, expectation, macroArgs);
f.fatal(); f.fatal();
} else { } else {
Debug::Fault f(file, line, Exception::Nature::PRECONDITION, 0, expectation, macroArgs, message); Debug::Fault f(file, line, 0, expectation, macroArgs, message);
f.fatal();
}
}
void inlineAssertFailure(const char* file, int line, const char* expectation,
const char* macroArgs, const char* message) {
if (message == nullptr) {
Debug::Fault f(file, line, Exception::Nature::LOCAL_BUG, 0, expectation, macroArgs);
f.fatal();
} else {
Debug::Fault f(file, line, Exception::Nature::LOCAL_BUG, 0, expectation, macroArgs, message);
f.fatal(); f.fatal();
} }
} }
......
...@@ -185,9 +185,6 @@ namespace _ { // private ...@@ -185,9 +185,6 @@ namespace _ { // private
KJ_NORETURN(void inlineRequireFailure( KJ_NORETURN(void inlineRequireFailure(
const char* file, int line, const char* expectation, const char* macroArgs, const char* file, int line, const char* expectation, const char* macroArgs,
const char* message = nullptr)); const char* message = nullptr));
KJ_NORETURN(void inlineAssertFailure(
const char* file, int line, const char* expectation, const char* macroArgs,
const char* message = nullptr));
KJ_NORETURN(void unreachable()); KJ_NORETURN(void unreachable());
...@@ -195,20 +192,13 @@ KJ_NORETURN(void unreachable()); ...@@ -195,20 +192,13 @@ KJ_NORETURN(void unreachable());
#ifdef KJ_DEBUG #ifdef KJ_DEBUG
#if _MSC_VER #if _MSC_VER
// TODO(msvc): Figure out __VA_ARGS__; see debug.h.
#define KJ_IREQUIRE(condition, ...) \ #define KJ_IREQUIRE(condition, ...) \
if (KJ_LIKELY(condition)); else ::kj::_::inlineRequireFailure( \ if (KJ_LIKELY(condition)); else ::kj::_::inlineRequireFailure( \
__FILE__, __LINE__, #condition, "") __FILE__, __LINE__, #condition, "" #__VA_ARGS__, __VA_ARGS__)
// Version of KJ_DREQUIRE() which is safe to use in headers that are #included by users. Used to // Version of KJ_DREQUIRE() which is safe to use in headers that are #included by users. Used to
// check preconditions inside inline methods. KJ_IREQUIRE is particularly useful in that // check preconditions inside inline methods. KJ_IREQUIRE is particularly useful in that
// it will be enabled depending on whether the application is compiled in debug mode rather than // it will be enabled depending on whether the application is compiled in debug mode rather than
// whether libkj is. // whether libkj is.
#define KJ_IASSERT(condition, ...) \
if (KJ_LIKELY(condition)); else ::kj::_::inlineAssertFailure( \
__FILE__, __LINE__, #condition, "")
// Version of KJ_DASSERT() which is safe to use in headers that are #included by users. Used to
// check state inside inline and templated methods.
#else #else
#define KJ_IREQUIRE(condition, ...) \ #define KJ_IREQUIRE(condition, ...) \
if (KJ_LIKELY(condition)); else ::kj::_::inlineRequireFailure( \ if (KJ_LIKELY(condition)); else ::kj::_::inlineRequireFailure( \
...@@ -217,18 +207,13 @@ KJ_NORETURN(void unreachable()); ...@@ -217,18 +207,13 @@ KJ_NORETURN(void unreachable());
// check preconditions inside inline methods. KJ_IREQUIRE is particularly useful in that // check preconditions inside inline methods. KJ_IREQUIRE is particularly useful in that
// it will be enabled depending on whether the application is compiled in debug mode rather than // it will be enabled depending on whether the application is compiled in debug mode rather than
// whether libkj is. // whether libkj is.
#define KJ_IASSERT(condition, ...) \
if (KJ_LIKELY(condition)); else ::kj::_::inlineAssertFailure( \
__FILE__, __LINE__, #condition, #__VA_ARGS__, ##__VA_ARGS__)
// Version of KJ_DASSERT() which is safe to use in headers that are #included by users. Used to
// check state inside inline and templated methods.
#endif #endif
#else #else
#define KJ_IREQUIRE(condition, ...) #define KJ_IREQUIRE(condition, ...)
#define KJ_IASSERT(condition, ...)
#endif #endif
#define KJ_IASSERT KJ_IREQUIRE
#define KJ_UNREACHABLE ::kj::_::unreachable(); #define KJ_UNREACHABLE ::kj::_::unreachable();
// Put this on code paths that cannot be reached to suppress compiler warnings about missing // Put this on code paths that cannot be reached to suppress compiler warnings about missing
// returns. // returns.
...@@ -556,11 +541,11 @@ static KJ_CONSTEXPR(const) MinValue_ minValue = MinValue_(); ...@@ -556,11 +541,11 @@ static KJ_CONSTEXPR(const) MinValue_ minValue = MinValue_();
inline constexpr float inf() { return __builtin_huge_valf(); } inline constexpr float inf() { return __builtin_huge_valf(); }
inline constexpr float nan() { return __builtin_nanf(""); } inline constexpr float nan() { return __builtin_nanf(""); }
#elif _MSC_VER #elif _MSC_VER
// Do what MSVC math.h does // Do what MSVC math.h does
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4756) // "overflow in constant arithmetic" #pragma warning(disable: 4756) // "overflow in constant arithmetic"
inline constexpr float inf() { return (float)(1e300 * 1e300); } inline constexpr float inf() { return (float)(1e300 * 1e300); }
#pragma warning(pop) #pragma warning(pop)
inline constexpr float nan() { return inf() * 0.0F; } inline constexpr float nan() { return inf() * 0.0F; }
#else #else
#error "Not sure how to support your compiler." #error "Not sure how to support your compiler."
......
...@@ -233,7 +233,7 @@ TEST(Debug, Log) { ...@@ -233,7 +233,7 @@ TEST(Debug, Log) {
KJ_ASSERT(1 == 1); KJ_ASSERT(1 == 1);
EXPECT_FATAL(KJ_ASSERT(1 == 2)); line = __LINE__; EXPECT_FATAL(KJ_ASSERT(1 == 2)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: expected "
"1 == 2\n", mockCallback.text); "1 == 2\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
...@@ -244,27 +244,38 @@ TEST(Debug, Log) { ...@@ -244,27 +244,38 @@ TEST(Debug, Log) {
bool recovered = false; bool recovered = false;
KJ_ASSERT(1 == 2, "1 is not 2") { recovered = true; break; } line = __LINE__; KJ_ASSERT(1 == 2, "1 is not 2") { recovered = true; break; } line = __LINE__;
EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + ": bug in code: expected " EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + ": failed: expected "
"1 == 2; 1 is not 2\n", mockCallback.text); "1 == 2; 1 is not 2\n", mockCallback.text);
EXPECT_TRUE(recovered); EXPECT_TRUE(recovered);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_FATAL(KJ_ASSERT(1 == 2, i, "hi", str)); line = __LINE__; EXPECT_FATAL(KJ_ASSERT(1 == 2, i, "hi", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text); "1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_FATAL(KJ_REQUIRE(1 == 2, i, "hi", str)); line = __LINE__; EXPECT_FATAL(KJ_REQUIRE(1 == 2, i, "hi", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": requirement not met: expected " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text); "1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_FATAL(KJ_FAIL_ASSERT("foo")); line = __LINE__; EXPECT_FATAL(KJ_FAIL_ASSERT("foo")); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: foo\n", EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": failed: foo\n",
mockCallback.text); mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
} }
TEST(Debug, Exception) {
int i = 123;
int line = __LINE__; Exception exception = KJ_EXCEPTION(DISCONNECTED, "foo", i);
EXPECT_EQ(Exception::Type::DISCONNECTED, exception.getType());
EXPECT_STREQ(__FILE__, exception.getFile());
EXPECT_EQ(line, exception.getLine());
EXPECT_EQ("foo; i = 123", exception.getDescription());
}
TEST(Debug, Catch) { TEST(Debug, Catch) {
int line; int line;
...@@ -280,7 +291,7 @@ TEST(Debug, Catch) { ...@@ -280,7 +291,7 @@ TEST(Debug, Catch) {
what = kj::str(what.slice(0, *eol)); what = kj::str(what.slice(0, *eol));
} }
std::string text(what.cStr()); std::string text(what.cStr());
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text); EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
} else { } else {
ADD_FAILURE() << "Expected exception."; ADD_FAILURE() << "Expected exception.";
} }
...@@ -299,7 +310,7 @@ TEST(Debug, Catch) { ...@@ -299,7 +310,7 @@ TEST(Debug, Catch) {
what = kj::str(what.slice(0, *eol)); what = kj::str(what.slice(0, *eol));
} }
std::string text(what.cStr()); std::string text(what.cStr());
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text); EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
} else { } else {
ADD_FAILURE() << "Expected exception."; ADD_FAILURE() << "Expected exception.";
} }
...@@ -318,7 +329,7 @@ TEST(Debug, Catch) { ...@@ -318,7 +329,7 @@ TEST(Debug, Catch) {
} else { } else {
text.assign(what.cStr()); text.assign(what.cStr());
} }
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text); EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
} }
} }
#endif #endif
...@@ -329,11 +340,6 @@ int mockSyscall(int i, int error = 0) { ...@@ -329,11 +340,6 @@ int mockSyscall(int i, int error = 0) {
return i; return i;
} }
int fail() {
errno = EBADF;
return -1;
}
TEST(Debug, Syscall) { TEST(Debug, Syscall) {
MockExceptionCallback mockCallback; MockExceptionCallback mockCallback;
int line; int line;
...@@ -346,7 +352,25 @@ TEST(Debug, Syscall) { ...@@ -346,7 +352,25 @@ TEST(Debug, Syscall) {
EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, EBADF), i, "bar", str)); line = __LINE__; EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, EBADF), i, "bar", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
": error from OS: mockSyscall(-1, EBADF): " + strerror(EBADF) + ": failed: mockSyscall(-1, EBADF): " + strerror(EBADF) +
"; i = 123; bar; str = foo\n", mockCallback.text);
mockCallback.text.clear();
EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ECONNRESET), i, "bar", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
": disconnected: mockSyscall(-1, ECONNRESET): " + strerror(ECONNRESET) +
"; i = 123; bar; str = foo\n", mockCallback.text);
mockCallback.text.clear();
EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ENOMEM), i, "bar", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
": overloaded: mockSyscall(-1, ENOMEM): " + strerror(ENOMEM) +
"; i = 123; bar; str = foo\n", mockCallback.text);
mockCallback.text.clear();
EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, ENOSYS), i, "bar", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) +
": unimplemented: mockSyscall(-1, ENOSYS): " + strerror(ENOSYS) +
"; i = 123; bar; str = foo\n", mockCallback.text); "; i = 123; bar; str = foo\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
...@@ -354,9 +378,9 @@ TEST(Debug, Syscall) { ...@@ -354,9 +378,9 @@ TEST(Debug, Syscall) {
bool recovered = false; bool recovered = false;
KJ_SYSCALL(result = mockSyscall(-2, EBADF), i, "bar", str) { recovered = true; break; } line = __LINE__; KJ_SYSCALL(result = mockSyscall(-2, EBADF), i, "bar", str) { recovered = true; break; } line = __LINE__;
EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) + EXPECT_EQ("recoverable exception: " + fileLine(__FILE__, line) +
": error from OS: mockSyscall(-2, EBADF): " + strerror(EBADF) + ": failed: mockSyscall(-2, EBADF): " + strerror(EBADF) +
"; i = 123; bar; str = foo\n", mockCallback.text); "; i = 123; bar; str = foo\n", mockCallback.text);
EXPECT_LT(result, 0); EXPECT_EQ(-2, result);
EXPECT_TRUE(recovered); EXPECT_TRUE(recovered);
} }
...@@ -374,7 +398,7 @@ TEST(Debug, Context) { ...@@ -374,7 +398,7 @@ TEST(Debug, Context) {
EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__; EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n" EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
+ fileLine(__FILE__, line) + ": bug in code: bar\n", + fileLine(__FILE__, line) + ": failed: bar\n",
mockCallback.text); mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
...@@ -386,7 +410,7 @@ TEST(Debug, Context) { ...@@ -386,7 +410,7 @@ TEST(Debug, Context) {
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n" EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
+ fileLine(__FILE__, cline2) + ": context: baz; i = 123; corge; str = qux\n" + fileLine(__FILE__, cline2) + ": context: baz; i = 123; corge; str = qux\n"
+ fileLine(__FILE__, line) + ": bug in code: bar\n", + fileLine(__FILE__, line) + ": failed: bar\n",
mockCallback.text); mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
} }
...@@ -397,7 +421,7 @@ TEST(Debug, Context) { ...@@ -397,7 +421,7 @@ TEST(Debug, Context) {
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n" EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
+ fileLine(__FILE__, cline2) + ": context: grault\n" + fileLine(__FILE__, cline2) + ": context: grault\n"
+ fileLine(__FILE__, line) + ": bug in code: bar\n", + fileLine(__FILE__, line) + ": failed: bar\n",
mockCallback.text); mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
} }
......
...@@ -49,14 +49,53 @@ ArrayPtr<const char> KJ_STRINGIFY(Debug::Severity severity) { ...@@ -49,14 +49,53 @@ ArrayPtr<const char> KJ_STRINGIFY(Debug::Severity severity) {
namespace { namespace {
Exception::Type typeOfErrno(int error) {
switch (error) {
case EDQUOT:
case EMFILE:
case ENFILE:
case ENOBUFS:
case ENOLCK:
case ENOMEM:
case ENOSPC:
case ETIMEDOUT:
case EUSERS:
return Exception::Type::OVERLOADED;
case ECONNABORTED:
case ECONNREFUSED:
case ECONNRESET:
case EHOSTDOWN:
case EHOSTUNREACH:
case ENETDOWN:
case ENETRESET:
case ENETUNREACH:
case ENONET:
case EPIPE:
return Exception::Type::DISCONNECTED;
case ENOSYS:
#if ENOTSUP
case ENOTSUP:
#endif
#if EOPNOTSUPP && EOPNOTSUPP != ENOTSUP
case EOPNOTSUPP:
#endif
return Exception::Type::UNIMPLEMENTED;
default:
return Exception::Type::FAILED;
}
}
enum DescriptionStyle { enum DescriptionStyle {
LOG, LOG,
ASSERTION, ASSERTION,
SYSCALL SYSCALL
}; };
static String makeDescription(DescriptionStyle style, const char* code, int errorNumber, static String makeDescriptionImpl(DescriptionStyle style, const char* code, int errorNumber,
const char* macroArgs, ArrayPtr<String> argValues) { const char* macroArgs, ArrayPtr<String> argValues) {
KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64); KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64);
if (argValues.size() > 0) { if (argValues.size() > 0) {
...@@ -193,7 +232,7 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro ...@@ -193,7 +232,7 @@ static String makeDescription(DescriptionStyle style, const char* code, int erro
void Debug::logInternal(const char* file, int line, Severity severity, const char* macroArgs, void Debug::logInternal(const char* file, int line, Severity severity, const char* macroArgs,
ArrayPtr<String> argValues) { ArrayPtr<String> argValues) {
getExceptionCallback().logMessage(file, line, 0, getExceptionCallback().logMessage(file, line, 0,
str(severity, ": ", makeDescription(LOG, nullptr, 0, macroArgs, argValues), '\n')); str(severity, ": ", makeDescriptionImpl(LOG, nullptr, 0, macroArgs, argValues), '\n'));
} }
Debug::Fault::~Fault() noexcept(false) { Debug::Fault::~Fault() noexcept(false) {
...@@ -213,15 +252,15 @@ void Debug::Fault::fatal() { ...@@ -213,15 +252,15 @@ void Debug::Fault::fatal() {
} }
void Debug::Fault::init( void Debug::Fault::init(
const char* file, int line, Exception::Nature nature, int errorNumber, const char* file, int line, int osErrorNumber,
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) { const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
exception = new Exception(nature, Exception::Durability::PERMANENT, file, line, exception = new Exception(typeOfErrno(osErrorNumber), file, line,
makeDescription(nature == Exception::Nature::OS_ERROR ? SYSCALL : ASSERTION, makeDescriptionImpl(osErrorNumber != 0 ? SYSCALL : ASSERTION, condition,
condition, errorNumber, macroArgs, argValues)); osErrorNumber, macroArgs, argValues));
} }
String Debug::makeContextDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) { String Debug::makeDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) {
return makeDescription(LOG, nullptr, 0, macroArgs, argValues); return makeDescriptionImpl(LOG, nullptr, 0, macroArgs, argValues);
} }
int Debug::getOsErrorNumber(bool nonblocking) { int Debug::getOsErrorNumber(bool nonblocking) {
......
This diff is collapsed.
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "miniposix.h" #include "miniposix.h"
#include <stdlib.h> #include <stdlib.h>
#include <exception> #include <exception>
#include <new>
#if (__linux__ && !__ANDROID__) || __APPLE__ #if (__linux__ && !__ANDROID__) || __APPLE__
#define KJ_HAS_BACKTRACE 1 #define KJ_HAS_BACKTRACE 1
...@@ -122,27 +123,15 @@ String getStackSymbols(ArrayPtr<void* const> trace) { ...@@ -122,27 +123,15 @@ String getStackSymbols(ArrayPtr<void* const> trace) {
} // namespace } // namespace
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature) { ArrayPtr<const char> KJ_STRINGIFY(Exception::Type type) {
static const char* NATURE_STRINGS[] = { static const char* TYPE_STRINGS[] = {
"requirement not met", "failed",
"bug in code", "overloaded",
"error from OS", "disconnected",
"network failure", "unimplemented"
"error"
}; };
const char* s = NATURE_STRINGS[static_cast<uint>(nature)]; const char* s = TYPE_STRINGS[static_cast<uint>(type)];
return arrayPtr(s, strlen(s));
}
ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability) {
static const char* DURABILITY_STRINGS[] = {
"permanent",
"temporary",
"overloaded"
};
const char* s = DURABILITY_STRINGS[static_cast<uint>(durability)];
return arrayPtr(s, strlen(s)); return arrayPtr(s, strlen(s));
} }
...@@ -174,17 +163,14 @@ String KJ_STRINGIFY(const Exception& e) { ...@@ -174,17 +163,14 @@ String KJ_STRINGIFY(const Exception& e) {
} }
return str(strArray(contextText, ""), return str(strArray(contextText, ""),
e.getFile(), ":", e.getLine(), ": ", e.getNature(), e.getFile(), ":", e.getLine(), ": ", e.getType(),
e.getDurability() == Exception::Durability::TEMPORARY ? " (temporary)" : "",
e.getDescription() == nullptr ? "" : ": ", e.getDescription(), e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "), e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "),
getStackSymbols(e.getStackTrace())); getStackSymbols(e.getStackTrace()));
} }
Exception::Exception(Nature nature, Durability durability, const char* file, int line, Exception::Exception(Type type, const char* file, int line, String description) noexcept
String description) noexcept : file(file), line(line), type(type), description(mv(description)) {
: file(file), line(line), nature(nature), durability(durability),
description(mv(description)) {
#ifndef KJ_HAS_BACKTRACE #ifndef KJ_HAS_BACKTRACE
traceCount = 0; traceCount = 0;
#else #else
...@@ -192,10 +178,9 @@ Exception::Exception(Nature nature, Durability durability, const char* file, int ...@@ -192,10 +178,9 @@ Exception::Exception(Nature nature, Durability durability, const char* file, int
#endif #endif
} }
Exception::Exception(Nature nature, Durability durability, String file, int line, Exception::Exception(Type type, String file, int line, String description) noexcept
String description) noexcept : ownFile(kj::mv(file)), file(ownFile.cStr()), line(line), type(type),
: ownFile(kj::mv(file)), file(ownFile.cStr()), line(line), nature(nature), description(mv(description)) {
durability(durability), description(mv(description)) {
#ifndef KJ_HAS_BACKTRACE #ifndef KJ_HAS_BACKTRACE
traceCount = 0; traceCount = 0;
#else #else
...@@ -204,7 +189,7 @@ Exception::Exception(Nature nature, Durability durability, String file, int line ...@@ -204,7 +189,7 @@ Exception::Exception(Nature nature, Durability durability, String file, int line
} }
Exception::Exception(const Exception& other) noexcept Exception::Exception(const Exception& other) noexcept
: file(other.file), line(other.line), nature(other.nature), durability(other.durability), : file(other.file), line(other.line), type(other.type),
description(heapString(other.description)), traceCount(other.traceCount) { description(heapString(other.description)), traceCount(other.traceCount) {
if (file == other.ownFile.cStr()) { if (file == other.ownFile.cStr()) {
ownFile = heapString(other.ownFile); ownFile = heapString(other.ownFile);
...@@ -334,8 +319,7 @@ private: ...@@ -334,8 +319,7 @@ private:
// We intentionally don't log the context since it should get re-added by the exception callback // We intentionally don't log the context since it should get re-added by the exception callback
// anyway. // anyway.
getExceptionCallback().logMessage(e.getFile(), e.getLine(), 0, str( getExceptionCallback().logMessage(e.getFile(), e.getLine(), 0, str(
e.getNature(), e.getDurability() == Exception::Durability::TEMPORARY ? " (temporary)" : "", e.getType(), e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "), e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "),
getStackSymbols(e.getStackTrace()), "\n")); getStackSymbols(e.getStackTrace()), "\n"));
} }
...@@ -404,17 +388,17 @@ uint uncaughtExceptionCount() { ...@@ -404,17 +388,17 @@ uint uncaughtExceptionCount() {
// https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp // https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp
// Alas, it doesn not appera to work on MSVC2015. The linker claims _getptd() doesn't exist. // Alas, it doesn not appera to work on MSVC2015. The linker claims _getptd() doesn't exist.
extern "C" char *__cdecl _getptd(); extern "C" char *__cdecl _getptd();
uint uncaughtExceptionCount() { uint uncaughtExceptionCount() {
return *reinterpret_cast<uint*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90)); return *reinterpret_cast<uint*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90));
} }
#endif #endif
uint uncaughtExceptionCount() { uint uncaughtExceptionCount() {
// Since the above doesn't work, fall back to uncaught_exception(). This will produce incorrect // Since the above doesn't work, fall back to uncaught_exception(). This will produce incorrect
// results in very obscure cases that Cap'n Proto doesn't really rely on anyway. // results in very obscure cases that Cap'n Proto doesn't really rely on anyway.
return std::uncaught_exception(); return std::uncaught_exception();
} }
#else #else
...@@ -467,11 +451,14 @@ Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept { ...@@ -467,11 +451,14 @@ Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept {
return nullptr; return nullptr;
} catch (Exception& e) { } catch (Exception& e) {
return kj::mv(e); return kj::mv(e);
} catch (std::bad_alloc& e) {
return Exception(Exception::Type::OVERLOADED,
"(unknown)", -1, str("std::bad_alloc: ", e.what()));
} catch (std::exception& e) { } catch (std::exception& e) {
return Exception(Exception::Nature::OTHER, Exception::Durability::PERMANENT, return Exception(Exception::Type::FAILED,
"(unknown)", -1, str("std::exception: ", e.what())); "(unknown)", -1, str("std::exception: ", e.what()));
} catch (...) { } catch (...) {
return Exception(Exception::Nature::OTHER, Exception::Durability::PERMANENT, return Exception(Exception::Type::FAILED,
"(unknown)", -1, str("Unknown non-KJ exception.")); "(unknown)", -1, str("Unknown non-KJ exception."));
} }
#endif #endif
......
...@@ -40,50 +40,44 @@ class Exception { ...@@ -40,50 +40,44 @@ class Exception {
// Actually, a subclass of this which also implements std::exception will be thrown, but we hide // Actually, a subclass of this which also implements std::exception will be thrown, but we hide
// that fact from the interface to avoid #including <exception>. // that fact from the interface to avoid #including <exception>.
#ifdef __CDT_PARSER__
// For some reason Eclipse gets confused by the definition of Nature if it's the first thing
// in the class.
typedef void WorkAroundCdtBug;
#endif
public: public:
enum class Nature { enum class Type {
// What kind of failure? This is informational, not intended for programmatic use. // What kind of failure?
// Note that the difference between some of these failure types is not always clear. For
// example, a precondition failure may be due to a "local bug" in the calling code, or it FAILED = 0,
// may be due to invalid input. // Something went wrong. This is the usual error type. KJ_ASSERT and KJ_REQUIRE throw this
// error type.
PRECONDITION,
LOCAL_BUG, OVERLOADED = 1,
OS_ERROR, // The call failed because of a temporary lack of resources. This could be space resources
NETWORK_FAILURE, // (out of memory, out of disk space) or time resources (request queue overflow, operation
OTHER // timed out).
//
// Make sure to update the stringifier if you add a new nature. // The operation might work if tried again, but it should NOT be repeated immediately as this
}; // may simply exacerbate the problem.
enum class Durability { DISCONNECTED = 2,
PERMANENT, // Retrying the exact same operation will fail in exactly the same way. // The call required communication over a connection that has been lost. The callee will need
TEMPORARY, // Retrying the exact same operation might succeed. // to re-establish connections and try again.
OVERLOADED // The error was possibly caused by the system being overloaded. Retrying the
// operation might work at a later point in time, but the caller should NOT retry UNIMPLEMENTED = 3
// immediately as this will probably exacerbate the problem. // The requested method is not implemented. The caller may wish to revert to a fallback
// approach based on other methods.
// Make sure to update the stringifier if you add a new durability.
// IF YOU ADD A NEW VALUE:
// - Update the stringifier.
// - Update Cap'n Proto's RPC protocol's Exception.Type enum.
}; };
Exception(Nature nature, Durability durability, const char* file, int line, Exception(Type type, const char* file, int line, String description = nullptr) noexcept;
String description = nullptr) noexcept; Exception(Type type, String file, int line, String description = nullptr) noexcept;
Exception(Nature nature, Durability durability, String file, int line,
String description = nullptr) noexcept;
Exception(const Exception& other) noexcept; Exception(const Exception& other) noexcept;
Exception(Exception&& other) = default; Exception(Exception&& other) = default;
~Exception() noexcept; ~Exception() noexcept;
const char* getFile() const { return file; } const char* getFile() const { return file; }
int getLine() const { return line; } int getLine() const { return line; }
Nature getNature() const { return nature; } Type getType() const { return type; }
Durability getDurability() const { return durability; }
StringPtr getDescription() const { return description; } StringPtr getDescription() const { return description; }
ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); } ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); }
...@@ -117,8 +111,7 @@ private: ...@@ -117,8 +111,7 @@ private:
String ownFile; String ownFile;
const char* file; const char* file;
int line; int line;
Nature nature; Type type;
Durability durability;
String description; String description;
Maybe<Own<Context>> context; Maybe<Own<Context>> context;
void* trace[16]; void* trace[16];
...@@ -128,8 +121,7 @@ private: ...@@ -128,8 +121,7 @@ private:
}; };
// TODO(soon): These should return StringPtr. // TODO(soon): These should return StringPtr.
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature); ArrayPtr<const char> KJ_STRINGIFY(Exception::Type type);
ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability);
String KJ_STRINGIFY(const Exception& e); String KJ_STRINGIFY(const Exception& e);
// ======================================================================================= // =======================================================================================
......
...@@ -26,9 +26,7 @@ ...@@ -26,9 +26,7 @@
namespace kj { namespace kj {
kj::Exception Timer::makeTimeoutException() { kj::Exception Timer::makeTimeoutException() {
return kj::Exception( return KJ_EXCEPTION(OVERLOADED, "operation timed out");
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::OVERLOADED,
__FILE__, __LINE__, kj::heapString("operation timed out"));
} }
} // namespace kj } // namespace kj
...@@ -84,12 +84,14 @@ public: ...@@ -84,12 +84,14 @@ public:
template <typename T> template <typename T>
Promise<T> timeoutAt(TimePoint time, Promise<T>&& promise); Promise<T> timeoutAt(TimePoint time, Promise<T>&& promise);
// Return a promise equivalent to `promise` but which throws an exception (and cancels the // Return a promise equivalent to `promise` but which throws an exception (and cancels the
// original promise) if it hasn't completed by `time`. // original promise) if it hasn't completed by `time`. The thrown exception is of type
// "OVERLOADED".
template <typename T> template <typename T>
Promise<T> timeoutAfter(Duration delay, Promise<T>&& promise); Promise<T> timeoutAfter(Duration delay, Promise<T>&& promise);
// Return a promise equivalent to `promise` but which throws an exception (and cancels the // Return a promise equivalent to `promise` but which throws an exception (and cancels the
// original promise) if it hasn't completed after `delay` from now. // original promise) if it hasn't completed after `delay` from now. The thrown exception is of
// type "OVERLOADED".
private: private:
static kj::Exception makeTimeoutException(); static kj::Exception makeTimeoutException();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment