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 {
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,18 +646,11 @@ 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));
}
}
kj::Maybe<kj::Exception> exception;
......
......@@ -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 ::uint16_t getObsoleteDurability() const;
inline ::capnp::rpc::Exception::Durability getDurability() 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 ::capnp::rpc::Exception::Durability getDurability();
inline void setDurability( ::capnp::rpc::Exception::Durability value);
inline ::uint16_t getObsoleteDurability();
inline void setObsoleteDurability( ::uint16_t 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;
......
......@@ -29,21 +29,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,13 +49,52 @@ 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,
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);
......@@ -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"));
}
......@@ -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 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.
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.
UNIMPLEMENTED = 3
// 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,
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