Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
bf04264d
Commit
bf04264d
authored
May 21, 2013
by
Kenton Varda
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement ability to easily attach context to an exception on the way up the stack.
parent
9058a3ae
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
334 additions
and
18 deletions
+334
-18
exception.c++
c++/src/capnproto/exception.c++
+35
-13
exception.h
c++/src/capnproto/exception.h
+29
-1
logging-test.c++
c++/src/capnproto/logging-test.c++
+43
-4
logging.c++
c++/src/capnproto/logging.c++
+22
-0
logging.h
c++/src/capnproto/logging.h
+60
-0
type-safety.c++
c++/src/capnproto/type-safety.c++
+2
-0
type-safety.h
c++/src/capnproto/type-safety.h
+143
-0
No files found.
c++/src/capnproto/exception.c++
View file @
bf04264d
...
...
@@ -59,27 +59,49 @@ Exception::Exception(Nature nature, Durability durability, const char* file, int
Array
<
char
>
description
)
noexcept
:
file
(
file
),
line
(
line
),
nature
(
nature
),
durability
(
durability
),
description
(
move
(
description
))
{
bool
hasDescription
=
this
->
description
!=
nullptr
;
void
*
trace
[
16
];
int
traceCount
=
backtrace
(
trace
,
16
);
ArrayPtr
<
void
*>
traceArray
=
arrayPtr
(
trace
,
traceCount
);
// Must be careful to NUL-terminate this.
whatStr
=
str
(
file
,
":"
,
line
,
": "
,
nature
,
durability
==
Durability
::
TEMPORARY
?
" (temporary)"
:
""
,
hasDescription
?
": "
:
""
,
this
->
description
,
"
\n
stack: "
,
strArray
(
traceArray
,
" "
),
'\0'
);
traceCount
=
backtrace
(
trace
,
16
);
}
Exception
::
Exception
(
const
Exception
&
other
)
noexcept
:
file
(
other
.
file
),
line
(
other
.
line
),
nature
(
other
.
nature
),
durability
(
other
.
durability
),
description
(
str
(
other
.
description
)),
whatStr
(
str
(
other
.
whatStr
))
{}
description
(
str
(
other
.
description
)),
traceCount
(
other
.
traceCount
)
{
memcpy
(
trace
,
other
.
trace
,
sizeof
(
trace
[
0
])
*
traceCount
);
}
Exception
::~
Exception
()
noexcept
{}
void
Exception
::
wrapContext
(
const
char
*
file
,
int
line
,
Array
<
char
>&&
description
)
{
context
=
heap
<
Context
>
(
file
,
line
,
move
(
description
),
move
(
context
));
}
const
char
*
Exception
::
what
()
const
noexcept
{
return
whatStr
.
begin
();
uint
contextDepth
=
0
;
const
Maybe
<
Own
<
Context
>>*
contextPtr
=
&
context
;
while
(
*
contextPtr
!=
nullptr
)
{
++
contextDepth
;
contextPtr
=
&
(
***
contextPtr
).
next
;
}
Array
<
Array
<
char
>>
contextText
=
newArray
<
Array
<
char
>>
(
contextDepth
);
contextDepth
=
0
;
contextPtr
=
&
context
;
while
(
*
contextPtr
!=
nullptr
)
{
const
Context
&
node
=
***
contextPtr
;
contextText
[
contextDepth
++
]
=
str
(
node
.
file
,
":"
,
node
.
line
,
": context: "
,
node
.
description
,
"
\n
"
);
contextPtr
=
&
node
.
next
;
}
// Must be careful to NUL-terminate this.
whatBuffer
=
str
(
strArray
(
contextText
,
""
),
file
,
":"
,
line
,
": "
,
nature
,
durability
==
Durability
::
TEMPORARY
?
" (temporary)"
:
""
,
this
->
description
==
nullptr
?
""
:
": "
,
this
->
description
,
"
\n
stack: "
,
strArray
(
arrayPtr
(
trace
,
traceCount
),
" "
),
'\0'
);
return
whatBuffer
.
begin
();
}
// =======================================================================================
...
...
c++/src/capnproto/exception.h
View file @
bf04264d
...
...
@@ -73,6 +73,31 @@ public:
Durability
getDurability
()
const
{
return
durability
;
}
ArrayPtr
<
const
char
>
getDescription
()
const
{
return
description
;
}
struct
Context
{
// Describes a bit about what was going on when the exception was thrown.
const
char
*
file
;
int
line
;
Array
<
char
>
description
;
Maybe
<
Own
<
Context
>>
next
;
Context
(
const
char
*
file
,
int
line
,
Array
<
char
>&&
description
,
Maybe
<
Own
<
Context
>>&&
next
)
:
file
(
file
),
line
(
line
),
description
(
move
(
description
)),
next
(
move
(
next
))
{}
};
inline
Maybe
<
const
Context
&>
getContext
()
const
{
if
(
context
==
nullptr
)
{
return
nullptr
;
}
else
{
return
**
context
;
}
}
void
wrapContext
(
const
char
*
file
,
int
line
,
Array
<
char
>&&
description
);
// Wraps the context in a new node. This becomes the head node returned by getContext() -- it
// is expected that contexts will be added in reverse order as the exception passes up the
// callback stack.
const
char
*
what
()
const
noexcept
override
;
private
:
...
...
@@ -81,7 +106,10 @@ private:
Nature
nature
;
Durability
durability
;
Array
<
char
>
description
;
Array
<
char
>
whatStr
;
Maybe
<
Own
<
Context
>>
context
;
void
*
trace
[
16
];
uint
traceCount
;
mutable
Array
<
char
>
whatBuffer
;
};
class
Stringifier
;
...
...
c++/src/capnproto/logging-test.c++
View file @
bf04264d
...
...
@@ -45,9 +45,9 @@ public:
void
onRecoverableException
(
Exception
&&
exception
)
override
{
text
+=
"recoverable exception: "
;
//
Only take the first line of "what" because the second line
is a stack trace.
//
Discard the last line of "what" because it
is a stack trace.
const
char
*
what
=
exception
.
what
();
const
char
*
end
=
strchr
(
what
,
'\n'
);
const
char
*
end
=
str
r
chr
(
what
,
'\n'
);
if
(
end
==
nullptr
)
{
text
+=
exception
.
what
();
}
else
{
...
...
@@ -58,9 +58,9 @@ public:
void
onFatalException
(
Exception
&&
exception
)
override
{
text
+=
"fatal exception: "
;
//
Only take the first line of "what" because the second line
is a stack trace.
//
Discard the last line of "what" because it
is a stack trace.
const
char
*
what
=
exception
.
what
();
const
char
*
end
=
strchr
(
what
,
'\n'
);
const
char
*
end
=
str
r
chr
(
what
,
'\n'
);
if
(
end
==
nullptr
)
{
text
+=
exception
.
what
();
}
else
{
...
...
@@ -159,6 +159,45 @@ TEST(Logging, Syscall) {
EXPECT_TRUE
(
recovered
);
}
TEST
(
Logging
,
Context
)
{
MockExceptionCallback
mockCallback
;
MockExceptionCallback
::
ScopedRegistration
reg
(
mockCallback
);
{
CONTEXT
(
"foo"
);
int
cline
=
__LINE__
;
EXPECT_THROW
(
FAIL_CHECK
(
"bar"
),
MockException
);
int
line
=
__LINE__
;
EXPECT_EQ
(
"fatal exception: "
+
fileLine
(
__FILE__
,
cline
)
+
": context: foo
\n
"
+
fileLine
(
__FILE__
,
line
)
+
": bug in code: bar
\n
"
,
mockCallback
.
text
);
mockCallback
.
text
.
clear
();
{
int
i
=
123
;
const
char
*
str
=
"qux"
;
CONTEXT
(
"baz"
,
i
,
"corge"
,
str
);
int
cline2
=
__LINE__
;
EXPECT_THROW
(
FAIL_CHECK
(
"bar"
),
MockException
);
line
=
__LINE__
;
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
"
,
mockCallback
.
text
);
mockCallback
.
text
.
clear
();
}
{
CONTEXT
(
"grault"
);
int
cline2
=
__LINE__
;
EXPECT_THROW
(
FAIL_CHECK
(
"bar"
),
MockException
);
line
=
__LINE__
;
EXPECT_EQ
(
"fatal exception: "
+
fileLine
(
__FILE__
,
cline
)
+
": context: foo
\n
"
+
fileLine
(
__FILE__
,
cline2
)
+
": context: grault
\n
"
+
fileLine
(
__FILE__
,
line
)
+
": bug in code: bar
\n
"
,
mockCallback
.
text
);
mockCallback
.
text
.
clear
();
}
}
}
}
// namespace
}
// namespace internal
}
// namespace capnproto
c++/src/capnproto/logging.c++
View file @
bf04264d
...
...
@@ -230,9 +230,31 @@ void Log::fatalFailedSyscallInternal(
abort
();
}
void
Log
::
addContextToInternal
(
Exception
&
exception
,
const
char
*
file
,
int
line
,
const
char
*
macroArgs
,
ArrayPtr
<
Array
<
char
>>
argValues
)
{
exception
.
wrapContext
(
file
,
line
,
makeDescription
(
LOG
,
nullptr
,
0
,
macroArgs
,
argValues
));
}
int
Log
::
getOsErrorNumber
()
{
int
result
=
errno
;
return
result
==
EINTR
?
-
1
:
result
;
}
Log
::
Context
::
Context
()
:
next
(
getExceptionCallback
()),
registration
(
*
this
)
{}
Log
::
Context
::~
Context
()
{}
void
Log
::
Context
::
onRecoverableException
(
Exception
&&
exception
)
{
addTo
(
exception
);
next
.
onRecoverableException
(
capnproto
::
move
(
exception
));
}
void
Log
::
Context
::
onFatalException
(
Exception
&&
exception
)
{
addTo
(
exception
);
next
.
onFatalException
(
capnproto
::
move
(
exception
));
}
void
Log
::
Context
::
logMessage
(
ArrayPtr
<
const
char
>
text
)
{
// TODO(someday): We could do something like log the context and then indent all log messages
// written until the end of the context.
next
.
logMessage
(
text
);
}
}
// namespace capnproto
c++/src/capnproto/logging.h
View file @
bf04264d
...
...
@@ -74,6 +74,14 @@
// fd = SYSCALL(open("/dev/null", O_RDONLY));
// }
//
// * `CONTEXT(...)`: Notes additional contextual information relevant to any exceptions thrown
// from within the current scope. That is, until control exits the block in which CONTEXT()
// is used, if any exception is generated, it will contain the given information in its context
// chain. This is helpful because it can otherwise be very difficult to come up with error
// messages that make sense within low-level helper code. Note that the parameters to CONTEXT()
// are only evaluated if an exception is thrown. This means that any variables used must remain
// valid until the end of the scope.
//
// Notes:
// * Do not write expressions with side-effects in the message content part of the macro, as the
// message will not necessarily be evaluated.
...
...
@@ -145,6 +153,42 @@ public:
int
errorNumber
,
const
char
*
file
,
int
line
,
const
char
*
callText
,
const
char
*
macroArgs
,
Params
&&
...
params
);
class
Context
:
public
ExceptionCallback
{
public
:
Context
();
virtual
~
Context
();
virtual
void
addTo
(
Exception
&
exception
)
=
0
;
virtual
void
onRecoverableException
(
Exception
&&
exception
)
override
;
virtual
void
onFatalException
(
Exception
&&
exception
)
override
;
virtual
void
logMessage
(
ArrayPtr
<
const
char
>
text
)
override
;
private
:
ExceptionCallback
&
next
;
ScopedRegistration
registration
;
};
template
<
typename
Func
>
class
ContextImpl
:
public
Context
{
public
:
inline
ContextImpl
(
Func
&&
func
)
:
func
(
capnproto
::
move
(
func
))
{}
void
addTo
(
Exception
&
exception
)
override
{
func
(
exception
);
}
private
:
Func
func
;
};
template
<
typename
Func
>
static
ContextImpl
<
RemoveReference
<
Func
>>
context
(
Func
&&
func
)
{
return
ContextImpl
<
RemoveReference
<
Func
>>
(
capnproto
::
forward
<
Func
>
(
func
));
}
template
<
typename
...
Params
>
static
void
addContextTo
(
Exception
&
exception
,
const
char
*
file
,
int
line
,
const
char
*
macroArgs
,
Params
&&
...
params
);
private
:
static
Severity
minSeverity
;
...
...
@@ -164,6 +208,8 @@ private:
const
char
*
file
,
int
line
,
const
char
*
call
,
int
errorNumber
,
const
char
*
macroArgs
,
ArrayPtr
<
Array
<
char
>>
argValues
)
CAPNPROTO_NORETURN
;
static
void
addContextToInternal
(
Exception
&
exception
,
const
char
*
file
,
int
line
,
const
char
*
macroArgs
,
ArrayPtr
<
Array
<
char
>>
argValues
);
static
int
getOsErrorNumber
();
// Get the error code of the last error (e.g. from errno). Returns -1 on EINTR.
...
...
@@ -224,6 +270,13 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity);
_errorNumber, __FILE__, __LINE__, #code, #__VA_ARGS__, ##__VA_ARGS__); \
} while (false)
#define CONTEXT(...) \
auto _capnpLoggingContext = ::capnproto::Log::context( \
[&](::capnproto::Exception& exception) { \
return ::capnproto::Log::addContextTo(exception, \
__FILE__, __LINE__, #__VA_ARGS__, ##__VA_ARGS__); \
})
#ifdef NDEBUG
#define DLOG(...) do {} while (false)
#define DCHECK(...) do {} while (false)
...
...
@@ -314,6 +367,13 @@ void Log::reportFailedSyscall(
arrayPtr
(
argValues
,
sizeof
...(
Params
)));
}
template
<
typename
...
Params
>
void
Log
::
addContextTo
(
Exception
&
exception
,
const
char
*
file
,
int
line
,
const
char
*
macroArgs
,
Params
&&
...
params
)
{
Array
<
char
>
argValues
[
sizeof
...(
Params
)]
=
{
str
(
params
)...};
addContextToInternal
(
exception
,
file
,
line
,
macroArgs
,
arrayPtr
(
argValues
,
sizeof
...(
Params
)));
}
}
// namespace capnproto
#endif // CAPNPROTO_LOGGING_H_
c++/src/capnproto/type-safety.c++
View file @
bf04264d
...
...
@@ -25,6 +25,8 @@
namespace
capnproto
{
Disposer
::~
Disposer
()
{}
String
::
String
(
const
char
*
value
)
:
content
(
newArray
<
char
>
(
strlen
(
value
)
+
1
))
{
strcpy
(
content
.
begin
(),
value
);
}
...
...
c++/src/capnproto/type-safety.h
View file @
bf04264d
...
...
@@ -126,6 +126,27 @@ public:
constructAt
(
&
value
,
other
.
value
);
}
}
template
<
typename
U
>
Maybe
(
Maybe
<
U
>&&
other
)
noexcept
(
noexcept
(
T
(
capnproto
::
move
(
other
.
value
))))
:
isSet
(
other
.
isSet
)
{
if
(
isSet
)
{
constructAt
(
&
value
,
capnproto
::
move
(
other
.
value
));
}
}
template
<
typename
U
>
Maybe
(
const
Maybe
<
U
>&
other
)
:
isSet
(
other
.
isSet
)
{
if
(
isSet
)
{
constructAt
(
&
value
,
other
.
value
);
}
}
template
<
typename
U
>
Maybe
(
const
Maybe
<
U
&>&
other
)
:
isSet
(
other
.
isSet
)
{
if
(
isSet
)
{
constructAt
(
&
value
,
*
other
.
ptr
);
}
}
Maybe
(
std
::
nullptr_t
)
:
isSet
(
false
)
{}
~
Maybe
()
{
...
...
@@ -227,6 +248,9 @@ private:
union
{
T
value
;
};
template
<
typename
U
>
friend
class
Maybe
;
};
template
<
typename
T
>
...
...
@@ -235,6 +259,14 @@ public:
Maybe
()
:
ptr
(
nullptr
)
{}
Maybe
(
T
&
t
)
:
ptr
(
&
t
)
{}
Maybe
(
std
::
nullptr_t
)
:
ptr
(
nullptr
)
{}
template
<
typename
U
>
Maybe
(
const
Maybe
<
U
>&
other
)
{
if
(
other
==
nullptr
)
{
ptr
=
nullptr
;
}
else
{
ptr
=
other
.
operator
->
();
}
}
~
Maybe
()
noexcept
{}
...
...
@@ -250,8 +282,119 @@ public:
private
:
T
*
ptr
;
template
<
typename
U
>
friend
class
Maybe
;
};
// =======================================================================================
// Own<T> -- An owned pointer.
class
Disposer
{
// Abstract interface for a thing that disposes of some other object. Often, it makes sense to
// decouple an object from the knowledge of how to dispose of it.
protected
:
virtual
~
Disposer
();
public
:
virtual
void
dispose
(
void
*
interiorPointer
)
=
0
;
// Disposes of the object that this Disposer owns, and possibly disposes of the disposer itself.
//
// Callers must assume that the Disposer itself is no longer valid once this returns -- e.g. it
// might delete itself. Callers must in particular be sure not to call the Disposer again even
// when dispose() throws an exception.
//
// `interiorPointer` points somewhere inside of the object -- NOT necessarily at the beginning,
// especially in the presence of multiple inheritance. Most implementations should ignore the
// pointer, though a tricky memory allocator could get away with sharing one Disposer among
// multiple objects if it can figure out how to find the beginning of the object given an
// arbitrary interior pointer.
};
template
<
typename
T
>
class
Own
{
// A transferrable title to a T. When an Own<T> goes out of scope, the object's Disposer is
// called to dispose of it. An Own<T> can be efficiently passed by move, without relocating the
// underlying object; this transfers ownership.
//
// This is much like std::unique_ptr, except:
// - You cannot release(). An owned object is not necessarily allocated with new (see next
// point), so it would be hard to use release() correctly.
// - The deleter is made polymorphic by virtual call rather than by template. This is a much
// more powerful default -- it allows any random module to decide to use a custom allocator.
// This could be accomplished with unique_ptr by forcing everyone to use e.g.
// std::unique_ptr<T, capnproto::Disposer&>, but at that point we've lost basically any benefit
// of interoperating with std::unique_ptr anyway.
public
:
Own
(
const
Own
&
other
)
=
delete
;
inline
Own
(
Own
&&
other
)
noexcept
:
disposer
(
other
.
disposer
),
ptr
(
other
.
ptr
)
{
other
.
ptr
=
nullptr
;
}
template
<
typename
U
>
inline
Own
(
Own
<
U
>&&
other
)
noexcept
:
disposer
(
other
.
disposer
),
ptr
(
other
.
ptr
)
{
other
.
ptr
=
nullptr
;
}
inline
Own
(
T
*
ptr
,
Disposer
*
disposer
)
noexcept
:
disposer
(
disposer
),
ptr
(
ptr
)
{}
~
Own
()
noexcept
{
dispose
();
}
inline
Own
&
operator
=
(
Own
&&
other
)
{
dispose
();
disposer
=
other
.
disposer
;
ptr
=
other
.
ptr
;
other
.
ptr
=
nullptr
;
return
*
this
;
}
inline
T
*
operator
->
()
{
return
ptr
;
}
inline
const
T
*
operator
->
()
const
{
return
ptr
;
}
inline
T
&
operator
*
()
{
return
*
ptr
;
}
inline
const
T
&
operator
*
()
const
{
return
*
ptr
;
}
inline
T
*
get
()
{
return
ptr
;
}
inline
const
T
*
get
()
const
{
return
ptr
;
}
inline
operator
T
*
()
{
return
ptr
;
}
inline
operator
const
T
*
()
const
{
return
ptr
;
}
private
:
Disposer
*
disposer
;
// Only valid if ptr != nullptr.
T
*
ptr
;
inline
void
dispose
()
{
// Make sure that if an exception is thrown, we are left with a null ptr, so we won't possibly
// dispose again.
void
*
ptrCopy
=
ptr
;
if
(
ptrCopy
!=
nullptr
)
{
ptr
=
nullptr
;
disposer
->
dispose
(
ptrCopy
);
}
}
};
namespace
internal
{
template
<
typename
T
>
class
HeapValue
final
:
public
Disposer
{
public
:
template
<
typename
...
Params
>
inline
HeapValue
(
Params
&&
...
params
)
:
value
(
capnproto
::
forward
<
Params
>
(
params
)...)
{}
virtual
void
dispose
(
void
*
)
override
{
delete
this
;
}
T
value
;
};
}
// namespace internal
template
<
typename
T
,
typename
...
Params
>
Own
<
T
>
heap
(
Params
&&
...
params
)
{
// heap<T>(...) allocates a T on the heap, forwarding the parameters to its constructor. The
// exact heap implementation is unspecified -- for now it is operator new, but you should not
// assume anything.
auto
result
=
new
internal
::
HeapValue
<
T
>
(
capnproto
::
forward
<
Params
>
(
params
)...);
return
Own
<
T
>
(
&
result
->
value
,
result
);
}
// =======================================================================================
// ArrayPtr
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment