Re: [PATCH 12/22] Add exception handling classes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2018-03-02 at 11:53 +0100, Lukáš Hrázký wrote:
> On Fri, 2018-03-02 at 03:03 -0500, Frediano Ziglio wrote:
> > > 
> > > > On 1 Mar 2018, at 13:13, Frediano Ziglio <fziglio@xxxxxxxxxx>
> > > > wrote:
> > > > 
> > > > > 
> > > > > From: Christophe de Dinechin <dinechin@xxxxxxxxxx>
> > > > > 
> > > > > Throwing 'runtime_error' directly should be reserved for the
> > > > > support
> > > > > library.  Add an 'Error' class as a base class for all errors
> > > > > thrown
> > > > > by the streaming agent, as well as subclasses used to
> > > > > discriminate
> > > > > between categories of error.
> > > > > 
> > > > > The Error class offers:
> > > > > - a 'format_message' member function that formats a message
> > > > > without
> > > > >  allocating memory, so that there is no risk of throwing
> > > > > another
> > > > >  exception at throw time.
> > > > > 
> > > > 
> > > > why not formatting the message constructing the object so
> > > > before the throw?
> > > 
> > > That requires dynamic allocation, which is entirely unnecessary
> > > and
> > > potentially dangerous (replacing one exception kind with
> > > another).
> > > 
> > 
> > As explained by my string.c_str example your interface is prone to
> > errors. In the rare case that on small allocations we are replacing
> > the error with a std::bad_alloc but introducing other problems.
> > I prefer to reuse standard classes and support better the not rare
> > cases.
> 
> This is a very good point by Frediano and I also don't find it
> acceptable, no matter how much you stress it in the docstring. The
> interface is inviting to make this error...

FWIW, I also find myself in agreement with Frediano and Lukas on this
point.

> 
> (And as I stated before, I agree with the rest of Frediano's
> reasoning
> on the delayed formatting)
> 
> > > You can still format the message at throw point for the purpose
> > > of sending it
> > > to the syslog. This only uses the stack, no dynamic allocation.
> > > 
> > 
> > Supposing that you only get the error is a bad design and also if,
> > as
> > you are saying, the only purpose is getting the message
> > runtime_error
> > is perfectly fine.
> > 
> > > It’s also more efficient, since we only format when we need to,
> > > not earlier.
> > > 
> > 
> > You are always formatting during the exception which being
> > "exceptional"
> > is by definition cold path. If in the event of error you don't log
> > anything
> > is faster, not a big point.
> > Also limit the possible output string and is using plain C function
> > to
> > achieve this
> > 
> > > > 
> > > > > - a 'syslog' member function that sends the formatted message
> > > > > to the
> > > > > syslog.
> > > > > 
> > > > 
> > > > why this is needed?
> > > 
> > > 1. DRY principle, avoids repeated (and dispersed) calls to
> > > syslog.
> > > 2. Along with Error constructor, offers a convenient breakpoint
> > > 3. Put all our syslog-for-errors stuff in a single place, easier
> > > to change.
> > > 
> > 
> > void report_error(const std::exception& exp); resolves all the
> > above
> > without introducing design dependencies.
> > 
> > > 
> > > > can't just call what() as a standard exception handling?
> > > 
> > > The standard what() is still supported, returns a base message.
> > > 
> > 
> > The question is why not using it instead of rewriting it?
> > To support rare cases and adding problems and code?
> > 
> > > 
> > > > 
> > > > > The derived classes are:
> > > > > 
> > > > > - IOError encapsulates typical I/O errors that report errors
> > > > > using
> > > > >  errno.  The format_message in that case prints the original
> > > > > message,
> > > > >  along with the strerror message.
> > > > > 
> > > > > - The ReadError and WriteError are two classes derived from
> > > > > IOError,
> > > > >  with the sole purpose of making it possible to discriminate
> > > > > the
> > > > >  error type at catch time, e.g. to retry writes.
> > > > > 
> > > > > These classes are used in the subsequent patches
> > > > > 
> > > > > Signed-off-by: Christophe de Dinechin <dinechin@xxxxxxxxxx>
> > > > > ---
> > > > > include/spice-streaming-agent/Makefile.am |  2 +-
> > > > > include/spice-streaming-agent/errors.hpp  | 55
> > > > > ++++++++++++++++++++++++++++++
> > > > > src/Makefile.am                           |  1 +
> > > > > src/errors.cpp                            | 56
> > > > > +++++++++++++++++++++++++++++++
> > > > > src/spice-streaming-agent.cpp             |  5 +++
> > > > > src/unittests/Makefile.am                 |  1 +
> > > > > 6 files changed, 119 insertions(+), 1 deletion(-)
> > > > > create mode 100644 include/spice-streaming-agent/errors.hpp
> > > > > create mode 100644 src/errors.cpp
> > > > > 
> > > > > diff --git a/include/spice-streaming-agent/Makefile.am
> > > > > b/include/spice-streaming-agent/Makefile.am
> > > > > index 844f791..a223d43 100644
> > > > > --- a/include/spice-streaming-agent/Makefile.am
> > > > > +++ b/include/spice-streaming-agent/Makefile.am
> > > > > @@ -3,5 +3,5 @@ public_includedir = $(includedir)/spice-
> > > > > streaming-agent
> > > > > public_include_HEADERS = \
> > > > > 	frame-capture.hpp \
> > > > > 	plugin.hpp \
> > > > > +	errors.hpp \
> > > > > 	$(NULL)
> > > > 
> > > > public header? So you are using it on the plugins ?
> > > 
> > > Not yet, but want to.
> > > 
> > 
> > If this is the quality level I would reject this patch.
> > This is mostly replacing a current no-problem with multiple
> > problems.
> > 
> > > > 
> > > > > -
> > > > > diff --git a/include/spice-streaming-agent/errors.hpp
> > > > > b/include/spice-streaming-agent/errors.hpp
> > > > > new file mode 100644
> > > > > index 0000000..ca70d2e
> > > > > --- /dev/null
> > > > > +++ b/include/spice-streaming-agent/errors.hpp
> > > > > @@ -0,0 +1,55 @@
> > > > > +/* Errors that may be thrown by the agent
> > > > > + *
> > > > > + * \copyright
> > > > > + * Copyright 2018 Red Hat Inc. All rights reserved.
> > > > > + */
> > > > > +#ifndef SPICE_STREAMING_AGENT_ERRORS_HPP
> > > > > +#define SPICE_STREAMING_AGENT_ERRORS_HPP
> > > > > +
> > > > > +#include <exception>
> > > > > +#include <stddef.h>
> > > > > +
> > > > > +namespace spice {
> > > > > +namespace streaming_agent {
> > > > > +
> > > > > +class Error : public std::exception
> > > > > +{
> > > > > +public:
> > > > > +    Error(const char *message): exception(),
> > > > > message(message) { }
> > > > 
> > > > what if the message came from a std::string::c_str() ?
> > > 
> > > There is no need to do that when you can pass arguments and do
> > > deferred
> > > formatting.
> > > 
> > 
> > Don't agree, having a base class and passing a "const char*" as a
> > general message suggests in many cases to have a dynamic message.
> > If for instance for some cases you want to add line informations
> > the message will be dynamic. You are basically forcing users to
> > know this
> > detail and add another class inheriting from Error and using C code
> > to format the string, is not that flexible just to add some
> > information.
> > 
> > > > Potentially message will point to an invalid memory location
> > > > during the
> > > > format_message/what.
> > > 
> > > Yes. Don’t do that. Added it to the documentation.
> > > 
> > > > 
> > > > > +    Error(const Error &error): exception(error),
> > > > > message(error.message)
> > > > > {}
> > > > > +    virtual const char *what() const noexcept override;
> > > > > +    virtual int format_message(char *buffer, size_t size)
> > > > > const noexcept;
> > > > > +    const Error &syslog() const noexcept;
> > > > > +protected:
> > > > > +    const char *message;
> > > > > +};
> > > > > +
> > > > 
> > > > As a public header these definition require more documentation.
> > > 
> > > Added.
> > > 
> > > 
> > > > Did you consider visibility?
> > > 
> > > Yes. The classes are defined in a a.out.
> > > 
> > 
> > Do you think that this make visible from plugins loaded
> > dynamically?
> > a.out? Windows?
> > 
> > > 
> > > > Did you consider Windows platform? These stuff should be in a
> > > > DLL.
> > > 
> > > No, they are in the .exe. Even if we move the agent server side,
> > > following
> > > Marc-André’s presentation, I’m inclined to think that this should
> > > remain a
> > > separate process. Too dangerous to bring arbitrary proprietary
> > > plugins in
> > > the QEMU process space.
> > > 
> > 
> > ?? don't apply here. We are talking about running on the guest and
> > portability design.
> > 
> > > > 
> > > > > +class IOError : public Error
> > > > > +{
> > > > > +public:
> > > > > +    IOError(const char *msg, int saved_errno) : Error(msg),
> > > > > saved_errno(saved_errno) {}
> > > > > +    int format_message(char *buffer, size_t size) const
> > > > > noexcept
> > > > > override;
> > > > > +    int append_strerror(char *buffer, size_t size, int
> > > > > written) const
> > > > > noexcept;
> > > > > +protected:
> > > > > +    int saved_errno;
> > > > > +};
> > > > > +
> > > > > +class WriteError : public IOError
> > > > > +{
> > > > > +public:
> > > > > +    WriteError(const char *msg, const char *operation, int
> > > > > saved_errno)
> > > > > +        : IOError(msg, saved_errno), operation(operation) {}
> > > > > +    int format_message(char *buffer, size_t size) const
> > > > > noexcept
> > > > > override;
> > > > > +protected:
> > > > > +    const char *operation;
> > > > > +};
> > > > > +
> > > > > +class ReadError : public IOError
> > > > > +{
> > > > > +public:
> > > > > +    ReadError(const char *msg, int saved_errno):
> > > > > IOError(msg,
> > > > > saved_errno)
> > > > > {}
> > > > > +};
> > > > > +
> > > > > +}} // namespace spice::streaming_agent
> > > > > +
> > > > > +#endif // SPICE_STREAMING_AGENT_ERRORS_HPP
> > > > > diff --git a/src/Makefile.am b/src/Makefile.am
> > > > > index 3717b5c..2507844 100644
> > > > > --- a/src/Makefile.am
> > > > > +++ b/src/Makefile.am
> > > > > @@ -55,4 +55,5 @@ spice_streaming_agent_SOURCES = \
> > > > > 	mjpeg-fallback.hpp \
> > > > > 	jpeg.cpp \
> > > > > 	jpeg.hpp \
> > > > > +	errors.cpp \
> > > > > 	$(NULL)
> > > > > diff --git a/src/errors.cpp b/src/errors.cpp
> > > > > new file mode 100644
> > > > > index 0000000..01bb162
> > > > > --- /dev/null
> > > > > +++ b/src/errors.cpp
> > > > > @@ -0,0 +1,56 @@
> > > > > +/* Errors that may be thrown by the agent
> > > > > + *
> > > > > + * \copyright
> > > > > + * Copyright 2018 Red Hat Inc. All rights reserved.
> > > > > + */
> > > > > +
> > > > > +#include <spice-streaming-agent/errors.hpp>
> > > > > +
> > > > > +#include <stdio.h>
> > > > > +#include <syslog.h>
> > > > > +#include <string.h>
> > > > > +
> > > > > +namespace spice {
> > > > > +namespace streaming_agent {
> > > > > +
> > > > > +const char *Error::what() const noexcept
> > > > > +{
> > > > > +    return message;
> > > > > +}
> > > > > +
> > > > > +int Error::format_message(char *buffer, size_t size) const
> > > > > noexcept
> > > > > +{
> > > > > +    return snprintf(buffer, size, "%s", message);
> > > > > +}
> > > > > +
> > > > > +const Error &Error::syslog() const noexcept
> > > > > +{
> > > > > +    char buffer[256];
> > > > 
> > > > I would use 1024 (standard syslog limit).
> > > 
> > > Good point. Could not find any obvious SYSLOG_MAX value, though.
> > > 
> > > > Still I don't think syslog call should be here.
> > > 
> > > You gave no argument against (“I don’t think” being an opinion,
> > > not an
> > > argument).
> > > I have given several arguments in favor of this approach, please
> > > make sure
> > > you address them.
> > > 
> > 
> > See above
> > 
> > > > 
> > > > > +    format_message(buffer, sizeof(buffer));
> > > > > +    ::syslog(LOG_ERR, "%s\n", buffer);
> > > > 
> > > > "\n" is not necessary
> > > 
> > > Removed.
> > > 
> > > > 
> > > > > +    return *this;
> > > > > +}
> > > > > +
> > > > > +int IOError::format_message(char *buffer, size_t size) const
> > > > > noexcept
> > > > > +{
> > > > > +    int written = snprintf(buffer, size, "%s", what());
> > > > > +    return append_strerror(buffer, size, written);
> > > > > +}
> > > > > +
> > > > > +int IOError::append_strerror(char *buffer, size_t size, int
> > > > > written)
> > > > > const
> > > > > noexcept
> > > > > +{
> > > > > +    // The conversion of written to size_t also deals with
> > > > > the case where
> > > > > written < 0
> > > > > +    if (saved_errno && (size_t) written < size) {
> > > > > +        written += snprintf(buffer + written, size -
> > > > > written, ": %s
> > > > > (errno
> > > > > %d)",
> > > > > +                            strerror(saved_errno),
> > > > > saved_errno);
> > > > > +    }
> > > > > +    return written;
> > > > > +}
> > > > > +
> > > > > +int WriteError::format_message(char *buffer, size_t size)
> > > > > const noexcept
> > > > > +{
> > > > > +    int written = snprintf(buffer, size, "%s writing %s",
> > > > > what(),
> > > > > operation);
> > > > > +    return append_strerror(buffer, size, written);
> > > > > +}
> > > > > +
> > > > > +}} // namespace spice::streaming_agent
> > > > > diff --git a/src/spice-streaming-agent.cpp b/src/spice-
> > > > > streaming-agent.cpp
> > > > > index 8494a8b..23ee824 100644
> > > > > --- a/src/spice-streaming-agent.cpp
> > > > > +++ b/src/spice-streaming-agent.cpp
> > > > > @@ -13,6 +13,7 @@
> > > > > 
> > > > > #include <spice-streaming-agent/frame-capture.hpp>
> > > > > #include <spice-streaming-agent/plugin.hpp>
> > > > > +#include <spice-streaming-agent/errors.hpp>
> > > > > 
> > > > > #include <stdio.h>
> > > > > #include <stdlib.h>
> > > > > @@ -597,6 +598,10 @@ int main(int argc, char* argv[])
> > > > >     try {
> > > > >         do_capture(stream, streamport, f_log);
> > > > >     }
> > > > > +    catch (Error &err) {
> > > > > +        err.syslog();
> > > > > +        ret = EXIT_FAILURE;
> > > > > +    }
> > > > >     catch (std::runtime_error &err) {
> > > > >         syslog(LOG_ERR, "%s\n", err.what());
> > > > >         ret = EXIT_FAILURE;
> > > > > diff --git a/src/unittests/Makefile.am
> > > > > b/src/unittests/Makefile.am
> > > > > index 0fc6d50..ce81b56 100644
> > > > > --- a/src/unittests/Makefile.am
> > > > > +++ b/src/unittests/Makefile.am
> > > > > @@ -39,6 +39,7 @@ test_mjpeg_fallback_SOURCES = \
> > > > > 	test-mjpeg-fallback.cpp \
> > > > > 	../jpeg.cpp \
> > > > > 	../mjpeg-fallback.cpp \
> > > > > +	../errors.cpp \
> > > > > 	$(NULL)
> > > > > 
> > > > > test_mjpeg_fallback_LDADD = \
> > > > 
> > > > Frediano
> > > 
> > > 
> > 
> > _______________________________________________
> > Spice-devel mailing list
> > Spice-devel@xxxxxxxxxxxxxxxxxxxxx
> > https://lists.freedesktop.org/mailman/listinfo/spice-devel
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel@xxxxxxxxxxxxxxxxxxxxx
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/spice-devel




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]