Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
168a2a2b
Unverified
Commit
168a2a2b
authored
Nov 21, 2018
by
Joseph Noir
Committed by
GitHub
Nov 21, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #797
Limit maximum memory consumption of the logger
parents
f52cf24f
af5eb9d3
Changes
12
Show whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
612 additions
and
342 deletions
+612
-342
CMakeLists.txt
CMakeLists.txt
+5
-19
Jenkinsfile
Jenkinsfile
+1
-1
cmake/build_config.hpp.in
cmake/build_config.hpp.in
+3
-1
configure
configure
+1
-22
libcaf_core/caf/atom.hpp
libcaf_core/caf/atom.hpp
+4
-0
libcaf_core/caf/detail/log_level.hpp
libcaf_core/caf/detail/log_level.hpp
+37
-0
libcaf_core/caf/detail/ringbuffer.hpp
libcaf_core/caf/detail/ringbuffer.hpp
+117
-0
libcaf_core/caf/logger.hpp
libcaf_core/caf/logger.hpp
+201
-154
libcaf_core/src/atom.cpp
libcaf_core/src/atom.cpp
+38
-15
libcaf_core/src/logger.cpp
libcaf_core/src/logger.cpp
+104
-129
libcaf_core/test/logger.cpp
libcaf_core/test/logger.cpp
+1
-1
libcaf_core/test/ringbuffer.cpp
libcaf_core/test/ringbuffer.cpp
+100
-0
No files found.
CMakeLists.txt
View file @
168a2a2b
...
...
@@ -367,8 +367,11 @@ endif()
# setup logging #
################################################################################
# make sure log level is defined and all-uppercase
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
()
################################################################################
...
...
@@ -662,23 +665,6 @@ add_custom_target(gui_dummy SOURCES configure ${script_files})
# 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
macro
(
invertYesNo in out
)
if
(
${
in
}
)
...
...
@@ -715,7 +701,7 @@ if(NOT CAF_NO_SUMMARY)
"
\n
Build static only:
${
CAF_BUILD_STATIC_ONLY
}
"
"
\n
Build static runtime:
${
CAF_BUILD_STATIC_RUNTIME
}
"
"
\n
Runtime checks:
${
CAF_ENABLE_RUNTIME_CHECKS
}
"
"
\n
Log level:
${
LOG_LEVEL_STR
}
"
"
\n
Log level:
${
CAF_LOG_LEVEL
}
"
"
\n
With mem. mgmt.:
${
CAF_BUILD_MEM_MANAGEMENT
}
"
"
\n
With exceptions:
${
CAF_BUILD_WITH_EXCEPTIONS
}
"
"
\n
"
...
...
Jenkinsfile
View file @
168a2a2b
...
...
@@ -17,7 +17,7 @@ releaseBuildFlags = defaultBuildFlags + [
debugBuildFlags
=
defaultBuildFlags
+
[
'CAF_ENABLE_RUNTIME_CHECKS: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
...
...
cmake/build_config.hpp.in
View file @
168a2a2b
...
...
@@ -19,9 +19,11 @@
#pragma once
#include "caf/detail/log_level.hpp"
// 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
...
...
configure
View file @
168a2a2b
...
...
@@ -284,28 +284,7 @@ while [ $# -ne 0 ]; do
append_cache_entry CAF_IOS_DEPLOYMENT_TARGET STRING
"
$optarg
"
;;
--with-log-level
=
*
)
level
=
`
echo
"
$optarg
"
|
tr
'[:lower:]'
'[:upper:]'
`
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
append_cache_entry CAF_LOG_LEVEL STRING
"
$optarg
"
;;
--with-clang
=
*
)
clang
=
"
$optarg
"
...
...
libcaf_core/caf/atom.hpp
View file @
168a2a2b
...
...
@@ -23,6 +23,7 @@
#include <type_traits>
#include "caf/detail/atom_val.hpp"
#include "caf/fwd.hpp"
namespace
caf
{
...
...
@@ -36,6 +37,9 @@ enum class atom_value : uint64_t {
/// @relates atom_value
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
);
/// Creates an atom from given string literal.
...
...
libcaf_core/caf/detail/log_level.hpp
0 → 100644
View file @
168a2a2b
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
libcaf_core/caf/detail/ringbuffer.hpp
0 → 100644
View file @
168a2a2b
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
libcaf_core/caf/logger.hpp
View file @
168a2a2b
...
...
@@ -30,22 +30,21 @@
#include "caf/abstract_actor.hpp"
#include "caf/config.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/arg_wrapper.hpp"
#include "caf/detail/pretty_type_name.hpp"
#include "caf/detail/ringbuffer.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/shared_spinlock.hpp"
#include "caf/fwd.hpp"
#include "caf/intrusive/drr_queue.hpp"
#include "caf/intrusive/fifo_inbox.hpp"
#include "caf/intrusive/singly_linked.hpp"
#include "caf/ref_counted.hpp"
#include "caf/string_view.hpp"
#include "caf/timestamp.hpp"
#include "caf/type_nr.hpp"
#include "caf/unifyn.hpp"
#include "caf/intrusive/drr_queue.hpp"
#include "caf/intrusive/fifo_inbox.hpp"
#include "caf/intrusive/singly_linked.hpp"
#include "caf/detail/arg_wrapper.hpp"
#include "caf/detail/pretty_type_name.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/shared_spinlock.hpp"
/*
* To enable logging, you have to define CAF_DEBUG. This enables
* CAF_LOG_ERROR messages. To enable more debugging output, you can
...
...
@@ -66,18 +65,64 @@ namespace caf {
/// default, the logger generates log4j compatible output.
class
logger
:
public
ref_counted
{
public:
// -- friends ----------------------------------------------------------------
friend
class
actor_system
;
// -- constants --------------------------------------------------------------
/// Configures the size of the circular event queue.
static
constexpr
size_t
queue_size
=
128
;
// -- member types -----------------------------------------------------------
/// Combines various logging-related flags and parameters into a bitfield.
struct
config
{
/// Stores `max(file_verbosity, console_verbosity)`.
unsigned
verbosity
:
4
;
/// Configures the verbosity for file output.
unsigned
file_verbosity
:
4
;
/// Configures the verbosity for console output.
unsigned
console_verbosity
:
4
;
/// Configures whether the logger immediately writes its output in the
/// calling thread, bypassing its queue. Use this option only in
/// single-threaded test environments.
bool
inline_output
:
1
;
/// Configures whether the logger generates colored output.
bool
console_coloring
:
1
;
config
();
};
/// Encapsulates a single logging event.
struct
event
:
intrusive
::
singly_linked
<
event
>
{
struct
event
{
// -- constructors, destructors, and assignment operators ------------------
event
()
=
default
;
event
(
int
lvl
,
string_view
cat
,
string_view
full_fun
,
string_view
fun
,
string_view
fn
,
int
line
,
std
::
string
msg
,
std
::
thread
::
id
t
,
event
(
event
&&
)
=
default
;
event
(
const
event
&
)
=
default
;
event
&
operator
=
(
event
&&
)
=
default
;
event
&
operator
=
(
const
event
&
)
=
default
;
event
(
unsigned
lvl
,
unsigned
line
,
string_view
cat
,
string_view
full_fun
,
string_view
fun
,
string_view
fn
,
std
::
string
msg
,
std
::
thread
::
id
t
,
actor_id
a
,
timestamp
ts
);
// -- member variables -----------------------------------------------------
/// Level/priority of the event.
int
level
;
unsigned
level
;
/// Current line in the file.
unsigned
line_number
;
/// Name of the category (component) logging the event.
string_view
category_name
;
...
...
@@ -91,9 +136,6 @@ public:
/// Name of the current file.
string_view
file_name
;
/// Current line in the file.
int
line_number
;
/// User-provided message.
std
::
string
message
;
...
...
@@ -107,37 +149,6 @@ public:
timestamp
tstamp
;
};
struct
policy
{
// -- member types ---------------------------------------------------------
using
mapped_type
=
event
;
using
task_size_type
=
long
;
using
deleter_type
=
std
::
default_delete
<
event
>
;
using
unique_pointer
=
std
::
unique_ptr
<
event
,
deleter_type
>
;
using
queue_type
=
intrusive
::
drr_queue
<
policy
>
;
using
deficit_type
=
long
;
// -- static member functions ----------------------------------------------
static
inline
void
release
(
mapped_type
*
ptr
)
noexcept
{
detail
::
disposer
d
;
d
(
ptr
);
}
static
inline
task_size_type
task_size
(
const
mapped_type
&
)
noexcept
{
return
1
;
}
static
inline
deficit_type
quantum
(
const
queue_type
&
,
deficit_type
x
)
{
return
x
;
}
};
/// Internal representation of format string entites.
enum
field_type
{
invalid_field
,
...
...
@@ -196,37 +207,52 @@ public:
std
::
string
str_
;
};
/// Returns the ID of the actor currently associated to the calling thread.
actor_id
thread_local_aid
();
/// Associates an actor ID to the calling thread and returns the last value.
actor_id
thread_local_aid
(
actor_id
aid
);
/// Writes an entry to the log file.
void
log
(
event
*
x
);
// -- constructors, destructors, and assignment operators --------------------
~
logger
()
override
;
/
** @cond PRIVATE */
/
/ -- logging ----------------------------------------------------------------
static
void
set_current_actor_system
(
actor_system
*
);
/// Writes an entry to the event-queue of the logger.
/// @thread-safe
void
log
(
event
&&
x
);
static
logger
*
current_logger
();
// -- properties -------------------------------------------------------------
/// Returns the ID of the actor currently associated to the calling thread.
actor_id
thread_local_aid
();
bool
accepts
(
int
level
,
string_view
cname
);
/// Associates an actor ID to the calling thread and returns the last value.
actor_id
thread_local_aid
(
actor_id
aid
);
/** @endcond */
/// Returns whether the logger is configured to accept input for given
/// component and log level.
bool
accepts
(
unsigned
level
,
string_view
component_name
);
/// Returns the output format used for the log file.
inline
const
line_format
&
file_format
()
const
{
const
line_format
&
file_format
()
const
{
return
file_format_
;
}
/// Returns the output format used for the console.
inline
const
line_format
&
console_format
()
const
{
const
line_format
&
console_format
()
const
{
return
console_format_
;
}
unsigned
verbosity
()
const
noexcept
{
return
cfg_
.
verbosity
;
}
unsigned
file_verbosity
()
const
noexcept
{
return
cfg_
.
file_verbosity
;
}
unsigned
console_verbosity
()
const
noexcept
{
return
cfg_
.
console_verbosity
;
}
// -- static utility functions -----------------------------------------------
/// Renders the prefix (namespace and class) of a fully qualified function.
static
void
render_fun_prefix
(
std
::
ostream
&
out
,
const
event
&
x
);
...
...
@@ -239,9 +265,6 @@ public:
/// Renders the date of `x` in ISO 8601 format.
static
void
render_date
(
std
::
ostream
&
out
,
timestamp
x
);
/// Renders `x` using the line format `lf` to `out`.
void
render
(
std
::
ostream
&
out
,
const
line_format
&
lf
,
const
event
&
x
)
const
;
/// Parses `format_str` into a format description vector.
/// @warning The returned vector can have pointers into `format_str`.
static
line_format
parse_format
(
const
std
::
string
&
format_str
);
...
...
@@ -249,6 +272,11 @@ public:
/// Skips path in `filename`.
static
string_view
skip_path
(
string_view
filename
);
// -- utility functions ------------------------------------------------------
/// Renders `x` using the line format `lf` to `out`.
void
render
(
std
::
ostream
&
out
,
const
line_format
&
lf
,
const
event
&
x
)
const
;
/// Returns a string representation of the joined groups of `x` if `x` is an
/// actor with the `subscriber` mixin.
template
<
class
T
>
...
...
@@ -272,30 +300,37 @@ public:
return
"[]"
;
}
// -- individual flags -------------------------------------------------------
// -- thread-local properties ------------------------------------------------
/// Stores the actor system for the current thread.
static
void
set_current_actor_system
(
actor_system
*
);
/// Returns the logger for the current thread or `nullptr` if none is
/// registered.
static
logger
*
current_logger
();
static
constexpr
int
inline_output_flag
=
0x01
;
private:
// -- constructors, destructors, and assignment operators --------------------
static
constexpr
int
uncolored_console_flag
=
0x02
;
logger
(
actor_system
&
sys
)
;
static
constexpr
int
colored_console_flag
=
0x04
;
// -- initialization ---------------------------------------------------------
// -- composed flags ---------------------------------------------------------
void
init
(
actor_system_config
&
cfg
);
static
constexpr
int
console_output_flag
=
0x06
;
// -- event handling ---------------------------------------------------------
private:
void
handle_event
(
event
&
x
);
void
handle_file_event
(
event
&
x
);
void
handle_console_event
(
event
&
x
);
void
handle_event
(
const
event
&
x
);
void
handle_file_event
(
const
event
&
x
);
void
handle_console_event
(
const
event
&
x
);
void
log_first_line
();
void
log_last_line
();
logger
(
actor_system
&
sys
);
void
init
(
actor_system_config
&
cfg
);
// -- thread management ------------------------------------------------------
void
run
();
...
...
@@ -303,78 +338,102 @@ private:
void
stop
();
inline
bool
has
(
int
flag
)
const
noexcept
{
return
(
flags_
&
flag
)
!=
0
;
}
// -- member variables -------------------------------------------------------
inline
void
set
(
int
flag
)
noexcept
{
flags_
|=
flag
;
}
// Configures verbosity and output generation.
config
cfg_
;
// Filters events by component name.
std
::
string
component_filter
;
// References the parent system.
actor_system
&
system_
;
int
max_level_
;
int
console_level_
;
int
file_level_
;
int
flags_
;
// Guards aids_.
detail
::
shared_spinlock
aids_lock_
;
// Maps thread IDs to actor IDs.
std
::
unordered_map
<
std
::
thread
::
id
,
actor_id
>
aids_
;
std
::
thread
thread_
;
std
::
mutex
queue_mtx_
;
std
::
condition_variable
queue_cv_
;
intrusive
::
fifo_inbox
<
policy
>
queue_
;
// Identifies the thread that called `logger::start`.
std
::
thread
::
id
parent_thread_
;
// Timestamp of the first log event.
timestamp
t0_
;
// Format for generating file output.
line_format
file_format_
;
// Format for generating console output.
line_format
console_format_
;
// Stream for file output.
std
::
fstream
file_
;
std
::
string
component_filter
;
// Filled with log events by other threads.
detail
::
ringbuffer
<
event
,
queue_size
>
queue_
;
// Executes `logger::run`.
std
::
thread
thread_
;
};
/// @relates logger::field_type
std
::
string
to_string
(
logger
::
field_type
x
);
/// @relates logger::field
std
::
string
to_string
(
const
logger
::
field
&
x
);
/// @relates logger::field
bool
operator
==
(
const
logger
::
field
&
x
,
const
logger
::
field
&
y
);
}
// namespace caf
#define CAF_VOID_STMT static_cast<void>(0)
#define CAF_CAT(a, b) a##b
#define CAF_LOG_LEVEL_QUIET -1
#define CAF_LOG_LEVEL_ERROR 0
#define CAF_LOG_LEVEL_WARNING 1
#define CAF_LOG_LEVEL_INFO 2
#define CAF_LOG_LEVEL_DEBUG 3
#define CAF_LOG_LEVEL_TRACE 4
// -- macro constants ----------------------------------------------------------
#define CAF_ARG(argument) caf::detail::make_arg_wrapper(#argument, argument)
/// Expands to a no-op.
#define CAF_VOID_STMT static_cast<void>(0)
#define CAF_ARG2(argname, argval) caf::detail::make_arg_wrapper(argname, argval)
#ifndef CAF_LOG_COMPONENT
/// Name of the current component when logging.
#define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT
#define CAF_ARG3(argname, first, last) \
caf::detail::make_arg_wrapper(argname, first, last)
// -- utility macros -----------------------------------------------------------
#ifdef CAF_MSVC
/// Expands to a string representation of the current funciton name that
/// includes the full function name and its signature.
#define CAF_PRETTY_FUN __FUNCSIG__
#else // CAF_MSVC
/// Expands to a string representation of the current funciton name that
/// includes the full function name and its signature.
#define CAF_PRETTY_FUN __PRETTY_FUNCTION__
#endif // CAF_MSVC
#ifndef CAF_LOG_COMPONENT
#define CAF_LOG_COMPONENT "caf"
#endif // CAF_LOG_COMPONENT
/// Concatenates `a` and `b` to a single preprocessor token.
#define CAF_CAT(a, b) a##b
#define CAF_LOG_MAKE_EVENT(aid, component, loglvl, message) \
::caf::logger::event { \
loglvl,
component, CAF_PRETTY_FUN, __func__,
\
caf::logger::skip_path(__FILE__),
__LINE__,
\
loglvl,
__LINE__, component, CAF_PRETTY_FUN, __func__,
\
caf::logger::skip_path(__FILE__),
\
(::caf::logger::line_builder{} << message).get(), \
::std::this_thread::get_id(), aid, ::caf::make_timestamp() \
}
#if CAF_LOG_LEVEL == -1
/// Expands to `argument = <argument>` in log output.
#define CAF_ARG(argument) caf::detail::make_arg_wrapper(#argument, argument)
/// Expands to `argname = <argval>` in log output.
#define CAF_ARG2(argname, argval) caf::detail::make_arg_wrapper(argname, argval)
/// Expands to `argname = [argval, last)` in log output.
#define CAF_ARG3(argname, first, last) \
caf::detail::make_arg_wrapper(argname, first, last)
// -- logging macros -----------------------------------------------------------
#if CAF_LOG_LEVEL == CAF_LOG_LEVEL_QUIET
#define CAF_LOG_IMPL(unused1, unused2, unused3)
...
...
@@ -391,7 +450,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
#define CAF_LOG_TRACE(unused)
#else // CAF_LOG_LEVEL
#else // CAF_LOG_LEVEL
== CAF_LOG_LEVEL_QUIET
#define CAF_LOG_IMPL(component, loglvl, message) \
do { \
...
...
@@ -399,8 +458,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
if (CAF_UNIFYN(caf_logger) != nullptr \
&& CAF_UNIFYN(caf_logger)->accepts(loglvl, component)) \
CAF_UNIFYN(caf_logger) \
->log( \
new CAF_LOG_MAKE_EVENT(CAF_UNIFYN(caf_logger)->thread_local_aid(), \
->log(CAF_LOG_MAKE_EVENT(CAF_UNIFYN(caf_logger)->thread_local_aid(), \
component, loglvl, message)); \
} while (false)
...
...
@@ -458,60 +516,49 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
#define CAF_LOG_ERROR(output) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output)
#endif // CAF_LOG_LEVEL
#endif // CAF_LOG_LEVEL
== CAF_LOG_LEVEL_QUIET
#ifndef CAF_LOG_INFO
# define CAF_LOG_INFO(output) CAF_VOID_STMT
#endif
#define CAF_LOG_INFO(output) CAF_VOID_STMT
#define CAF_LOG_INFO_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_INFO
#define CAF_LOG_INFO_IF(cond, output) \
if (cond) \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output); \
CAF_VOID_STMT
#endif // CAF_LOG_INFO
#ifndef CAF_LOG_DEBUG
# define CAF_LOG_DEBUG(output) CAF_VOID_STMT
#endif
#ifndef CAF_LOG_WARNING
# define CAF_LOG_WARNING(output) CAF_VOID_STMT
#endif
#ifndef CAF_LOG_ERROR
# define CAF_LOG_ERROR(output) CAF_VOID_STMT
#endif
#ifdef CAF_LOG_LEVEL
#define CAF_LOG_DEBUG(output) CAF_VOID_STMT
#define CAF_LOG_DEBUG_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_DEBUG
#define CAF_LOG_DEBUG_IF(cond, output) \
if (cond)
{
\
if (cond)
\
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_DEBUG, output); \
} \
CAF_VOID_STMT
#define CAF_LOG_INFO_IF(cond, output) \
if (cond) { \
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_INFO, output); \
} \
CAF_VOID_STMT
#endif // CAF_LOG_DEBUG
#ifndef CAF_LOG_WARNING
#define CAF_LOG_WARNING(output) CAF_VOID_STMT
#define CAF_LOG_WARNING_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_WARNING
#define CAF_LOG_WARNING_IF(cond, output) \
if (cond)
{
\
if (cond)
\
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_WARNING, output); \
} \
CAF_VOID_STMT
#endif // CAF_LOG_WARNING
#ifndef CAF_LOG_ERROR
#define CAF_LOG_ERROR(output) CAF_VOID_STMT
#define CAF_LOG_ERROR_IF(cond, output) CAF_VOID_STMT
#else // CAF_LOG_ERROR
#define CAF_LOG_ERROR_IF(cond, output) \
if (cond)
{
\
if (cond)
\
CAF_LOG_IMPL(CAF_LOG_COMPONENT, CAF_LOG_LEVEL_ERROR, output); \
} \
CAF_VOID_STMT
#endif // CAF_LOG_ERROR
#else // CAF_LOG_LEVEL
#define CAF_LOG_DEBUG_IF(unused1, unused2)
#define CAF_LOG_INFO_IF(unused1, unused2)
#define CAF_LOG_WARNING_IF(unused1, unused2)
#define CAF_LOG_ERROR_IF(unused1, unused2)
#endif // CAF_LOG_LEVEL
// -- Standardized CAF events according to SE-0001.
// -- macros for logging CE-0001 events ----------------------------------------
/// The log component responsible for logging control flow events that are
/// crucial for understanding happens-before relations. See RFC SE-0001.
...
...
libcaf_core/src/atom.cpp
View file @
168a2a2b
...
...
@@ -18,34 +18,57 @@
#include "caf/atom.hpp"
#include <array>
#include <cstring>
#include "caf/string_view.hpp"
namespace
caf
{
atom_value
atom_from_string
(
const
std
::
string
&
x
)
{
if
(
x
.
size
()
>
10
)
return
atom
(
""
);
char
buf
[
11
];
memcpy
(
buf
,
x
.
c_str
(),
x
.
size
());
buf
[
x
.
size
()]
=
'\0'
;
return
atom
(
buf
);
}
namespace
{
/// A buffer for decoding atom values.
using
atom_value_buf
=
std
::
array
<
char
,
11
>
;
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
);
std
::
string
result
;
result
.
reserve
(
11
);
// don't read characters before we found the leading 0xF
// first four bits set?
// Don't read characters before we found the leading 0xF.
bool
read_chars
=
((
x
&
0xF000000000000000
)
>>
60
)
==
0xF
;
uint64_t
mask
=
0x0FC0000000000000
;
for
(
int
bitshift
=
54
;
bitshift
>=
0
;
bitshift
-=
6
,
mask
>>=
6
)
{
if
(
read_chars
)
result
+
=
detail
::
decoding_table
[(
x
&
mask
)
>>
bitshift
];
buf
[
pos
++
]
=
detail
::
decoding_table
[(
x
&
mask
)
>>
bitshift
];
else
if
(((
x
&
mask
)
>>
bitshift
)
==
0xF
)
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
libcaf_core/src/logger.cpp
View file @
168a2a2b
...
...
@@ -48,11 +48,22 @@ namespace caf {
namespace
{
constexpr
string_view
log_level_name
[]
=
{
"QUIET"
,
""
,
""
,
"ERROR"
,
""
,
""
,
"WARN"
,
""
,
""
,
"INFO"
,
""
,
""
,
"DEBUG"
,
"TRACE"
""
,
""
,
"TRACE"
,
};
constexpr
string_view
fun_prefixes
[]
=
{
...
...
@@ -70,6 +81,26 @@ constexpr string_view anon_ns[] = {
"`anonymous-namespace'"
,
// MSVC
};
/// Converts a verbosity atom to its integer counterpart.
unsigned
to_level_int
(
atom_value
x
)
{
switch
(
atom_uint
(
to_lowercase
(
x
)))
{
default:
return
CAF_LOG_LEVEL_QUIET
;
case
atom_uint
(
"quiet"
):
return
CAF_LOG_LEVEL_QUIET
;
case
atom_uint
(
"error"
):
return
CAF_LOG_LEVEL_ERROR
;
case
atom_uint
(
"warning"
):
return
CAF_LOG_LEVEL_WARNING
;
case
atom_uint
(
"info"
):
return
CAF_LOG_LEVEL_INFO
;
case
atom_uint
(
"debug"
):
return
CAF_LOG_LEVEL_DEBUG
;
case
atom_uint
(
"trace"
):
return
CAF_LOG_LEVEL_TRACE
;
}
}
// Reduces symbol by printing all prefixes to `out` and returning the
// remainder. For example, "ns::foo::bar" prints "ns.foo" to `out` and returns
// "bar".
...
...
@@ -214,15 +245,25 @@ inline logger* get_current_logger() {
}
// namespace <anonymous>
logger
::
event
::
event
(
int
lvl
,
string_view
cat
,
string_view
full_fun
,
string_view
fun
,
string_view
fn
,
int
line
,
std
::
string
msg
,
std
::
thread
::
id
t
,
actor_id
a
,
timestamp
ts
)
logger
::
config
::
config
()
:
verbosity
(
CAF_LOG_LEVEL
),
file_verbosity
(
CAF_LOG_LEVEL
),
console_verbosity
(
CAF_LOG_LEVEL
),
inline_output
(
false
),
console_coloring
(
false
)
{
// nop
}
logger
::
event
::
event
(
unsigned
lvl
,
unsigned
line
,
string_view
cat
,
string_view
full_fun
,
string_view
fun
,
string_view
fn
,
std
::
string
msg
,
std
::
thread
::
id
t
,
actor_id
a
,
timestamp
ts
)
:
level
(
lvl
),
line_number
(
line
),
category_name
(
cat
),
pretty_fun
(
full_fun
),
simple_fun
(
fun
),
file_name
(
fn
),
line_number
(
line
),
message
(
std
::
move
(
msg
)),
tid
(
std
::
move
(
t
)),
aid
(
a
),
...
...
@@ -292,14 +333,11 @@ actor_id logger::thread_local_aid(actor_id aid) {
return
0
;
// was empty before
}
void
logger
::
log
(
event
*
x
)
{
CAF_ASSERT
(
x
->
level
>=
0
&&
x
->
level
<=
4
);
if
(
has
(
inline_output_flag
))
{
std
::
unique_ptr
<
event
>
ptr
{
x
};
handle_event
(
*
ptr
);
}
else
{
queue_
.
synchronized_push_back
(
queue_mtx_
,
queue_cv_
,
x
);
}
void
logger
::
log
(
event
&&
x
)
{
if
(
cfg_
.
inline_output
)
handle_event
(
x
);
else
queue_
.
push_back
(
std
::
move
(
x
));
}
void
logger
::
set_current_actor_system
(
actor_system
*
x
)
{
...
...
@@ -313,9 +351,8 @@ logger* logger::current_logger() {
return
get_current_logger
();
}
bool
logger
::
accepts
(
int
level
,
string_view
cname
)
{
CAF_ASSERT
(
level
>=
0
&&
level
<=
4
);
if
(
level
>
max_level_
)
bool
logger
::
accepts
(
unsigned
level
,
string_view
cname
)
{
if
(
level
>
cfg_
.
verbosity
)
return
false
;
if
(
!
component_filter
.
empty
())
{
auto
it
=
std
::
search
(
component_filter
.
begin
(),
component_filter
.
end
(),
...
...
@@ -325,6 +362,10 @@ bool logger::accepts(int level, string_view cname) {
return
true
;
}
logger
::
logger
(
actor_system
&
sys
)
:
system_
(
sys
)
{
// nop
}
logger
::~
logger
()
{
stop
();
// tell system our dtor is done
...
...
@@ -333,46 +374,8 @@ logger::~logger() {
system_
.
logger_dtor_cv_
.
notify_one
();
}
logger
::
logger
(
actor_system
&
sys
)
:
system_
(
sys
),
max_level_
(
CAF_LOG_LEVEL
),
flags_
(
0
),
queue_
(
policy
{})
{
// nop
}
namespace
{
int
to_level_int
(
const
atom_value
atom
)
{
switch
(
atom_uint
(
atom
))
{
case
atom_uint
(
"quiet"
):
case
atom_uint
(
"QUIET"
):
return
CAF_LOG_LEVEL_QUIET
;
case
atom_uint
(
"error"
):
case
atom_uint
(
"ERROR"
):
return
CAF_LOG_LEVEL_ERROR
;
case
atom_uint
(
"warning"
):
case
atom_uint
(
"WARNING"
):
return
CAF_LOG_LEVEL_WARNING
;
case
atom_uint
(
"info"
):
case
atom_uint
(
"INFO"
):
return
CAF_LOG_LEVEL_INFO
;
case
atom_uint
(
"debug"
):
case
atom_uint
(
"DEBUG"
):
return
CAF_LOG_LEVEL_DEBUG
;
case
atom_uint
(
"trace"
):
case
atom_uint
(
"TRACE"
):
return
CAF_LOG_LEVEL_TRACE
;
default:
{
// nop
}
}
return
CAF_LOG_LEVEL_QUIET
;
}
}
// namespace
void
logger
::
init
(
actor_system_config
&
cfg
)
{
CAF_IGNORE_UNUSED
(
cfg
);
#if CAF_LOG_LEVEL >= 0
namespace
lg
=
defaults
::
logger
;
component_filter
=
get_or
(
cfg
,
"logger.component-filter"
,
lg
::
component_filter
);
...
...
@@ -383,9 +386,9 @@ void logger::init(actor_system_config& cfg) {
file_verbosity
=
get_or
(
cfg
,
"logger.file-verbosity"
,
file_verbosity
);
console_verbosity
=
get_or
(
cfg
,
"logger.console-verbosity"
,
console_verbosity
);
file_level_
=
to_level_int
(
file_verbosity
);
c
onsole_level_
=
to_level_int
(
console_verbosity
);
max_level_
=
std
::
max
(
file_level_
,
console_level_
);
cfg_
.
file_verbosity
=
to_level_int
(
file_verbosity
);
c
fg_
.
console_verbosity
=
to_level_int
(
console_verbosity
);
cfg_
.
verbosity
=
std
::
max
(
cfg_
.
file_verbosity
,
cfg_
.
console_verbosity
);
// Parse the format string.
file_format_
=
parse_format
(
get_or
(
cfg
,
"logger.file-format"
,
lg
::
file_format
));
...
...
@@ -393,13 +396,14 @@ void logger::init(actor_system_config& cfg) {
lg
::
console_format
));
// Set flags.
if
(
get_or
(
cfg
,
"logger.inline-output"
,
false
))
set
(
inline_output_flag
)
;
cfg_
.
inline_output
=
true
;
auto
con_atm
=
get_or
(
cfg
,
"logger.console"
,
lg
::
console
);
if
(
con_atm
==
atom
(
"UNCOLORED"
))
set
(
uncolored_console_flag
);
else
if
(
con_atm
==
atom
(
"COLORED"
))
set
(
colored_console_flag
);
#endif
if
(
to_lowercase
(
con_atm
)
==
atom
(
"colored"
))
{
cfg_
.
console_coloring
=
true
;
}
else
if
(
to_lowercase
(
con_atm
)
!=
atom
(
"uncolored"
))
{
// Disable console output if neither 'colored' nor 'uncolored' are present.
cfg_
.
console_verbosity
=
CAF_LOG_LEVEL_QUIET
;
}
}
void
logger
::
render_fun_prefix
(
std
::
ostream
&
out
,
const
event
&
x
)
{
...
...
@@ -543,42 +547,29 @@ string_view logger::skip_path(string_view path) {
}
void
logger
::
run
()
{
#if CAF_LOG_LEVEL >= 0
log_first_line
();
// receive log entries from other threads and actors
bool
stop
=
false
;
auto
f
=
[
&
](
event
&
x
)
{
// empty message means: shut down
if
(
x
.
message
.
empty
())
{
stop
=
true
;
return
intrusive
::
task_result
::
stop
;
}
handle_event
(
x
);
return
intrusive
::
task_result
::
resume
;
};
do
{
// make sure we have data to read and consume events
queue_
.
synchronized_await
(
queue_mtx_
,
queue_cv_
);
queue_
.
new_round
(
1000
,
f
);
}
while
(
!
stop
);
for
(;;)
{
queue_
.
wait_nonempty
();
auto
&
e
=
queue_
.
front
();
if
(
e
.
message
.
empty
())
{
log_last_line
();
#endif
return
;
}
handle_event
(
e
);
queue_
.
pop_front
();
}
}
void
logger
::
handle_file_event
(
event
&
x
)
{
void
logger
::
handle_file_event
(
const
event
&
x
)
{
// Print to file if available.
if
(
file_
&&
x
.
level
<=
file_
level_
)
if
(
file_
&&
x
.
level
<=
file_
verbosity
()
)
render
(
file_
,
file_format_
,
x
);
}
void
logger
::
handle_console_event
(
event
&
x
)
{
// Stop if no console output is configured.
if
(
!
has
(
console_output_flag
)
||
x
.
level
>
console_level_
)
void
logger
::
handle_console_event
(
const
event
&
x
)
{
if
(
x
.
level
>
console_verbosity
())
return
;
if
(
has
(
uncolored_console_flag
))
{
render
(
std
::
clog
,
console_format_
,
x
);
std
::
clog
<<
std
::
endl
;
}
else
{
if
(
cfg_
.
console_coloring
)
{
switch
(
x
.
level
)
{
default:
break
;
...
...
@@ -600,63 +591,50 @@ void logger::handle_console_event(event& x) {
}
render
(
std
::
clog
,
console_format_
,
x
);
std
::
clog
<<
term
::
reset_endl
;
}
else
{
render
(
std
::
clog
,
console_format_
,
x
);
std
::
clog
<<
std
::
endl
;
}
}
void
logger
::
handle_event
(
event
&
x
)
{
void
logger
::
handle_event
(
const
event
&
x
)
{
handle_file_event
(
x
);
handle_console_event
(
x
);
}
void
logger
::
log_first_line
()
{
auto
make_event
=
[
&
](
string_view
config_val
,
const
atom_value
default_val
)
->
event
{
auto
e
=
CAF_LOG_MAKE_EVENT
(
0
,
CAF_LOG_COMPONENT
,
CAF_LOG_LEVEL_DEBUG
,
""
);
auto
make_message
=
[
&
](
string_view
config_name
,
atom_value
default_value
)
{
std
::
string
msg
=
"level = "
;
msg
+=
to_string
(
get_or
(
system_
.
config
(),
config_
val
,
default_val
));
msg
+=
to_string
(
get_or
(
system_
.
config
(),
config_
name
,
default_value
));
msg
+=
", node = "
;
msg
+=
to_string
(
system_
.
node
());
return
{
CAF_LOG_LEVEL_INFO
,
CAF_LOG_COMPONENT
,
CAF_PRETTY_FUN
,
__func__
,
__FILE__
,
__LINE__
,
std
::
move
(
msg
),
std
::
this_thread
::
get_id
(),
0
,
make_timestamp
()};
msg
+=
", component_filter = "
;
msg
+=
deep_to_string
(
component_filter
);
return
msg
;
};
event
file
=
make_event
(
"logger.file-verbosity"
,
defaults
::
logger
::
file_verbosity
);
handle_file_event
(
file
);
event
console
=
make_event
(
"logger.console-verbosity"
,
defaults
::
logger
::
console_verbosity
);
handle_console_event
(
console
);
namespace
lg
=
defaults
::
logger
;
e
.
message
=
make_message
(
"logger.file-verbosity"
,
lg
::
file_verbosity
);
handle_file_event
(
e
);
e
.
message
=
make_message
(
"logger.console-verbosity"
,
lg
::
console_verbosity
);
handle_console_event
(
e
);
}
void
logger
::
log_last_line
()
{
event
tmp
{
CAF_LOG_LEVEL_INFO
,
CAF_LOG_COMPONENT
,
CAF_PRETTY_FUN
,
__func__
,
__FILE__
,
__LINE__
,
"EOF"
,
std
::
this_thread
::
get_id
(),
0
,
make_timestamp
()};
handle_event
(
tmp
);
auto
e
=
CAF_LOG_MAKE_EVENT
(
0
,
CAF_LOG_COMPONENT
,
CAF_LOG_LEVEL_DEBUG
,
""
);
handle_event
(
e
);
}
void
logger
::
start
()
{
#if CAF_LOG_LEVEL >= 0
parent_thread_
=
std
::
this_thread
::
get_id
();
if
(
max_level_
==
CAF_LOG_LEVEL_QUIET
)
if
(
verbosity
()
==
CAF_LOG_LEVEL_QUIET
)
return
;
t0_
=
make_timestamp
();
auto
f
=
get_or
(
system_
.
config
(),
"logger.file-name"
,
defaults
::
logger
::
file_name
);
if
(
f
.
empty
())
{
// No need to continue if console and log file are disabled.
if
(
has
(
console_output_flag
)
)
if
(
console_verbosity
()
==
CAF_LOG_LEVEL_QUIET
)
return
;
}
else
{
// Replace placeholders.
...
...
@@ -685,7 +663,7 @@ void logger::start() {
return
;
}
}
if
(
has
(
inline_output_flag
)
)
if
(
cfg_
.
inline_output
)
log_first_line
();
else
thread_
=
std
::
thread
{[
this
]
{
...
...
@@ -694,21 +672,18 @@ void logger::start() {
this
->
run
();
this
->
system_
.
thread_terminates
();
}};
#endif
}
void
logger
::
stop
()
{
#if CAF_LOG_LEVEL >= 0
if
(
has
(
inline_output_flag
))
{
if
(
cfg_
.
inline_output
)
{
log_last_line
();
return
;
}
if
(
!
thread_
.
joinable
())
return
;
//
an empty string means: shut down
queue_
.
synchronized_push_back
(
queue_mtx_
,
queue_cv_
,
new
event
);
//
A default-constructed event causes the logger to shutdown.
queue_
.
push_back
(
event
{}
);
thread_
.
join
();
#endif
}
std
::
string
to_string
(
logger
::
field_type
x
)
{
...
...
libcaf_core/test/logger.cpp
View file @
168a2a2b
...
...
@@ -179,11 +179,11 @@ CAF_TEST(rendering) {
// Rendering of events.
logger
::
event
e
{
CAF_LOG_LEVEL_WARNING
,
42
,
"unit.test"
,
"void ns::foo::bar()"
,
"bar"
,
"foo.cpp"
,
42
,
"hello world"
,
std
::
this_thread
::
get_id
(),
0
,
...
...
libcaf_core/test/ringbuffer.cpp
0 → 100644
View file @
168a2a2b
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
()
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