Commit 0af31360 authored by Kenton Varda's avatar Kenton Varda

Merge branch 'exception-rewrite'

Conflicts:
	c++/src/kj/common.h
parents 79a81a09 4ee25e43
......@@ -575,8 +575,7 @@ class BrokenClient final: public ClientHook, public kj::Refcounted {
public:
BrokenClient(const kj::Exception& exception): exception(exception) {}
BrokenClient(const kj::StringPtr description)
: exception(kj::Exception::Nature::PRECONDITION, kj::Exception::Durability::PERMANENT,
"", 0, kj::str(description)) {}
: exception(kj::Exception::Type::FAILED, "", 0, kj::str(description)) {}
Request<AnyPointer, AnyPointer> newCall(
uint64_t interfaceId, uint16_t methodId, kj::Maybe<MessageSize> sizeHint) override {
......
......@@ -646,17 +646,10 @@ public:
private:
struct ParseErrorCatcher: public kj::ExceptionCallback {
void onRecoverableException(kj::Exception&& e) {
if (e.getNature() == kj::Exception::Nature::PRECONDITION) {
// This is probably a problem with the input. Let's try to report it more nicely.
// Only capture the first exception, on the assumption that later exceptions are probably
// 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));
// Only capture the first exception, on the assumption that later exceptions are probably
// just cascading problems.
if (exception == nullptr) {
exception = kj::mv(e);
}
}
......
......@@ -190,9 +190,7 @@ public:
TestNetworkAdapter(TestNetwork& network): network(network) {}
~TestNetworkAdapter() {
kj::Exception exception(
kj::Exception::Nature::PRECONDITION, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__, kj::str("Network was destroyed."));
kj::Exception exception = KJ_EXCEPTION(FAILED, "Network was destroyed.");
for (auto& entry: connections) {
entry.second->disconnect(kj::cp(exception));
}
......
......@@ -107,45 +107,15 @@ Orphan<List<rpc::PromisedAnswer::Op>> fromPipelineOps(
}
kj::Exception toException(const rpc::Exception::Reader& exception) {
kj::Exception::Nature nature =
exception.getIsCallersFault()
? 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()));
return kj::Exception(static_cast<kj::Exception::Type>(exception.getType()),
"(remote)", 0, kj::str("remote exception: ", exception.getReason()));
}
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
// transmit stack traces?
builder.setReason(exception.getDescription());
builder.setIsCallersFault(exception.getNature() == kj::Exception::Nature::PRECONDITION);
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;
}
builder.setType(static_cast<rpc::Exception::Type>(exception.getType()));
}
uint exceptionSizeHint(const kj::Exception& exception) {
......@@ -318,9 +288,8 @@ public:
return;
}
kj::Exception networkException(
kj::Exception::Nature::NETWORK_FAILURE, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__, kj::str("Disconnected: ", exception.getDescription()));
kj::Exception networkException(kj::Exception::Type::DISCONNECTED,
exception.getFile(), exception.getLine(), kj::heapString(exception.getDescription()));
KJ_IF_MAYBE(newException, kj::runCatchingExceptions([&]() {
// Carefully pull all the objects out of the tables prior to releasing them because their
......@@ -1970,9 +1939,7 @@ private:
handleMessage(kj::mv(*m));
return true;
} else {
disconnect(kj::Exception(
kj::Exception::Nature::PRECONDITION, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__, kj::str("Peer disconnected.")));
disconnect(KJ_EXCEPTION(DISCONNECTED, "Peer disconnected."));
return false;
}
}).then([this](bool keepGoing) {
......@@ -2626,9 +2593,7 @@ public:
// disassemble it.
if (!connections.empty()) {
kj::Vector<kj::Own<RpcConnectionState>> deleteMe(connections.size());
kj::Exception shutdownException(
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__, kj::str("RpcSystem was destroyed."));
kj::Exception shutdownException = KJ_EXCEPTION(FAILED, "RpcSystem was destroyed.");
for (auto& entry: connections) {
entry.second->disconnect(kj::cp(shutdownException));
deleteMe.add(kj::mv(entry.second));
......
......@@ -1053,29 +1053,86 @@ struct Exception {
# **(level 0)**
#
# 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;
# Human-readable failure description.
isCallersFault @1 :Bool;
# In the best estimate of the error source, is it the caller's fault that this error occurred
# (like HTTP 400), or is it the callee's fault (like HTTP 500)? Or, put another way, if an
# 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
# the callee -- at the very least, the callee should be on the hook for improving their error
# handling to be more confident in assigning blame.
durability @2 :Durability;
# In the best estimate of the error source, is this error likely to repeat if the same call is
# executed again? Callers might use this to decide when to retry a request.
enum Durability {
permanent @0; # Retrying the exact same operation will fail in the same way.
temporary @1; # Retrying the exact same operation might succeed.
overloaded @2; # The error may be due to the system being overloaded. Retrying may work
# later on, but for now the caller should not retry right away as this will
# likely exacerbate the problem.
type @3 :Type;
# The type of the error. The purpose of this enum is not to describe the error itself, but
# rather to describe how the client might want to respond to the error.
enum Type {
failed @0;
# 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.
#
# A client might respond to this error by logging it for investigation by the developer and/or
# displaying it to the user.
overloaded @1;
# The request was rejected due to a temporary lack of resources.
#
# Examples include:
# - There's not enough CPU time to keep up with incoming requests, so some are rejected.
# - 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 = {
0, 2, i_d37007fde1f0027d, nullptr, nullptr, { &s_d37007fde1f0027d, nullptr, nullptr, 0, 0, nullptr }
};
#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,
26, 105, 207, 58, 6, 183, 37, 214,
16, 0, 0, 0, 1, 0, 1, 0,
......@@ -1744,7 +1744,7 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
21, 0, 0, 0, 210, 0, 0, 0,
33, 0, 0, 0, 23, 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,
99, 97, 112, 110, 112, 47, 114, 112,
......@@ -1752,32 +1752,38 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
69, 120, 99, 101, 112, 116, 105, 111,
110, 0, 0, 0, 0, 0, 0, 0,
4, 0, 0, 0, 1, 0, 1, 0,
88, 249, 182, 7, 38, 218, 174, 187,
1, 0, 0, 0, 90, 0, 0, 0,
68, 117, 114, 97, 98, 105, 108, 105,
116, 121, 0, 0, 0, 0, 0, 0,
12, 0, 0, 0, 3, 0, 4, 0,
88, 189, 76, 63, 226, 150, 140, 178,
1, 0, 0, 0, 42, 0, 0, 0,
84, 121, 112, 101, 0, 0, 0, 0,
16, 0, 0, 0, 3, 0, 4, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 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,
64, 0, 0, 0, 3, 0, 1, 0,
76, 0, 0, 0, 2, 0, 1, 0,
1, 0, 0, 0, 0, 0, 0, 0,
92, 0, 0, 0, 3, 0, 1, 0,
104, 0, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 1, 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,
72, 0, 0, 0, 3, 0, 1, 0,
84, 0, 0, 0, 2, 0, 1, 0,
2, 0, 0, 0, 1, 0, 0, 0,
104, 0, 0, 0, 3, 0, 1, 0,
116, 0, 0, 0, 2, 0, 1, 0,
3, 0, 0, 0, 1, 0, 0, 0,
0, 0, 1, 0, 2, 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,
80, 0, 0, 0, 3, 0, 1, 0,
92, 0, 0, 0, 2, 0, 1, 0,
116, 0, 0, 0, 3, 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,
12, 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 = {
12, 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,
1, 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 = {
1, 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,
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,
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,
15, 0, 0, 0, 0, 0, 0, 0,
......@@ -1808,60 +1824,63 @@ static const ::capnp::_::AlignedData<69> b_d625b7063acf691a = {
::capnp::word const* const bp_d625b7063acf691a = b_d625b7063acf691a.words;
#if !CAPNP_LITE
static const ::capnp::_::RawSchema* const d_d625b7063acf691a[] = {
&s_bbaeda2607b6f958,
&s_b28c96e23f4cbd58,
};
static const uint16_t m_d625b7063acf691a[] = {2, 1, 0};
static const uint16_t i_d625b7063acf691a[] = {0, 1, 2};
static const uint16_t m_d625b7063acf691a[] = {2, 1, 0, 3};
static const uint16_t i_d625b7063acf691a[] = {0, 1, 2, 3};
const ::capnp::_::RawSchema s_d625b7063acf691a = {
0xd625b7063acf691a, b_d625b7063acf691a.words, 69, d_d625b7063acf691a, m_d625b7063acf691a,
1, 3, i_d625b7063acf691a, nullptr, nullptr, { &s_d625b7063acf691a, nullptr, nullptr, 0, 0, nullptr }
0xd625b7063acf691a, b_d625b7063acf691a.words, 85, d_d625b7063acf691a, m_d625b7063acf691a,
1, 4, i_d625b7063acf691a, nullptr, nullptr, { &s_d625b7063acf691a, nullptr, nullptr, 0, 0, nullptr }
};
#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,
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, 105, 207, 58, 6, 183, 37, 214,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
21, 0, 0, 0, 42, 1, 0, 0,
37, 0, 0, 0, 7, 0, 0, 0,
21, 0, 0, 0, 250, 0, 0, 0,
33, 0, 0, 0, 7, 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,
99, 97, 112, 110, 112, 47, 114, 112,
99, 46, 99, 97, 112, 110, 112, 58,
69, 120, 99, 101, 112, 116, 105, 111,
110, 46, 68, 117, 114, 97, 98, 105,
108, 105, 116, 121, 0, 0, 0, 0,
110, 46, 84, 121, 112, 101, 0, 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,
29, 0, 0, 0, 82, 0, 0, 0,
41, 0, 0, 0, 58, 0, 0, 0,
0, 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,
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,
112, 101, 114, 109, 97, 110, 101, 110,
116, 0, 0, 0, 0, 0, 0, 0,
116, 101, 109, 112, 111, 114, 97, 114,
121, 0, 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0, 0, 0,
25, 0, 0, 0, 114, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
102, 97, 105, 108, 101, 100, 0, 0,
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
static const uint16_t m_bbaeda2607b6f958[] = {2, 0, 1};
const ::capnp::_::RawSchema s_bbaeda2607b6f958 = {
0xbbaeda2607b6f958, b_bbaeda2607b6f958.words, 34, nullptr, m_bbaeda2607b6f958,
0, 3, nullptr, nullptr, nullptr, { &s_bbaeda2607b6f958, nullptr, nullptr, 0, 0, nullptr }
static const uint16_t m_b28c96e23f4cbd58[] = {2, 0, 1, 3};
const ::capnp::_::RawSchema s_b28c96e23f4cbd58 = {
0xb28c96e23f4cbd58, b_b28c96e23f4cbd58.words, 37, nullptr, m_b28c96e23f4cbd58,
0, 4, nullptr, nullptr, nullptr, { &s_b28c96e23f4cbd58, nullptr, nullptr, 0, 0, nullptr }
};
#endif // !CAPNP_LITE
CAPNP_DEFINE_ENUM(Durability_bbaeda2607b6f958, bbaeda2607b6f958);
CAPNP_DEFINE_ENUM(Type_b28c96e23f4cbd58, b28c96e23f4cbd58);
} // namespace schemas
} // namespace capnp
......
......@@ -34,13 +34,14 @@ CAPNP_DECLARE_SCHEMA(d800b1d6cd6f1ca0);
CAPNP_DECLARE_SCHEMA(f316944415569081);
CAPNP_DECLARE_SCHEMA(d37007fde1f0027d);
CAPNP_DECLARE_SCHEMA(d625b7063acf691a);
CAPNP_DECLARE_SCHEMA(bbaeda2607b6f958);
enum class Durability_bbaeda2607b6f958: uint16_t {
PERMANENT,
TEMPORARY,
CAPNP_DECLARE_SCHEMA(b28c96e23f4cbd58);
enum class Type_b28c96e23f4cbd58: uint16_t {
FAILED,
OVERLOADED,
DISCONNECTED,
UNIMPLEMENTED,
};
CAPNP_DECLARE_ENUM(Durability, bbaeda2607b6f958);
CAPNP_DECLARE_ENUM(Type, b28c96e23f4cbd58);
} // namespace schemas
} // namespace capnp
......@@ -397,7 +398,7 @@ struct Exception {
class Reader;
class Builder;
class Pipeline;
typedef ::capnp::schemas::Durability_bbaeda2607b6f958 Durability;
typedef ::capnp::schemas::Type_b28c96e23f4cbd58 Type;
struct _capnpPrivate {
......@@ -2390,9 +2391,11 @@ public:
inline bool hasReason() 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:
::capnp::_::StructReader _reader;
......@@ -2429,11 +2432,14 @@ public:
inline void adoptReason(::capnp::Orphan< ::capnp::Text>&& value);
inline ::capnp::Orphan< ::capnp::Text> disownReason();
inline bool getIsCallersFault();
inline void setIsCallersFault(bool value);
inline bool getObsoleteIsCallersFault();
inline void setObsoleteIsCallersFault(bool value);
inline ::uint16_t getObsoleteDurability();
inline void setObsoleteDurability( ::uint16_t value);
inline ::capnp::rpc::Exception::Durability getDurability();
inline void setDurability( ::capnp::rpc::Exception::Durability value);
inline ::capnp::rpc::Exception::Type getType();
inline void setType( ::capnp::rpc::Exception::Type value);
private:
::capnp::_::StructBuilder _builder;
......@@ -4754,34 +4760,48 @@ inline ::capnp::Orphan< ::capnp::Text> Exception::Builder::disownReason() {
_builder.getPointerField(0 * ::capnp::POINTERS));
}
inline bool Exception::Reader::getIsCallersFault() const {
inline bool Exception::Reader::getObsoleteIsCallersFault() const {
return _reader.getDataField<bool>(
0 * ::capnp::ELEMENTS);
}
inline bool Exception::Builder::getIsCallersFault() {
inline bool Exception::Builder::getObsoleteIsCallersFault() {
return _builder.getDataField<bool>(
0 * ::capnp::ELEMENTS);
}
inline void Exception::Builder::setIsCallersFault(bool value) {
inline void Exception::Builder::setObsoleteIsCallersFault(bool value) {
_builder.setDataField<bool>(
0 * ::capnp::ELEMENTS, value);
}
inline ::capnp::rpc::Exception::Durability Exception::Reader::getDurability() const {
return _reader.getDataField< ::capnp::rpc::Exception::Durability>(
inline ::uint16_t Exception::Reader::getObsoleteDurability() const {
return _reader.getDataField< ::uint16_t>(
1 * ::capnp::ELEMENTS);
}
inline ::capnp::rpc::Exception::Durability Exception::Builder::getDurability() {
return _builder.getDataField< ::capnp::rpc::Exception::Durability>(
inline ::uint16_t Exception::Builder::getObsoleteDurability() {
return _builder.getDataField< ::uint16_t>(
1 * ::capnp::ELEMENTS);
}
inline void Exception::Builder::setDurability( ::capnp::rpc::Exception::Durability value) {
_builder.setDataField< ::capnp::rpc::Exception::Durability>(
inline void Exception::Builder::setObsoleteDurability( ::uint16_t value) {
_builder.setDataField< ::uint16_t>(
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
......
......@@ -462,8 +462,8 @@ public:
void reportError(SourcePos start, SourcePos end, kj::StringPtr message) const override {
kj::getExceptionCallback().onRecoverableException(kj::Exception(
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::PERMANENT,
kj::heapString(diskPath), start.line, kj::heapString(message)));
kj::Exception::Type::FAILED, kj::heapString(diskPath), start.line,
kj::heapString(message)));
}
private:
......
......@@ -828,9 +828,7 @@ private:
delete this;
} else {
if (inner->isWaiting()) {
inner->reject(kj::Exception(
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::PERMANENT,
__FILE__, __LINE__,
inner->reject(kj::Exception(kj::Exception::Type::FAILED, __FILE__, __LINE__,
kj::heapString("PromiseFulfiller was destroyed without fulfilling the promise.")));
}
inner = nullptr;
......
......@@ -32,21 +32,10 @@ namespace _ { // private
void inlineRequireFailure(const char* file, int line, const char* expectation,
const char* macroArgs, const char* message) {
if (message == nullptr) {
Debug::Fault f(file, line, Exception::Nature::PRECONDITION, 0, expectation, macroArgs);
Debug::Fault f(file, line, 0, expectation, macroArgs);
f.fatal();
} else {
Debug::Fault f(file, line, Exception::Nature::PRECONDITION, 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);
Debug::Fault f(file, line, 0, expectation, macroArgs, message);
f.fatal();
}
}
......
......@@ -185,9 +185,6 @@ namespace _ { // private
KJ_NORETURN(void inlineRequireFailure(
const char* file, int line, const char* expectation, const char* macroArgs,
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());
......@@ -195,20 +192,13 @@ KJ_NORETURN(void unreachable());
#ifdef KJ_DEBUG
#if _MSC_VER
// TODO(msvc): Figure out __VA_ARGS__; see debug.h.
#define KJ_IREQUIRE(condition, ...) \
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
// 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
// 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
#define KJ_IREQUIRE(condition, ...) \
if (KJ_LIKELY(condition)); else ::kj::_::inlineRequireFailure( \
......@@ -217,18 +207,13 @@ KJ_NORETURN(void unreachable());
// 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
// 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
#else
#define KJ_IREQUIRE(condition, ...)
#define KJ_IASSERT(condition, ...)
#endif
#define KJ_IASSERT KJ_IREQUIRE
#define KJ_UNREACHABLE ::kj::_::unreachable();
// Put this on code paths that cannot be reached to suppress compiler warnings about missing
// returns.
......
......@@ -233,7 +233,7 @@ TEST(Debug, Log) {
KJ_ASSERT(1 == 1);
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);
mockCallback.text.clear();
......@@ -244,27 +244,38 @@ TEST(Debug, Log) {
bool recovered = false;
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);
EXPECT_TRUE(recovered);
mockCallback.text.clear();
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);
mockCallback.text.clear();
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);
mockCallback.text.clear();
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.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) {
int line;
......@@ -280,7 +291,7 @@ TEST(Debug, Catch) {
what = kj::str(what.slice(0, *eol));
}
std::string text(what.cStr());
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
} else {
ADD_FAILURE() << "Expected exception.";
}
......@@ -299,7 +310,7 @@ TEST(Debug, Catch) {
what = kj::str(what.slice(0, *eol));
}
std::string text(what.cStr());
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
} else {
ADD_FAILURE() << "Expected exception.";
}
......@@ -318,7 +329,7 @@ TEST(Debug, Catch) {
} else {
text.assign(what.cStr());
}
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
EXPECT_EQ(fileLine(__FILE__, line) + ": failed: foo", text);
}
}
#endif
......@@ -329,11 +340,6 @@ int mockSyscall(int i, int error = 0) {
return i;
}
int fail() {
errno = EBADF;
return -1;
}
TEST(Debug, Syscall) {
MockExceptionCallback mockCallback;
int line;
......@@ -346,7 +352,25 @@ TEST(Debug, Syscall) {
EXPECT_FATAL(KJ_SYSCALL(mockSyscall(-1, EBADF), i, "bar", str)); line = __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);
mockCallback.text.clear();
......@@ -354,9 +378,9 @@ TEST(Debug, Syscall) {
bool recovered = false;
KJ_SYSCALL(result = mockSyscall(-2, EBADF), i, "bar", str) { recovered = true; break; } line = __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);
EXPECT_LT(result, 0);
EXPECT_EQ(-2, result);
EXPECT_TRUE(recovered);
}
......@@ -374,7 +398,7 @@ TEST(Debug, Context) {
EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
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.clear();
......@@ -386,7 +410,7 @@ TEST(Debug, Context) {
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\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.clear();
}
......@@ -397,7 +421,7 @@ TEST(Debug, Context) {
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\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.clear();
}
......
......@@ -49,14 +49,53 @@ ArrayPtr<const char> KJ_STRINGIFY(Debug::Severity severity) {
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 {
LOG,
ASSERTION,
SYSCALL
};
static String makeDescription(DescriptionStyle style, const char* code, int errorNumber,
const char* macroArgs, ArrayPtr<String> argValues) {
static String makeDescriptionImpl(DescriptionStyle style, const char* code, int errorNumber,
const char* macroArgs, ArrayPtr<String> argValues) {
KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64);
if (argValues.size() > 0) {
......@@ -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,
ArrayPtr<String> argValues) {
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) {
......@@ -213,15 +252,15 @@ void Debug::Fault::fatal() {
}
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) {
exception = new Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(nature == Exception::Nature::OS_ERROR ? SYSCALL : ASSERTION,
condition, errorNumber, macroArgs, argValues));
exception = new Exception(typeOfErrno(osErrorNumber), file, line,
makeDescriptionImpl(osErrorNumber != 0 ? SYSCALL : ASSERTION, condition,
osErrorNumber, macroArgs, argValues));
}
String Debug::makeContextDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) {
return makeDescription(LOG, nullptr, 0, macroArgs, argValues);
String Debug::makeDescriptionInternal(const char* macroArgs, ArrayPtr<String> argValues) {
return makeDescriptionImpl(LOG, nullptr, 0, macroArgs, argValues);
}
int Debug::getOsErrorNumber(bool nonblocking) {
......
This diff is collapsed.
......@@ -26,6 +26,7 @@
#include "miniposix.h"
#include <stdlib.h>
#include <exception>
#include <new>
#if (__linux__ && !__ANDROID__) || __APPLE__
#define KJ_HAS_BACKTRACE 1
......@@ -122,27 +123,15 @@ String getStackSymbols(ArrayPtr<void* const> trace) {
} // namespace
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature) {
static const char* NATURE_STRINGS[] = {
"requirement not met",
"bug in code",
"error from OS",
"network failure",
"error"
ArrayPtr<const char> KJ_STRINGIFY(Exception::Type type) {
static const char* TYPE_STRINGS[] = {
"failed",
"overloaded",
"disconnected",
"unimplemented"
};
const char* s = NATURE_STRINGS[static_cast<uint>(nature)];
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)];
const char* s = TYPE_STRINGS[static_cast<uint>(type)];
return arrayPtr(s, strlen(s));
}
......@@ -174,17 +163,14 @@ String KJ_STRINGIFY(const Exception& e) {
}
return str(strArray(contextText, ""),
e.getFile(), ":", e.getLine(), ": ", e.getNature(),
e.getDurability() == Exception::Durability::TEMPORARY ? " (temporary)" : "",
e.getFile(), ":", e.getLine(), ": ", e.getType(),
e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "),
getStackSymbols(e.getStackTrace()));
}
Exception::Exception(Nature nature, Durability durability, const char* file, int line,
String description) noexcept
: file(file), line(line), nature(nature), durability(durability),
description(mv(description)) {
Exception::Exception(Type type, const char* file, int line, String description) noexcept
: file(file), line(line), type(type), description(mv(description)) {
#ifndef KJ_HAS_BACKTRACE
traceCount = 0;
#else
......@@ -192,10 +178,9 @@ Exception::Exception(Nature nature, Durability durability, const char* file, int
#endif
}
Exception::Exception(Nature nature, Durability durability, String file, int line,
String description) noexcept
: ownFile(kj::mv(file)), file(ownFile.cStr()), line(line), nature(nature),
durability(durability), description(mv(description)) {
Exception::Exception(Type type, String file, int line, String description) noexcept
: ownFile(kj::mv(file)), file(ownFile.cStr()), line(line), type(type),
description(mv(description)) {
#ifndef KJ_HAS_BACKTRACE
traceCount = 0;
#else
......@@ -204,7 +189,7 @@ Exception::Exception(Nature nature, Durability durability, String file, int line
}
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) {
if (file == other.ownFile.cStr()) {
ownFile = heapString(other.ownFile);
......@@ -334,8 +319,7 @@ private:
// We intentionally don't log the context since it should get re-added by the exception callback
// anyway.
getExceptionCallback().logMessage(e.getFile(), e.getLine(), 0, str(
e.getNature(), e.getDurability() == Exception::Durability::TEMPORARY ? " (temporary)" : "",
e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
e.getType(), e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
e.getStackTrace().size() > 0 ? "\nstack: " : "", strArray(e.getStackTrace(), " "),
getStackSymbols(e.getStackTrace()), "\n"));
}
......@@ -404,17 +388,17 @@ uint uncaughtExceptionCount() {
// 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.
extern "C" char *__cdecl _getptd();
uint uncaughtExceptionCount() {
return *reinterpret_cast<uint*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90));
extern "C" char *__cdecl _getptd();
uint uncaughtExceptionCount() {
return *reinterpret_cast<uint*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90));
}
#endif
uint uncaughtExceptionCount() {
// 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.
return std::uncaught_exception();
uint uncaughtExceptionCount() {
// 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.
return std::uncaught_exception();
}
#else
......@@ -467,11 +451,14 @@ Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept {
return nullptr;
} catch (Exception& 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) {
return Exception(Exception::Nature::OTHER, Exception::Durability::PERMANENT,
return Exception(Exception::Type::FAILED,
"(unknown)", -1, str("std::exception: ", e.what()));
} catch (...) {
return Exception(Exception::Nature::OTHER, Exception::Durability::PERMANENT,
return Exception(Exception::Type::FAILED,
"(unknown)", -1, str("Unknown non-KJ exception."));
}
#endif
......
......@@ -40,50 +40,44 @@ class Exception {
// 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>.
#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:
enum class Nature {
// What kind of failure? This is informational, not intended for programmatic use.
// 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
// may be due to invalid input.
PRECONDITION,
LOCAL_BUG,
OS_ERROR,
NETWORK_FAILURE,
OTHER
// Make sure to update the stringifier if you add a new nature.
};
enum class Durability {
PERMANENT, // Retrying the exact same operation will fail in exactly the same way.
TEMPORARY, // Retrying the exact same operation might succeed.
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
// immediately as this will probably exacerbate the problem.
// Make sure to update the stringifier if you add a new durability.
enum class Type {
// What kind of failure?
FAILED = 0,
// Something went wrong. This is the usual error type. KJ_ASSERT and KJ_REQUIRE throw this
// error type.
OVERLOADED = 1,
// The call failed because of a temporary lack of resources. This could be space resources
// (out of memory, out of disk space) or time resources (request queue overflow, operation
// timed out).
//
// The operation might work if tried again, but it should NOT be repeated immediately as this
// may simply exacerbate the problem.
DISCONNECTED = 2,
// The call required communication over a connection that has been lost. The callee will need
// to re-establish connections and try again.
UNIMPLEMENTED = 3
// The requested method is not implemented. The caller may wish to revert to a fallback
// approach based on other methods.
// 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,
String description = nullptr) noexcept;
Exception(Nature nature, Durability durability, String file, int line,
String description = nullptr) noexcept;
Exception(Type type, const char* file, int line, String description = nullptr) noexcept;
Exception(Type type, String file, int line, String description = nullptr) noexcept;
Exception(const Exception& other) noexcept;
Exception(Exception&& other) = default;
~Exception() noexcept;
const char* getFile() const { return file; }
int getLine() const { return line; }
Nature getNature() const { return nature; }
Durability getDurability() const { return durability; }
Type getType() const { return type; }
StringPtr getDescription() const { return description; }
ArrayPtr<void* const> getStackTrace() const { return arrayPtr(trace, traceCount); }
......@@ -117,8 +111,7 @@ private:
String ownFile;
const char* file;
int line;
Nature nature;
Durability durability;
Type type;
String description;
Maybe<Own<Context>> context;
void* trace[16];
......@@ -128,8 +121,7 @@ private:
};
// TODO(soon): These should return StringPtr.
ArrayPtr<const char> KJ_STRINGIFY(Exception::Nature nature);
ArrayPtr<const char> KJ_STRINGIFY(Exception::Durability durability);
ArrayPtr<const char> KJ_STRINGIFY(Exception::Type type);
String KJ_STRINGIFY(const Exception& e);
// =======================================================================================
......
......@@ -26,9 +26,7 @@
namespace kj {
kj::Exception Timer::makeTimeoutException() {
return kj::Exception(
kj::Exception::Nature::LOCAL_BUG, kj::Exception::Durability::OVERLOADED,
__FILE__, __LINE__, kj::heapString("operation timed out"));
return KJ_EXCEPTION(OVERLOADED, "operation timed out");
}
} // namespace kj
......@@ -84,12 +84,14 @@ public:
template <typename T>
Promise<T> timeoutAt(TimePoint time, Promise<T>&& promise);
// 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>
Promise<T> timeoutAfter(Duration delay, Promise<T>&& promise);
// 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:
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