Unverified Commit 168a2a2b authored by Joseph Noir's avatar Joseph Noir Committed by GitHub

Merge pull request #797

Limit maximum memory consumption of the logger
parents f52cf24f af5eb9d3
...@@ -367,8 +367,11 @@ endif() ...@@ -367,8 +367,11 @@ endif()
# setup logging # # setup logging #
################################################################################ ################################################################################
# make sure log level is defined and all-uppercase
if(NOT CAF_LOG_LEVEL) if(NOT CAF_LOG_LEVEL)
set(CAF_LOG_LEVEL "-1") set(CAF_LOG_LEVEL "QUIET")
else()
string(TOUPPER "${CAF_LOG_LEVEL}" CAF_LOG_LEVEL)
endif() endif()
################################################################################ ################################################################################
...@@ -662,23 +665,6 @@ add_custom_target(gui_dummy SOURCES configure ${script_files}) ...@@ -662,23 +665,6 @@ add_custom_target(gui_dummy SOURCES configure ${script_files})
# print summary # # print summary #
################################################################################ ################################################################################
# set human-readable representation for log level
set(LOG_LEVEL_STR "none")
if(CAF_LOG_LEVEL)
if(${CAF_LOG_LEVEL} EQUAL 0)
set(LOG_LEVEL_STR "ERROR")
elseif(${CAF_LOG_LEVEL} EQUAL 1)
set(LOG_LEVEL_STR "WARNING")
elseif(${CAF_LOG_LEVEL} EQUAL 2)
set(LOG_LEVEL_STR "INFO")
elseif(${CAF_LOG_LEVEL} EQUAL 3)
set(LOG_LEVEL_STR "DEBUG")
elseif(${CAF_LOG_LEVEL} EQUAL 4)
set(LOG_LEVEL_STR "TRACE")
else()
set(LOG_LEVEL_STR "invalid")
endif()
endif()
# little helper macro to invert a boolean # little helper macro to invert a boolean
macro(invertYesNo in out) macro(invertYesNo in out)
if(${in}) if(${in})
...@@ -715,7 +701,7 @@ if(NOT CAF_NO_SUMMARY) ...@@ -715,7 +701,7 @@ if(NOT CAF_NO_SUMMARY)
"\nBuild static only: ${CAF_BUILD_STATIC_ONLY}" "\nBuild static only: ${CAF_BUILD_STATIC_ONLY}"
"\nBuild static runtime: ${CAF_BUILD_STATIC_RUNTIME}" "\nBuild static runtime: ${CAF_BUILD_STATIC_RUNTIME}"
"\nRuntime checks: ${CAF_ENABLE_RUNTIME_CHECKS}" "\nRuntime checks: ${CAF_ENABLE_RUNTIME_CHECKS}"
"\nLog level: ${LOG_LEVEL_STR}" "\nLog level: ${CAF_LOG_LEVEL}"
"\nWith mem. mgmt.: ${CAF_BUILD_MEM_MANAGEMENT}" "\nWith mem. mgmt.: ${CAF_BUILD_MEM_MANAGEMENT}"
"\nWith exceptions: ${CAF_BUILD_WITH_EXCEPTIONS}" "\nWith exceptions: ${CAF_BUILD_WITH_EXCEPTIONS}"
"\n" "\n"
......
...@@ -17,7 +17,7 @@ releaseBuildFlags = defaultBuildFlags + [ ...@@ -17,7 +17,7 @@ releaseBuildFlags = defaultBuildFlags + [
debugBuildFlags = defaultBuildFlags + [ debugBuildFlags = defaultBuildFlags + [
'CAF_ENABLE_RUNTIME_CHECKS:BOOL=yes', 'CAF_ENABLE_RUNTIME_CHECKS:BOOL=yes',
'CAF_ENABLE_ADDRESS_SANITIZER:BOOL=yes', 'CAF_ENABLE_ADDRESS_SANITIZER:BOOL=yes',
'CAF_LOG_LEVEL:STRING=4', 'CAF_LOG_LEVEL:STRING=TRACE',
] ]
// Our build matrix. The keys are the operating system labels and the values // Our build matrix. The keys are the operating system labels and the values
......
...@@ -19,9 +19,11 @@ ...@@ -19,9 +19,11 @@
#pragma once #pragma once
#include "caf/detail/log_level.hpp"
// this header is auto-generated by CMake // this header is auto-generated by CMake
#define CAF_LOG_LEVEL @CAF_LOG_LEVEL@ #define CAF_LOG_LEVEL CAF_LOG_LEVEL_@CAF_LOG_LEVEL@
#cmakedefine CAF_NO_MEM_MANAGEMENT #cmakedefine CAF_NO_MEM_MANAGEMENT
......
...@@ -284,28 +284,7 @@ while [ $# -ne 0 ]; do ...@@ -284,28 +284,7 @@ while [ $# -ne 0 ]; do
append_cache_entry CAF_IOS_DEPLOYMENT_TARGET STRING "$optarg" append_cache_entry CAF_IOS_DEPLOYMENT_TARGET STRING "$optarg"
;; ;;
--with-log-level=*) --with-log-level=*)
level=`echo "$optarg" | tr '[:lower:]' '[:upper:]'` append_cache_entry CAF_LOG_LEVEL STRING "$optarg"
case $level in
ERROR)
append_cache_entry CAF_LOG_LEVEL STRING 0
;;
WARNING)
append_cache_entry CAF_LOG_LEVEL STRING 1
;;
INFO)
append_cache_entry CAF_LOG_LEVEL STRING 2
;;
DEBUG)
append_cache_entry CAF_LOG_LEVEL STRING 3
;;
TRACE)
append_cache_entry CAF_LOG_LEVEL STRING 4
;;
*)
echo "Invalid log level '$level'. Try '$0 --help' to see valid values."
exit 1
;;
esac
;; ;;
--with-clang=*) --with-clang=*)
clang="$optarg" clang="$optarg"
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <type_traits> #include <type_traits>
#include "caf/detail/atom_val.hpp" #include "caf/detail/atom_val.hpp"
#include "caf/fwd.hpp"
namespace caf { namespace caf {
...@@ -36,6 +37,9 @@ enum class atom_value : uint64_t { ...@@ -36,6 +37,9 @@ enum class atom_value : uint64_t {
/// @relates atom_value /// @relates atom_value
std::string to_string(const atom_value& what); std::string to_string(const atom_value& what);
/// @relates atom_value
atom_value to_lowercase(atom_value x);
atom_value atom_from_string(const std::string& x); atom_value atom_from_string(const std::string& x);
/// Creates an atom from given string literal. /// Creates an atom from given string literal.
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
/// Integer value for the QUIET log level.
#define CAF_LOG_LEVEL_QUIET 0
/// Integer value for the ERROR log level.
#define CAF_LOG_LEVEL_ERROR 3
/// Integer value for the WARNING log level.
#define CAF_LOG_LEVEL_WARNING 6
/// Integer value for the INFO log level.
#define CAF_LOG_LEVEL_INFO 9
/// Integer value for the DEBUG log level.
#define CAF_LOG_LEVEL_DEBUG 12
/// Integer value for the TRACE log level.
#define CAF_LOG_LEVEL_TRACE 15
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <array>
#include <atomic>
#include <condition_variable>
#include <mutex>
namespace caf {
namespace detail {
// A ringbuffer designed for a single consumer and any number of producers that
// can hold a maximum of `Size - 1` elements.
template <class T, size_t Size>
class ringbuffer {
public:
using guard_type = std::unique_lock<std::mutex>;
ringbuffer() : wr_pos_(0), rd_pos_(0) {
// nop
}
void wait_nonempty() {
// Double-checked locking to reduce contention on mtx_.
if (!empty())
return;
guard_type guard{mtx_};
while (empty())
cv_empty_.wait(guard);
}
T& front() {
// Safe to access without lock, because we assume a single consumer.
return buf_[rd_pos_];
}
void pop_front() {
guard_type guard{mtx_};
auto rp = rd_pos_.load();
rd_pos_ = next(rp);
// Wakeup a waiting producers if the queue became non-full.
if (rp == next(wr_pos_))
cv_full_.notify_all();
}
void push_back(T&& x) {
guard_type guard{mtx_};
while (full())
cv_full_.wait(guard);
auto wp = wr_pos_.load();
buf_[wp] = std::move(x);
wr_pos_ = next(wp);
if (rd_pos_ == wp)
cv_empty_.notify_all();
}
bool empty() const noexcept {
return rd_pos_ == wr_pos_;
}
bool full() const noexcept {
return rd_pos_ == next(wr_pos_);
}
size_t size() const noexcept {
auto rp = rd_pos_.load();
auto wp = wr_pos_.load();
if (rp == wp)
return 0;
if (rp < wp)
return wp - rp;
return Size - rp + wp;
}
private:
static size_t next(size_t pos) {
return (pos + 1) % Size;
}
// Guards queue_.
mutable std::mutex mtx_;
// Signals the empty condition.
std::condition_variable cv_empty_;
// Signals the full condition.
std::condition_variable cv_full_;
// Stores the current write position in the ringbuffer.
std::atomic<size_t> wr_pos_;
// Stores the current read position in the ringbuffer.
std::atomic<size_t> rd_pos_;
// Stores events in a circular ringbuffer.
std::array<T, Size> buf_;
};
} // namespace detail
} // namespace caf
This diff is collapsed.
...@@ -18,34 +18,57 @@ ...@@ -18,34 +18,57 @@
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include <array>
#include <cstring> #include <cstring>
#include "caf/string_view.hpp"
namespace caf { namespace caf {
atom_value atom_from_string(const std::string& x) { namespace {
if (x.size() > 10)
return atom(""); /// A buffer for decoding atom values.
char buf[11]; using atom_value_buf = std::array<char, 11>;
memcpy(buf, x.c_str(), x.size());
buf[x.size()] = '\0';
return atom(buf);
}
std::string to_string(const atom_value& what) { size_t decode(atom_value_buf& buf, atom_value what) {
size_t pos = 0;
auto x = static_cast<uint64_t>(what); auto x = static_cast<uint64_t>(what);
std::string result; // Don't read characters before we found the leading 0xF.
result.reserve(11);
// don't read characters before we found the leading 0xF
// first four bits set?
bool read_chars = ((x & 0xF000000000000000) >> 60) == 0xF; bool read_chars = ((x & 0xF000000000000000) >> 60) == 0xF;
uint64_t mask = 0x0FC0000000000000; uint64_t mask = 0x0FC0000000000000;
for (int bitshift = 54; bitshift >= 0; bitshift -= 6, mask >>= 6) { for (int bitshift = 54; bitshift >= 0; bitshift -= 6, mask >>= 6) {
if (read_chars) if (read_chars)
result += detail::decoding_table[(x & mask) >> bitshift]; buf[pos++] = detail::decoding_table[(x & mask) >> bitshift];
else if (((x & mask) >> bitshift) == 0xF) else if (((x & mask) >> bitshift) == 0xF)
read_chars = true; read_chars = true;
} }
return result; buf[pos] = '\0';
return pos;
}
} // namespace <anonymous>
atom_value to_lowercase(atom_value x) {
atom_value_buf buf;
decode(buf, x);
for (auto ch = buf.data(); *ch != '\0'; ++ch)
*ch = static_cast<char>(tolower(*ch));
return static_cast<atom_value>(detail::atom_val(buf.data()));
}
atom_value atom_from_string(const std::string& x) {
if (x.size() > 10)
return atom("");
atom_value_buf buf;
memcpy(buf.data(), x.c_str(), x.size());
buf[x.size()] = '\0';
return static_cast<atom_value>(detail::atom_val(buf.data()));
}
std::string to_string(const atom_value& x) {
atom_value_buf str;
auto len = decode(str, x);
return std::string(str.begin(), str.begin() + len);
} }
} // namespace caf } // namespace caf
This diff is collapsed.
...@@ -179,11 +179,11 @@ CAF_TEST(rendering) { ...@@ -179,11 +179,11 @@ CAF_TEST(rendering) {
// Rendering of events. // Rendering of events.
logger::event e{ logger::event e{
CAF_LOG_LEVEL_WARNING, CAF_LOG_LEVEL_WARNING,
42,
"unit.test", "unit.test",
"void ns::foo::bar()", "void ns::foo::bar()",
"bar", "bar",
"foo.cpp", "foo.cpp",
42,
"hello world", "hello world",
std::this_thread::get_id(), std::this_thread::get_id(),
0, 0,
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE ringbuffer
#include "caf/detail/ringbuffer.hpp"
#include "caf/test/dsl.hpp"
#include <algorithm>
using namespace caf;
namespace {
static constexpr size_t buf_size = 64;
using int_ringbuffer = detail::ringbuffer<int, buf_size>;
std::vector<int> consumer(int_ringbuffer& buf, size_t num) {
std::vector<int> result;
for (size_t i = 0; i < num; ++i) {
buf.wait_nonempty();
result.emplace_back(buf.front());
buf.pop_front();
}
return result;
}
void producer(int_ringbuffer& buf, int first, int last) {
for (auto i = first; i != last; ++i)
buf.push_back(std::move(i));
}
struct fixture {
int_ringbuffer buf;
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(ringbuffer_tests, fixture)
CAF_TEST(construction) {
CAF_CHECK_EQUAL(buf.empty(), true);
CAF_CHECK_EQUAL(buf.full(), false);
CAF_CHECK_EQUAL(buf.size(), 0u);
}
CAF_TEST(push_back) {
CAF_MESSAGE("add one element");
buf.push_back(42);
CAF_CHECK_EQUAL(buf.empty(), false);
CAF_CHECK_EQUAL(buf.full(), false);
CAF_CHECK_EQUAL(buf.size(), 1u);
CAF_CHECK_EQUAL(buf.front(), 42);
CAF_MESSAGE("remove element");
buf.pop_front();
CAF_CHECK_EQUAL(buf.empty(), true);
CAF_CHECK_EQUAL(buf.full(), false);
CAF_CHECK_EQUAL(buf.size(), 0u);
CAF_MESSAGE("fill buffer");
for (int i = 0; i < static_cast<int>(buf_size - 1); ++i)
buf.push_back(std::move(i));
CAF_CHECK_EQUAL(buf.empty(), false);
CAF_CHECK_EQUAL(buf.full(), true);
CAF_CHECK_EQUAL(buf.size(), buf_size - 1);
CAF_CHECK_EQUAL(buf.front(), 0);
}
CAF_TEST(concurrent access) {
std::vector<std::thread> producers;
producers.emplace_back(producer, std::ref(buf), 0, 100);
producers.emplace_back(producer, std::ref(buf), 100, 200);
producers.emplace_back(producer, std::ref(buf), 200, 300);
auto vec = consumer(buf, 300);
std::sort(vec.begin(), vec.end());
CAF_CHECK(std::is_sorted(vec.begin(), vec.end()));
CAF_CHECK_EQUAL(vec.size(), 300u);
CAF_CHECK_EQUAL(vec.front(), 0);
CAF_CHECK_EQUAL(vec.back(), 299);
for (auto& t : producers)
t.join();
}
CAF_TEST_FIXTURE_SCOPE_END()
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