From 2d1fd273a2ea0a43642518c84497523a4ea49f34 Mon Sep 17 00:00:00 2001 From: Helge Norberg Date: Wed, 2 Dec 2015 17:25:46 +0100 Subject: [PATCH] Created a context_info attachment to our exceptions with information about the context in which the exception was thrown. Each part of the code can at any time contribute to the context using a scoped_context, literally appending information to the thread of execution --- common/CMakeLists.txt | 1 + common/except.cpp | 115 ++++++++++++++++++++++++++++++++++++++++++ common/except.h | 40 ++++++++++++++- 3 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 common/except.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a4bc89b88..d39d10a7d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES base64.cpp env.cpp + except.cpp filesystem.cpp log.cpp polling_filesystem_monitor.cpp diff --git a/common/except.cpp b/common/except.cpp new file mode 100644 index 000000000..7257e7997 --- /dev/null +++ b/common/except.cpp @@ -0,0 +1,115 @@ +/* +* Copyright (c) 2011 Sveriges Television AB +* +* This file is part of CasparCG (www.casparcg.com). +* +* CasparCG is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* CasparCG is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with CasparCG. If not, see . +* +* Author: Robert Nagy, ronag89@gmail.com +*/ + +#include "stdafx.h" + +#include "except.h" + +#include +#include + +namespace { + +boost::thread_specific_ptr>& context_stacks_per_thread() +{ + static boost::thread_specific_ptr> instances; + + return instances; +} + +} + +namespace caspar { + +std::list& context_stack_for_thread() +{ + auto local = context_stacks_per_thread().get(); + + if (!local) + { + local = new std::list(); + context_stacks_per_thread().reset(local); + } + + return *local; +} + +std::string get_context() +{ + return boost::join(context_stack_for_thread(), ""); +} + +scoped_context::scoped_context() + : scoped_context::scoped_context("") +{ +} + +scoped_context::scoped_context(std::string msg) + : for_thread_(context_stack_for_thread()) +{ + for_thread_.push_back(std::move(msg)); + msg_ = &for_thread_.back(); +} + +void scoped_context::replace_msg(std::string msg) +{ + if (&for_thread_ != &context_stack_for_thread()) + CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info("Called from wrong thread")); + + *msg_ = std::move(msg); +} + +void scoped_context::clear_msg() +{ + replace_msg(""); +} + +scoped_context::~scoped_context() +{ + for_thread_.pop_back(); +} + + + +std::string get_message_and_context(const caspar_exception& e) +{ + std::string result; + + auto msg = boost::get_error_info(e); + auto ctx = boost::get_error_info(e); + + if (msg) + result += *msg; + + if (ctx && !ctx->empty()) + { + result += " ("; + result += *ctx; + result += ")"; + } + + if (!result.empty() && result.back() != '.') + result += "."; + + return result; +} + +} diff --git a/common/except.h b/common/except.h index 5a498acbe..6a52f3530 100644 --- a/common/except.h +++ b/common/except.h @@ -26,9 +26,12 @@ #include "os/stack_trace.h" #include +#include + #include #include #include +#include namespace caspar { @@ -39,6 +42,7 @@ typedef boost::error_info call_stack_in typedef boost::error_info error_info_t; typedef boost::error_info source_info_t; typedef boost::error_info file_name_info_t; +typedef boost::error_info context_info_t; template inline arg_name_info_t arg_name_info(const T& str) {return arg_name_info_t(u8(str));} @@ -54,6 +58,8 @@ template inline source_info_t source_info(const T& str) {return source_info_t(u8(str));} template inline file_name_info_t file_name_info(const T& str) {return file_name_info_t(u8(str));} +template +inline context_info_t context_info(const T& str) {return context_info_t(u8(str));} typedef boost::error_info line_info; typedef boost::error_info nested_exception; @@ -88,7 +94,39 @@ struct not_implemented : virtual caspar_exception {}; struct user_error : virtual caspar_exception {}; struct not_supported : virtual user_error {}; -#define CASPAR_THROW_EXCEPTION(e) BOOST_THROW_EXCEPTION(e << call_stack_info(caspar::get_call_stack())) +std::string get_context(); + +class scoped_context : boost::noncopyable +{ +public: + scoped_context(); + scoped_context(std::string msg); + template + scoped_context(Str msg) + : scoped_context(u8(std::move(msg))) + { + } + + ~scoped_context(); + void replace_msg(std::string msg); + template + void replace_msg(std::string msg) + { + replace_msg(u8(std::move(msg))); + } + void clear_msg(); +private: + std::list& for_thread_; + std::string* msg_; +}; + +#define _CASPAR_GENERATE_UNIQUE_IDENTIFIER_CAT(name, line) name##line +#define _CASPAR_GENERATE_UNIQUE_IDENTIFIER(name, line) _CASPAR_GENERATE_UNIQUE_IDENTIFIER_CAT(name, line) +#define CASPAR_SCOPED_CONTEXT_MSG(ctx_msg) ::caspar::scoped_context _CASPAR_GENERATE_UNIQUE_IDENTIFIER(SCOPED_CONTEXT, __LINE__)(u8(ctx_msg)); + +#define CASPAR_THROW_EXCEPTION(e) BOOST_THROW_EXCEPTION(e << call_stack_info(caspar::get_call_stack()) << context_info(get_context())) + +std::string get_message_and_context(const caspar_exception& e); } -- 2.39.2