Commit 7c67a86d authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'develop'

parents 14467ae0 99fcd2b1
......@@ -22,6 +22,9 @@ endif()
if(NOT CAF_BUILD_STATIC)
set(CAF_BUILD_STATIC no)
endif()
if(NOT CAF_NO_OPENCL)
set(CAF_NO_OPENCL no)
endif()
################################################################################
......@@ -98,31 +101,83 @@ if(NOT WIN32 AND NOT NO_COMPILER_CHECK)
"or is not supported")
endif()
endif()
# set optional build flags
set(EXTRA_FLAGS "")
# add "-Werror" flag if --pedantic-build is used
if(CXX_WARNINGS_AS_ERROS)
set(EXTRA_FLAGS "-Werror")
endif()
# enable a ton of warnings if --more-clang-warnings is used
if(MORE_WARNINGS)
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(WFLAGS "-Weverything -Wno-c++98-compat -Wno-padded "
"-Wno-documentation-unknown-command -Wno-exit-time-destructors "
"-Wno-global-constructors -Wno-missing-prototypes "
"-Wno-c++98-compat-pedantic -Wno-unused-member-function "
"-Wno-unused-const-variable -Wno-switch-enum "
"-Wno-missing-noreturn -Wno-covered-switch-default")
elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
set(WFLAGS "-Waddress -Wall -Warray-bounds "
"-Wattributes -Wbuiltin-macro-redefined -Wcast-align "
"-Wcast-qual -Wchar-subscripts -Wclobbered -Wcomment "
"-Wconversion -Wconversion-null -Wcoverage-mismatch "
"-Wcpp -Wdelete-non-virtual-dtor -Wdeprecated "
"-Wdeprecated-declarations -Wdiv-by-zero -Wdouble-promotion "
"-Wempty-body -Wendif-labels -Wenum-compare -Wextra "
"-Wfloat-equal -Wformat -Wfree-nonheap-object "
"-Wignored-qualifiers -Winit-self "
"-Winline -Wint-to-pointer-cast -Winvalid-memory-model "
"-Winvalid-offsetof -Wlogical-op -Wmain -Wmaybe-uninitialized "
"-Wmissing-braces -Wmissing-field-initializers -Wmultichar "
"-Wnarrowing -Wnoexcept -Wnon-template-friend "
"-Wnon-virtual-dtor -Wnonnull -Woverflow "
"-Woverlength-strings -Wparentheses "
"-Wpmf-conversions -Wpointer-arith -Wreorder "
"-Wreturn-type -Wsequence-point -Wshadow "
"-Wsign-compare -Wswitch -Wtype-limits -Wundef "
"-Wuninitialized -Wunused -Wvla -Wwrite-strings")
endif()
# convert CMake list to a single string, erasing the ";" separators
string(REPLACE ";" "" WFLAGS_STR ${WFLAGS})
set(EXTRA_FLAGS "${EXTRA_FLAGS} ${WFLAGS_STR}")
endif()
# add -stdlib=libc++ when using Clang
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
message(STATUS "NOTE: Automatically added -stdlib=libc++ flag, "
"you can override this by defining CMAKE_CXX_FLAGS "
"(see 'configure --help')")
set(EXTRA_FLAGS "${EXTRA_FLAGS} -stdlib=libc++")
endif()
# enable address sanitizer if requested by the user
if(ENABLE_ADDRESS_SANITIZER)
# check whether address sanitizer is available
set(CXXFLAGS_BACKUP "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
try_run(ProgramResult
CompilationSucceeded
"${CMAKE_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/get_compiler_version.cpp")
if(NOT CompilationSucceeded)
message(WARNING "Address Sanitizer is not available on selected compiler")
else()
message(STATUS "Enable Address Sanitizer")
set(EXTRA_FLAGS "${EXTRA_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
endif()
# restore CXX flags
set(CMAKE_CXX_FLAGS "${CXXFLAGS_BACKUP}")
endif(ENABLE_ADDRESS_SANITIZER)
# -pthread is ignored on MacOSX but required on other platforms
if(NOT APPLE AND NOT WIN32)
set(EXTRA_FLAGS "${EXTRA_FLAGS} -pthread")
endif()
# check if the user provided CXXFLAGS, set defaults otherwise
if(CMAKE_CXX_FLAGS)
set(CXXFLAGS_PROVIDED true)
set(CMAKE_CXX_FLAGS_DEBUG "")
set(CMAKE_CXX_FLAGS_MINSIZEREL "")
set(CMAKE_CXX_FLAGS_RELEASE "")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "")
else()
set(CXXFLAGS_PROVIDED false)
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
message(STATUS "NOTE: Automatically added -stdlib=libc++ flag, "
"you can override this by defining CMAKE_CXX_FLAGS "
"(see 'configure --help')")
set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++ -fPIC")
if(MORE_CLANG_WARNINGS)
set(CMAKE_CXX_FLAGS "-pedantic -Weverything -Wno-c++98-compat "
"-Wno-padded -Wno-documentation-unknown-command "
"-Wno-exit-time-destructors -Wno-global-constructors "
"-Wno-missing-prototypes -Wno-c++98-compat-pedantic "
"-Wno-unused-member-function "
"-Wno-unused-const-variable")
endif()
else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wextra -Wall -pedantic -fPIC")
endif()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wextra -Wall -pedantic -fPIC ${EXTRA_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
......@@ -132,16 +187,6 @@ endif()
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
# enable clang's address sanitizer if requested by the user
if(ENABLE_ADDRESS_SANITIZER)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
message(STATUS "Enable address sanitizer")
endif(ENABLE_ADDRESS_SANITIZER)
# -pthread is ignored on MacOSX, enable it for all other platforms
if(NOT APPLE AND NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
# extra setup steps needed on MinGW
if(MINGW)
add_definitions(-D_WIN32_WINNT=0x0600)
......@@ -184,7 +229,7 @@ install(DIRECTORY libcaf_core/cppa/
install(DIRECTORY libcaf_io/caf/ DESTINATION include/caf
FILES_MATCHING PATTERN "*.hpp")
# install includes from opencl
if(EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/caf")
if(EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/CMakeLists.txt")
install(DIRECTORY libcaf_opencl/caf/ DESTINATION include/caf
FILES_MATCHING PATTERN "*.hpp")
endif()
......@@ -208,7 +253,7 @@ set(LIBCAF_INCLUDE_DIRS
"${CMAKE_SOURCE_DIR}/libcaf_core"
"${CMAKE_SOURCE_DIR}/libcaf_io")
# path to caf opencl headers
if(EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/caf")
if(EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/CMakeLists.txt")
set(LIBCAF_INCLUDE_DIRS
"${CMAKE_SOURCE_DIR}/libcaf_opencl/" "${LIBCAF_INCLUDE_DIRS}")
endif()
......@@ -230,7 +275,7 @@ add_subdirectory(libcaf_io)
# set io lib for sub directories
set(LIBCAF_IO_LIBRARY libcaf_io)
# set opencl lib for sub directories if not told otherwise
if(NOT CAF_NO_OPENCL AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/caf")
if(NOT CAF_NO_OPENCL AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/CMakeLists.txt")
message(STATUS "Enter subdirectory libcaf_opencl")
find_package(OPENCL REQUIRED)
add_subdirectory(libcaf_opencl)
......@@ -253,7 +298,7 @@ if(NOT CAF_NO_UNIT_TESTS)
message(STATUS "Enter subdirectory unit_testing")
add_subdirectory(unit_testing)
add_dependencies(all_unit_tests libcaf_io)
if(NOT CAF_NO_OPENCL AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/caf")
if(NOT CAF_NO_OPENCL AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/CMakeLists.txt")
add_subdirectory(libcaf_opencl/unit_testing)
endif()
endif()
......@@ -262,13 +307,13 @@ if(NOT CAF_NO_EXAMPLES)
message(STATUS "Enter subdirectory examples")
add_subdirectory(examples)
add_dependencies(all_examples libcaf_io)
if(NOT CAF_NO_OPENCL AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/caf")
if(NOT CAF_NO_OPENCL AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_opencl/CMakeLists.txt")
add_subdirectory(libcaf_opencl/examples)
add_dependencies(opencl_examples libcaf_opencl)
endif()
endif()
# build RIAC if not being told otherwise
if(NOT CAF_NO_RIAC AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_riac/caf/")
if(NOT CAF_NO_RIAC AND EXISTS "${CMAKE_SOURCE_DIR}/libcaf_riac/CMakeLists.txt")
message(STATUS "Enter subdirectory probe")
add_subdirectory(libcaf_riac)
if(CAF_BUILD_STATIC_ONLY OR CAF_BUILD_STATIC)
......@@ -288,7 +333,7 @@ else()
set(CAF_HAS_RIAC no)
endif()
# build nexus if not being told otherwise
if(NOT CAF_NO_NEXUS AND EXISTS "${CMAKE_SOURCE_DIR}/nexus/caf/")
if(NOT CAF_NO_NEXUS AND EXISTS "${CMAKE_SOURCE_DIR}/nexus/CMakeLists.txt")
if(NOT CAF_HAS_RIAC)
message(WARNING "cannot build nexus without RIAC submodule")
set(CAF_NO_NEXUS yes)
......@@ -307,7 +352,7 @@ else()
set(CAF_NO_NEXUS yes)
endif()
# build cash if not being told otherwise
if(NOT CAF_NO_CASH AND EXISTS "${CMAKE_SOURCE_DIR}/cash/caf/")
if(NOT CAF_NO_CASH AND EXISTS "${CMAKE_SOURCE_DIR}/cash/CMakeLists.txt")
if(NOT CAF_HAS_RIAC)
message(WARNING "cannot build cash without RIAC submodule")
set(CAF_NO_CASH yes)
......@@ -326,7 +371,7 @@ else()
set(CAF_NO_CASH yes)
endif()
# build benchmarks if not being told otherwise
if(NOT CAF_NO_BENCHMARKS AND EXISTS "${CMAKE_SOURCE_DIR}/benchmarks/caf/")
if(NOT CAF_NO_BENCHMARKS AND EXISTS "${CMAKE_SOURCE_DIR}/benchmarks/CMakeLists.txt")
message(STATUS "Enter subdirectory benchmarks")
add_subdirectory(benchmarks)
add_dependencies(all_benchmarks libcaf_io)
......@@ -363,6 +408,12 @@ if(DOXYGEN_FOUND)
endif(DOXYGEN_FOUND)
################################################################################
# Add additional project files to GUI #
################################################################################
add_custom_target(gui_dummy SOURCES configure)
################################################################################
# print summary #
################################################################################
......
Subproject commit ed985aed8bbd0f3eb6c149beff01a5a32276433f
Subproject commit 725fb2bee0e24fe8dce73d1c3896f72f7be2424b
Subproject commit 8d709e6da3796161680c726802ea6152e88eaf08
Subproject commit 429df03e9483483b9fe78a6676b67c5407b5ac11
......@@ -19,6 +19,8 @@ if [ -d "$sourcedir/benchmarks/caf" ] ; then
benchmark_suite_options="\
Benchmark Suite Options:
--with-javac=FILE path to Java compiler
--with-java=FILE path to Java Runtime
--with-scalac=FILE path to Scala compiler
--with-erlc=FILE path to Erlang compiler
--with-charmc=FILE path to Charm++ compiler
......@@ -43,8 +45,9 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
--dual-build build using both gcc and clang
--build-static build as static and shared library
--build-static-only build as static library only
--more-clang-warnings enables most of Clang's warning flags
--more-warnings enables most warnings
--no-compiler-check disable compiler version check
--warnings-as-errors enables -Werror
Installation Directories:
--prefix=PREFIX installation directory [/usr/local]
......@@ -73,8 +76,7 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
- INFO
- DEBUG
- TRACE
(implicitly sets --enable-debug)
--enable-address-sanitizer build with Clang's address sanitizer
--with-address-sanitizer build with address sanitizer if available
Influential Environment Variables (only on first invocation):
CXX C++ compiler command
......@@ -88,7 +90,16 @@ $benchmark_suite_options"
# $3 is the cache entry variable value
append_cache_entry ()
{
CMakeCacheEntries="$CMakeCacheEntries -D $1:$2=$3"
case "$3" in
*\ * )
# string contains whitespace
CMakeCacheEntries="$CMakeCacheEntries -D \"$1:$2=$3\""
;;
*)
# string contains whitespace
CMakeCacheEntries="$CMakeCacheEntries -D $1:$2=$3"
;;
esac
}
# Creates a build directory via CMake.
......@@ -99,6 +110,7 @@ append_cache_entry ()
# $5 is the CMake generator.
configure ()
{
CMakeCacheEntries=$CMakeDefaultCache
if [ -n "$1" ]; then
......@@ -135,23 +147,23 @@ configure ()
append_cache_entry LIBRARY_OUTPUT_PATH PATH "$absolute_builddir/lib"
fi
if [ -d $workdir ]; then
if [ -d "$workdir" ]; then
# If a build directory exists, check if it has a CMake cache.
if [ -f $workdir/CMakeCache.txt ]; then
if [ -f "$workdir/CMakeCache.txt" ]; then
# If the CMake cache exists, delete it so that this configuration
# is not tainted by a previous one.
rm -f $workdir/CMakeCache.txt
rm -f "$workdir/CMakeCache.txt"
fi
else
mkdir -p $workdir
mkdir -p "$workdir"
fi
cd $workdir
cd "$workdir"
if [ -n "$5" ]; then
cmake -G "$5" $CMakeCacheEntries $sourcedir
cmake -G "$5" $CMakeCacheEntries "$sourcedir"
else
cmake $CMakeCacheEntries $sourcedir
cmake $CMakeCacheEntries "$sourcedir"
fi
echo "# This is the command used to configure this build" > config.status
......@@ -204,6 +216,10 @@ while [ $# -ne 0 ]; do
append_cache_entry CAF_ENABLE_RUNTIME_CHECKS BOOL yes
;;
--enable-address-sanitizer)
echo "*** warning: --enable-address-sanitizer is deprecated, please use --with-address-sanitizer instead"
append_cache_entry ENABLE_ADDRESS_SANITIZER BOOL yes
;;
--with-address-sanitizer)
append_cache_entry ENABLE_ADDRESS_SANITIZER BOOL yes
;;
--no-memory-management)
......@@ -216,12 +232,15 @@ while [ $# -ne 0 ]; do
--standalone-build)
echo "*** WARNING: --standalone-build is deprecated"
;;
--more-clang-warnings)
append_cache_entry MORE_CLANG_WARNINGS BOOL yes
--more-warnings)
append_cache_entry MORE_WARNINGS BOOL yes
;;
--no-compiler-check)
append_cache_entry NO_COMPILER_CHECK BOOL yes
;;
--warnings-as-errors)
append_cache_entry CXX_WARNINGS_AS_ERROS BOOL yes
;;
--with-log-level=*)
level=`echo "$optarg" | tr '[:lower:]' '[:upper:]'`
case $level in
......@@ -256,40 +275,46 @@ while [ $# -ne 0 ]; do
append_cache_entry CMAKE_BUILD_TYPE STRING $optarg
;;
--build-dir=*)
builddir=$optarg
builddir="$optarg"
;;
--bin-dir=*)
bindir=$optarg
bindir="$optarg"
;;
--lib-dir=*)
libdir=$optarg
libdir="$optarg"
;;
--dual-build)
dualbuild=1
;;
--no-examples)
append_cache_entry CAF_NO_EXAMPLES STRING yes
append_cache_entry CAF_NO_EXAMPLES BOOL yes
;;
--no-qt-examples)
append_cache_entry CAF_NO_QT_EXAMPLES STRING yes
append_cache_entry CAF_NO_QT_EXAMPLES BOOL yes
;;
--no-protobuf-examples)
append_cache_entry CAF_NO_PROTOBUF_EXAMPLES STRING yes
append_cache_entry CAF_NO_PROTOBUF_EXAMPLES BOOL yes
;;
--no-curl-examples)
append_cache_entry CAF_NO_CURL_EXAMPLES STRING yes
append_cache_entry CAF_NO_CURL_EXAMPLES BOOL yes
;;
--no-unit-tests)
append_cache_entry CAF_NO_UNIT_TESTS STRING yes
append_cache_entry CAF_NO_UNIT_TESTS BOOL yes
;;
--no-opencl)
append_cache_entry CAF_NO_OPENCL STRING yes
append_cache_entry CAF_NO_OPENCL BOOL yes
;;
--build-static)
append_cache_entry CAF_BUILD_STATIC STRING yes
append_cache_entry CAF_BUILD_STATIC BOOL yes
;;
--build-static-only)
append_cache_entry CAF_BUILD_STATIC_ONLY STRING yes
append_cache_entry CAF_BUILD_STATIC_ONLY BOOL yes
;;
--with-javac=*)
append_cache_entry CAF_JAVA_COMPILER FILEPATH "$optarg"
;;
--with-java=*)
append_cache_entry CAF_JAVA_BIN FILEPATH "$optarg"
;;
--with-scalac=*)
append_cache_entry CAF_SCALA_COMPILER FILEPATH "$optarg"
......
......@@ -107,6 +107,7 @@ if(NOT CAF_NO_CURL_EXAMPLES)
find_package(CURL)
if(CURL_FOUND)
add_executable(curl_fuse curl/curl_fuse.cpp)
include_directories(${CURL_INCLUDE_DIRS})
target_link_libraries(curl_fuse ${CMAKE_DL_LIBS} ${LIBCAF_LIBRARIES} ${PTHREAD_LIBRARIES} ${CURL_LIBRARY})
add_dependencies(curl_fuse all_examples)
endif(CURL_FOUND)
......
......@@ -67,12 +67,25 @@ void write_int(broker* self, connection_handle hdl, T value) {
self->flush(hdl);
}
void write_int(broker* self, connection_handle hdl, uint64_t value) {
// write two uint32 values instead (htonl does not work for 64bit integers)
write_int(self, hdl, static_cast<uint32_t>(value));
write_int(self, hdl, static_cast<uint32_t>(value >> sizeof(uint32_t)));
}
// utility function for reading an ingeger from incoming data
template <class T>
T read_int(const void* data) {
T value;
memcpy(&value, data, sizeof(T));
return static_cast<T>(ntohl(value));
void read_int(const void* data, T& storage) {
memcpy(&storage, data, sizeof(T));
storage = static_cast<T>(ntohl(storage));
}
void read_int(const void* data, uint64_t& storage) {
uint32_t first;
uint32_t second;
read_int(data, first);
read_int(reinterpret_cast<const char*>(data) + sizeof(uint32_t), second);
storage = first | (static_cast<uint64_t>(second) << sizeof(uint32_t));
}
// implemenation of our broker
......@@ -115,12 +128,14 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
},
[=](const new_data_msg& msg) {
// read the atom value as uint64_t from buffer
auto atm_val = read_int<uint64_t>(msg.buf.data());
uint64_t atm_val;
read_int(msg.buf.data(), atm_val);
// cast to original type
auto atm = static_cast<atom_value>(atm_val);
// read integer value from buffer, jumping to the correct
// position via offset_data(...)
auto ival = read_int<int32_t>(msg.buf.data() + sizeof(uint64_t));
int32_t ival;
read_int(msg.buf.data() + sizeof(uint64_t), ival);
// show some output
aout(self) << "received {" << to_string(atm) << ", " << ival << "}"
<< endl;
......
......@@ -38,6 +38,7 @@
// C++ includes
#include <string>
#include <vector>
#include <random>
#include <iostream>
// libcurl
......@@ -46,7 +47,7 @@
// libcaf
#include "caf/all.hpp"
// disable some clang warnings here caused by CURL and srand(time(nullptr))
// disable some clang warnings here caused by CURL
#ifdef __clang__
# pragma clang diagnostic ignored "-Wshorten-64-to-32"
# pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
......@@ -101,6 +102,8 @@ class base_actor : public event_based_actor {
// nop
}
~base_actor();
inline actor_ostream& print() {
return m_out << m_color << m_name << " (id = " << id() << "): ";
}
......@@ -117,6 +120,10 @@ class base_actor : public event_based_actor {
actor_ostream m_out;
};
base_actor::~base_actor() {
// avoid weak-vtables warning
}
// encapsulates an HTTP request
class client_job : public base_actor {
public:
......@@ -125,6 +132,8 @@ class client_job : public base_actor {
// nop
}
~client_job();
protected:
behavior make_behavior() override {
print() << "init" << color::reset_endl;
......@@ -149,15 +158,24 @@ class client_job : public base_actor {
}
};
client_job::~client_job() {
// avoid weak-vtables warning
}
// spawns HTTP requests
class client : public base_actor {
public:
client(const actor& parent)
: base_actor(parent, "client", color::green), m_count(0) {
: base_actor(parent, "client", color::green),
m_count(0),
m_re(m_rd()),
m_dist(min_req_interval, max_req_interval) {
// nop
}
~client();
protected:
behavior make_behavior() override {
using std::chrono::milliseconds;
......@@ -175,7 +193,7 @@ class client : public base_actor {
// and should thus be spawned in a separate thread
spawn<client_job, detached+linked>(m_parent);
// compute random delay until next job is launched
auto delay = (rand() + min_req_interval) % max_req_interval;
auto delay = m_dist(m_re);
delayed_send(this, milliseconds(delay), atom("next"));
}
);
......@@ -183,17 +201,25 @@ class client : public base_actor {
private:
size_t m_count;
std::random_device m_rd;
std::default_random_engine m_re;
std::uniform_int_distribution<int> m_dist;
};
client::~client() {
// avoid weak-vtables warning
}
// manages a CURL session
class curl_worker : public base_actor {
public:
curl_worker(const actor& parent)
: base_actor(parent, "curl_worker", color::yellow) {
// nop
}
~curl_worker();
protected:
behavior make_behavior() override {
print() << "init" << color::reset_endl;
......@@ -203,7 +229,7 @@ class curl_worker : public base_actor {
return (
on(atom("read"), arg_match)
>> [=](const std::string& fname, uint64_t offset, uint64_t range)
-> cow_tuple<atom_value, buffer_type> {
-> message {
print() << "read" << color::reset_endl;
for (;;) {
m_buf.clear();
......@@ -241,7 +267,7 @@ class curl_worker : public base_actor {
<< color::reset_endl;
// tell parent that this worker is done
send(m_parent, atom("finished"));
return make_cow_tuple(atom("reply"), m_buf);
return make_message(atom("reply"), m_buf);
case 404: // file does not exist
print() << "http error: download failed with "
<< "'HTTP RETURN CODE': 404 (file does "
......@@ -275,6 +301,11 @@ class curl_worker : public base_actor {
buffer_type m_buf;
};
curl_worker::~curl_worker() {
// avoid weak-vtables warning
}
// manages {num_curl_workers} workers with a round-robin protocol
class curl_master : public base_actor {
public:
......@@ -282,6 +313,8 @@ class curl_master : public base_actor {
// nop
}
~curl_master();
protected:
behavior make_behavior() override {
print() << "init" << color::reset_endl;
......@@ -337,12 +370,17 @@ class curl_master : public base_actor {
std::vector<actor> m_busy_worker;
};
curl_master::~curl_master() {
// avoid weak-vtables warning
}
// signal handling for ctrl+c
namespace {
std::atomic<bool> shutdown_flag{false};
} // namespace <anonymous>
int main() {
// random number setup
srand(time(nullptr));
// install signal handler
struct sigaction act;
act.sa_handler = [](int) { shutdown_flag = true; };
......
......@@ -80,9 +80,10 @@ void tester(event_based_actor* self, const calculator_type& testee) {
int main() {
// announce custom message types
announce<shutdown_request>();
announce<plus_request>(&plus_request::a, &plus_request::b);
announce<minus_request>(&minus_request::a, &minus_request::b);
announce<shutdown_request>("shutdown_request");
announce<plus_request>("plus_request", &plus_request::a, &plus_request::b);
announce<minus_request>("minus_request", &minus_request::a,
&minus_request::b);
// test function-based impl
spawn(tester, spawn_typed(typed_calculator));
await_all_actors_done();
......
......@@ -8,8 +8,8 @@
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\ ******************************************************************************/
#include <cstdlib>
#include <string>
#include <cstdlib>
#include <iostream>
#include "caf/all.hpp"
......@@ -20,9 +20,9 @@ using namespace caf;
optional<uint16_t> to_port(const string& arg) {
char* last = nullptr;
auto res = strtol(arg.c_str(), &last, 10);
if (last == (arg.c_str() + arg.size()) && res > 1024) {
return res;
auto res = strtoul(arg.c_str(), &last, 10);
if (last == (arg.c_str() + arg.size()) && res <= 65536) {
return static_cast<uint16_t>(res);
}
return none;
}
......
......@@ -77,11 +77,11 @@ int main(int, char**) {
// announces foo to the libcaf type system;
// the function expects member pointers to all elements of foo
announce<foo>(&foo::a, &foo::b);
announce<foo>("foo", &foo::a, &foo::b);
// announce foo2 to the libcaf type system,
// note that recursive containers are managed automatically by libcaf
announce<foo2>(&foo2::a, &foo2::b);
announce<foo2>("foo2", &foo2::a, &foo2::b);
foo2 vd;
vd.a = 5;
......@@ -98,17 +98,11 @@ int main(int, char**) {
assert(vd == vd2);
// announce std::pair<int, int> to the type system;
// NOTE: foo_pair is NOT distinguishable from foo_pair2!
auto uti = announce<foo_pair>(&foo_pair::first, &foo_pair::second);
// since foo_pair and foo_pair2 are not distinguishable since typedefs
// do not 'create' an own type, this announce fails, since
// std::pair<int, int> is already announced
assert(announce<foo_pair2>(&foo_pair2::first, &foo_pair2::second) == uti);
// announce std::pair<int, int> to the type system
announce<foo_pair>("foo_pair", &foo_pair::first, &foo_pair::second);
// libcaf returns the same uniform_type_info
// instance for foo_pair and foo_pair2
// instance for the type aliases foo_pair and foo_pair2
assert(uniform_typeid<foo_pair>() == uniform_typeid<foo_pair2>());
// spawn a testee that receives two messages
......@@ -124,4 +118,3 @@ int main(int, char**) {
shutdown();
return 0;
}
......@@ -54,8 +54,8 @@ void testee(event_based_actor* self) {
int main(int, char**) {
// if a class uses getter and setter member functions,
// we pass those to the announce function as { getter, setter } pairs.
announce<foo>(make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
announce<foo>("foo", make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
{
scoped_actor self;
auto t = spawn(testee);
......
......@@ -68,14 +68,15 @@ int main(int, char**) {
foo_setter s2 = &foo::b;
// equal to example 3
announce<foo>(make_pair(g1, s1), make_pair(g2, s2));
announce<foo>("foo", make_pair(g1, s1), make_pair(g2, s2));
// alternative syntax that uses casts instead of variables
// (returns false since foo is already announced)
announce<foo>(make_pair(static_cast<foo_getter>(&foo::a),
static_cast<foo_setter>(&foo::a)),
make_pair(static_cast<foo_getter>(&foo::b),
static_cast<foo_setter>(&foo::b)));
announce<foo>("foo",
make_pair(static_cast<foo_getter>(&foo::a),
static_cast<foo_setter>(&foo::a)),
make_pair(static_cast<foo_getter>(&foo::b),
static_cast<foo_setter>(&foo::b)));
// spawn a new testee and send it a foo
{
......
......@@ -108,23 +108,20 @@ int main(int, char**) {
// it takes a pointer to the non-trivial member as first argument
// followed by all "sub-members" either as member pointer or
// { getter, setter } pair
announce<bar>(compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
&bar::i);
auto meta_bar_f = [] {
return compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
};
// with meta_bar_f, we can now announce bar
announce<bar>("bar", meta_bar_f(), &bar::i);
// baz has non-trivial data members with getter/setter pair
// and getter returning a mutable reference
announce<baz>(compound_member(make_pair(&baz::f, &baz::set_f),
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
// compound member that has a compound member
compound_member(&baz::b,
compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
&bar::i));
announce<baz>("baz", compound_member(make_pair(&baz::f, &baz::set_f),
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
// compound member that has a compound member
compound_member(&baz::b, meta_bar_f(), &bar::i));
// spawn a testee that receives two messages
auto t = spawn(testee, 2);
{
......
......@@ -86,9 +86,12 @@ bool operator==(const tree& lhs, const tree& rhs) {
// - does have a copy constructor
// - does provide operator==
class tree_type_info : public detail::abstract_uniform_type_info<tree> {
public:
tree_type_info() : detail::abstract_uniform_type_info<tree>("tree") {
// nop
}
protected:
void serialize(const void* ptr, serializer* sink) const {
// ptr is guaranteed to be a pointer of type tree
auto tree_ptr = reinterpret_cast<const tree*>(ptr);
......@@ -105,7 +108,6 @@ class tree_type_info : public detail::abstract_uniform_type_info<tree> {
}
private:
void serialize_node(const tree_node& node, serializer* sink) const {
// value, ... children ...
sink->write_value(node.value);
......@@ -127,7 +129,6 @@ class tree_type_info : public detail::abstract_uniform_type_info<tree> {
}
source->end_sequence();
}
};
using tree_vector = std::vector<tree>;
......@@ -171,7 +172,7 @@ void testee(event_based_actor* self, size_t remaining) {
int main() {
// the tree_type_info is owned by libcaf after this function call
announce(typeid(tree), std::unique_ptr<uniform_type_info>{new tree_type_info});
announce<tree_vector>();
announce<tree_vector>("tree_vector");
tree t0; // create a tree and fill it with some data
......
......@@ -38,13 +38,13 @@ set (LIBCAF_CORE_SRCS
src/continue_helper.cpp
src/decorated_tuple.cpp
src/default_attachable.cpp
src/demangle.cpp
src/deserializer.cpp
src/duration.cpp
src/event_based_actor.cpp
src/exception.cpp
src/execution_unit.cpp
src/exit_reason.cpp
src/forwarding_actor_proxy.cpp
src/get_mac_addresses.cpp
src/get_root_uuid.cpp
src/group.cpp
......@@ -59,9 +59,11 @@ set (LIBCAF_CORE_SRCS
src/message_builder.cpp
src/message_data.cpp
src/message_handler.cpp
src/message_iterator.cpp
src/node_id.cpp
src/ref_counted.cpp
src/response_promise.cpp
src/replies_to.cpp
src/resumable.cpp
src/ripemd_160.cpp
src/scoped_actor.cpp
......@@ -73,7 +75,7 @@ set (LIBCAF_CORE_SRCS
src/string_algorithms.cpp
src/string_serialization.cpp
src/sync_request_bouncer.cpp
src/to_uniform_name.cpp
src/try_match.cpp
src/uniform_type_info.cpp
src/uniform_type_info_map.cpp)
......
......@@ -78,16 +78,12 @@ class abstract_actor : public abstract_channel {
/**
* Attaches `ptr` to this actor. The actor will call `ptr->detach(...)` on
* exit, or immediately if it already finished execution.
* @returns `true` if `ptr` was successfully attached to the actor,
* otherwise (actor already exited) `false`.
*/
void attach(attachable_ptr ptr);
/**
* Convenience function that attaches the functor `f` to this actor. The
* actor executes `f()` on exit or immediatley if it is not running.
* @returns `true` if `f` was successfully attached to the actor,
* otherwise (actor already exited) `false`.
*/
template <class F>
void attach_functor(F f) {
......
......@@ -149,10 +149,10 @@ class actor : detail::comparable<actor>,
actor_id id() const;
private:
void swap(actor& other);
private:
inline abstract_actor* get() const {
return m_ptr.get();
}
......
......@@ -38,8 +38,8 @@ class actor_proxy;
using actor_proxy_ptr = intrusive_ptr<actor_proxy>;
/**
* Represents a remote actor.
* @extends abstract_actor
* Represents an actor running on a remote machine,
* or different hardware, or in a separate process.
*/
class actor_proxy : public abstract_actor {
public:
......
......@@ -29,6 +29,7 @@
#include "caf/match.hpp"
#include "caf/spawn.hpp"
#include "caf/config.hpp"
#include "caf/either.hpp"
#include "caf/extend.hpp"
#include "caf/channel.hpp"
#include "caf/message.hpp"
......
......@@ -79,8 +79,8 @@ namespace caf {
*/
/**
* Adds a new mapping to the type system. Returns `false` if a mapping
* for `tinfo` already exists, otherwise `true`.
* Adds a new mapping to the type system. Returns `utype.get()` on
* success, otherwise a pointer to the previously installed singleton.
* @warning `announce` is **not** thead-safe!
*/
const uniform_type_info* announce(const std::type_info& tinfo,
......@@ -95,7 +95,7 @@ const uniform_type_info* announce(const std::type_info& tinfo,
template <class C, class Parent, class... Ts>
std::pair<C Parent::*, detail::abstract_uniform_type_info<C>*>
compound_member(C Parent::*c_ptr, const Ts&... args) {
return {c_ptr, new detail::default_uniform_type_info<C>(args...)};
return {c_ptr, new detail::default_uniform_type_info<C>("???", args...)};
}
// deals with getter returning a mutable reference
......@@ -108,7 +108,7 @@ compound_member(C Parent::*c_ptr, const Ts&... args) {
template <class C, class Parent, class... Ts>
std::pair<C& (Parent::*)(), detail::abstract_uniform_type_info<C>*>
compound_member(C& (Parent::*getter)(), const Ts&... args) {
return {getter, new detail::default_uniform_type_info<C>(args...)};
return {getter, new detail::default_uniform_type_info<C>("???", args...)};
}
// deals with getter/setter pair
......@@ -126,16 +126,17 @@ compound_member(const std::pair<GRes (Parent::*)() const,
SRes (Parent::*)(SArg)>& gspair,
const Ts&... args) {
using mtype = typename std::decay<GRes>::type;
return {gspair, new detail::default_uniform_type_info<mtype>(args...)};
return {gspair, new detail::default_uniform_type_info<mtype>("???", args...)};
}
/**
* Adds a new type mapping for `C` to the type system.
* Adds a new type mapping for `C` to the type system
* using `tname` as its uniform name.
* @warning `announce` is **not** thead-safe!
*/
template <class C, class... Ts>
inline const uniform_type_info* announce(const Ts&... args) {
auto ptr = new detail::default_uniform_type_info<C>(args...);
inline const uniform_type_info* announce(std::string tname, const Ts&... args) {
auto ptr = new detail::default_uniform_type_info<C>(std::move(tname), args...);
return announce(typeid(C), uniform_type_info_ptr{ptr});
}
......
......@@ -21,6 +21,7 @@
#define CAF_ATOM_HPP
#include <string>
#include <type_traits>
#include "caf/detail/atom_val.hpp"
......@@ -46,6 +47,9 @@ constexpr atom_value atom(char const (&str)[Size]) {
return static_cast<atom_value>(detail::atom_val(str, 0xF));
}
template <atom_value Value>
using atom_constant = std::integral_constant<atom_value, Value>;
} // namespace caf
#endif // CAF_ATOM_HPP
......@@ -44,11 +44,6 @@ class behavior {
public:
friend class message_handler;
/**
* The type of continuations that can be used to extend an existing behavior.
*/
using continuation_fun = std::function<optional<message>(message&)>;
behavior() = default;
behavior(behavior&&) = default;
behavior(const behavior&) = default;
......@@ -115,12 +110,6 @@ class behavior {
return (m_impl) ? m_impl->invoke(std::forward<T>(arg)) : none;
}
/**
* Adds a continuation to this behavior that is executed
* whenever this behavior was successfully applied to a message.
*/
behavior add_continuation(continuation_fun fun);
/**
* Checks whether this behavior is not empty.
*/
......
......@@ -51,6 +51,8 @@ class blocking_actor
blocking_actor();
~blocking_actor();
/**************************************************************************
* utility stuff and receive() member function family *
**************************************************************************/
......
......@@ -34,7 +34,7 @@
* whereas each number is a two-digit decimal number without
* leading zeros (e.g. 900 is version 0.9.0).
*/
#define CAF_VERSION 1102
#define CAF_VERSION 1200
#define CAF_MAJOR_VERSION (CAF_VERSION / 10000)
#define CAF_MINOR_VERSION ((CAF_VERSION / 100) % 100)
......@@ -61,7 +61,18 @@
_Pragma("clang diagnostic ignored \"-Wconversion\"") \
_Pragma("clang diagnostic ignored \"-Wcast-align\"") \
_Pragma("clang diagnostic ignored \"-Wundef\"") \
_Pragma("clang diagnostic ignored \"-Wnested-anon-types\"")
_Pragma("clang diagnostic ignored \"-Wnested-anon-types\"") \
_Pragma("clang diagnostic ignored \"-Wdeprecated\"") \
_Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \
_Pragma("clang diagnostic ignored \"-Wdocumentation\"") \
_Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \
_Pragma("clang diagnostic ignored \"-Wimplicit-fallthrough\"") \
_Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
_Pragma("clang diagnostic ignored \"-Wshorten-64-to-32\"") \
_Pragma("clang diagnostic ignored \"-Wsign-conversion\"") \
_Pragma("clang diagnostic ignored \"-Wundef\"") \
_Pragma("clang diagnostic ignored \"-Wweak-vtables\"")
# define CAF_POP_WARNINGS \
_Pragma("clang diagnostic pop")
# define CAF_ANNOTATE_FALLTHROUGH [[clang::fallthrough]]
......@@ -104,44 +115,32 @@
# error Platform and/or compiler not supportet
#endif
#include <memory>
#include <cstdio>
#include <cstdlib>
// import backtrace and backtrace_symbols_fd into caf::detail
#ifdef CAF_WINDOWS
#include "caf/detail/execinfo_windows.hpp"
#else
#include <execinfo.h>
namespace caf {
namespace detail {
using ::backtrace;
using ::backtrace_symbols_fd;
} // namespace detail
} // namespace caf
#endif
#ifdef CAF_ENABLE_RUNTIME_CHECKS
# define CAF_REQUIRE__(stmt, file, line) \
printf("%s:%u: requirement failed '%s'\n", file, line, stmt); { \
void* array[10]; \
auto caf_bt_size = ::caf::detail::backtrace(array, 10); \
::caf::detail::backtrace_symbols_fd(array, caf_bt_size, 2); \
} abort()
# define CAF_REQUIRE(stmt) \
if (static_cast<bool>(stmt) == false) { \
CAF_REQUIRE__(#stmt, __FILE__, __LINE__); \
} static_cast<void>(0)
#else
# define CAF_REQUIRE(unused) static_cast<void>(0)
#endif
#define CAF_CRITICAL__(error, file, line) { \
printf("%s:%u: critical error: '%s'\n", file, line, error); \
exit(7); \
#ifndef CAF_ENABLE_RUNTIME_CHECKS
# define CAF_REQUIRE(unused) static_cast<void>(0)
#elif defined(CAF_WINDOWS) || defined(CAF_BSD)
# define CAF_REQUIRE(stmt) \
if (static_cast<bool>(stmt) == false) { \
printf("%s:%u: requirement failed '%s'\n", __FILE__, __LINE__, #stmt); \
abort(); \
} static_cast<void>(0)
#else // defined(CAF_LINUX) || defined(CAF_MACOS)
# include <execinfo.h>
# define CAF_REQUIRE(stmt) \
if (static_cast<bool>(stmt) == false) { \
printf("%s:%u: requirement failed '%s'\n", __FILE__, __LINE__, #stmt); \
void* array[10]; \
auto caf_bt_size = ::backtrace(array, 10); \
::backtrace_symbols_fd(array, caf_bt_size, 2); \
abort(); \
} static_cast<void>(0)
#endif
#define CAF_CRITICAL(error) CAF_CRITICAL__(error, __FILE__, __LINE__)
#define CAF_CRITICAL(error) \
printf("%s:%u: critical error: '%s'\n", __FILE__, __LINE__, error); \
abort()
#endif // CAF_CONFIG_HPP
......@@ -38,35 +38,17 @@ class local_actor;
class continue_helper {
public:
using message_id_wrapper_tag = int;
using getter = std::function<optional<behavior&> (message_id msg_id)>;
continue_helper(message_id mid, getter get_sync_handler);
/**
* Adds the continuation `fun` to the synchronous message handler
* that is invoked if the response handler successfully returned.
*/
template <class F>
continue_helper& continue_with(F fun) {
return continue_with(behavior::continuation_fun{
message_handler{on(any_vals, arg_match) >> fun}});
}
/**
* Adds the continuation `fun` to the synchronous message handler
* that is invoked if the response handler successfully returned.
*/
continue_helper& continue_with(behavior::continuation_fun fun);
continue_helper(message_id mid);
/**
* Returns the ID of the expected response message.
*/
message_id get_message_id() const { return m_mid; }
message_id get_message_id() const {
return m_mid;
}
private:
message_id m_mid;
getter m_getter;
//local_actor* m_self;
};
} // namespace caf
......
......@@ -26,7 +26,6 @@
#include "caf/detail/type_traits.hpp"
#include "caf/detail/to_uniform_name.hpp"
#include "caf/detail/uniform_type_info_map.hpp"
namespace caf {
......@@ -61,13 +60,8 @@ class abstract_uniform_type_info : public uniform_type_info {
protected:
abstract_uniform_type_info() {
auto uname = detail::to_uniform_name<T>();
auto cname = detail::mapped_name_by_decorated_name(uname.c_str());
if (cname == uname.c_str())
m_name = std::move(uname);
else
m_name = cname;
abstract_uniform_type_info(std::string tname) {
m_name = detail::mapped_name_by_decorated_name(std::move(tname));
}
static inline const T& deref(const void* ptr) {
......
......@@ -20,6 +20,8 @@
#ifndef CAF_DETAIL_APPLY_ARGS_HPP
#define CAF_DETAIL_APPLY_ARGS_HPP
#include <utility>
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_list.hpp"
......
......@@ -29,6 +29,7 @@
#include "caf/intrusive_ptr.hpp"
#include "caf/atom.hpp"
#include "caf/either.hpp"
#include "caf/message.hpp"
#include "caf/duration.hpp"
#include "caf/ref_counted.hpp"
......@@ -39,10 +40,6 @@
#include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp"
// <backward_compatibility version="0.9">
#include "cppa/cow_tuple.hpp"
// </backward_compatibility>
namespace caf {
class message_handler;
......@@ -72,7 +69,8 @@ struct optional_message_visitor_enable_tpl {
skip_message_t,
optional<skip_message_t>
>::value
&& !is_message_id_wrapper<T>::value;
&& !is_message_id_wrapper<T>::value
&& !std::is_convertible<T, message>::value;
};
struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
......@@ -95,15 +93,19 @@ struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
}
template <class T, class... Ts>
typename std::enable_if<optional_message_visitor_enable_tpl<T>::value,
bhvr_invoke_result>::type
typename std::enable_if<
optional_message_visitor_enable_tpl<T>::value,
bhvr_invoke_result
>::type
operator()(T& v, Ts&... vs) const {
return make_message(std::move(v), std::move(vs)...);
}
template <class T>
typename std::enable_if<is_message_id_wrapper<T>::value,
bhvr_invoke_result>::type
typename std::enable_if<
is_message_id_wrapper<T>::value,
bhvr_invoke_result
>::type
operator()(T& value) const {
return make_message(atom("MESSAGE_ID"),
value.get_message_id().integer_value());
......@@ -113,17 +115,24 @@ struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
return std::move(value);
}
template <class L, class R>
bhvr_invoke_result operator()(either_or_t<L, R>& value) const {
return std::move(value.value);
}
template <class... Ts>
bhvr_invoke_result operator()(std::tuple<Ts...>& value) const {
return detail::apply_args(*this, detail::get_indices(value), value);
}
// <backward_compatibility version="0.9">
template <class... Ts>
bhvr_invoke_result operator()(cow_tuple<Ts...>& value) const {
return static_cast<message>(std::move(value));
template <class T>
typename std::enable_if<
std::is_convertible<T, message>::value,
bhvr_invoke_result
>::type
operator()(const T& value) const {
return static_cast<message>(value);
}
// </backward_compatibility>
};
......
......@@ -27,9 +27,9 @@ namespace detail {
* Barton–Nackman trick implementation.
* `Subclass` must provide a compare member function that compares
* to instances of `T` and returns an integer x with:
* - `x < 0</tt> if <tt>*this < other
* - `x > 0</tt> if <tt>*this > other
* - `x == 0</tt> if <tt>*this == other
* - `x < 0` if `*this < other`
* - `x > 0` if `*this > other`
* - `x == 0` if `*this == other`
*/
template <class Subclass, class T = Subclass>
class comparable {
......
......@@ -17,43 +17,56 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
/******************************************************************************\
* Based on work by the mingw-w64 project; *
* original header: *
* *
* Copyright (c) 2012 mingw-w64 project *
* *
* Contributing author: Kai Tietz *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
* DEALINGS IN THE SOFTWARE. *
\ ******************************************************************************/
#ifndef CAF_DETAIL_EXECINFO_WINDOWS_HPP
#define CAF_DETAIL_EXECINFO_WINDOWS_HPP
#ifndef CAF_DETAIL_CTM_HPP
#define CAF_DETAIL_CTM_HPP
#include "caf/replies_to.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/typed_actor_util.hpp"
namespace caf {
namespace detail {
int backtrace(void** buffer, int size);
void backtrace_symbols_fd(void* const* buffer, int size, int fd);
// CTM: Compile-Time Match
// left hand side is the MPI we are comparing to, this is *not* commutative
template <class A, class B>
struct ctm_cmp : std::false_type { };
template <class T>
struct ctm_cmp<T, T> : std::true_type { };
template <class In, class Out>
struct ctm_cmp<typed_mpi<In, Out, empty_type_list>,
typed_mpi<In, type_list<typed_continue_helper<Out>>, empty_type_list>>
: std::true_type { };
template <class In, class L, class R>
struct ctm_cmp<typed_mpi<In, L, R>,
typed_mpi<In, type_list<skip_message_t>, empty_type_list>>
: std::true_type { };
template <class A, class B>
struct ctm : std::false_type { };
template <>
struct ctm<empty_type_list, empty_type_list> : std::true_type { };
template <class A, class... As, class... Bs>
struct ctm<type_list<A, As...>, type_list<Bs...>>
: std::conditional<
sizeof...(As) + 1 != sizeof...(Bs),
std::false_type,
ctm<type_list<As...>,
typename tl_filter_not<
type_list<Bs...>,
tbind<ctm_cmp, A>::template type
>::type
>
>::type { };
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_EXECINFO_WINDOWS_HPP
#endif // CAF_DETAIL_CTM_HPP
......@@ -248,16 +248,22 @@ template <class T, class AccessPolicy,
bool IsEmptyType = std::is_class<T>::value&& std::is_empty<T>::value>
class member_tinfo : public detail::abstract_uniform_type_info<T> {
public:
using super = detail::abstract_uniform_type_info<T>;
member_tinfo(AccessPolicy apol, SerializePolicy spol)
: m_apol(std::move(apol)), m_spol(std::move(spol)) {
: super("--member--"),
m_apol(std::move(apol)), m_spol(std::move(spol)) {
// nop
}
member_tinfo(AccessPolicy apol) : m_apol(std::move(apol)) {
member_tinfo(AccessPolicy apol)
: super("--member--"),
m_apol(std::move(apol)) {
// nop
}
member_tinfo() = default;
member_tinfo() : super("--member--") {
// nop
}
void serialize(const void* vptr, serializer* s) const override {
m_spol(m_apol(vptr), s);
......@@ -289,15 +295,19 @@ template <class T, class A, class S>
class member_tinfo<T, A, S, false, true>
: public detail::abstract_uniform_type_info<T> {
public:
member_tinfo(const A&, const S&) {
using super = detail::abstract_uniform_type_info<T>;
member_tinfo(const A&, const S&) : super("--member--") {
// nop
}
member_tinfo(const A&) {
member_tinfo(const A&) : super("--member--") {
// nop
}
member_tinfo() = default;
member_tinfo() : super("--member--") {
// nop
}
void serialize(const void*, serializer*) const override {
// nop
......@@ -312,14 +322,24 @@ template <class T, class AccessPolicy, class SerializePolicy>
class member_tinfo<T, AccessPolicy, SerializePolicy, true, false>
: public detail::abstract_uniform_type_info<T> {
public:
using super = detail::abstract_uniform_type_info<T>;
using value_type = typename std::underlying_type<T>::type;
member_tinfo(AccessPolicy apol, SerializePolicy spol)
: m_apol(std::move(apol)), m_spol(std::move(spol)) {}
: super("--member--"),
m_apol(std::move(apol)), m_spol(std::move(spol)) {
// nop
}
member_tinfo(AccessPolicy apol) : m_apol(std::move(apol)) {}
member_tinfo(AccessPolicy apol)
: super("--member--"),
m_apol(std::move(apol)) {
// nop
}
member_tinfo() = default;
member_tinfo() : super("--member--") {
// nop
}
void serialize(const void* p, serializer* s) const override {
auto val = m_apol(p);
......@@ -453,12 +473,15 @@ uniform_type_info_ptr new_member_tinfo(GRes (C::*getter)() const,
template <class T>
class default_uniform_type_info : public detail::abstract_uniform_type_info<T> {
public:
using super = detail::abstract_uniform_type_info<T>;
template <class... Ts>
default_uniform_type_info(Ts&&... args) {
default_uniform_type_info(std::string tname, Ts&&... args)
: super(std::move(tname)) {
push_back(std::forward<Ts>(args)...);
}
default_uniform_type_info() {
default_uniform_type_info(std::string tname) : super(std::move(tname)) {
using result_type = member_tinfo<T, fake_access_policy<T>>;
m_members.push_back(uniform_type_info_ptr(new result_type));
}
......@@ -552,9 +575,10 @@ template <class... Rs>
class default_uniform_type_info<typed_actor<Rs...>> :
public detail::abstract_uniform_type_info<typed_actor<Rs...>> {
public:
using super = detail::abstract_uniform_type_info<typed_actor<Rs...>>;
using handle_type = typed_actor<Rs...>;
default_uniform_type_info() {
default_uniform_type_info(std::string tname) : super(std::move(tname)) {
sub_uti = uniform_typeid<actor>();
}
......
......@@ -166,7 +166,7 @@ class double_ended_queue {
{ // lifetime scope of guard
lock_guard guard(m_head_lock);
first.reset(m_head.load());
node* next = m_head.load()->next;
node* next = first->next;
if (next == nullptr) {
// queue is empty
first.release();
......
......@@ -68,7 +68,9 @@ typename ieee_754_trait<T>::packed_type pack754(T f) {
typedef ieee_754_trait<T> trait; // using trait = ... fails on GCC 4.7
using result_type = typename trait::packed_type;
// filter special type
if (fabs(f) <= trait::zero) return 0; // only true if f equals +0 or -0
if (std::fabs(f) <= trait::zero) {
return 0; // only true if f equals +0 or -0
}
auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
// check sign and begin normalization
result_type sign;
......@@ -92,14 +94,14 @@ typename ieee_754_trait<T>::packed_type pack754(T f) {
}
fnorm = fnorm - static_cast<T>(1);
// calculate 2^significandbits
auto pownum = static_cast<result_type>(1) << significandbits;
auto pownum = static_cast<T>(result_type{1} << significandbits);
// calculate the binary form (non-float) of the significand data
auto significand = static_cast<result_type>(fnorm * (pownum + trait::p5));
// get the biased exponent
auto exp = shift + ((1 << (trait::expbits - 1)) - 1); // shift + bias
// return the final answer
return (sign << (trait::bits - 1)) |
(exp << (trait::bits - trait::expbits - 1)) | significand;
return (sign << (trait::bits - 1))
| (exp << (trait::bits - trait::expbits - 1)) | significand;
}
template <class T>
......@@ -109,11 +111,10 @@ typename ieee_754_trait<T>::float_type unpack754(T i) {
using result_type = typename trait::float_type;
if (i == 0) return trait::zero;
auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
// pull the significand
result_type result =
(i & ((static_cast<T>(1) << significandbits) - 1)); // mask
result /= (static_cast<T>(1) << significandbits); // convert back to float
result += static_cast<result_type>(1); // add the one back on
// pull the significand: mask, convert back to float + add the one back on
auto result = static_cast<result_type>(i & ((T{1} << significandbits) - 1));
result /= static_cast<result_type>(T{1} << significandbits);
result += static_cast<result_type>(1);
// deal with the exponent
auto si = static_cast<signed_type>(i);
auto bias = (1 << (trait::expbits - 1)) - 1;
......
......@@ -82,24 +82,24 @@ class lifted_fun_invoker {
using arg_types = typename get_callable_trait<F>::arg_types;
static constexpr size_t args = tl_size<arg_types>::value;
static constexpr size_t num_args = tl_size<arg_types>::value;
public:
lifted_fun_invoker(F& fun) : f(fun) {}
template <class... Ts>
typename std::enable_if<sizeof...(Ts) == args, R>::type
operator()(Ts&... args) const {
if (has_none(args...)) return none;
return f(unopt(args)...);
typename std::enable_if<sizeof...(Ts) == num_args, R>::type
operator()(Ts&... vs) const {
if (has_none(vs...)) return none;
return f(unopt(vs)...);
}
template <class T, class... Ts>
typename std::enable_if<(sizeof...(Ts) + 1 > args), R>::type
operator()(T& arg, Ts&... args) const {
if (has_none(arg)) return none;
return (*this)(args...);
typename std::enable_if<(sizeof...(Ts) + 1 > num_args), R>::type
operator()(T& v, Ts&... vs) const {
if (has_none(v)) return none;
return (*this)(vs...);
}
private:
......@@ -113,25 +113,25 @@ class lifted_fun_invoker<bool, F> {
using arg_types = typename get_callable_trait<F>::arg_types;
static constexpr size_t args = tl_size<arg_types>::value;
static constexpr size_t num_args = tl_size<arg_types>::value;
public:
lifted_fun_invoker(F& fun) : f(fun) {}
template <class... Ts>
typename std::enable_if<sizeof...(Ts) == args, bool>::type
operator()(Ts&&... args) const {
if (has_none(args...)) return false;
f(unopt(args)...);
typename std::enable_if<sizeof...(Ts) == num_args, bool>::type
operator()(Ts&&... vs) const {
if (has_none(vs...)) return false;
f(unopt(vs)...);
return true;
}
template <class T, class... Ts>
typename std::enable_if<(sizeof...(Ts) + 1 > args), bool>::type
operator()(T&& arg, Ts&&... args) const {
typename std::enable_if<(sizeof...(Ts) + 1 > num_args), bool>::type
operator()(T&& arg, Ts&&... vs) const {
if (has_none(arg)) return false;
return (*this)(args...);
return (*this)(vs...);
}
private:
......
......@@ -29,7 +29,6 @@
#include "caf/to_string.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/detail/demangle.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/scope_guard.hpp"
......@@ -184,7 +183,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
caf::detail::singletons::get_logger()->set_aid(aid_arg)
#endif
#define CAF_CLASS_NAME caf::detail::demangle(typeid(decltype(*this))).c_str()
#define CAF_CLASS_NAME typeid(*this).name()
#define CAF_PRINT0(lvlname, classname, funname, msg) \
CAF_LOG_IMPL(lvlname, classname, funname, msg)
......@@ -201,7 +200,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
#define CAF_PRINT_IF1(stmt, lvlname, classname, funname, msg) \
CAF_PRINT_IF0(stmt, lvlname, classname, funname, msg)
#if CAF_LOG_LEVEL < CAF_TRACE
#if !defined(CAF_LOG_LEVEL) || CAF_LOG_LEVEL < CAF_TRACE
#define CAF_PRINT4(arg0, arg1, arg2, arg3)
#else
#define CAF_PRINT4(lvlname, classname, funname, msg) \
......@@ -211,7 +210,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
}
#endif
#if CAF_LOG_LEVEL < CAF_DEBUG
#if !defined(CAF_LOG_LEVEL) || CAF_LOG_LEVEL < CAF_DEBUG
#define CAF_PRINT3(arg0, arg1, arg2, arg3)
#define CAF_PRINT_IF3(arg0, arg1, arg2, arg3, arg4)
#else
......@@ -221,7 +220,7 @@ inline caf::actor_id caf_set_aid_dummy() { return 0; }
CAF_PRINT_IF0(stmt, lvlname, classname, funname, msg)
#endif
#if CAF_LOG_LEVEL < CAF_INFO
#if !defined(CAF_LOG_LEVEL) || CAF_LOG_LEVEL < CAF_INFO
#define CAF_PRINT2(arg0, arg1, arg2, arg3)
#define CAF_PRINT_IF2(arg0, arg1, arg2, arg3, arg4)
#else
......
......@@ -31,7 +31,6 @@
#include "caf/uniform_type_info.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/message_iterator.hpp"
namespace caf {
......@@ -74,7 +73,7 @@ class message_data : public ref_counted {
bool equals(const message_data& other) const;
using const_iterator = message_iterator<message_data>;
using const_iterator = message_iterator;
inline const_iterator begin() const {
return {this};
......@@ -133,37 +132,6 @@ class message_data : public ref_counted {
};
struct full_eq_type {
constexpr full_eq_type() {}
template <class Tuple>
inline bool operator()(const message_iterator<Tuple>& lhs,
const message_iterator<Tuple>& rhs) const {
return lhs.type() == rhs.type() &&
lhs.type()->equals(lhs.value(), rhs.value());
}
};
struct types_only_eq_type {
constexpr types_only_eq_type() {}
template <class Tuple>
inline bool operator()(const message_iterator<Tuple>& lhs,
const uniform_type_info* rhs) const {
return lhs.type() == rhs;
}
template <class Tuple>
inline bool operator()(const uniform_type_info* lhs,
const message_iterator<Tuple>& rhs) const {
return lhs == rhs.type();
}
};
namespace {
constexpr full_eq_type full_eq;
constexpr types_only_eq_type types_only_eq;
} // namespace <anonymous>
std::string get_tuple_type_names(const detail::message_data&);
} // namespace detail
......
......@@ -17,53 +17,41 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_TUPLE_ITERATOR_HPP
#define CAF_DETAIL_TUPLE_ITERATOR_HPP
#ifndef CAF_DETAIL_MESSAGE_ITERATOR_HPP
#define CAF_DETAIL_MESSAGE_ITERATOR_HPP
#include <cstddef>
#include "caf/config.hpp"
#include "caf/fwd.hpp"
namespace caf {
namespace detail {
template <class Tuple>
class message_iterator {
size_t m_pos;
const Tuple* m_tuple;
class message_data;
class message_iterator {
public:
using pointer = message_data*;
using const_pointer = const message_data*;
inline message_iterator(const Tuple* tup, size_t pos = 0)
: m_pos(pos), m_tuple(tup) {}
message_iterator(const_pointer data, size_t pos = 0);
message_iterator(const message_iterator&) = default;
message_iterator& operator=(const message_iterator&) = default;
inline bool operator==(const message_iterator& other) const {
CAF_REQUIRE(other.m_tuple == other.m_tuple);
return other.m_pos == m_pos;
}
inline bool operator!=(const message_iterator& other) const {
return !(*this == other);
}
inline message_iterator& operator++() {
++m_pos;
return *this;
}
inline message_iterator& operator--() {
CAF_REQUIRE(m_pos > 0);
--m_pos;
return *this;
}
inline message_iterator operator+(size_t offset) {
return {m_tuple, m_pos + offset};
return {m_data, m_pos + offset};
}
inline message_iterator& operator+=(size_t offset) {
......@@ -72,29 +60,46 @@ class message_iterator {
}
inline message_iterator operator-(size_t offset) {
CAF_REQUIRE(m_pos >= offset);
return {m_tuple, m_pos - offset};
return {m_data, m_pos - offset};
}
inline message_iterator& operator-=(size_t offset) {
CAF_REQUIRE(m_pos >= offset);
m_pos -= offset;
return *this;
}
inline size_t position() const { return m_pos; }
inline const void* value() const { return m_tuple->at(m_pos); }
inline size_t position() const {
return m_pos;
}
inline const uniform_type_info* type() const {
return m_tuple->type_at(m_pos);
inline const_pointer data() const {
return m_data;
}
inline message_iterator& operator*() { return *this; }
const void* value() const;
const uniform_type_info* type() const;
inline message_iterator& operator*() {
return *this;
}
private:
size_t m_pos;
const message_data* m_data;
};
inline bool operator==(const message_iterator& lhs,
const message_iterator& rhs) {
return lhs.data() == rhs.data() && lhs.position() == rhs.position();
}
inline bool operator!=(const message_iterator& lhs,
const message_iterator& rhs) {
return !(lhs == rhs);
}
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_TUPLE_ITERATOR_HPP
#endif // CAF_DETAIL_MESSAGE_ITERATOR_HPP
......@@ -64,10 +64,10 @@ class proper_actor_base : public Policies::resume_policy::template
scheduling_policy().enqueue(dptr(), sender, mid, msg, eu);
}
inline void launch(bool hide, execution_unit* host) {
inline void launch(bool hide, bool lazy, execution_unit* eu) {
CAF_LOG_TRACE("");
this->is_registered(!hide);
this->scheduling_policy().launch(this, host);
this->scheduling_policy().launch(this, eu, lazy);
}
template <class F>
......@@ -255,17 +255,17 @@ class proper_actor<Base, Policies, true>
}
restore_cache();
}
bool has_timeout = false;
bool timeout_valid = false;
uint32_t timeout_id;
// request timeout if needed
if (bhvr.timeout().valid()) {
has_timeout = true;
timeout_valid = true;
timeout_id = this->request_timeout(bhvr.timeout());
}
// workaround for GCC 4.7 bug (const this when capturing refs)
auto& pending_timeouts = m_pending_timeouts;
auto guard = detail::make_scope_guard([&] {
if (has_timeout) {
if (timeout_valid) {
auto e = pending_timeouts.end();
auto i = std::find(pending_timeouts.begin(), e, timeout_id);
if (i != e) {
......
......@@ -47,15 +47,17 @@ template <size_t N, class... Ts>
const typename detail::type_at<N, Ts...>::type&
get(const detail::pseudo_tuple<Ts...>& tv) {
static_assert(N < sizeof...(Ts), "N >= tv.size()");
return *reinterpret_cast<const typename detail::type_at<N, Ts...>::type*>(
tv.at(N));
auto vp = tv.at(N);
CAF_REQUIRE(vp != nullptr);
return *reinterpret_cast<const typename detail::type_at<N, Ts...>::type*>(vp);
}
template <size_t N, class... Ts>
typename detail::type_at<N, Ts...>::type& get(detail::pseudo_tuple<Ts...>& tv) {
static_assert(N < sizeof...(Ts), "N >= tv.size()");
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>(
tv.mutable_at(N));
auto vp = tv.mutable_at(N);
CAF_REQUIRE(vp != nullptr);
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>(vp);
}
} // namespace detail
......
......@@ -17,19 +17,13 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_SPLIT_HPP
#define CAF_DETAIL_SPLIT_HPP
#ifndef CAF_DETAIL_SAFE_EQUAL_HPP
#define CAF_DETAIL_SAFE_EQUAL_HPP
#include <cmath> // fabs
#include <string>
#include <vector>
#include <limits>
#include <sstream>
#include <algorithm>
#include <type_traits>
#include "caf/detail/type_traits.hpp"
namespace caf {
namespace detail {
......@@ -39,18 +33,18 @@ namespace detail {
* performs an epsilon comparison.
*/
template <class T, typename U>
typename std::enable_if< !std::is_floating_point<T>::value
&& !std::is_floating_point<U>::value,
bool
typename std::enable_if<
!std::is_floating_point<T>::value && !std::is_floating_point<U>::value,
bool
>::type
safe_equal(const T& lhs, const U& rhs) {
return lhs == rhs;
}
template <class T, typename U>
typename std::enable_if< std::is_floating_point<T>::value
|| std::is_floating_point<U>::value,
bool
typename std::enable_if<
std::is_floating_point<T>::value || std::is_floating_point<U>::value,
bool
>::type
safe_equal(const T& lhs, const U& rhs) {
using res_type = decltype(lhs - rhs);
......@@ -60,4 +54,4 @@ safe_equal(const T& lhs, const U& rhs) {
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_SPLIT_HPP
#endif // CAF_DETAIL_SAFE_EQUAL_HPP
......@@ -336,7 +336,7 @@ class single_reader_queue {
pointer reader_blocked_dummy() {
// we are not going to dereference this pointer either
return reinterpret_cast<pointer>(reinterpret_cast<intptr_t>(this)
+ sizeof(void*));
+ static_cast<intptr_t>(sizeof(void*)));
}
bool is_dummy(pointer ptr) {
......
......@@ -20,7 +20,9 @@
#ifndef CAF_DETAIL_MATCHES_HPP
#define CAF_DETAIL_MATCHES_HPP
#include <array>
#include <numeric>
#include <typeinfo>
#include "caf/message.hpp"
#include "caf/wildcard_position.hpp"
......@@ -31,80 +33,75 @@
namespace caf {
namespace detail {
template <class Pattern, class FilteredPattern>
struct matcher;
template <class... Ts, class... Us>
struct matcher<type_list<Ts...>, type_list<Us...>> {
template <class TupleIter, class PatternIter, class Push, class Commit,
class Rollback>
bool operator()(TupleIter tbegin, TupleIter tend, PatternIter pbegin,
PatternIter pend, Push& push, Commit& commit,
Rollback& rollback) const {
while (!(pbegin == pend && tbegin == tend)) {
if (pbegin == pend) {
// reached end of pattern while some values remain unmatched
return false;
}
if (*pbegin == nullptr) { // nullptr == wildcard (anything)
// perform submatching
++pbegin;
// always true at the end of the pattern
if (pbegin == pend) {
return true;
}
// safe current mapping as fallback
commit();
// iterate over tuple values until we found a match
for (; tbegin != tend; ++tbegin) {
if ((*this)(tbegin, tend, pbegin, pend, push, commit, rollback)) {
return true;
}
// restore mapping to fallback (delete invalid mappings)
rollback();
}
return false; // no submatch found
}
// compare types
if (tbegin.type() != *pbegin) {
// type mismatch
return false;
}
// next iteration
push(tbegin);
++tbegin;
++pbegin;
}
return true; // pbegin == pend && tbegin == tend
bool match_element(const std::type_info* type, const message_iterator& iter,
void** storage);
template <class T>
bool match_integral_constant_element(const std::type_info* type,
const message_iterator& iter,
void** storage) {
auto value = T::value;
auto uti = iter.type();
if (!uti->equal_to(*type) || !uti->equals(iter.value(), &value)) {
return false;
}
if (storage) {
// This assignment implicitly casts `T*` to `integral_constant<T, V>*`.
// This type violation could theoretically cause undefined behavior.
// However, `T::value` does have an address that is guaranteed to be valid
// throughout the runtime of the program and the integral constant
// objects does not have any members. Hence, this is nonetheless safe.
auto ptr = reinterpret_cast<const void*>(&T::value);
// Again, this const cast is always safe because we will never derefence
// this pointer since `integral_constant<T, V>` has no members.
*storage = const_cast<void*>(ptr);
}
return true;
}
struct meta_element {
const std::type_info* type;
bool (*fun)(const std::type_info*, const message_iterator&, void**);
};
template <class T>
struct meta_element_factory {
static meta_element create() {
return {&typeid(T), match_element};
}
};
bool operator()(const message& tup, pseudo_tuple<Us...>* out) const {
auto& tarr = static_types_array<Ts...>::arr;
if (sizeof...(Us) == 0) {
// this pattern only has wildcards and thus always matches
return true;
}
if (tup.size() < sizeof...(Us)) {
return false;
}
if (out) {
size_t pos = 0;
size_t fallback_pos = 0;
auto fpush = [&](const typename message::const_iterator& iter) {
(*out)[pos++] = const_cast<void*>(iter.value());
};
auto fcommit = [&] { fallback_pos = pos; };
auto frollback = [&] { pos = fallback_pos; };
return (*this)(tup.begin(), tup.end(), tarr.begin(), tarr.end(), fpush,
fcommit, frollback);
}
auto no_push = [](const typename message::const_iterator&) { };
auto nop = [] { };
return (*this)(tup.begin(), tup.end(), tarr.begin(), tarr.end(), no_push,
nop, nop);
template <class T, T V>
struct meta_element_factory<std::integral_constant<T, V>> {
static meta_element create() {
return {&typeid(T),
match_integral_constant_element<std::integral_constant<T, V>>};
}
};
template <>
struct meta_element_factory<anything> {
static meta_element create() {
return {nullptr, nullptr};
}
};
template <class TypeList>
struct meta_elements;
template <class... Ts>
struct meta_elements<type_list<Ts...>> {
std::array<meta_element, sizeof...(Ts)> arr;
meta_elements() : arr{{meta_element_factory<Ts>::create()...}} {
// nop
}
};
bool try_match(const message& msg,
const meta_element* pattern_begin,
size_t pattern_size,
void** out = nullptr);
} // namespace detail
} // namespace caf
......
......@@ -207,8 +207,8 @@ class is_forward_iterator {
};
/**
* Checks wheter `T` has `begin()</tt> and <tt>end() member
* functions returning forward iterators.
* Checks wheter `T` has `begin()` and `end()` member
* functions returning forward iterators.
*/
template <class T>
class is_iterable {
......@@ -256,6 +256,23 @@ struct is_mutable_ref {
&& !std::is_const<T>::value;
};
/**
* Checks whether `T::static_type_name()` exists.
*/
template <class T>
class has_static_type_name {
private:
template <class U,
class = typename std::enable_if<
!std::is_member_pointer<decltype(&U::is_baz)>::value
>::type>
static std::true_type sfinae_fun(int);
template <class>
static std::false_type sfinae_fun(...);
public:
static constexpr bool value = decltype(sfinae_fun<T>(0))::value;
};
/**
* Returns either `T` or `T::type` if `T` is an option.
*/
......@@ -464,6 +481,16 @@ struct is_optional<optional<T>> : std::true_type {
// no members
};
template <class T>
struct is_integral_constant : std::false_type {
// no members
};
template <class T, T V>
struct is_integral_constant<std::integral_constant<T, V>> : std::true_type {
// no members
};
} // namespace detail
} // namespace caf
......
......@@ -23,6 +23,7 @@
#include <tuple>
#include "caf/replies_to.hpp"
#include "caf/system_messages.hpp"
#include "caf/detail/type_list.hpp"
......@@ -36,26 +37,56 @@ class typed_continue_helper;
namespace caf {
namespace detail {
template <class R, typename T>
struct deduce_signature_helper;
template <class T>
struct unwrap_std_tuple {
using type = type_list<T>;
};
template <class... Ts>
struct unwrap_std_tuple<std::tuple<Ts...>> {
using type = type_list<Ts...>;
};
template <class R, class... Ts>
struct deduce_signature_helper<R, type_list<Ts...>> {
using type = typename replies_to<Ts...>::template with<R>;
template <class T>
struct deduce_lhs_result {
using type = typename unwrap_std_tuple<T>::type;
};
template <class L, class R>
struct deduce_lhs_result<either_or_t<L, R>> {
using type = L;
};
template <class... Rs, class... Ts>
struct deduce_signature_helper<std::tuple<Rs...>, type_list<Ts...>> {
using type = typename replies_to<Ts...>::template with<Rs...>;
template <class T>
struct deduce_rhs_result {
using type = type_list<>;
};
template <class L, class R>
struct deduce_rhs_result<either_or_t<L, R>> {
using type = R;
};
template <class T>
struct deduce_signature {
using result_ = typename implicit_conversions<typename T::result_type>::type;
struct is_hidden_msg_handler : std::false_type { };
template <>
struct is_hidden_msg_handler<typed_mpi<type_list<exit_msg>,
type_list<void>,
empty_type_list>> : std::true_type { };
template <>
struct is_hidden_msg_handler<typed_mpi<type_list<down_msg>,
type_list<void>,
empty_type_list>> : std::true_type { };
template <class T>
struct deduce_mpi {
using result = typename implicit_conversions<typename T::result_type>::type;
using arg_t = typename tl_map<typename T::arg_types, std::decay>::type;
using type = typename deduce_signature_helper<result_, arg_t>::type;
using type = typed_mpi<arg_t,
typename deduce_lhs_result<result>::type,
typename deduce_rhs_result<result>::type>;
};
template <class Arguments>
......@@ -67,20 +98,50 @@ struct input_is {
};
};
template <class OutputList, typename F>
inline void assert_types() {
using arg_types =
typename tl_map<
typename get_callable_trait<F>::arg_types,
std::decay
>::type;
static constexpr size_t fun_args = tl_size<arg_types>::value;
static_assert(fun_args <= tl_size<OutputList>::value,
"functor takes too much arguments");
using recv_types = typename tl_right<OutputList, fun_args>::type;
static_assert(std::is_same<arg_types, recv_types>::value,
"wrong functor signature");
}
template <class OutputPair, class... Fs>
struct type_checker;
template <class OutputList, class F1>
struct type_checker<OutputList, F1> {
static void check() {
using arg_types =
typename tl_map<
typename get_callable_trait<F1>::arg_types,
std::decay
>::type;
static constexpr size_t args = tl_size<arg_types>::value;
static_assert(args <= tl_size<OutputList>::value,
"functor takes too much arguments");
using rtypes = typename tl_right<OutputList, args>::type;
static_assert(std::is_same<arg_types, rtypes>::value,
"wrong functor signature");
}
};
template <class Opt1, class Opt2, class F1>
struct type_checker<type_pair<Opt1, Opt2>, F1> {
static void check() {
type_checker<Opt1, F1>::check();
}
};
template <class OutputPair, class F1>
struct type_checker<OutputPair, F1, none_t> : type_checker<OutputPair, F1> { };
template <class Opt1, class Opt2, class F1, class F2>
struct type_checker<type_pair<Opt1, Opt2>, F1, F2> {
static void check() {
type_checker<Opt1, F1>::check();
type_checker<Opt2, F2>::check();
}
};
template <class A, class B, template <class, class> class Predicate>
struct static_asserter {
static void verify_match() {
static_assert(Predicate<A, B>::value, "exact match needed");
}
};
template <class T>
struct lifted_result_type {
......@@ -93,27 +154,50 @@ struct lifted_result_type<std::tuple<Ts...>> {
};
template <class T>
struct deduce_output_type_step2 {
struct deduce_lifted_output_type {
using type = T;
};
template <class R>
struct deduce_output_type_step2<type_list<typed_continue_helper<R>>> {
struct deduce_lifted_output_type<type_list<typed_continue_helper<R>>> {
using type = typename lifted_result_type<R>::type;
};
template <class Signatures, typename InputTypes>
struct deduce_output_type {
static constexpr int input_pos = tl_find_if<
Signatures,
input_is<InputTypes>::template eval
>::value;
static_assert(input_pos >= 0, "typed actor does not support given input");
static_assert(tl_find<
InputTypes,
atom_value
>::value == -1,
"atom(...) notation is not sufficient for static type "
"checking, please use atom_constant instead in this context");
static constexpr int input_pos =
tl_find_if<
Signatures,
input_is<InputTypes>::template eval
>::value;
static_assert(input_pos != -1, "typed actor does not support given input");
using signature = typename tl_at<Signatures, input_pos>::type;
using type =
typename deduce_output_type_step2<
typename signature::output_types
>::type;
using type = detail::type_pair<typename signature::output_opt1_types,
typename signature::output_opt2_types>;
};
template <class... Ts>
struct common_result_type;
template <class T>
struct common_result_type<T> {
using type = T;
};
template <class T, class... Us>
struct common_result_type<T, T, Us...> {
using type = typename common_result_type<T, Us...>::type;
};
template <class T1, class T2, class... Us>
struct common_result_type<T1, T2, Us...> {
using type = void;
};
} // namespace detail
......
......@@ -23,15 +23,11 @@
#include <atomic>
#include <typeinfo>
#include "caf/uniform_typeid.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/type_traits.hpp"
// forward declarations
namespace caf {
class uniform_type_info;
const uniform_type_info* uniform_typeid(const std::type_info&);
} // namespace caf
namespace caf {
namespace detail {
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_EITHER_HPP
#define CAF_EITHER_HPP
#include <tuple>
#include <string>
#include <type_traits>
#include "caf/message.hpp"
#include "caf/replies_to.hpp"
#include "caf/type_name_access.hpp"
#include "caf/illegal_message_element.hpp"
#include "caf/detail/type_list.hpp"
namespace caf {
/** @cond PRIVATE */
std::string either_or_else_type_name(size_t lefts_size,
const std::string* lefts,
size_t rights_size,
const std::string* rights);
template <class L, class R>
struct either_or_t;
template <class... Ls, class... Rs>
struct either_or_t<detail::type_list<Ls...>,
detail::type_list<Rs...>> : illegal_message_element {
static_assert(!std::is_same<detail::type_list<Ls...>,
detail::type_list<Rs...>>::value,
"template parameter packs must differ");
either_or_t(Ls... ls) : value(make_message(std::move(ls)...)) {
// nop
}
either_or_t(Rs... rs) : value(make_message(std::move(rs)...)) {
// nop
}
using opt1_type = std::tuple<Ls...>;
using opt2_type = std::tuple<Rs...>;
message value;
static std::string static_type_name() {
std::string lefts[] = {type_name_access<Ls>::get()...};
std::string rights[] = {type_name_access<Rs>::get()...};
return either_or_else_type_name(sizeof...(Ls), lefts,
sizeof...(Rs), rights);
}
};
/** @endcond */
template <class... Ts>
struct either {
template <class... Us>
using or_else = either_or_t<detail::type_list<Ts...>,
detail::type_list<Us...>>;
};
} // namespace caf
#endif // CAF_EITHER_HPP
......@@ -17,50 +17,24 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_IO_REMOTE_ACTOR_PROXY_HPP
#define CAF_IO_REMOTE_ACTOR_PROXY_HPP
#ifndef CAF_FORWARDING_ACTOR_PROXY_HPP
#define CAF_FORWARDING_ACTOR_PROXY_HPP
#include "caf/extend.hpp"
#include "caf/actor.hpp"
#include "caf/actor_proxy.hpp"
#include "caf/mixin/memory_cached.hpp"
#include "caf/detail/single_reader_queue.hpp"
namespace caf {
namespace detail {
class memory;
} // namespace detail
} // namespace caf
#include "caf/detail/shared_spinlock.hpp"
namespace caf {
namespace io {
class middleman;
class sync_request_info : public extend<memory_managed>::
with<mixin::memory_cached> {
friend class detail::memory;
/**
* Implements a simple proxy forwarding all operations to a manager.
*/
class forwarding_actor_proxy : public actor_proxy {
public:
using pointer = sync_request_info*;
~sync_request_info();
forwarding_actor_proxy(actor_id mid, node_id pinfo, actor parent);
pointer next; // intrusive next pointer
actor_addr sender; // points to the sender of the message
message_id mid; // sync message ID
private:
sync_request_info(actor_addr sptr, message_id id);
};
class remote_actor_proxy : public actor_proxy {
using super = actor_proxy;
public:
remote_actor_proxy(actor_id mid, node_id pinfo,
actor parent);
~forwarding_actor_proxy();
void enqueue(const actor_addr&, message_id,
message, execution_unit*) override;
......@@ -73,17 +47,17 @@ class remote_actor_proxy : public actor_proxy {
void kill_proxy(uint32_t reason) override;
protected:
~remote_actor_proxy();
actor manager() const;
void manager(actor new_manager);
private:
void forward_msg(const actor_addr& sender, message_id mid, message msg);
actor m_parent;
};
using remote_actor_proxy_ptr = intrusive_ptr<remote_actor_proxy>;
mutable detail::shared_spinlock m_manager_mtx;
actor m_manager;
};
} // namespace io
} // namespace caf
#endif // CAF_IO_REMOTE_ACTOR_PROXY_HPP
#endif // CAF_FORWARDING_ACTOR_PROXY_HPP
......@@ -47,11 +47,13 @@ class blocking_actor;
class message_handler;
class uniform_type_info;
class event_based_actor;
class forwarding_actor_proxy;
// structs
struct anything;
struct invalid_actor_t;
struct invalid_actor_addr_t;
struct illegal_message_element;
// enums
enum class atom_value : uint64_t;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_ILLEGAL_MESSAGE_ELEMENT_HPP
#define CAF_ILLEGAL_MESSAGE_ELEMENT_HPP
#include <type_traits>
namespace caf {
/**
* Marker class identifying classes in CAF that are not allowed
* to be used as message element.
*/
struct illegal_message_element {
// no members (marker class)
};
template <class T>
struct is_illegal_message_element
: std::is_base_of<illegal_message_element, T> { };
} // namespace caf
#endif // CAF_ILLEGAL_MESSAGE_ELEMENT_HPP
......@@ -504,8 +504,8 @@ class local_actor : public extend<abstract_actor>::with<mixin::memory_cached> {
// returns 0 if last_dequeued() is an asynchronous or sync request message,
// a response id generated from the request id otherwise
inline message_id get_response_id() {
auto id = m_current_node->mid;
return (id.is_request()) ? id.response_id() : message_id();
auto mid = m_current_node->mid;
return (mid.is_request()) ? mid.response_id() : message_id();
}
void reply_message(message&& what);
......
......@@ -34,7 +34,7 @@
#include "caf/detail/type_traits.hpp"
#include "caf/detail/left_or_right.hpp"
#include "caf/detail/matcher.hpp"
#include "caf/detail/try_match.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/lifted_fun.hpp"
#include "caf/detail/pseudo_tuple.hpp"
......@@ -227,10 +227,9 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) {
}
auto& f = get<N>(fs);
using ft = typename std::decay<decltype(f)>::type;
detail::matcher<typename ft::pattern, typename ft::filtered_pattern> match;
meta_elements<typename ft::pattern> ms;
typename ft::intermediate_tuple targs;
if (match(msg, &targs)) {
//if (policy::prepare_invoke(targs, type_token, is_dynamic, ptr, tup)) {
if (try_match(msg, ms.arr.data(), ms.arr.size(), targs.data)) {
auto is = detail::get_indices(targs);
auto res = detail::apply_args(f, is, deduce_const(msg, targs));
if (unroll_expr_result_valid(res)) {
......@@ -240,19 +239,21 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) {
return none;
}
template <class PPFPs, class Tuple>
uint64_t calc_bitmask(PPFPs&, minus1l, const std::type_info&, const Tuple&) {
template <class PPFPs>
uint64_t calc_bitmask(PPFPs&, minus1l, const std::type_info&, const message&) {
return 0x00;
}
template <class Case, long N, class Tuple>
template <class Case, long N>
uint64_t calc_bitmask(Case& fs, long_constant<N>,
const std::type_info& tinf, const Tuple& tup) {
const std::type_info& tinf, const message& msg) {
auto& f = get<N>(fs);
using ft = typename std::decay<decltype(f)>::type;
detail::matcher<typename ft::pattern, typename ft::filtered_pattern> match;
uint64_t result = match(tup, nullptr) ? (0x01 << N) : 0x00;
return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, tup);
meta_elements<typename ft::pattern> ms;
uint64_t result = try_match(msg, ms.arr.data(), ms.arr.size(), nullptr)
? (0x01 << N)
: 0x00;
return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, msg);
}
template <bool IsManipulator, typename T0, typename T1>
......
......@@ -181,16 +181,12 @@ class message {
/**
* Returns an iterator to the beginning.
*/
inline const_iterator begin() const {
return m_vals->begin();
}
const_iterator begin() const;
/**
* Returns an iterator to the end.
*/
inline const_iterator end() const {
return m_vals->end();
}
const_iterator end() const;
/**
* Returns a copy-on-write pointer to the internal data.
......@@ -285,6 +281,16 @@ inline bool operator!=(const message& lhs, const message& rhs) {
return !(lhs == rhs);
}
template <class T>
struct lift_message_element {
using type = T;
};
template <class T, T V>
struct lift_message_element<std::integral_constant<T, V>> {
using type = T;
};
/**
* Creates a new `message` containing the elements `args...`.
* @relates message
......@@ -296,10 +302,14 @@ typename std::enable_if<
message
>::type
make_message(T&& arg, Ts&&... args) {
using namespace detail;
using data = tuple_vals<typename strip_and_convert<T>::type,
typename strip_and_convert<Ts>::type...>;
auto ptr = new data(std::forward<T>(arg), std::forward<Ts>(args)...);
using storage
= detail::tuple_vals<typename lift_message_element<
typename detail::strip_and_convert<T>::type
>::type,
typename lift_message_element<
typename detail::strip_and_convert<Ts>::type
>::type...>;
auto ptr = new storage(std::forward<T>(arg), std::forward<Ts>(args)...);
return message{detail::message_data::ptr{ptr}};
}
......
......@@ -69,6 +69,13 @@ class message_handler {
*/
message_handler(impl_ptr ptr);
/**
* Checks whether the message handler is not empty.
*/
inline operator bool() const {
return static_cast<bool>(m_impl);
}
/**
* Create a message handler a list of match expressions,
* functors, or other message handlers.
......@@ -104,7 +111,13 @@ class message_handler {
// using a behavior is safe here, because we "cast"
// it back to a message_handler when appropriate
behavior tmp{std::forward<Ts>(args)...};
return m_impl->or_else(tmp.as_behavior_impl());
if (! tmp) {
return *this;
}
if (m_impl) {
return m_impl->or_else(tmp.as_behavior_impl());
}
return tmp;
}
private:
......
......@@ -36,7 +36,6 @@ template <class Base, class Subtype, class ResponseHandleTag>
class sync_sender_impl : public Base {
public:
using response_handle_type = response_handle<Subtype, message,
ResponseHandleTag>;
......@@ -130,10 +129,12 @@ class sync_sender_impl : public Base {
**************************************************************************/
template <class... Rs, class... Ts>
response_handle<
Subtype, typename detail::deduce_output_type<
detail::type_list<Rs...>, detail::type_list<Ts...>>::type,
ResponseHandleTag>
response_handle<Subtype,
typename detail::deduce_output_type<
detail::type_list<Rs...>,
detail::type_list<Ts...>
>::type,
ResponseHandleTag>
sync_send_tuple(message_priority prio, const typed_actor<Rs...>& dest,
std::tuple<Ts...> what) {
return sync_send_impl(prio, dest, detail::type_list<Ts...>{},
......@@ -157,7 +158,8 @@ class sync_sender_impl : public Base {
typename detail::deduce_output_type<
detail::type_list<Rs...>,
typename detail::implicit_conversions<
typename std::decay<Ts>::type>::type...>::type,
typename std::decay<Ts>::type>::type...
>::type,
ResponseHandleTag>
sync_send(message_priority prio, const typed_actor<Rs...>& dest,
Ts&&... what) {
......
......@@ -27,6 +27,8 @@
#include "caf/unit.hpp"
#include "caf/config.hpp"
#include "caf/detail/safe_equal.hpp"
namespace caf {
/**
......@@ -272,7 +274,7 @@ class optional<T&> {
template <class T, typename U>
bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
if ((lhs) && (rhs)) {
return *lhs == *rhs;
return detail::safe_equal(*lhs, *rhs);
}
return !lhs && !rhs;
}
......
......@@ -40,13 +40,18 @@ class cooperative_scheduling {
using timeout_type = int;
template <class Actor>
inline void launch(Actor* self, execution_unit* host) {
inline void launch(Actor* self, execution_unit* host, bool lazy) {
// detached in scheduler::worker::run
self->attach_to_scheduler();
if (host)
if (lazy) {
self->mailbox().try_block();
return;
}
if (host) {
host->exec_later(self);
else
} else {
detail::singletons::get_scheduling_coordinator()->enqueue(self);
}
}
template <class Actor>
......
......@@ -125,6 +125,7 @@ class event_based_resume {
auto ptr = d->next_message();
if (ptr) {
if (d->invoke_message(ptr)) {
d->bhvr_stack().cleanup();
++handled_msgs;
if (actor_done()) {
CAF_LOG_DEBUG("actor exited");
......@@ -170,9 +171,8 @@ class event_based_resume {
}
}
catch (std::exception& e) {
CAF_LOG_INFO("actor died because of an exception: "
<< detail::demangle(typeid(e))
<< ", what() = " << e.what());
CAF_LOG_INFO("actor died because of an exception, what: " << e.what());
static_cast<void>(e); // keep compiler happy when not logging
if (d->exit_reason() == exit_reason::not_exited) {
d->quit(exit_reason::unhandled_exception);
}
......
......@@ -153,36 +153,22 @@ class invoke_policy {
auto id = res->template get_as<uint64_t>(1);
auto msg_id = message_id::from_integer_value(id);
auto ref_opt = self->sync_handler(msg_id);
// calls self->response_promise() if hdl is a dummy
// argument, forwards hdl otherwise to reply to the
// original request message
auto fhdl = fetch_response_promise(self, hdl);
// install a behavior that calls the user-defined behavior
// and using the result of its inner behavior as response
if (ref_opt) {
behavior cpy = *ref_opt;
*ref_opt =
cpy.add_continuation([=](message & intermediate)
->optional<message> {
if (!intermediate.empty()) {
// do no use lamba expresion type to
// avoid recursive template instantiaton
behavior::continuation_fun f2 = [=](
message & m)->optional<message> {
return std::move(m);
};
auto mutable_mid = mid;
// recursively call invoke_fun on the
// result to correctly handle stuff like
// sync_send(...).then(...).then(...)...
return this->invoke_fun(self, intermediate,
mutable_mid, f2, fhdl);
auto fhdl = fetch_response_promise(self, hdl);
behavior inner = *ref_opt;
*ref_opt = behavior {
others() >> [=] {
// inner is const inside this lambda and mutable a C++14 feature
behavior cpy = inner;
auto inner_res = cpy(self->last_dequeued());
if (inner_res) {
fhdl.deliver(*inner_res);
}
return none;
});
}
};
}
// reset res to prevent "outer" invoke_fun
// from handling the result again
res->reset();
} else {
// respond by using the result of 'fun'
CAF_LOG_DEBUG("respond via response_promise");
......
......@@ -65,7 +65,7 @@ class no_scheduling {
}
template <class Actor>
void launch(Actor* self, execution_unit*) {
void launch(Actor* self, execution_unit*, bool) {
CAF_REQUIRE(self != nullptr);
CAF_PUSH_AID(self->id());
CAF_LOG_TRACE(CAF_ARG(self));
......
......@@ -20,22 +20,88 @@
#ifndef CAF_REPLIES_TO_HPP
#define CAF_REPLIES_TO_HPP
#include <string>
#include "caf/either.hpp"
#include "caf/uniform_typeid.hpp"
#include "caf/type_name_access.hpp"
#include "caf/uniform_type_info.hpp"
#include "caf/illegal_message_element.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/type_pair.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
/** @cond PRIVATE */
std::string replies_to_type_name(size_t input_size,
const std::string* input,
size_t output_opt1_size,
const std::string* output_opt1,
size_t output_opt2_size,
const std::string* output_opt2);
/** @endcond */
template <class InputTypes, class LeftOutputTypes, class RightOutputTypes>
struct typed_mpi;
template <class... Is, class... Ls, class... Rs>
struct typed_mpi<detail::type_list<Is...>,
detail::type_list<Ls...>,
detail::type_list<Rs...>> {
static_assert(sizeof...(Is) > 0, "template parameter pack Is empty");
static_assert(sizeof...(Ls) > 0, "template parameter pack Ls empty");
using input_types = detail::type_list<Is...>;
using output_opt1_types = detail::type_list<Ls...>;
using output_opt2_types = detail::type_list<Rs...>;
static_assert(!std::is_same<output_opt1_types, output_opt2_types>::value,
"result types must differ when using with_either<>::or_else<>");
static_assert(!detail::tl_exists<
input_types,
is_illegal_message_element
>::value
&& !detail::tl_exists<
output_opt1_types,
is_illegal_message_element
>::value
&& !detail::tl_exists<
output_opt2_types,
is_illegal_message_element
>::value,
"interface definition contains an illegal message type, "
"did you use with<either...> instead of with_either<...>?");
static std::string static_type_name() {
std::string input[] = {type_name_access<Is>::get()...};
std::string output_opt1[] = {type_name_access<Ls>::get()...};
// Rs... is allowed to be empty, hence we need to add a dummy element
// to make sure this array is not of size 0 (to prevent compiler errors)
std::string output_opt2[] = {std::string(), type_name_access<Rs>::get()...};
return replies_to_type_name(sizeof...(Is), input,
sizeof...(Ls), output_opt1,
sizeof...(Rs), output_opt2 + 1);
}
};
template <class... Is>
struct replies_to {
template <class... Os>
struct with {
using input_types = detail::type_list<Is...>;
using output_types = detail::type_list<Os...>;
using with = typed_mpi<detail::type_list<Is...>,
detail::type_list<Os...>,
detail::empty_type_list>;
template <class... Ls>
struct with_either {
template <class... Rs>
using or_else = typed_mpi<detail::type_list<Is...>,
detail::type_list<Ls...>,
detail::type_list<Rs...>>;
};
};
template <class... Is>
using reacts_to = typename replies_to<Is...>::template with<void>;
using reacts_to = typed_mpi<detail::type_list<Is...>,
detail::type_list<void>,
detail::empty_type_list>;
} // namespace caf
......
......@@ -51,7 +51,7 @@ struct blocking_response_handle_tag {};
* This helper class identifies an expected response message
* and enables `sync_send(...).then(...)`.
*/
template <class Self, class Result, class Tag>
template <class Self, class ResultOptPairOrMessage, class Tag>
class response_handle;
/******************************************************************************
......@@ -80,8 +80,7 @@ class response_handle<Self, message, nonblocking_response_handle_tag> {
}
};
m_self->bhvr_stack().push_back(std::move(bhvr), m_mid);
auto ptr = m_self;
return {m_mid, [ptr](message_id mid) { return ptr->sync_handler(mid); }};
return {m_mid};
}
private:
......@@ -92,9 +91,8 @@ class response_handle<Self, message, nonblocking_response_handle_tag> {
/******************************************************************************
* nonblocking + typed *
******************************************************************************/
template <class Self, class... Ts>
class response_handle<Self, detail::type_list<Ts...>,
nonblocking_response_handle_tag> {
template <class Self, class TypedOutputPair>
class response_handle<Self, TypedOutputPair, nonblocking_response_handle_tag> {
public:
response_handle() = delete;
response_handle(const response_handle&) = default;
......@@ -104,28 +102,30 @@ class response_handle<Self, detail::type_list<Ts...>,
// nop
}
template <class F,
class Enable = typename std::enable_if<
detail::is_callable<F>::value
&& !is_match_expr<F>::value
>::type>
template <class... Fs>
typed_continue_helper<
typename detail::lifted_result_type<
typename detail::get_callable_trait<F>::result_type
typename detail::common_result_type<
typename detail::get_callable_trait<Fs>::result_type...
>::type
>::type>
then(F fun) {
detail::assert_types<detail::type_list<Ts...>, F>();
then(Fs... fs) {
static_assert(sizeof...(Fs) > 0, "at least one functor is requried");
static_assert(detail::conjunction<detail::is_callable<Fs>::value...>::value,
"all arguments must be callable");
static_assert(detail::conjunction<!is_match_expr<Fs>::value...>::value,
"match expressions are not allowed in this context");
detail::type_checker<TypedOutputPair, Fs...>::check();
auto selfptr = m_self;
behavior tmp{
fun,
fs...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
m_self->bhvr_stack().push_back(std::move(tmp), m_mid);
auto get = [selfptr](message_id mid) { return selfptr->sync_handler(mid); };
return {m_mid, get};
return {m_mid};
}
private:
......@@ -173,12 +173,9 @@ class response_handle<Self, message, blocking_response_handle_tag> {
/******************************************************************************
* blocking + typed *
******************************************************************************/
template <class Self, class... Ts>
class response_handle<Self, detail::type_list<Ts...>,
blocking_response_handle_tag> {
template <class Self, class OutputPair>
class response_handle<Self, OutputPair, blocking_response_handle_tag> {
public:
using result_types = detail::type_list<Ts...>;
response_handle() = delete;
response_handle(const response_handle&) = default;
response_handle& operator=(const response_handle&) = default;
......@@ -187,26 +184,27 @@ class response_handle<Self, detail::type_list<Ts...>,
// nop
}
template <class F>
void await(F fun) {
using arg_types =
typename detail::tl_map<
typename detail::get_callable_trait<F>::arg_types,
std::decay
>::type;
static constexpr size_t fun_args = detail::tl_size<arg_types>::value;
static_assert(fun_args <= detail::tl_size<result_types>::value,
"functor takes too much arguments");
using recv_types =
typename detail::tl_right<
result_types,
fun_args
>::type;
static_assert(std::is_same<arg_types, recv_types>::value,
"wrong functor signature");
static constexpr bool is_either_or_handle =
!std::is_same<
none_t,
typename OutputPair::second
>::value;
template <class... Fs>
void await(Fs... fs) {
static_assert(sizeof...(Fs) > 0,
"at least one argument is required");
static_assert((is_either_or_handle && sizeof...(Fs) == 2)
|| sizeof...(Fs) == 1,
"wrong number of functors");
static_assert(detail::conjunction<detail::is_callable<Fs>::value...>::value,
"all arguments must be callable");
static_assert(detail::conjunction<!is_match_expr<Fs>::value...>::value,
"match expressions are not allowed in this context");
detail::type_checker<OutputPair, Fs...>::check();
auto selfptr = m_self;
behavior tmp{
fun,
fs...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
......
......@@ -43,10 +43,10 @@ class worker : public execution_unit {
using coordinator_ptr = coordinator<Policy>*;
using policy_data = typename Policy::worker_data;
worker(size_t id, coordinator_ptr parent, size_t max_throughput)
: m_max_throughput(max_throughput),
m_id(id),
m_parent(parent) {
worker(size_t worker_id, coordinator_ptr worker_parent, size_t throughput)
: m_max_throughput(throughput),
m_id(worker_id),
m_parent(worker_parent) {
// nop
}
......
......@@ -26,8 +26,6 @@
#include "caf/uniform_type_info.hpp"
#include "caf/primitive_variant.hpp"
#include "caf/detail/to_uniform_name.hpp"
namespace caf {
class actor_namespace;
......@@ -111,8 +109,7 @@ template <class T>
serializer& operator<<(serializer& s, const T& what) {
auto mtype = uniform_typeid<T>();
if (mtype == nullptr) {
throw std::logic_error("no uniform type info found for "
+ detail::to_uniform_name(typeid(T)));
throw std::logic_error("no uniform type info found for T");
}
mtype->serialize(&what, &s);
return s;
......
......@@ -69,7 +69,7 @@ intrusive_ptr<C> spawn_impl(execution_unit* host,
"spawned without blocking_api_flag");
static_assert(is_unbound(Os),
"top-level spawns cannot have monitor or link flag");
CAF_LOGF_TRACE("spawn " << detail::demangle<C>());
CAF_LOGF_TRACE("");
using scheduling_policy =
typename std::conditional<
has_detach_flag(Os) || has_blocking_api_flag(Os),
......@@ -114,7 +114,7 @@ intrusive_ptr<C> spawn_impl(execution_unit* host,
CAF_LOGF_DEBUG("spawned actor with ID " << ptr->id());
CAF_PUSH_AID(ptr->id());
before_launch_fun(ptr.get());
ptr->launch(has_hide_flag(Os), host);
ptr->launch(has_hide_flag(Os), has_lazy_init_flag(Os), host);
return ptr;
}
......
......@@ -40,8 +40,8 @@ enum class spawn_options : int {
detach_flag = 0x04,
hide_flag = 0x08,
blocking_api_flag = 0x10,
priority_aware_flag = 0x20
priority_aware_flag = 0x20,
lazy_init_flag = 0x40
};
#endif
......@@ -95,6 +95,12 @@ constexpr spawn_options blocking_api = spawn_options::blocking_api_flag;
*/
constexpr spawn_options priority_aware = spawn_options::priority_aware_flag;
/**
* Causes the new actor to delay its
* initialization until a message arrives.
*/
constexpr spawn_options lazy_init = spawn_options::lazy_init_flag;
/**
* Checks wheter `haystack` contains `needle`.
* @relates spawn_options
......@@ -151,6 +157,14 @@ constexpr bool has_blocking_api_flag(spawn_options opts) {
return has_spawn_option(opts, blocking_api);
}
/**
* Checks wheter the {@link lazy_init} flag is set in `opts`.
* @relates spawn_options
*/
constexpr bool has_lazy_init_flag(spawn_options opts) {
return has_spawn_option(opts, lazy_init);
}
/** @} */
/** @cond PRIVATE */
......
......@@ -72,21 +72,22 @@ void splice(std::string& str, const std::string& glue, T&& arg, Ts&&... args) {
splice(str, glue, std::forward<Ts>(args)...);
}
template <size_t WhatSize, size_t WithSize>
template <ptrdiff_t WhatSize, ptrdiff_t WithSize>
void replace_all(std::string& str,
const char (&what)[WhatSize],
const char (&with)[WithSize]) {
// end(what) - 1 points to the null-terminator
auto next = [&](std::string::iterator pos) -> std::string::iterator{
auto next = [&](std::string::iterator pos) -> std::string::iterator {
return std::search(pos, str.end(), std::begin(what), std::end(what) - 1);
};
auto i = next(std::begin(str));
while (i != std::end(str)) {
auto before = static_cast<size_t>(std::distance(std::begin(str), i));
auto before = std::distance(std::begin(str), i);
CAF_REQUIRE(before >= 0);
str.replace(i, i + WhatSize - 1, with);
// i became invalidated -> use new iterator pointing
// to the first character after the replaced text
i = next(std::begin(str) + before + (WithSize - 1));
i = next(str.begin() + before + (WithSize - 1));
}
}
......
......@@ -44,11 +44,6 @@ std::string to_string(const actor_addr& what);
std::string to_string(const actor& what);
/**
* @relates node_id
*/
//std::string to_string(const node_id::host_id_type& node_id);
/**
* @relates node_id
*/
......@@ -60,7 +55,7 @@ std::string to_string(const node_id& what);
std::string to_string(const atom_value& what);
/**
* Converts `e` to a string including the demangled type of `e` and `e.what()`.
* Converts `e` to a string including `e.what()`.
*/
std::string to_verbose_string(const std::exception& e);
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_TYPE_NAME_ACCESS_HPP
#define CAF_TYPE_NAME_ACCESS_HPP
#include <string>
#include "caf/uniform_typeid.hpp"
#include "caf/uniform_type_info.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
template <class T, bool HasTypeName = detail::has_static_type_name<T>::value>
struct type_name_access {
static std::string get() {
auto uti = uniform_typeid<T>(true);
return uti ? uti->name() : "void";
}
};
template <class T>
struct type_name_access<T, true> {
static std::string get() {
return T::static_type_name();
}
};
} // namespace caf
#endif // CAF_TYPE_NAME_ACCESS_HPP
......@@ -131,7 +131,7 @@ class typed_actor
}
static std::set<std::string> message_types() {
return {detail::to_uniform_name<Rs>()...};
return {Rs::static_type_name()...};
}
explicit operator bool() const { return static_cast<bool>(m_ptr); }
......
......@@ -20,11 +20,13 @@
#ifndef TYPED_BEHAVIOR_HPP
#define TYPED_BEHAVIOR_HPP
#include "caf/either.hpp"
#include "caf/behavior.hpp"
#include "caf/match_expr.hpp"
#include "caf/system_messages.hpp"
#include "caf/typed_continue_helper.hpp"
#include "caf/detail/ctm.hpp"
#include "caf/detail/typed_actor_util.hpp"
namespace caf {
......@@ -46,17 +48,27 @@ struct input_only<detail::type_list<Ts...>> {
using skip_list = detail::type_list<skip_message_t>;
template <class Opt1, class Opt2>
struct collapse_replies_to_statement {
using type = typename either<Opt1>::template or_else<Opt2>;
};
template <class List>
struct collapse_replies_to_statement<List, none_t> {
using type = List;
};
/*
template <class List>
struct unbox_typed_continue_helper {
// do nothing if List is actually a list, i.e., not a typed_continue_helper
using type = List;
};
template <class List>
struct unbox_typed_continue_helper<
detail::type_list<typed_continue_helper<List>>> {
struct unbox_typed_continue_helper<typed_continue_helper<List>> {
using type = List;
};
*/
template <class Input, class RepliesToWith>
struct same_input : std::is_same<Input, typename RepliesToWith::input_types> {};
......@@ -75,8 +87,9 @@ struct valid_input_predicate {
struct inner {
using input_types = typename Expr::input_types;
using output_types =
typename unbox_typed_continue_helper<
typename Expr::output_types
typename collapse_replies_to_statement<
typename Expr::output_opt1_types,
typename Expr::output_opt2_types
>::type;
// get matching elements for input type
using filtered_slist =
......@@ -215,10 +228,12 @@ class typed_behavior {
template <class... Cs>
void set(match_expr<Cs...>&& expr) {
using input =
detail::type_list<typename detail::deduce_signature<Cs>::type...>;
// check types
detail::static_check_typed_behavior_input<signatures, input>();
using mpi =
typename detail::tl_filter_not<
detail::type_list<typename detail::deduce_mpi<Cs>::type...>,
detail::is_hidden_msg_handler
>::type;
detail::static_asserter<signatures, mpi, detail::ctm>::verify_match();
// final (type-erasure) step
m_bhvr = std::move(expr);
}
......
......@@ -32,10 +32,8 @@ template <class OutputList>
class typed_continue_helper {
public:
using message_id_wrapper_tag = int;
using getter = std::function<optional<behavior&> (message_id msg_id)>;
typed_continue_helper(message_id mid, getter get_sync_handler)
: m_ch(mid, std::move(get_sync_handler)) {
typed_continue_helper(message_id mid) : m_ch(mid) {
// nop
}
......@@ -43,14 +41,6 @@ class typed_continue_helper {
// nop
}
template <class F>
typed_continue_helper<typename detail::get_callable_trait<F>::result_type>
continue_with(F fun) {
detail::assert_types<OutputList, F>();
m_ch.continue_with(std::move(fun));
return {m_ch};
}
message_id get_message_id() const {
return m_ch.get_message_id();
}
......
......@@ -49,7 +49,7 @@ class typed_event_based_actor : public
using behavior_type = typed_behavior<Rs...>;
std::set<std::string> message_types() const override {
return {detail::to_uniform_name<Rs>()...};
return {Rs::static_type_name()...};
}
protected:
......
......@@ -29,20 +29,16 @@
#include <type_traits>
#include "caf/config.hpp"
#include "caf/uniform_typeid.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/demangle.hpp"
#include "caf/detail/to_uniform_name.hpp"
namespace caf {
class serializer;
class deserializer;
class uniform_type_info;
const uniform_type_info* uniform_typeid(const std::type_info&);
struct uniform_value_t;
using uniform_value = std::unique_ptr<uniform_value_t>;
......@@ -203,8 +199,8 @@ class uniform_type_info {
/**
* Creates a copy of `other`.
*/
virtual uniform_value
create(const uniform_value& other = uniform_value{}) const = 0;
virtual uniform_value create(const uniform_value& other
= uniform_value{}) const = 0;
/**
* Deserializes an object of this type from `source`.
......@@ -279,18 +275,14 @@ using uniform_type_info_ptr = std::unique_ptr<uniform_type_info>;
/**
* @relates uniform_type_info
*/
template <class T>
inline const uniform_type_info* uniform_typeid() {
return uniform_typeid(typeid(T));
}
/**
* @relates uniform_type_info
*/
inline bool operator==(const uniform_type_info& lhs,
const uniform_type_info& rhs) {
// uniform_type_info instances are singletons,
// thus, equal == identical
const uniform_type_info& rhs) {
// uniform_type_info instances are singletons
return &lhs == &rhs;
}
......@@ -306,7 +298,7 @@ inline bool operator!=(const uniform_type_info& lhs,
* @relates uniform_type_info
*/
inline bool operator==(const uniform_type_info& lhs,
const std::type_info& rhs) {
const std::type_info& rhs) {
return lhs.equal_to(rhs);
}
......@@ -314,7 +306,7 @@ inline bool operator==(const uniform_type_info& lhs,
* @relates uniform_type_info
*/
inline bool operator!=(const uniform_type_info& lhs,
const std::type_info& rhs) {
const std::type_info& rhs) {
return !(lhs.equal_to(rhs));
}
......@@ -322,7 +314,7 @@ inline bool operator!=(const uniform_type_info& lhs,
* @relates uniform_type_info
*/
inline bool operator==(const std::type_info& lhs,
const uniform_type_info& rhs) {
const uniform_type_info& rhs) {
return rhs.equal_to(lhs);
}
......@@ -330,7 +322,7 @@ inline bool operator==(const std::type_info& lhs,
* @relates uniform_type_info
*/
inline bool operator!=(const std::type_info& lhs,
const uniform_type_info& rhs) {
const uniform_type_info& rhs) {
return !(rhs.equal_to(lhs));
}
......
......@@ -17,24 +17,23 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_TO_UNIFORM_NAME_HPP
#define CAF_DETAIL_TO_UNIFORM_NAME_HPP
#ifndef CAF_UNIFORM_TYPEID_HPP
#define CAF_UNIFORM_TYPEID_HPP
#include <string>
#include <typeinfo>
namespace caf {
namespace detail {
std::string to_uniform_name(const std::string& demangled_name);
std::string to_uniform_name(const std::type_info& tinfo);
class uniform_type_info;
const uniform_type_info* uniform_typeid(const std::type_info& tinf,
bool allow_nullptr = false);
template <class T>
inline std::string to_uniform_name() {
return to_uniform_name(typeid(T));
const uniform_type_info* uniform_typeid(bool allow_nullptr = false) {
return uniform_typeid(typeid(T), allow_nullptr);
}
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_TO_UNIFORM_NAME_HPP
#endif // CAF_UNIFORM_TYPEID_HPP
......@@ -23,11 +23,17 @@
namespace caf {
struct unit_t {
constexpr unit_t() {}
constexpr unit_t(const unit_t&) {}
constexpr unit_t() {
// nop
}
constexpr unit_t(const unit_t&) {
// nop
}
template <class T>
explicit constexpr unit_t(T&&) {}
explicit constexpr unit_t(T&&) {
// nop
}
unit_t& operator=(const unit_t&) = default;
};
static constexpr unit_t unit = unit_t{};
......@@ -35,25 +41,21 @@ static constexpr unit_t unit = unit_t{};
template <class T>
struct lift_void {
using type = T;
};
template <>
struct lift_void<void> {
using type = unit_t;
};
template <class T>
struct unlift_void {
using type = T;
};
template <>
struct unlift_void<unit_t> {
using type = void;
};
} // namespace caf
......
......@@ -94,7 +94,7 @@ class cow_tuple<Head, Tail...> {
return cow_tuple<Tail...>::offset_subtuple(m_vals, 1);
}
inline operator message() {
inline operator message() const {
return message{m_vals};
}
......
......@@ -32,8 +32,6 @@
#include "caf/on.hpp"
#include "caf/optional.hpp"
#include "caf/detail/demangle.hpp"
#include "cppa/opt_impls.hpp"
namespace caf {
......@@ -43,7 +41,8 @@ using string_proj = std::function<optional<std::string> (const std::string&)>;
inline string_proj extract_longopt_arg(const std::string& prefix) {
return [prefix](const std::string& arg) -> optional<std::string> {
if (arg.compare(0, prefix.size(), prefix) == 0) {
return std::string(arg.begin() + prefix.size(), arg.end());
return std::string(arg.begin() + static_cast<ptrdiff_t>(prefix.size()),
arg.end());
}
return none;
};
......
......@@ -93,7 +93,7 @@ class rd_arg_functor {
auto opt = conv_arg_impl<T>::_(arg);
if (!opt) {
std::cerr << "*** error: cannot convert \"" << arg << "\" to "
<< detail::demangle(typeid(T).name())
<< typeid(T).name()
<< " [option: \"" << m_storage->arg_name << "\"]"
<< std::endl;
return false;
......@@ -126,7 +126,7 @@ class add_arg_functor {
auto opt = conv_arg_impl<T>::_(arg);
if (!opt) {
std::cerr << "*** error: cannot convert \"" << arg << "\" to "
<< detail::demangle(typeid(T))
<< typeid(T).name()
<< " [option: \"" << m_storage->arg_name << "\"]"
<< std::endl;
return false;
......
......@@ -32,6 +32,8 @@
#include "caf/detail/types_array.hpp"
#include "caf/detail/decorated_tuple.hpp"
#include "cppa/cow_tuple.hpp"
namespace caf {
template <class TupleIter, class PatternIter,
......@@ -133,8 +135,11 @@ auto moving_tuple_cast(message& tup)
}
}
// same for nil, leading, and trailing
if (std::equal(sub.begin(), sub.end(),
arr_pos, detail::types_only_eq)) {
auto eq = [](const detail::message_iterator& lhs,
const uniform_type_info* rhs) {
return lhs.type() == rhs;
};
if (std::equal(sub.begin(), sub.end(), arr_pos, eq)) {
return result_type::from(sub);
}
return none;
......
......@@ -121,8 +121,9 @@ bool abstract_actor::link_impl(linking_operation op, const actor_addr& other) {
return remove_link_impl(other);
case remove_backlink_op:
return remove_backlink_impl(other);
default:
return false;
}
return false;
}
bool abstract_actor::establish_link_impl(const actor_addr& other) {
......@@ -217,7 +218,7 @@ void abstract_actor::cleanup(uint32_t reason) {
CAF_LOG_INFO_IF(!is_remote(), "cleanup actor with ID "
<< m_id << "; exit reason = "
<< reason << ", class = "
<< detail::demangle(typeid(*this)));
<< class_name());
// send exit messages
for (attachable* i = head.get(); i != nullptr; i = i->next.get()) {
i->actor_exited(this, reason);
......
......@@ -44,7 +44,7 @@ bool abstract_group::subscription::matches(const token& what) {
return ot.group == m_group;
}
abstract_group::module::module(std::string name) : m_name(std::move(name)) {
abstract_group::module::module(std::string mname) : m_name(std::move(mname)) {
// nop
}
......
......@@ -91,7 +91,7 @@ uint32_t actor_registry::next_id() {
}
void actor_registry::inc_running() {
# if CAF_LOG_LEVEL >= CAF_DEBUG
# if defined(CAF_LOG_LEVEL) && CAF_LOG_LEVEL >= CAF_DEBUG
CAF_LOG_DEBUG("new value = " << ++m_running);
# else
++m_running;
......
......@@ -24,57 +24,8 @@
namespace caf {
namespace {
class continuation_decorator : public detail::behavior_impl {
public:
using super = behavior_impl;
using continuation_fun = behavior::continuation_fun;
using pointer = typename behavior_impl::pointer;
continuation_decorator(continuation_fun fun, pointer ptr)
: super(ptr->timeout()), m_fun(fun), m_decorated(std::move(ptr)) {
CAF_REQUIRE(m_decorated != nullptr);
}
template <class T>
inline bhvr_invoke_result invoke_impl(T& tup) {
auto res = m_decorated->invoke(tup);
if (res) {
return m_fun(*res);
}
return none;
}
bhvr_invoke_result invoke(message& tup) {
return invoke_impl(tup);
}
bhvr_invoke_result invoke(const message& tup) {
return invoke_impl(tup);
}
pointer copy(const generic_timeout_definition& tdef) const {
return new continuation_decorator(m_fun, m_decorated->copy(tdef));
}
void handle_timeout() {
m_decorated->handle_timeout();
}
private:
continuation_fun m_fun;
pointer m_decorated;
};
} // namespace <anonymous>
behavior::behavior(const message_handler& mh) : m_impl(mh.as_behavior_impl()) {
// nop
}
behavior behavior::add_continuation(continuation_fun fun) {
return behavior::impl_ptr{new continuation_decorator(std::move(fun), m_impl)};
}
} // namespace caf
......@@ -30,6 +30,10 @@ blocking_actor::blocking_actor() {
is_blocking(true);
}
blocking_actor::~blocking_actor() {
// avoid weak-vtables warning
}
void blocking_actor::await_all_other_actors_done() {
detail::singletons::get_actor_registry()->await_running_count_equal(1);
}
......
......@@ -22,18 +22,8 @@
namespace caf {
continue_helper::continue_helper(message_id mid, getter g)
: m_mid(mid), m_getter(std::move(g)) {}
continue_helper& continue_helper::continue_with(behavior::continuation_fun f) {
auto ref_opt = m_getter(m_mid); //m_self->sync_handler(m_mid);
if (ref_opt) {
behavior cpy = *ref_opt;
*ref_opt = cpy.add_continuation(std::move(f));
} else {
CAF_LOG_ERROR("failed to add continuation");
}
return *this;
continue_helper::continue_helper(message_id mid) : m_mid(mid) {
// nop
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#include "caf/either.hpp"
#include "caf/to_string.hpp"
#include "caf/string_algorithms.hpp"
namespace caf {
std::string either_or_else_type_name(size_t lefts_size,
const std::string* lefts,
size_t rights_size,
const std::string* rights) {
std::string glue = ",";
std::string result;
result = "caf::either<";
result += join(lefts, lefts + lefts_size, glue);
result += ">::or_else<";
result += join(rights, rights + rights_size, glue);
result += ">";
return result;
}
} // namespace caf
......@@ -64,8 +64,8 @@ actor_exited::~actor_exited() noexcept {
// nop
}
actor_exited::actor_exited(uint32_t reason) : caf_exception(ae_what(reason)) {
m_reason = reason;
actor_exited::actor_exited(uint32_t rsn) : caf_exception(ae_what(rsn)) {
m_reason = rsn;
}
network_error::network_error(const std::string& str) : super(str) {
......
/******************************************************************************
* _________________________ *
* __ ____/__ |__ ____/ C++ *
* _ / __ /| |_ /_ Actor *
* / /___ _ ___ | __/ Framework *
* ____/ /_/ |_/_/ *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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 LICENCE_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. *
******************************************************************************/
/******************************************************************************\
* Based on work by the mingw-w64 project; *
* original header: *
* *
* Copyright (c) 2012 mingw-w64 project *
* *
* Contributing author: Kai Tietz *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
* DEALINGS IN THE SOFTWARE. *
\ ******************************************************************************/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <sstream>
#include <iostream>
#include "cppa/detail/execinfo_windows.hpp"
namespace cppa {
namespace detail {
int backtrace(void** buffer, int size) {
if (size <= 0) return 0;
auto frames = CaptureStackBackTrace (0, (DWORD) size, buffer, NULL);
return static_cast<int>(frames);
}
void backtrace_symbols_fd(void* const* buffer, int size, int fd) {
std::ostringstream out;
for (int i = 0; i < size; i++) {
out << "[" << std::hex << reinterpret_cast<size_t>(buffer[i])
<< "]" << std::endl;
auto s = out.str();
write(fd, s.c_str(), s.size());
}
_commit(fd);
}
} // namespace detail
} // namespace cppa
......@@ -17,63 +17,63 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/forwarding_actor_proxy.hpp"
#include "caf/send.hpp"
#include "caf/locks.hpp"
#include "caf/to_string.hpp"
#include "caf/io/middleman.hpp"
#include "caf/io/remote_actor_proxy.hpp"
#include "caf/detail/memory.hpp"
#include "caf/detail/logging.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/sync_request_bouncer.hpp"
using namespace std;
namespace caf {
namespace io {
inline sync_request_info* new_req_info(actor_addr sptr, message_id id) {
return detail::memory::create<sync_request_info>(std::move(sptr), id);
}
sync_request_info::~sync_request_info() {
// nop
forwarding_actor_proxy::forwarding_actor_proxy(actor_id aid, node_id nid,
actor mgr)
: actor_proxy(aid, nid),
m_manager(mgr) {
CAF_REQUIRE(mgr != invalid_actor);
CAF_LOG_INFO(CAF_ARG(aid) << ", " << CAF_TARG(nid, to_string));
}
sync_request_info::sync_request_info(actor_addr sptr, message_id id)
: next(nullptr), sender(std::move(sptr)), mid(id) {
// nop
forwarding_actor_proxy::~forwarding_actor_proxy() {
anon_send(m_manager, make_message(atom("_DelProxy"), node(), id()));
}
remote_actor_proxy::remote_actor_proxy(actor_id aid, node_id nid, actor parent)
: super(aid, nid), m_parent(parent) {
CAF_REQUIRE(parent != invalid_actor);
CAF_LOG_INFO(CAF_ARG(aid) << ", " << CAF_TARG(nid, to_string));
actor forwarding_actor_proxy::manager() const {
actor result;
{
shared_lock<detail::shared_spinlock> m_guard(m_manager_mtx);
result = m_manager;
}
return result;
}
remote_actor_proxy::~remote_actor_proxy() {
anon_send(m_parent, make_message(atom("_DelProxy"), node(), id()));
void forwarding_actor_proxy::manager(actor new_manager) {
std::unique_lock<detail::shared_spinlock> m_guard(m_manager_mtx);
m_manager.swap(new_manager);
}
void remote_actor_proxy::forward_msg(const actor_addr& sender, message_id mid,
message msg) {
void forwarding_actor_proxy::forward_msg(const actor_addr& sender,
message_id mid, message msg) {
CAF_LOG_TRACE(CAF_ARG(id()) << ", " << CAF_TSARG(sender) << ", "
<< CAF_MARG(mid, integer_value) << ", "
<< CAF_TSARG(msg));
m_parent->enqueue(
invalid_actor_addr, invalid_message_id,
make_message(atom("_Dispatch"), sender, address(), mid, std::move(msg)),
nullptr);
shared_lock<detail::shared_spinlock> m_guard(m_manager_mtx);
m_manager->enqueue(invalid_actor_addr, invalid_message_id,
make_message(atom("_Dispatch"), sender,
address(), mid, std::move(msg)),
nullptr);
}
void remote_actor_proxy::enqueue(const actor_addr& sender, message_id mid,
message m, execution_unit*) {
void forwarding_actor_proxy::enqueue(const actor_addr& sender, message_id mid,
message m, execution_unit*) {
forward_msg(sender, mid, std::move(m));
}
bool remote_actor_proxy::link_impl(linking_operation op,
const actor_addr& other) {
bool forwarding_actor_proxy::link_impl(linking_operation op,
const actor_addr& other) {
switch (op) {
case establish_link_op:
if (establish_link_impl(other)) {
......@@ -83,7 +83,7 @@ bool remote_actor_proxy::link_impl(linking_operation op,
make_message(atom("_Link"), other));
return true;
}
return false;
break;
case remove_link_op:
if (remove_link_impl(other)) {
// causes remote actor to unlink from (proxy of) other
......@@ -91,7 +91,7 @@ bool remote_actor_proxy::link_impl(linking_operation op,
make_message(atom("_Unlink"), other));
return true;
}
return false;
break;
case establish_backlink_op:
if (establish_backlink_impl(other)) {
// causes remote actor to unlink from (proxy of) other
......@@ -99,7 +99,7 @@ bool remote_actor_proxy::link_impl(linking_operation op,
make_message(atom("_Link"), other));
return true;
}
return false;
break;
case remove_backlink_op:
if (remove_backlink_impl(other)) {
// causes remote actor to unlink from (proxy of) other
......@@ -107,22 +107,21 @@ bool remote_actor_proxy::link_impl(linking_operation op,
make_message(atom("_Unlink"), other));
return true;
}
return false;
break;
}
return false;
}
void remote_actor_proxy::local_link_to(const actor_addr& other) {
void forwarding_actor_proxy::local_link_to(const actor_addr& other) {
establish_link_impl(other);
}
void remote_actor_proxy::local_unlink_from(const actor_addr& other) {
void forwarding_actor_proxy::local_unlink_from(const actor_addr& other) {
remove_link_impl(other);
}
void remote_actor_proxy::kill_proxy(uint32_t reason) {
void forwarding_actor_proxy::kill_proxy(uint32_t reason) {
cleanup(reason);
}
} // namespace io
} // namespace caf
......@@ -134,10 +134,10 @@ std::vector<iface_info> get_mac_addresses() {
oss << hex;
oss.width(2);
oss << ctoi(item->ifr_hwaddr.sa_data[0]);
for (size_t i = 1; i < 6; ++i) {
for (size_t j = 1; j < 6; ++j) {
oss << ":";
oss.width(2);
oss << ctoi(item->ifr_hwaddr.sa_data[i]);
oss << ctoi(item->ifr_hwaddr.sa_data[j]);
}
auto addr = oss.str();
if (addr != "00:00:00:00:00:00") {
......
......@@ -30,7 +30,7 @@ group::group(const invalid_group_t&) : m_ptr(nullptr) {
// nop
}
group::group(abstract_group_ptr ptr) : m_ptr(std::move(ptr)) {
group::group(abstract_group_ptr gptr) : m_ptr(std::move(gptr)) {
// nop
}
......
......@@ -32,7 +32,7 @@ namespace caf {
// later on in spawn(); this prevents subtle bugs that lead to segfaults,
// e.g., when calling address() in the ctor of a derived class
local_actor::local_actor()
: super(1),
: super(size_t{1}),
m_dummy_node(),
m_current_node(&m_dummy_node),
m_planned_exit_reason(exit_reason::not_exited) {
......@@ -103,13 +103,13 @@ void local_actor::reply_message(message&& what) {
if (!whom) {
return;
}
auto& id = m_current_node->mid;
if (id.valid() == false || id.is_response()) {
auto& mid = m_current_node->mid;
if (mid.valid() == false || mid.is_response()) {
send_tuple(actor_cast<channel>(whom), std::move(what));
} else if (!id.is_answered()) {
} else if (!mid.is_answered()) {
auto ptr = actor_cast<actor>(whom);
ptr->enqueue(address(), id.response_id(), std::move(what), host());
id.mark_as_answered();
ptr->enqueue(address(), mid.response_id(), std::move(what), host());
mid.mark_as_answered();
}
}
......@@ -117,10 +117,10 @@ void local_actor::forward_message(const actor& dest, message_priority prio) {
if (!dest) {
return;
}
auto id = (prio == message_priority::high)
? m_current_node->mid.with_high_priority()
: m_current_node->mid.with_normal_priority();
dest->enqueue(m_current_node->sender, id, m_current_node->msg, host());
auto mid = (prio == message_priority::high)
? m_current_node->mid.with_high_priority()
: m_current_node->mid.with_normal_priority();
dest->enqueue(m_current_node->sender, mid, m_current_node->msg, host());
// treat this message as asynchronous message from now on
m_current_node->mid = invalid_message_id;
}
......@@ -130,11 +130,11 @@ void local_actor::send_tuple(message_priority prio, const channel& dest,
if (!dest) {
return;
}
message_id id;
message_id mid;
if (prio == message_priority::high) {
id = id.with_high_priority();
mid = mid.with_high_priority();
}
dest->enqueue(address(), id, std::move(what), host());
dest->enqueue(address(), mid, std::move(what), host());
}
void local_actor::send_exit(const actor_addr& whom, uint32_t reason) {
......@@ -166,8 +166,8 @@ void local_actor::cleanup(uint32_t reason) {
}
void local_actor::quit(uint32_t reason) {
CAF_LOG_TRACE("reason = " << reason << ", class "
<< detail::demangle(typeid(*this)));
CAF_LOG_TRACE("reason = " << reason << ", class = "
<< class_name());
planned_exit_reason(reason);
if (is_blocking()) {
throw actor_exited(reason);
......
......@@ -33,7 +33,7 @@ message::message(message&& other) : m_vals(std::move(other.m_vals)) {
// nop
}
message::message(const data_ptr& vals) : m_vals(vals) {
message::message(const data_ptr& ptr) : m_vals(ptr) {
// nop
}
......@@ -103,4 +103,15 @@ bool message::dynamically_typed() const {
return m_vals ? m_vals->dynamically_typed() : false;
}
message::const_iterator message::begin() const {
return m_vals ? m_vals->begin() : const_iterator{nullptr, 0};
}
/**
* Returns an iterator to the end.
*/
message::const_iterator message::end() const {
return m_vals ? m_vals->end() : const_iterator{nullptr, 0};
}
} // namespace caf
......@@ -46,6 +46,8 @@ class message_builder::dynamic_msg_data : public detail::message_data {
// nop
}
~dynamic_msg_data();
const void* at(size_t pos) const override {
CAF_REQUIRE(pos < size());
return m_elements[pos]->val;
......@@ -76,6 +78,10 @@ class message_builder::dynamic_msg_data : public detail::message_data {
std::vector<uniform_value> m_elements;
};
message_builder::dynamic_msg_data::~dynamic_msg_data() {
// avoid weak-vtables warning
}
message_builder::message_builder() {
init();
}
......
......@@ -27,9 +27,13 @@ message_data::message_data(bool is_dynamic) : m_is_dynamic(is_dynamic) {
}
bool message_data::equals(const message_data& other) const {
auto full_eq = [](const message_iterator& lhs, const message_iterator& rhs) {
return lhs.type() == rhs.type()
&& lhs.type()->equals(lhs.value(), rhs.value());
};
return this == &other
|| (size() == other.size()
&& std::equal(begin(), end(), other.begin(), detail::full_eq));
&& std::equal(begin(), end(), other.begin(), full_eq));
}
message_data::message_data(const message_data& other)
......
......@@ -17,24 +17,26 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_DEMANGLE_HPP
#define CAF_DETAIL_DEMANGLE_HPP
#include "caf/detail/message_iterator.hpp"
#include <string>
#include <typeinfo>
#include "caf/detail/message_data.hpp"
namespace caf {
namespace detail {
std::string demangle(const char* typeid_name);
std::string demangle(const std::type_info& tinf);
message_iterator::message_iterator(const_pointer dataptr, size_t pos)
: m_pos(pos),
m_data(dataptr) {
// nop
}
const void* message_iterator::value() const {
return m_data->at(m_pos);
}
template <class T>
inline std::string demangle() {
return demangle(typeid(T));
const uniform_type_info* message_iterator::type() const {
return m_data->type_at(m_pos);
}
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_DEMANGLE_HPP
......@@ -68,8 +68,9 @@ void host_id_from_string(const std::string& hash,
for (size_t i = 0; i < node_id.size(); ++i) {
// read two characters, each representing 4 bytes
auto& val = node_id[i];
val = static_cast<uint8_t>(hex_char_value(*j++) << 4);
val |= hex_char_value(*j++);
val = static_cast<uint8_t>(hex_char_value(j[0]) << 4)
| hex_char_value(j[1]);
j += 2;
}
}
......@@ -84,8 +85,9 @@ bool equal(const std::string& hash, const node_id::host_id_type& node_id) {
for (size_t i = 0; i < node_id.size(); ++i) {
// read two characters, each representing 4 bytes
uint8_t val;
val = static_cast<uint8_t>(hex_char_value(*j++) << 4);
val |= hex_char_value(*j++);
val = static_cast<uint8_t>(hex_char_value(j[0]) << 4)
| hex_char_value(j[1]);
j += 2;
if (val != node_id[i]) {
return false;
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#include "caf/replies_to.hpp"
#include "caf/to_string.hpp"
#include "caf/string_algorithms.hpp"
namespace caf {
std::string replies_to_type_name(size_t input_size,
const std::string* input,
size_t output_opt1_size,
const std::string* output_opt1,
size_t output_opt2_size,
const std::string* output_opt2) {
std::string glue = ",";
std::string result;
// 'void' is not an announced type, hence we check whether uniform_typeid
// did return a valid pointer to identify 'void' (this has the
// possibility of false positives, but those will be catched anyways)
result = "caf::replies_to<";
result += join(input, input + input_size, glue);
if (output_opt2_size == 0) {
result += ">::with<";
result += join(output_opt1, output_opt1 + output_opt1_size, glue);
} else {
result += ">::with_either<";
result += join(output_opt1, output_opt1 + output_opt1_size, glue);
result += ">::or_else<";
result += join(output_opt2, output_opt2 + output_opt2_size, glue);
}
result += ">";
return result;
}
} // namespace caf
......@@ -65,12 +65,16 @@ namespace {
using byte = unsigned char;
using dword = uint32_t;
static_assert(sizeof(dword) == sizeof(unsigned), "platform not supported");
// macro definitions
// collect four bytes into one word:
#define BYTES_TO_DWORD(strptr) \
(((dword) * ((strptr) + 3) << 24) | ((dword) * ((strptr) + 2) << 16) \
| ((dword) * ((strptr) + 1) << 8) | ((dword) * (strptr)))
((static_cast<dword>(*((strptr) + 3)) << 24) \
| (static_cast<dword>(*((strptr) + 2)) << 16) \
| (static_cast<dword>(*((strptr) + 1)) << 8) \
| (static_cast<dword>(*(strptr))))
// ROL(x, n) cyclically rotates x over n bits to the left
// x must be of an unsigned 32 bits type and 0 <= n < 32.
......@@ -93,28 +97,28 @@ using dword = uint32_t;
#define GG(a, b, c, d, e, x, s) \
{ \
(a) += G((b), (c), (d)) + (x) + 0x5a827999UL; \
(a) += G((b), (c), (d)) + (x) + 0x5a827999U; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
#define HH(a, b, c, d, e, x, s) \
{ \
(a) += H((b), (c), (d)) + (x) + 0x6ed9eba1UL; \
(a) += H((b), (c), (d)) + (x) + 0x6ed9eba1U; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
#define II(a, b, c, d, e, x, s) \
{ \
(a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcUL; \
(a) += I((b), (c), (d)) + (x) + 0x8f1bbcdcU; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
#define JJ(a, b, c, d, e, x, s) \
{ \
(a) += J((b), (c), (d)) + (x) + 0xa953fd4eUL; \
(a) += J((b), (c), (d)) + (x) + 0xa953fd4eU; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
......@@ -128,28 +132,28 @@ using dword = uint32_t;
#define GGG(a, b, c, d, e, x, s) \
{ \
(a) += G((b), (c), (d)) + (x) + 0x7a6d76e9UL; \
(a) += G((b), (c), (d)) + (x) + 0x7a6d76e9U; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
#define HHH(a, b, c, d, e, x, s) \
{ \
(a) += H((b), (c), (d)) + (x) + 0x6d703ef3UL; \
(a) += H((b), (c), (d)) + (x) + 0x6d703ef3U; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
#define III(a, b, c, d, e, x, s) \
{ \
(a) += I((b), (c), (d)) + (x) + 0x5c4dd124UL; \
(a) += I((b), (c), (d)) + (x) + 0x5c4dd124U; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
#define JJJ(a, b, c, d, e, x, s) \
{ \
(a) += J((b), (c), (d)) + (x) + 0x50a28be6UL; \
(a) += J((b), (c), (d)) + (x) + 0x50a28be6U; \
(a) = ROL((a), (s)) + (e); \
(c) = ROL((c), 10); \
}
......@@ -360,10 +364,10 @@ void MDfinish(dword* MDbuf, const byte* strptr, dword lswlen, dword mswlen) {
// put bytes from strptr into X
for (unsigned int i = 0; i < (lswlen & 63); ++i) {
// byte i goes into word X[i div 4] at pos. 8*(i mod 4)
X[i >> 2] ^= (dword) * strptr++ << (8 * (i & 3));
X[i >> 2] ^= static_cast<dword>(*strptr++) << (8 * (i & 3));
}
// append the bit m_n == 1
X[(lswlen >> 2) & 15] ^= (dword)1 << (8 * (lswlen & 3) + 7);
X[(lswlen >> 2) & 15] ^= static_cast<dword>(1) << (8 * (lswlen & 3) + 7);
if ((lswlen & 63) > 55) {
// length goes to next block
compress(MDbuf, X);
......
......@@ -609,7 +609,7 @@ string to_string(const node_id& what) {
string to_verbose_string(const std::exception& e) {
std::ostringstream oss;
oss << detail::demangle(typeid(e)) << ": " << e.what();
oss << "std::exception, what(): " << e.what();
return oss.str();
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#include <map>
#include <cwchar>
#include <limits>
#include <vector>
#include <cstring>
#include <typeinfo>
#include <stdexcept>
#include <algorithm>
#include "caf/string_algorithms.hpp"
#include "caf/atom.hpp"
#include "caf/actor.hpp"
#include "caf/channel.hpp"
#include "caf/message.hpp"
#include "caf/message.hpp"
#include "caf/abstract_group.hpp"
#include "caf/detail/demangle.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/to_uniform_name.hpp"
#include "caf/detail/uniform_type_info_map.hpp"
//#define DEBUG_PARSER
#ifdef DEBUG_PARSER
# include <iostream>
namespace {
size_t s_indentation = 0;
} // namespace <anonymous>
# define PARSER_INIT(message) \
std::cout << std::string(s_indentation, ' ') << ">>> " << message \
<< std::endl; \
s_indentation += 2; \
auto ____sg = caf::detail::make_scope_guard([] { s_indentation -= 2; })
# define PARSER_OUT(condition, message) \
if (condition) { \
std::cout << std::string(s_indentation, ' ') << "### " << message \
<< std::endl; \
} \
static_cast<void>(0)
#else
# define PARSER_INIT(unused) static_cast<void>(0)
# define PARSER_OUT(unused1, unused2) static_cast<void>(0)
#endif
namespace caf {
namespace detail {
namespace {
using namespace std;
struct platform_int_mapping {
const char* name;
size_t size;
bool is_signed;
};
// WARNING: this list is sorted and searched with std::lower_bound;
// keep ordered when adding elements!
constexpr platform_int_mapping platform_dependent_sizes[] = {
{"char", sizeof(char), true },
{"char16_t", sizeof(char16_t), true },
{"char32_t", sizeof(char32_t), true },
{"int", sizeof(int), true },
{"long", sizeof(long), true },
{"long int", sizeof(long int), true },
{"long long", sizeof(long long), true },
{"short", sizeof(short), true },
{"short int", sizeof(short int), true },
{"signed char", sizeof(signed char), true },
{"signed int", sizeof(signed int), true },
{"signed long", sizeof(signed long), true },
{"signed long int", sizeof(signed long int), true },
{"signed long long", sizeof(signed long long), true },
{"signed short", sizeof(signed short), true },
{"signed short int", sizeof(signed short int), true },
{"unsigned char", sizeof(unsigned char), false},
{"unsigned int", sizeof(unsigned int), false},
{"unsigned long", sizeof(unsigned long), false},
{"unsigned long int", sizeof(unsigned long int), false},
{"unsigned long long", sizeof(unsigned long long), false},
{"unsigned short", sizeof(unsigned short), false},
{"unsigned short int", sizeof(unsigned short int), false}
};
string map2decorated(string&& name) {
auto cmp = [](const platform_int_mapping& pim, const string& str) {
return strcmp(pim.name, str.c_str()) < 0;
};
auto e = end(platform_dependent_sizes);
auto i = lower_bound(begin(platform_dependent_sizes), e, name, cmp);
if (i != e && i->name == name) {
PARSER_OUT(true, name << " => "
<< mapped_int_names[i->size][i->is_signed ? 1 : 0]);
return mapped_int_names[i->size][i->is_signed ? 1 : 0];
}
# ifdef DEBUG_PARSER
auto mapped = mapped_name_by_decorated_name(name.c_str());
PARSER_OUT(mapped != name, name << " => " << string{mapped});
return mapped;
# else
return mapped_name_by_decorated_name(std::move(name));
# endif
}
class parse_tree {
public:
string compile(bool parent_invoked = false) {
string result;
propagate_flags();
if (!parent_invoked) {
if (m_volatile) {
result += "volatile ";
}
if (m_const) {
result += "const ";
}
}
if (has_children()) {
string sub_result;
for (auto& child : m_children) {
if (!sub_result.empty()) {
sub_result += "::";
}
sub_result += child.compile(true);
}
result += map2decorated(std::move(sub_result));
} else {
string full_name = map2decorated(std::move(m_name));
if (is_template()) {
full_name += "<";
for (auto& tparam : m_template_parameters) {
// decorate each single template parameter
if (full_name.back() != '<') {
full_name += ",";
}
full_name += tparam.compile();
}
full_name += ">";
// decorate full name
}
result += map2decorated(std::move(full_name));
}
if (!parent_invoked) {
if (m_pointer) {
result += "*";
}
if (m_lvalue_ref) {
result += "&";
}
if (m_rvalue_ref) {
result += "&&";
}
}
return map2decorated(std::move(result));
}
template <class Iterator>
static vector<parse_tree> parse_tpl_args(Iterator first, Iterator last);
template <class Iterator>
static parse_tree parse(Iterator first, Iterator last) {
PARSER_INIT((std::string{first, last}));
parse_tree result;
using range = std::pair<Iterator, Iterator>;
std::vector<range> subranges;
{ // lifetime scope of temporary variables needed to fill 'subranges'
auto find_end = [&](Iterator from)->Iterator {
auto open = 1;
for (auto i = from + 1; i != last && open > 0; ++i) {
switch (*i) {
default:
break;
case '<':
++open;
break;
case '>':
if (--open == 0) return i;
break;
}
}
return last;
};
auto sub_first = find(first, last, '<');
while (sub_first != last) {
auto sub_last = find_end(sub_first);
subranges.emplace_back(sub_first, sub_last);
sub_first = find(sub_last + 1, last, '<');
}
}
auto islegal = [](char c) {
return isalnum(c) || c == ':' || c == '_';
};
vector<string> tokens;
tokens.push_back("");
vector<Iterator> scope_resolution_ops;
auto is_in_subrange = [&](Iterator i) -> bool {
for (auto& r : subranges) {
if (i >= r.first && i < r.second) {
return true;
}
}
return false;
};
auto add_child = [&](Iterator ch_first, Iterator ch_last) {
PARSER_OUT(true, "new child: [" << distance(first, ch_first) << ", "
<< ", " << distance(first, ch_last)
<< ")");
result.m_children.push_back(parse(ch_first, ch_last));
};
// scan string for "::" separators
const char* scope_resultion = "::";
auto sr_first = scope_resultion;
auto sr_last = sr_first + 2;
auto scope_iter = search(first, last, sr_first, sr_last);
if (scope_iter != last) {
auto itermediate = first;
if (!is_in_subrange(scope_iter)) {
add_child(first, scope_iter);
itermediate = scope_iter + 2;
}
while (scope_iter != last) {
scope_iter = search(scope_iter + 2, last, sr_first, sr_last);
if (scope_iter != last && !is_in_subrange(scope_iter)) {
add_child(itermediate, scope_iter);
itermediate = scope_iter + 2;
}
}
if (!result.m_children.empty()) {
add_child(itermediate, last);
}
}
if (result.m_children.empty()) {
// no children -> leaf node; parse non-template part now
CAF_REQUIRE(subranges.size() < 2);
vector<range> non_template_ranges;
if (subranges.empty()) {
non_template_ranges.emplace_back(first, last);
} else {
non_template_ranges.emplace_back(first, subranges[0].first);
for (size_t i = 1; i < subranges.size(); ++i) {
non_template_ranges.emplace_back(subranges[i - 1].second + 1,
subranges[i].first);
}
non_template_ranges.emplace_back(subranges.back().second + 1, last);
}
for (auto& ntr : non_template_ranges) {
for (auto i = ntr.first; i != ntr.second; ++i) {
char c = *i;
if (islegal(c)) {
if (!tokens.back().empty() && !islegal(tokens.back().back())) {
tokens.push_back("");
}
tokens.back() += c;
} else if (c == ' ') {
tokens.push_back("");
} else if (c == '&') {
if (tokens.back().empty() || tokens.back().back() == '&') {
tokens.back() += c;
} else {
tokens.push_back("&");
}
} else if (c == '*') {
tokens.push_back("*");
}
}
tokens.push_back("");
}
if (!subranges.empty()) {
auto& range0 = subranges.front();
PARSER_OUT(true, "subrange: [" << distance(first, range0.first + 1)
<< "," << distance(first, range0.second)
<< ")");
result.m_template_parameters
= parse_tpl_args(range0.first + 1, range0.second);
}
for (auto& token : tokens) {
if (token == "const") {
result.m_const = true;
} else if (token == "volatile") {
result.m_volatile = true;
} else if (token == "&") {
result.m_lvalue_ref = true;
} else if (token == "&&") {
result.m_rvalue_ref = true;
} else if (token == "*") {
result.m_pointer = true;
} else if (token == "class" || token == "struct") {
// ignored (created by visual c++ compilers)
} else if (!token.empty()) {
if (!result.m_name.empty()) {
result.m_name += " ";
}
result.m_name += token;
}
}
}
PARSER_OUT(!subranges.empty(), subranges.size() << " subranges");
PARSER_OUT(!result.m_children.empty(), result.m_children.size()
<< " children");
return result;
}
inline bool has_children() const {
return !m_children.empty();
}
inline bool is_template() const {
return !m_template_parameters.empty();
}
private:
void propagate_flags() {
for (auto& c : m_children) {
c.propagate_flags();
if (c.m_volatile) m_volatile = true;
if (c.m_const) m_const = true;
if (c.m_pointer) m_pointer = true;
if (c.m_lvalue_ref) m_lvalue_ref = true;
if (c.m_rvalue_ref) m_rvalue_ref = true;
}
}
parse_tree()
: m_const(false), m_pointer(false), m_volatile(false),
m_lvalue_ref(false), m_rvalue_ref(false) {
// nop
}
bool m_const;
bool m_pointer;
bool m_volatile;
bool m_lvalue_ref;
bool m_rvalue_ref;
bool m_nested_type;
string m_name;
vector<parse_tree> m_children;
vector<parse_tree> m_template_parameters;
};
template <class Iterator>
vector<parse_tree> parse_tree::parse_tpl_args(Iterator first, Iterator last) {
vector<parse_tree> result;
long open_brackets = 0;
auto i0 = first;
for (; first != last; ++first) {
switch (*first) {
case '<':
++open_brackets;
break;
case '>':
--open_brackets;
break;
case ',':
if (open_brackets == 0) {
result.push_back(parse(i0, first));
i0 = first + 1;
}
break;
default:
break;
}
}
result.push_back(parse(i0, first));
return result;
}
const char raw_anonymous_namespace[] = "anonymous namespace";
const char unified_anonymous_namespace[] = "$";
} // namespace <anonymous>
std::string to_uniform_name(const std::string& dname) {
auto r = parse_tree::parse(begin(dname), end(dname)).compile();
// replace compiler-dependent "anonmyous namespace" with "@_"
replace_all(r, raw_anonymous_namespace, unified_anonymous_namespace);
return r.c_str();
}
std::string to_uniform_name(const std::type_info& tinfo) {
return to_uniform_name(demangle(tinfo.name()));
}
} // namespace detail
} // namespace caf
......@@ -17,103 +17,99 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include <memory>
#include <string>
#include <cstdlib>
#include <stdexcept>
#include "caf/config.hpp"
#include "caf/detail/demangle.hpp"
#if defined(CAF_CLANG) || defined(CAF_GCC)
# include <cxxabi.h>
# include <stdlib.h>
#endif
#include "caf/detail/try_match.hpp"
namespace caf {
namespace detail {
namespace {
using pattern_iterator = const meta_element*;
bool is_wildcard(const meta_element& me) {
return me.type == nullptr;
}
// filter unnecessary characters from undecorated cstr
std::string filter_whitespaces(const char* cstr, size_t size = 0) {
std::string result;
if (size > 0) {
result.reserve(size);
bool match_element(const std::type_info* type, const message_iterator& iter,
void** storage) {
if (!iter.type()->equal_to(*type)) {
return false;
}
char c = *cstr;
while (c != '\0') {
if (c == ' ') {
char previous_c = result.empty() ? ' ' : *(result.rbegin());
for (c = *++cstr; c == ' '; c = *++cstr) {
// scan for next non-space character
}
if (c != '\0') {
// skip whitespace unless it separates two alphanumeric
// characters (such as in "unsigned int")
if (isalnum(c) && isalnum(previous_c)) {
result += ' ';
result += c;
} else {
result += c;
}
c = *++cstr;
}
} else {
result += c;
c = *++cstr;
}
if (storage) {
*storage = const_cast<void*>(iter.value());
}
return result;
return true;
}
} // namespace <anonymous>
#if defined(CAF_CLANG) || defined(CAF_GCC)
std::string demangle(const char* decorated) {
using std::string;
size_t size;
int status;
using uptr = std::unique_ptr<char, void (*)(void*)>;
uptr undecorated{abi::__cxa_demangle(decorated, nullptr, &size, &status),
std::free};
if (status != 0) {
string error_msg = "Could not demangle type name ";
error_msg += decorated;
throw std::logic_error(error_msg);
class set_commit_rollback {
public:
using pointer = void**;
set_commit_rollback(const set_commit_rollback&) = delete;
set_commit_rollback& operator=(const set_commit_rollback&) = delete;
set_commit_rollback(pointer ptr) : m_data(ptr), m_pos(0), m_fallback_pos(0) {
// nop
}
inline void inc() {
++m_pos;
}
inline pointer current() {
return m_data? &m_data[m_pos] : nullptr;
}
inline void commit() {
m_fallback_pos = m_pos;
}
inline void rollback() {
m_pos = m_fallback_pos;
}
private:
pointer m_data;
size_t m_pos;
size_t m_fallback_pos;
};
bool try_match(message_iterator mbegin, message_iterator mend,
pattern_iterator pbegin, pattern_iterator pend,
set_commit_rollback& storage) {
while (mbegin != mend) {
if (pbegin == pend) {
return false;
}
// the undecorated typeid name
string result = filter_whitespaces(undecorated.get(), size);
# ifdef CAF_CLANG
// replace "std::__1::" with "std::" (fixes strange clang names)
string needle = "std::__1::";
string fixed_string = "std::";
for (auto pos = result.find(needle); pos != string::npos;
pos = result.find(needle)) {
result.replace(pos, needle.size(), fixed_string);
if (is_wildcard(*pbegin)) {
// perform submatching
++pbegin;
// always true at the end of the pattern
if (pbegin == pend) {
return true;
}
# endif
return result;
}
#elif defined(CAF_MSVC)
string demangle(const char* decorated) {
// on MSVC, name() returns a human-readable version, all we need
// to do is to remove "struct" and "class" qualifiers
// (handled in to_uniform_name)
return filter_whitespaces(decorated);
// safe current mapping as fallback
storage.commit();
// iterate over remaining values until we found a match
for (; mbegin != mend; ++mbegin) {
if (try_match(mbegin, mend, pbegin, pend, storage)) {
return true;
}
// restore mapping to fallback (delete invalid mappings)
storage.rollback();
}
return false; // no submatch found
}
// inspect current element
if (!pbegin->fun(pbegin->type, mbegin, storage.current())) {
// type mismatch
return false;
}
// next iteration
storage.inc();
++mbegin;
++pbegin;
}
// we found a match if we've inspected each element and consumed
// the whole pattern (or the remainder consists of wildcards only)
return std::all_of(pbegin, pend, is_wildcard);
}
#else
# error "compiler or platform not supported"
#endif
std::string demangle(const std::type_info& tinf) {
return demangle(tinf.name());
bool try_match(const message& msg, pattern_iterator pb, size_t ps, void** out) {
set_commit_rollback scr{out};
auto res = try_match(msg.begin(), msg.end(), pb, pb + ps, scr);
return res;
}
} // namespace detail
......
......@@ -40,10 +40,8 @@
#include "caf/uniform_type_info.hpp"
#include "caf/detail/logging.hpp"
#include "caf/detail/demangle.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/actor_registry.hpp"
#include "caf/detail/to_uniform_name.hpp"
#include "caf/detail/uniform_type_info_map.hpp"
namespace caf {
......@@ -73,8 +71,8 @@ const uniform_type_info* uniform_type_info::from(const std::type_info& tinf) {
auto result = uti_map().by_rtti(tinf);
if (result == nullptr) {
std::string error = "uniform_type_info::by_type_info(): ";
error += detail::to_uniform_name(tinf);
error += " is an unknown typeid name";
error += tinf.name();
error += " has not been announced";
CAF_LOGM_ERROR("caf::uniform_type_info", error);
throw std::runtime_error(error);
}
......@@ -89,9 +87,9 @@ const uniform_type_info* uniform_type_info::from(const std::string& name) {
return result;
}
uniform_value uniform_type_info::deserialize(deserializer* from) const {
uniform_value uniform_type_info::deserialize(deserializer* src) const {
auto uval = create();
deserialize(uval->val, from);
deserialize(uval->val, src);
return std::move(uval);
}
......@@ -99,8 +97,18 @@ std::vector<const uniform_type_info*> uniform_type_info::instances() {
return uti_map().get_all();
}
const uniform_type_info* uniform_typeid(const std::type_info& tinfo) {
return uniform_type_info::from(tinfo);
const uniform_type_info* uniform_typeid(const std::type_info& tinf,
bool allow_nullptr) {
auto result = uti_map().by_rtti(tinf);
if (result == nullptr && !allow_nullptr) {
std::string error = "uniform_typeid(): ";
error += tinf.name();
error += " has not been announced";
CAF_LOGM_ERROR("caf::uniform_type_info", error);
throw std::runtime_error(error);
}
return result;
}
} // namespace caf
......@@ -116,7 +116,8 @@ using static_type_table = type_list<bool,
std::u16string,
std::u32string,
std::map<std::string, std::string>,
std::vector<char>>;
std::vector<char>,
std::vector<std::string>>;
} // namespace <anonymous>
template <class T>
......@@ -453,6 +454,30 @@ inline void deserialize_impl(const sync_timeout_msg&, deserializer*) {
// nop
}
inline void serialize_impl(const std::map<std::string, std::string>& smap,
serializer* sink) {
default_serialize_policy sp;
sp(smap, sink);
}
inline void deserialize_impl(std::map<std::string, std::string>& smap,
deserializer* source) {
default_serialize_policy sp;
sp(smap, source);
}
template <class T>
inline void serialize_impl(const std::vector<T>& vec, serializer* sink) {
default_serialize_policy sp;
sp(vec, sink);
}
template <class T>
inline void deserialize_impl(std::vector<T>& vec, deserializer* source) {
default_serialize_policy sp;
sp(vec, source);
}
bool types_equal(const std::type_info* lhs, const std::type_info* rhs) {
// in some cases (when dealing with dynamic libraries),
// address can be different although types are equal
......@@ -575,10 +600,10 @@ protected:
class default_meta_message : public uniform_type_info {
public:
default_meta_message(const std::string& name) {
m_name = name;
default_meta_message(const std::string& tname) {
m_name = tname;
std::vector<std::string> elements;
split(elements, name, is_any_of("+"));
split(elements, tname, is_any_of("+"));
auto uti_map = detail::singletons::get_uniform_type_info_map();
CAF_REQUIRE(elements.size() > 0 && elements.front() == "@<>");
// ignore first element, because it's always "@<>"
......@@ -625,10 +650,7 @@ class default_meta_message : public uniform_type_info {
return false;
}
bool equals(const void* instance1, const void* instance2) const override {
auto& lhs = *cast(instance1);
auto& rhs = *cast(instance2);
full_eq_type cmp;
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), cmp);
return *cast(instance1) == *cast(instance2);
}
private:
......@@ -795,7 +817,7 @@ class utim_impl : public uniform_type_info_map {
uti_impl<std::string>,
uti_impl<std::u16string>,
uti_impl<std::u32string>,
default_uniform_type_info<strmap>,
uti_impl<strmap>,
uti_impl<bool>,
uti_impl<float>,
uti_impl<double>,
......@@ -808,8 +830,8 @@ class utim_impl : public uniform_type_info_map {
int_tinfo<uint32_t>,
int_tinfo<int64_t>,
int_tinfo<uint64_t>,
default_uniform_type_info<charbuf>,
default_uniform_type_info<strvec>>;
uti_impl<charbuf>,
uti_impl<strvec>>;
builtin_types m_storage;
......
......@@ -16,7 +16,6 @@ set (LIBCAF_IO_SRCS
src/publish.cpp
src/publish_local_groups.cpp
src/remote_actor.cpp
src/remote_actor_proxy.cpp
src/remote_group.cpp
src/manager.cpp
src/stream_manager.cpp
......
......@@ -25,6 +25,7 @@
#include "caf/io/spawn_io.hpp"
#include "caf/io/middleman.hpp"
#include "caf/io/unpublish.hpp"
#include "caf/io/basp_broker.hpp"
#include "caf/io/max_msg_size.hpp"
#include "caf/io/remote_actor.hpp"
#include "caf/io/remote_group.hpp"
......
......@@ -81,12 +81,15 @@ inline bool nonzero(T aid) {
/**
* Send from server, i.e., the node with a published actor, to client,
* i.e., node that initiates a new connection using remote_actor().
* @param source_node ID of server
* @param dest_node invalid
* @param source_actor Optional: ID of published actor
* @param dest_actor 0
* @param payload_len Optional: size of actor id + interface definition
* @param operation_data BASP version of the server
*
* Field | Assignment
* ---------------|----------------------------------------------------------
* source_node | ID of server
* dest_node | invalid
* source_actor | Optional: ID of published actor
* dest_actor | 0
* payload_len | Optional: size of actor id + interface definition
* operation_data | BASP version of the server
*/
constexpr uint32_t server_handshake = 0x00;
......@@ -96,18 +99,21 @@ inline bool server_handshake_valid(const header& hdr) {
&& zero(hdr.dest_actor)
&& nonzero(hdr.operation_data)
&& ( (nonzero(hdr.source_actor) && nonzero(hdr.payload_len))
|| (zero(hdr.source_actor) && zero(hdr.payload_len)));
|| (zero(hdr.source_actor) && zero(hdr.payload_len)));
}
/**
* Send from client to server after it has successfully received the
* server_handshake to establish the connection.
* @param source_node ID of client
* @param dest_node ID of server
* @param source_actor 0
* @param dest_actor 0
* @param payload_len 0
* @param operation_data 0
*
* Field | Assignment
* ---------------|----------------------------------------------------------
* source_node | ID of client
* dest_node | ID of server
* source_actor | 0
* dest_actor | 0
* payload_len | 0
* operation_data | 0
*/
constexpr uint32_t client_handshake = 0x01;
......@@ -124,12 +130,15 @@ inline bool client_handshake_valid(const header& hdr) {
/**
* Transmits a message from source_node:source_actor to
* dest_node:dest_actor.
* @param source_node ID of sending node (invalid in case of anon_send)
* @param dest_node ID of receiving node
* @param source_actor ID of sending actor (invalid in case of anon_send)
* @param dest_actor ID of receiving actor, must not be invalid
* @param payload_len size of serialized message object, must not be 0
* @param operation_data message ID (0 for asynchronous messages)
*
* Field | Assignment
* ---------------|----------------------------------------------------------
* source_node | ID of sending node (invalid in case of anon_send)
* dest_node | ID of receiving node
* source_actor | ID of sending actor (invalid in case of anon_send)
* dest_actor | ID of receiving actor, must not be invalid
* payload_len | size of serialized message object, must not be 0
* operation_data | message ID (0 for asynchronous messages)
*/
constexpr uint32_t dispatch_message = 0x02;
......@@ -144,12 +153,15 @@ inline bool dispatch_message_valid(const header& hdr) {
* instance for one of its actors. Causes the receiving node to attach
* a functor to the actor that triggers a kill_proxy_instance
* message on termination.
* @param source_node ID of sending node
* @param dest_node ID of receiving node
* @param source_actor 0
* @param dest_actor ID of monitored actor
* @param payload_len 0
* @param operation_data 0
*
* Field | Assignment
* ---------------|----------------------------------------------------------
* source_node | ID of sending node
* dest_node | ID of receiving node
* source_actor | 0
* dest_actor | ID of monitored actor
* payload_len | 0
* operation_data | 0
*/
constexpr uint32_t announce_proxy_instance = 0x03;
......@@ -166,12 +178,15 @@ inline bool announce_proxy_instance_valid(const header& hdr) {
/**
* Informs the receiving node that it has a proxy for an actor
* that has been terminated.
* @param source_node ID of sending node
* @param dest_node ID of receiving node
* @param source_actor ID of monitored actor
* @param dest_actor 0
* @param payload_len 0
* @param operation_data exit reason (uint32)
*
* Field | Assignment
* ---------------|----------------------------------------------------------
* source_node | ID of sending node
* dest_node | ID of receiving node
* source_actor | ID of monitored actor
* dest_actor | 0
* payload_len | 0
* operation_data | exit reason (uint32)
*/
constexpr uint32_t kill_proxy_instance = 0x04;
......
......@@ -29,10 +29,10 @@
#include "caf/actor_namespace.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/forwarding_actor_proxy.hpp"
#include "caf/io/basp.hpp"
#include "caf/io/broker.hpp"
#include "caf/io/remote_actor_proxy.hpp"
namespace caf {
namespace io {
......@@ -66,9 +66,8 @@ class basp_broker : public broker, public actor_namespace::backend {
struct client_handshake_data {
id_type remote_id;
std::promise<abstract_actor_ptr>* result;
const std::set<std::string>* expected_ifs;
actor client;
std::set<std::string> expected_ifs;
};
void init_client(connection_handle hdl, client_handshake_data* data);
......@@ -117,9 +116,6 @@ class basp_broker : public broker, public actor_namespace::backend {
void write(binary_serializer& bs, const basp::header& msg);
void send(const connection_context& ctx, const basp::header& msg,
message payload);
void send_kill_proxy_instance(const id_type& nid, actor_id aid,
uint32_t reason);
......
......@@ -252,6 +252,8 @@ class broker : public extend<local_actor>::
m_scribes.insert(std::make_pair(ptr->hdl(), ptr));
}
connection_handle add_tcp_scribe(const std::string& host, uint16_t port);
inline void add_doorman(const doorman_pointer& ptr) {
m_doormen.insert(std::make_pair(ptr->hdl(), ptr));
if (is_initialized()) {
......@@ -259,26 +261,15 @@ class broker : public extend<local_actor>::
}
}
std::pair<accept_handle, uint16_t> add_tcp_doorman(uint16_t port = 0,
const char* in = nullptr,
bool reuse_addr = false);
void invoke_message(const actor_addr& sender, message_id mid, message& msg);
void enqueue(const actor_addr&, message_id, message,
execution_unit*) override;
template <class F>
static broker_ptr from(F fun) {
// transform to STD function here, because GCC is unable
// to select proper overload otherwise ...
using fres = decltype(fun((broker*)nullptr));
std::function<fres(broker*)> stdfun{std::move(fun)};
return from_impl(std::move(stdfun));
}
template <class F, typename T, class... Ts>
static broker_ptr from(F fun, T&& v, Ts&&... vs) {
return from(std::bind(fun, std::placeholders::_1, std::forward<T>(v),
std::forward<Ts>(vs)...));
}
/**
* Closes all connections and acceptors.
*/
......@@ -297,7 +288,7 @@ class broker : public extend<local_actor>::
class functor_based;
void launch(bool is_hidden, execution_unit*);
void launch(bool is_hidden, bool, execution_unit*);
// <backward_compatibility version="0.9">
......
......@@ -27,7 +27,6 @@ class basp_broker;
class broker;
class middleman;
class receive_policy;
class remote_actor_proxy;
namespace network {
......
......@@ -62,7 +62,7 @@ class middleman : public detail::abstract_singleton {
return static_cast<Impl*>(i->second.get());
}
intrusive_ptr<Impl> result{new Impl};
result->launch(true, nullptr);
result->launch(true, false, nullptr);
m_named_brokers.insert(std::make_pair(name, result));
return result;
}
......
......@@ -246,8 +246,8 @@ class event_handler {
/**
* Sets the bit field storing the subscribed events.
*/
inline void eventbf(int eventbf) {
m_eventbf = eventbf;
inline void eventbf(int value) {
m_eventbf = value;
}
/**
......@@ -313,18 +313,19 @@ class default_multiplexer : public multiplexer {
event_handler* ptr;
};
connection_handle add_tcp_scribe(broker*, default_socket&& sock);
connection_handle add_tcp_scribe(broker*, default_socket_acceptor&& sock);
connection_handle add_tcp_scribe(broker*, native_socket fd) override;
connection_handle add_tcp_scribe(broker*, const std::string& h,
uint16_t port) override;
accept_handle add_tcp_doorman(broker*, default_socket&& sock);
accept_handle add_tcp_doorman(broker*, default_socket_acceptor&& sock);
accept_handle add_tcp_doorman(broker*, native_socket fd) override;
accept_handle add_tcp_doorman(broker*, uint16_t p, const char* h) override;
std::pair<accept_handle, uint16_t>
add_tcp_doorman(broker*, uint16_t p, const char* h, bool reuse_addr) override;
void dispatch_runnable(runnable_ptr ptr) override;
......@@ -356,8 +357,8 @@ class default_multiplexer : public multiplexer {
CAF_LOG_TRACE(CAF_TARG(op, static_cast<int>)
<< ", " << CAF_ARG(fd) << ", " CAF_ARG(ptr)
<< ", " << CAF_ARG(old_bf));
auto event_less = [](const event& e, native_socket fd) -> bool {
return e.fd < fd;
auto event_less = [](const event& e, native_socket arg) -> bool {
return e.fd < arg;
};
auto last = m_events.end();
auto i = std::lower_bound(m_events.begin(), last, fd, event_less);
......@@ -441,9 +442,9 @@ class stream : public event_handler {
*/
using buffer_type = std::vector<char>;
stream(default_multiplexer& backend)
: event_handler(backend),
m_sock(backend),
stream(default_multiplexer& backend_ref)
: event_handler(backend_ref),
m_sock(backend_ref),
m_writing(false) {
configure_read(receive_policy::at_most(1024));
}
......@@ -465,8 +466,8 @@ class stream : public event_handler {
/**
* Initializes this stream, setting the socket handle to `fd`.
*/
void init(Socket fd) {
m_sock = std::move(fd);
void init(Socket sockfd) {
m_sock = std::move(sockfd);
}
/**
......@@ -483,7 +484,7 @@ class stream : public event_handler {
switch (op) {
case operation::read: m_reader.reset(); break;
case operation::write: m_writer.reset(); break;
default: break;
case operation::propagate_error: break;
}
}
......@@ -680,10 +681,10 @@ class acceptor : public event_handler {
*/
using manager_ptr = intrusive_ptr<manager_type>;
acceptor(default_multiplexer& backend)
: event_handler(backend),
m_accept_sock(backend),
m_sock(backend) {
acceptor(default_multiplexer& backend_ref)
: event_handler(backend_ref),
m_accept_sock(backend_ref),
m_sock(backend_ref) {
// nop
}
......@@ -736,10 +737,10 @@ class acceptor : public event_handler {
CAF_LOG_TRACE("m_accept_sock.fd = " << m_accept_sock.fd()
<< ", op = " << static_cast<int>(op));
if (m_mgr && op == operation::read) {
native_socket fd = invalid_native_socket;
if (try_accept(fd, m_accept_sock.fd())) {
if (fd != invalid_native_socket) {
m_sock = socket_type{backend(), fd};
native_socket sockfd = invalid_native_socket;
if (try_accept(sockfd, m_accept_sock.fd())) {
if (sockfd != invalid_native_socket) {
m_sock = socket_type{backend(), sockfd};
m_mgr->new_connection();
}
}
......@@ -775,17 +776,18 @@ void ipv4_connect(Socket& sock, const std::string& host, uint16_t port) {
sock = new_ipv4_connection(host, port);
}
native_socket new_ipv4_acceptor_impl(uint16_t port, const char* addr);
default_socket_acceptor new_ipv4_acceptor(uint16_t port,
const char* addr = nullptr);
std::pair<default_socket_acceptor, uint16_t>
new_ipv4_acceptor(uint16_t port, const char* addr = nullptr,
bool reuse_addr = false);
template <class SocketAcceptor>
void ipv4_bind(SocketAcceptor& sock,
uint16_t ipv4_bind(SocketAcceptor& sock,
uint16_t port,
const char* addr = nullptr) {
CAF_LOGF_TRACE(CAF_ARG(port));
sock = new_ipv4_acceptor(port, addr);
auto acceptor = new_ipv4_acceptor(port, addr);
sock = std::move(acceptor.first);
return acceptor.second;
}
} // namespace network
......
......@@ -74,8 +74,9 @@ class multiplexer {
* Tries to create a new TCP doorman running on port `p`, optionally
* accepting only connections from IP address `in`.
*/
virtual accept_handle add_tcp_doorman(broker* ptr, uint16_t port,
const char* in = nullptr) = 0;
virtual std::pair<accept_handle, uint16_t>
add_tcp_doorman(broker* ptr, uint16_t port, const char* in = nullptr,
bool reuse_addr = false) = 0;
/**
* Simple wrapper for runnables
......@@ -91,7 +92,7 @@ class multiplexer {
* Makes sure the multipler does not exit its event loop until
* the destructor of `supervisor` has been called.
*/
struct supervisor {
class supervisor {
public:
virtual ~supervisor();
};
......
......@@ -29,32 +29,38 @@
namespace caf {
namespace io {
void publish_impl(abstract_actor_ptr whom, uint16_t port, const char* in);
uint16_t publish_impl(abstract_actor_ptr whom, uint16_t port,
const char* in, bool reuse_addr);
/**
* Publishes `whom` at `port`. The connection is managed by the middleman.
* @param whom Actor that should be published at `port`.
* @param port Unused TCP port.
* @param addr The IP address to listen to or `INADDR_ANY` if `in == nullptr`.
* @param in The IP address to listen to or `INADDR_ANY` if `in == nullptr`.
* @returns The actual port the OS uses after `bind()`. If `port == 0` the OS
* chooses a random high-level port.
* @throws bind_failure
*/
inline void publish(caf::actor whom, uint16_t port, const char* in = nullptr) {
inline uint16_t publish(caf::actor whom, uint16_t port,
const char* in = nullptr, bool reuse_addr = false) {
if (!whom) {
return;
return 0;
}
publish_impl(actor_cast<abstract_actor_ptr>(whom), port, in);
return publish_impl(actor_cast<abstract_actor_ptr>(whom), port, in,
reuse_addr);
}
/**
* @copydoc publish(actor,uint16_t,const char*)
*/
template <class... Rs>
void typed_publish(typed_actor<Rs...> whom, uint16_t port,
const char* in = nullptr) {
uint16_t typed_publish(typed_actor<Rs...> whom, uint16_t port,
const char* in = nullptr, bool reuse_addr = false) {
if (!whom) {
return;
return 0;
}
publish_impl(actor_cast<abstract_actor_ptr>(whom), port, in);
return publish_impl(actor_cast<abstract_actor_ptr>(whom), port, in,
reuse_addr);
}
} // namespace io
......
......@@ -27,10 +27,12 @@ namespace io {
/**
* Makes *all* local groups accessible via network on address `addr` and `port`.
* @returns The actual port the OS uses after `bind()`. If `port == 0` the OS
* chooses a random high-level port.
* @throws bind_failure
* @throws network_error
*/
void publish_local_groups(uint16_t port, const char* addr = nullptr);
uint16_t publish_local_groups(uint16_t port, const char* addr = nullptr);
} // namespace io
} // namespace caf
......
......@@ -31,7 +31,7 @@
namespace caf {
namespace io {
abstract_actor_ptr remote_actor_impl(const std::set<std::string>& ifs,
abstract_actor_ptr remote_actor_impl(std::set<std::string> ifs,
const std::string& host, uint16_t port);
template <class List>
......
......@@ -41,11 +41,17 @@ struct new_connection_msg {
connection_handle handle;
};
/**
* @relates new_connection_msg
*/
inline bool operator==(const new_connection_msg& lhs,
const new_connection_msg& rhs) {
return lhs.source == rhs.source && lhs.handle == rhs.handle;
}
/**
* @relates new_connection_msg
*/
inline bool operator!=(const new_connection_msg& lhs,
const new_connection_msg& rhs) {
return !(lhs == rhs);
......@@ -65,10 +71,16 @@ struct new_data_msg {
std::vector<char> buf;
};
/**
* @relates new_data_msg
*/
inline bool operator==(const new_data_msg& lhs, const new_data_msg& rhs) {
return lhs.handle == rhs.handle && lhs.buf == rhs.buf;
}
/**
* @relates new_data_msg
*/
inline bool operator!=(const new_data_msg& lhs, const new_data_msg& rhs) {
return !(lhs == rhs);
}
......@@ -83,11 +95,17 @@ struct connection_closed_msg {
connection_handle handle;
};
/**
* @relates connection_closed_msg
*/
inline bool operator==(const connection_closed_msg& lhs,
const connection_closed_msg& rhs) {
return lhs.handle == rhs.handle;
}
/**
* @relates connection_closed_msg
*/
inline bool operator!=(const connection_closed_msg& lhs,
const connection_closed_msg& rhs) {
return !(lhs == rhs);
......@@ -103,11 +121,17 @@ struct acceptor_closed_msg {
accept_handle handle;
};
/**
* @relates acceptor_closed_msg
*/
inline bool operator==(const acceptor_closed_msg& lhs,
const acceptor_closed_msg& rhs) {
return lhs.handle == rhs.handle;
}
/**
* @relates acceptor_closed_msg
*/
inline bool operator!=(const acceptor_closed_msg& lhs,
const acceptor_closed_msg& rhs) {
return !(lhs == rhs);
......
......@@ -17,9 +17,12 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/io/basp_broker.hpp"
#include "caf/exception.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/forwarding_actor_proxy.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/make_counted.hpp"
......@@ -28,8 +31,6 @@
#include "caf/io/basp.hpp"
#include "caf/io/middleman.hpp"
#include "caf/io/unpublish.hpp"
#include "caf/io/basp_broker.hpp"
#include "caf/io/remote_actor_proxy.hpp"
using std::string;
......@@ -73,8 +74,7 @@ behavior basp_broker::make_behavior() {
if (j != m_ctx.end()) {
auto hd = j->second.handshake_data;
if (hd) {
network_error err{"disconnect during handshake"};
hd->result->set_exception(std::make_exception_ptr(err));
send(hd->client, atom("ERROR"), "disconnect during handshake");
}
m_ctx.erase(j);
}
......@@ -262,7 +262,7 @@ void basp_broker::dispatch(const actor_addr& from, const actor_addr& to,
}
auto& buf = wr_buf(route.hdl);
// reserve space in the buffer to write the broker message later on
auto wr_pos = buf.size();
auto wr_pos = static_cast<ptrdiff_t>(buf.size());
char placeholder[basp::header_size];
buf.insert(buf.end(), std::begin(placeholder), std::end(placeholder));
auto before = buf.size();
......@@ -434,7 +434,7 @@ basp_broker::handle_basp_header(connection_context& ctx,
auto str = bd.read<string>();
remote_ifs.insert(std::move(str));
}
auto& ifs = *(ctx.handshake_data->expected_ifs);
auto& ifs = ctx.handshake_data->expected_ifs;
if (!std::includes(ifs.begin(), ifs.end(),
remote_ifs.begin(), remote_ifs.end())) {
auto tostr = [](const std::set<string>& what) -> string {
......@@ -475,15 +475,14 @@ basp_broker::handle_basp_header(connection_context& ctx,
+ iface_str;
}
// abort with error
std::runtime_error err{error_msg};
ctx.handshake_data->result->set_exception(std::make_exception_ptr(err));
send(ctx.handshake_data->client, atom("ERROR"), std::move(error_msg));
return close_connection;
}
auto nid = ctx.handshake_data->remote_id;
if (nid == node()) {
CAF_LOG_INFO("incoming connection from self: drop connection");
auto res = detail::singletons::get_actor_registry()->get(remote_aid);
ctx.handshake_data->result->set_value(std::move(res));
send(ctx.handshake_data->client, atom("OK"), actor_cast<actor>(res));
ctx.handshake_data = nullptr;
return close_connection;
}
......@@ -492,7 +491,7 @@ basp_broker::handle_basp_header(connection_context& ctx,
<< " (re-use old one)");
auto proxy = m_namespace.get_or_put(nid, remote_aid);
// discard this peer; there's already an open connection
ctx.handshake_data->result->set_value(std::move(proxy));
send(ctx.handshake_data->client, atom("OK"), actor_cast<actor>(proxy));
ctx.handshake_data = nullptr;
return close_connection;
}
......@@ -505,7 +504,7 @@ basp_broker::handle_basp_header(connection_context& ctx,
// prepare to receive messages
auto proxy = m_namespace.get_or_put(nid, remote_aid);
ctx.published_actor = proxy;
ctx.handshake_data->result->set_value(std::move(proxy));
send(ctx.handshake_data->client, atom("OK"), actor_cast<actor>(proxy));
ctx.handshake_data = nullptr;
parent().notify<hook::new_connection_established>(nid);
break;
......@@ -570,7 +569,7 @@ actor_proxy_ptr basp_broker::make_proxy(const id_type& nid, actor_id aid) {
// receive a kill_proxy_instance message
intrusive_ptr<basp_broker> self = this;
auto mm = middleman::instance();
auto res = make_counted<remote_actor_proxy>(aid, nid, self);
auto res = make_counted<forwarding_actor_proxy>(aid, nid, self);
res->attach_functor([=](uint32_t) {
mm->backend().dispatch([=] {
// using res->id() instead of aid keeps this actor instance alive
......@@ -638,7 +637,7 @@ void basp_broker::init_handshake_as_sever(connection_context& ctx,
CAF_LOG_TRACE(CAF_ARG(this));
CAF_REQUIRE(node() != invalid_node_id);
auto& buf = wr_buf(ctx.hdl);
auto wrpos = buf.size();
auto wrpos = static_cast<ptrdiff_t>(buf.size());
char padding[basp::header_size];
buf.insert(buf.end(), std::begin(padding), std::end(padding));
auto before = buf.size();
......
......@@ -62,8 +62,9 @@ void broker::servant::disconnect(bool invoke_disconnect_message) {
}
}
broker::scribe::scribe(broker* parent, connection_handle hdl)
: servant(parent), m_hdl(hdl) {
broker::scribe::scribe(broker* ptr, connection_handle conn_hdl)
: servant(ptr),
m_hdl(conn_hdl) {
std::vector<char> tmp;
m_read_msg = make_message(new_data_msg{m_hdl, std::move(tmp)});
}
......@@ -100,8 +101,8 @@ void broker::scribe::io_failure(network::operation op) {
disconnect(true);
}
broker::doorman::doorman(broker* parent, accept_handle hdl)
: servant(parent), m_hdl(hdl) {
broker::doorman::doorman(broker* ptr, accept_handle acc_hdl)
: servant(ptr), m_hdl(acc_hdl) {
auto hdl2 = connection_handle::from_int(-1);
m_accept_msg = make_message(new_connection_msg{m_hdl, hdl2});
}
......@@ -170,8 +171,8 @@ void broker::invoke_message(const actor_addr& sender, message_id mid,
std::swap(msg, m_dummy_node.msg);
try {
auto bhvr = bhvr_stack().back();
auto mid = bhvr_stack().back_id();
switch (m_invoke_policy.handle_message(this, &m_dummy_node, bhvr, mid)) {
auto bid = bhvr_stack().back_id();
switch (m_invoke_policy.handle_message(this, &m_dummy_node, bhvr, bid)) {
case policy::hm_msg_handled: {
CAF_LOG_DEBUG("handle_message returned hm_msg_handled");
while (!bhvr_stack().empty()
......@@ -187,7 +188,7 @@ void broker::invoke_message(const actor_addr& sender, message_id mid,
case policy::hm_skip_msg:
case policy::hm_cache_msg: {
CAF_LOG_DEBUG("handle_message returned hm_skip_msg or hm_cache_msg");
auto e = mailbox_element::create(sender, mid,
auto e = mailbox_element::create(sender, bid,
std::move(m_dummy_node.msg));
m_priority_policy.push_to_cache(unique_mailbox_element_pointer{e});
break;
......@@ -266,7 +267,7 @@ void broker::cleanup(uint32_t reason) {
deref(); // release implicit reference count from middleman
}
void broker::launch(bool is_hidden, execution_unit*) {
void broker::launch(bool is_hidden, bool, execution_unit*) {
// add implicit reference count held by the middleman
ref();
is_registered(!is_hidden);
......@@ -334,8 +335,8 @@ void broker::close_all() {
std::vector<connection_handle> broker::connections() const {
std::vector<connection_handle> result;
for (auto& scribe : m_scribes) {
result.push_back(scribe.first);
for (auto& kvp : m_scribes) {
result.push_back(kvp.first);
}
return result;
}
......@@ -352,5 +353,15 @@ network::multiplexer& broker::backend() {
return m_mm.backend();
}
connection_handle broker::add_tcp_scribe(const std::string& hst, uint16_t prt) {
return backend().add_tcp_scribe(this, hst, prt);
}
std::pair<accept_handle, uint16_t>
broker::add_tcp_doorman(uint16_t port, const char* in, bool reuse_addr) {
return backend().add_tcp_doorman(this, port, in, reuse_addr);
}
} // namespace io
} // namespace caf
......@@ -43,6 +43,14 @@
using std::string;
namespace {
#ifdef CAF_MACOS
constexpr int no_sigpipe_flag = SO_NOSIGPIPE;
#else
constexpr int no_sigpipe_flag = MSG_NOSIGNAL;
#endif
} // namespace <anonymous>
namespace caf {
namespace io {
namespace network {
......@@ -478,7 +486,7 @@ namespace network {
} else {
// update event mask of existing entry
CAF_REQUIRE(*j == e.ptr);
i->events = e.mask;
i->events = static_cast<short>(e.mask);
}
if (e.ptr) {
auto remove_from_loop_if_needed = [&](int flag, operation flag_op) {
......@@ -503,7 +511,7 @@ int add_flag(operation op, int bf) {
return bf | input_mask;
case operation::write:
return bf | output_mask;
default:
case operation::propagate_error:
CAF_LOGF_ERROR("unexpected operation");
break;
}
......@@ -517,7 +525,7 @@ int del_flag(operation op, int bf) {
return bf & ~input_mask;
case operation::write:
return bf & ~output_mask;
default:
case operation::propagate_error:
CAF_LOGF_ERROR("unexpected operation");
break;
}
......@@ -549,7 +557,7 @@ void default_multiplexer::wr_dispatch_request(runnable* ptr) {
// on windows, we actually have sockets, otherwise we have file handles
# ifdef CAF_WINDOWS
::send(m_pipe.second, reinterpret_cast<socket_send_ptr>(&ptrval),
sizeof(ptrval), 0);
sizeof(ptrval), no_sigpipe_flag);
# else
auto unused = ::write(m_pipe.second, &ptrval, sizeof(ptrval));
static_cast<void>(unused);
......@@ -668,8 +676,8 @@ connection_handle default_multiplexer::add_tcp_scribe(broker* self,
CAF_LOG_TRACE("");
class impl : public broker::scribe {
public:
impl(broker* parent, default_socket&& s)
: scribe(parent, network::conn_hdl_from_socket(s)),
impl(broker* ptr, default_socket&& s)
: scribe(ptr, network::conn_hdl_from_socket(s)),
m_launched(false),
m_stream(s.backend()) {
m_stream.init(std::move(s));
......@@ -715,8 +723,8 @@ accept_handle default_multiplexer::add_tcp_doorman(broker* self,
CAF_REQUIRE(sock.fd() != network::invalid_native_socket);
class impl : public broker::doorman {
public:
impl(broker* parent, default_socket_acceptor&& s)
: doorman(parent, network::accept_hdl_from_socket(s)),
impl(broker* ptr, default_socket_acceptor&& s)
: doorman(ptr, network::accept_hdl_from_socket(s)),
m_acceptor(s.backend()) {
m_acceptor.init(std::move(s));
}
......@@ -759,10 +767,12 @@ accept_handle default_multiplexer::add_tcp_doorman(broker* self,
return add_tcp_doorman(self, default_socket_acceptor{*this, fd});
}
accept_handle default_multiplexer::add_tcp_doorman(broker* self,
uint16_t port,
const char* host) {
return add_tcp_doorman(self, new_ipv4_acceptor(port, host));
std::pair<accept_handle, uint16_t>
default_multiplexer::add_tcp_doorman(broker* self, uint16_t port,
const char* host, bool reuse_addr) {
auto acceptor = new_ipv4_acceptor(port, host, reuse_addr);
auto bound_port = acceptor.second;
return {add_tcp_doorman(self, std::move(acceptor.first)), bound_port};
}
/******************************************************************************
......@@ -814,7 +824,8 @@ bool read_some(size_t& result, native_socket fd, void* buf, size_t len) {
bool write_some(size_t& result, native_socket fd, const void* buf, size_t len) {
CAF_LOGF_TRACE(CAF_ARG(fd) << ", " << CAF_ARG(len));
auto sres = ::send(fd, reinterpret_cast<socket_send_ptr>(buf), len, 0);
auto sres = ::send(fd, reinterpret_cast<socket_send_ptr>(buf),
len, no_sigpipe_flag);
CAF_LOGF_DEBUG("tried to write " << len << " bytes to socket " << fd
<< ", send returned " << sres);
if (is_error(sres, true))
......@@ -850,11 +861,11 @@ event_handler::~event_handler() {
// nop
}
default_socket::default_socket(default_multiplexer& parent, native_socket fd)
: m_parent(parent),
m_fd(fd) {
CAF_LOG_TRACE(CAF_ARG(fd));
if (fd != invalid_native_socket) {
default_socket::default_socket(default_multiplexer& ref, native_socket sockfd)
: m_parent(ref),
m_fd(sockfd) {
CAF_LOG_TRACE(CAF_ARG(sockfd));
if (sockfd != invalid_native_socket) {
// enable nonblocking IO & disable Nagle's algorithm
nonblocking(m_fd, true);
tcp_nodelay(m_fd, true);
......@@ -926,7 +937,8 @@ native_socket new_ipv4_connection_impl(const std::string& host, uint16_t port) {
static_cast<size_t>(server->h_length));
serv_addr.sin_port = htons(port);
CAF_LOGF_DEBUG("call connect()");
if (connect(fd, (const sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) {
if (connect(fd, reinterpret_cast<const sockaddr*>(&serv_addr),
sizeof(serv_addr)) != 0) {
CAF_LOGF_ERROR("could not connect to to " << host << " on port " << port);
throw network_error("could not connect to host");
}
......@@ -939,9 +951,9 @@ default_socket new_ipv4_connection(const std::string& host, uint16_t port) {
return default_socket{backend, new_ipv4_connection_impl(host, port)};
}
native_socket new_ipv4_acceptor_impl(uint16_t port, const char* addr) {
CAF_LOGF_TRACE(CAF_ARG(port)
<< ", addr = " << (addr ? addr : "nullptr"));
std::pair<native_socket, uint16_t>
new_ipv4_acceptor_impl(uint16_t port, const char* addr, bool reuse_addr) {
CAF_LOGF_TRACE(CAF_ARG(port) << ", addr = " << (addr ? addr : "nullptr"));
# ifdef CAF_WINDOWS
// make sure TCP has been initialized via WSAStartup
get_multiplexer_singleton();
......@@ -952,13 +964,15 @@ native_socket new_ipv4_acceptor_impl(uint16_t port, const char* addr) {
}
// sguard closes the socket in case of exception
socket_guard sguard(fd);
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<setsockopt_ptr>(&on), sizeof(on)) < 0) {
throw_io_failure("unable to set SO_REUSEADDR");
if (reuse_addr) {
int on = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<setsockopt_ptr>(&on), sizeof(on)) < 0) {
throw_io_failure("unable to set SO_REUSEADDR");
}
}
struct sockaddr_in serv_addr;
memset((char*)&serv_addr, 0, sizeof(serv_addr));
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
if (!addr) {
serv_addr.sin_addr.s_addr = INADDR_ANY;
......@@ -966,22 +980,34 @@ native_socket new_ipv4_acceptor_impl(uint16_t port, const char* addr) {
throw network_error("invalid IPv4 address");
}
serv_addr.sin_port = htons(port);
if (bind(fd, (sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
if (bind(fd, reinterpret_cast<sockaddr*>(&serv_addr),
sizeof(serv_addr)) < 0) {
throw bind_failure(last_socket_error_as_string());
}
if (listen(fd, SOMAXCONN) != 0) {
throw network_error("listen() failed");
throw network_error("listen() failed: " + last_socket_error_as_string());
}
if (port == 0) {
socklen_t len = sizeof(serv_addr);
if (getsockname(fd, reinterpret_cast<sockaddr*>(&serv_addr), &len) < 0) {
throw network_error("getsockname(): " + last_socket_error_as_string());
}
}
// ok, no exceptions so far
sguard.release();
CAF_LOGF_DEBUG("sockfd = " << fd);
return fd;
CAF_LOGF_DEBUG("sockfd = " << fd << ", port = " << ntohs(serv_addr.sin_port));
return {fd, ntohs(serv_addr.sin_port)};
}
default_socket_acceptor new_ipv4_acceptor(uint16_t port, const char* addr) {
std::pair<default_socket_acceptor, uint16_t>
new_ipv4_acceptor(uint16_t port, const char* addr, bool reuse) {
CAF_LOGF_TRACE(CAF_ARG(port) << ", addr = " << (addr ? addr : "nullptr"));
auto& backend = get_multiplexer_singleton();
return default_socket_acceptor{backend, new_ipv4_acceptor_impl(port, addr)};
auto acceptor = new_ipv4_acceptor_impl(port, addr, reuse);
auto bound_port = acceptor.second;
CAF_REQUIRE(port == 0 || bound_port == port);
return {default_socket_acceptor{backend, std::move(acceptor.first)},
bound_port};
}
} // namespace network
......
......@@ -35,7 +35,6 @@
#include "caf/io/middleman.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/io/remote_actor_proxy.hpp"
#include "caf/detail/logging.hpp"
#include "caf/detail/ripemd_160.hpp"
......@@ -117,7 +116,7 @@ deserialize_impl(T& dm, deserializer* source) {
template <class T>
class uti_impl : public uniform_type_info {
public:
uti_impl() : m_native(&typeid(T)), m_name(detail::demangle<T>()) {
uti_impl(const char* tname) : m_native(&typeid(T)), m_name(tname) {
// nop
}
......@@ -165,23 +164,10 @@ class uti_impl : public uniform_type_info {
std::string m_name;
};
template <class... Ts>
struct announce_helper;
template <class T, class... Ts>
struct announce_helper<T, Ts...> {
static inline void exec() {
announce(typeid(T), uniform_type_info_ptr{new uti_impl<T>});
announce_helper<Ts...>::exec();
}
};
template <>
struct announce_helper<> {
static inline void exec() {
// end of recursion
}
};
template <class T>
void do_announce(const char* tname) {
announce(typeid(T), uniform_type_info_ptr{new uti_impl<T>(tname)});
}
} // namespace <anonymous>
......@@ -209,11 +195,16 @@ void middleman::initialize() {
});
m_backend->thread_id(m_thread.get_id());
// announce io-related types
announce_helper<new_data_msg, new_connection_msg,
acceptor_closed_msg, connection_closed_msg,
accept_handle, acceptor_closed_msg,
connection_closed_msg, connection_handle,
new_connection_msg, new_data_msg>::exec();
do_announce<new_data_msg>("caf::io::new_data_msg");
do_announce<new_connection_msg>("caf::io::new_connection_msg");
do_announce<acceptor_closed_msg>("caf::io::acceptor_closed_msg");
do_announce<connection_closed_msg>("caf::io::connection_closed_msg");
do_announce<accept_handle>("caf::io::accept_handle");
do_announce<acceptor_closed_msg>("caf::io::acceptor_closed_msg");
do_announce<connection_closed_msg>("caf::io::connection_closed_msg");
do_announce<connection_handle>("caf::io::connection_handle");
do_announce<new_connection_msg>("caf::io::new_connection_msg");
do_announce<new_data_msg>("caf::io::new_data_msg");
}
void middleman::stop() {
......
......@@ -17,39 +17,57 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include <future>
#include "caf/io/publish.hpp"
#include "caf/send.hpp"
#include "caf/actor_cast.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/actor_registry.hpp"
#include "caf/io/publish.hpp"
#include "caf/io/middleman.hpp"
#include "caf/io/basp_broker.hpp"
namespace caf {
namespace io {
void publish_impl(abstract_actor_ptr whom, uint16_t port, const char* in) {
uint16_t publish_impl(abstract_actor_ptr whom, uint16_t port,
const char* in, bool reuse_addr) {
using namespace detail;
auto mm = middleman::instance();
std::promise<bool> res;
scoped_actor self;
actor selfhdl = self;
mm->run_later([&] {
auto bro = mm->get_named_broker<basp_broker>(atom("_BASP"));
try {
auto hdl = mm->backend().add_tcp_doorman(bro.get(), port, in);
bro->add_published_actor(hdl, whom, port);
mm->notify<hook::actor_published>(whom->address(), port);
res.set_value(true);
auto hdl = mm->backend().add_tcp_doorman(bro.get(), port, in, reuse_addr);
bro->add_published_actor(std::move(hdl.first), whom, hdl.second);
mm->notify<hook::actor_published>(whom->address(), hdl.second);
anon_send(selfhdl, atom("OK"), hdl.second);
}
catch (bind_failure& e) {
anon_send(selfhdl, atom("BIND_FAIL"), e.what());
}
catch (...) {
res.set_exception(std::current_exception());
catch (network_error& e) {
anon_send(selfhdl, atom("ERROR"), e.what());
}
});
uint16_t bound_port = 0;
// block caller and re-throw exception here in case of an error
res.get_future().get();
self->receive(
on(atom("OK"), arg_match) >> [&](uint16_t p) {
bound_port = p;
},
on(atom("BIND_FAIL"), arg_match) >> [](std::string& str) {
throw bind_failure(std::move(str));
},
on(atom("ERROR"), arg_match) >> [](std::string& str) {
throw network_error(std::move(str));
}
);
return bound_port;
}
} // namespace io
......
......@@ -39,10 +39,10 @@ struct group_nameserver : event_based_actor {
} // namespace <anonymous>
void publish_local_groups(uint16_t port, const char* addr) {
uint16_t publish_local_groups(uint16_t port, const char* addr) {
auto gn = spawn<group_nameserver, hidden>();
try {
publish(gn, port, addr);
return publish(gn, port, addr);
}
catch (std::exception&) {
anon_send_exit(gn, exit_reason::user_shutdown);
......
......@@ -27,6 +27,8 @@
#include <cstdint>
#include <algorithm>
#include "caf/send.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/binary_deserializer.hpp"
......@@ -39,22 +41,39 @@
namespace caf {
namespace io {
abstract_actor_ptr remote_actor_impl(const std::set<std::string>& ifs,
abstract_actor_ptr remote_actor_impl(std::set<std::string> ifs,
const std::string& host, uint16_t port) {
auto mm = middleman::instance();
std::promise<abstract_actor_ptr> res;
basp_broker::client_handshake_data hdata{invalid_node_id, &res, &ifs};
scoped_actor self;
actor selfhdl = self;
basp_broker::client_handshake_data hdata{invalid_node_id,
selfhdl, std::move(ifs)};
mm->run_later([&] {
std::string err;
try {
auto bro = mm->get_named_broker<basp_broker>(atom("_BASP"));
auto hdl = mm->backend().add_tcp_scribe(bro.get(), host, port);
bro->init_client(hdl, &hdata);
}
catch (...) {
res.set_exception(std::current_exception());
catch (std::exception& e) {
err = e.what();
}
// accessing variables from the outer scope inside the
// catch block triggers a silly compiler error on GCC 4.7
if (!err.empty()) {
anon_send(selfhdl, atom("ERROR"), std::move(err));
}
});
return res.get_future().get();
abstract_actor_ptr result;
self->receive(
on(atom("OK"), arg_match) >> [&](const actor& res) {
result = actor_cast<abstract_actor_ptr>(res);
},
on(atom("ERROR"), arg_match) >> [](std::string& str) {
throw network_error(std::move(str));
}
);
return result;
}
} // namespace io
......
Subproject commit 5a11746201ff5dca5306278f1b07384d0cb4345a
Subproject commit 1c3bd281611d3b56f56a53b2f40291981a638cf3
Subproject commit abcc82a94eb67ee453365dc3b158fc523dce9015
Subproject commit fcf305450e030116ff4446576596ad11cdf5d47d
......@@ -7,13 +7,21 @@ All functions shown in this section can be accessed by including the header \lst
\subsection{Publishing of Actors}
\begin{lstlisting}
void publish(actor whom, std::uint16_t port, const char* addr = 0)
uint16_t publish(actor whom, uint16_t port, const char* addr = 0, bool reuse_addr = false)
\end{lstlisting}
The function \lstinline^publish^ binds an actor to a given port.
It throws \lstinline^network_error^ if socket related errors occur or \lstinline^bind_failure^ if the specified port is already in use.
The optional \lstinline^addr^ parameter can be used to listen only to the given IP address.
To choose the next high-level port available available for binding, one can specify \lstinline^port == 0^ and retrieves the bound port as return value.
The return value is equal to \lstinline^port^ if \lstinline^port != 0^.
The function throws \lstinline^network_error^ if socket related errors occur or \lstinline^bind_failure^ if the specified port is already in use.
The optional \lstinline^addr^ parameter can be used to listen only to the given address.
Otherwise, the actor accepts all incoming connections (\lstinline^INADDR_ANY^).
The flag \lstinline^reuse_addr^ controls the behavior when binding an IP
address to a port, with the same semantics as the BSD socket flag \lstinline^SO_REUSEADDR^.
For example, if \lstinline^reuse_addr = false^, binding two sockets to 0.0.0.0:42 and 10.0.0.1:42 will fail with \texttt{EADDRINUSE} since 0.0.0.0 includes 10.0.0.1.
With \lstinline^reuse_addr = true^ binding would succeed because 10.0.0.1 and
0.0.0.0 are not literally equal addresses.
\begin{lstlisting}
publish(self, 4242);
......
Subproject commit bd18ef316da3cb23a9a1eed3a127d093e510e6aa
Subproject commit 17c4df0035d5c22e73931d2e8c8406b1ce31e31b
......@@ -29,7 +29,7 @@ add_unit_test(spawn ping_pong.cpp)
add_unit_test(simple_reply_response)
add_unit_test(serial_reply)
add_unit_test(or_else)
add_unit_test(continuation)
add_unit_test(either)
add_unit_test(constructor_attach)
add_unit_test(custom_exception_handler)
add_unit_test(typed_spawn)
......
......@@ -2,6 +2,7 @@
#include "test.hpp"
#include "caf/all.hpp"
#include "caf/string_algorithms.hpp"
using namespace std;
using namespace caf;
......@@ -10,9 +11,13 @@ namespace {
atomic<size_t> s_error_count{0};
}
size_t caf_error_count() { return s_error_count; }
size_t caf_error_count() {
return s_error_count;
}
void caf_inc_error_count() { ++s_error_count; }
void caf_inc_error_count() {
++s_error_count;
}
string caf_fill4(size_t value) {
string result = to_string(value);
......@@ -68,3 +73,29 @@ void set_default_test_settings() {
set_terminate(verbose_terminate);
cout.unsetf(ios_base::unitbuf);
}
std::thread run_program_impl(actor rc, const char* cpath, vector<string> args) {
string path = cpath;
replace_all(path, "'", "\\'");
ostringstream oss;
oss << "'" << path << "'";
for (auto& arg : args) {
oss << " " << arg;
}
oss << " 2>&1";
string cmdstr = oss.str();
return std::thread([cmdstr, rc] {
string output;
auto fp = popen(cmdstr.c_str(), "r");
if (!fp) {
CAF_PRINTERR("FATAL: command line failed: " << cmdstr);
abort();
}
char buf[512];
while (fgets(buf, sizeof(buf), fp)) {
output += buf;
}
pclose(fp);
anon_send(rc, output);
});
}
......@@ -3,6 +3,7 @@
#include <vector>
#include <string>
#include <thread>
#include <cstring>
#include <cstddef>
#include <sstream>
......@@ -42,7 +43,7 @@ void caf_unexpected_timeout(const char* file, size_t line);
#define CAF_PRINT(message) CAF_PRINTC(__FILE__, __LINE__, message)
#if CAF_LOG_LEVEL > 1
#if defined(CAF_LOG_LEVEL) && CAF_LOG_LEVEL > 1
#define CAF_PRINTERRC(fname, linenum, msg) \
CAF_LOGF_ERROR(CAF_STREAMIFY(fname, linenum, msg)); \
std::cerr << "ERROR: " << CAF_STREAMIFY(fname, linenum, msg) << std::endl
......@@ -129,11 +130,16 @@ inline void caf_check_value(V1 v1, V2 v2, const char* fname, size_t line,
#define CAF_VERBOSE_EVAL(LineOfCode) \
CAF_PRINT(#LineOfCode << " = " << (LineOfCode));
#define CAF_TEST(testname) \
auto caf_test_scope_guard = ::caf::detail::make_scope_guard([] { \
std::cout << caf_error_count() << " error(s) detected" << std::endl; \
}); \
set_default_test_settings(); \
#define CAF_TEST(testname) \
::std::thread([] { \
::std::this_thread::sleep_for(std::chrono::seconds(10)); \
::std::cerr << "WATCHDOG: unit test did finish within 10s, abort\n"; \
::abort(); \
}).detach(); \
auto caf_test_scope_guard = ::caf::detail::make_scope_guard([] { \
std::cout << caf_error_count() << " error(s) detected" << std::endl; \
}); \
set_default_test_settings(); \
CAF_LOGF_INFO("run unit test " << #testname)
#define CAF_TEST_RESULT() ((caf_error_count() == 0) ? 0 : -1)
......@@ -199,10 +205,25 @@ caf::optional<T> spro(const std::string& str) {
return caf::none;
}
std::vector<std::string> split(const std::string& str, char delim = ' ',
bool keep_empties = true);
std::thread run_program_impl(caf::actor, const char*, std::vector<std::string>);
std::map<std::string, std::string> get_kv_pairs(int argc, char** argv,
int begin = 1);
template <class T>
typename std::enable_if<
std::is_arithmetic<T>::value,
std::string
>::type
convert_to_str(T value) {
return std::to_string(value);
}
inline std::string convert_to_str(std::string value) {
return std::move(value);
}
template <class... Ts>
std::thread run_program(caf::actor listener, const char* path, Ts&&... args) {
std::vector<std::string> vec{convert_to_str(std::forward<Ts>(args))...};
return run_program_impl(listener, path, std::move(vec));
}
#endif // TEST_HPP
......@@ -9,17 +9,17 @@ using std::cout;
using std::endl;
using namespace caf;
std::atomic<long> s_dudes;
namespace {
std::atomic<long> s_testees;
} // namespace <anonymous>
class dude : public event_based_actor {
class testee : public event_based_actor {
public:
dude() {
++s_dudes;
testee() {
++s_testees;
}
~dude() {
--s_dudes;
}
~testee();
behavior make_behavior() {
return {
......@@ -30,52 +30,65 @@ class dude : public event_based_actor {
}
};
behavior linking_dude(event_based_actor* self, const actor& other_dude) {
CAF_CHECKPOINT();
self->trap_exit(true);
self->link_to(other_dude);
anon_send_exit(other_dude, exit_reason::user_shutdown);
CAF_CHECKPOINT();
return {
[self](const exit_msg&) {
CAF_CHECKPOINT();
self->send(self, atom("check"));
},
on(atom("check")) >> [self] {
// make sure dude's dtor has been called
CAF_CHECK_EQUAL(s_dudes.load(), 0);
self->quit();
}
};
testee::~testee() {
// avoid weak-vtables warning
--s_testees;
}
behavior monitoring_dude(event_based_actor* self, actor other_dude) {
template <class ExitMsgType>
behavior tester(event_based_actor* self, const actor& aut) {
CAF_CHECKPOINT();
if (std::is_same<ExitMsgType, exit_msg>::value) {
self->trap_exit(true);
self->link_to(aut);
} else {
self->monitor(aut);
}
CAF_CHECKPOINT();
self->monitor(other_dude);
anon_send_exit(other_dude, exit_reason::user_shutdown);
anon_send_exit(aut, exit_reason::user_shutdown);
CAF_CHECKPOINT();
return {
[self](const down_msg&) {
CAF_CHECKPOINT();
self->send(self, atom("check"));
[self](const ExitMsgType&) {
// must be still alive at this point
CAF_CHECK_EQUAL(s_testees.load(), 1);
// testee might be still running its cleanup code in
// another worker thread; by waiting some milliseconds, we make sure
// testee had enough time to return control to the scheduler
// which in turn destroys it by dropping the last remaining reference
self->delayed_send(self, std::chrono::milliseconds(30), atom("check"));
},
on(atom("check")) >> [self] {
// make sure dude's dtor has been called
CAF_CHECK_EQUAL(s_dudes.load(), 0);
CAF_CHECK_EQUAL(s_testees.load(), 0);
self->quit();
}
};
}
void test_actor_lifetime() {
spawn(monitoring_dude, spawn<dude>());
template <spawn_options O1, spawn_options O2>
void run() {
CAF_PRINT("run test using links");
spawn<O1>(tester<exit_msg>, spawn<testee, O2>());
await_all_actors_done();
spawn(linking_dude, spawn<dude>());
CAF_PRINT("run test using monitors");
spawn<O1>(tester<down_msg>, spawn<testee, O2>());
await_all_actors_done();
}
void test_actor_lifetime() {
CAF_PRINT("run<no_spawn_options, no_spawn_options>");
run<no_spawn_options, no_spawn_options>();
CAF_PRINT("run<detached, no_spawn_options>");
run<detached, no_spawn_options>();
CAF_PRINT("run<no_spawn_options, detached>");
run<no_spawn_options, detached>();
CAF_PRINT("run<detached, detached>");
run<detached, detached>();
}
int main() {
CAF_TEST(test_actor_lifetime);
test_actor_lifetime();
CAF_CHECK_EQUAL(s_testees.load(), 0);
return CAF_TEST_RESULT();
}
......@@ -21,6 +21,29 @@ using namespace caf;
namespace {
constexpr auto s_foo = atom("FooBar");
using abc_atom = atom_constant<atom("abc")>;
}
using testee = typed_actor<replies_to<abc_atom>::with<int>>;
testee::behavior_type testee_impl(testee::pointer self) {
return {
[=](abc_atom) {
self->quit();
return 42;
}
};
}
void test_typed_atom_interface() {
scoped_actor self;
auto tst = spawn_typed(testee_impl);
self->sync_send(tst, abc_atom()).await(
[](int i) {
CAF_CHECK_EQUAL(i, 42);
},
CAF_UNEXPECTED_MSG_CB_REF(self)
);
}
template <atom_value AtomValue, class... Types>
......@@ -36,7 +59,6 @@ struct send_to_self {
m_self->send(m_self, std::forward<Ts>(args)...);
}
blocking_actor* m_self;
};
int main() {
......@@ -53,22 +75,19 @@ int main() {
send_to_self f{self.get()};
f(atom("foo"), static_cast<uint32_t>(42));
f(atom(":Attach"), atom(":Baz"), "cstring");
// m(atom("b"), atom("a"), atom("c"), 23.f, 1.f, 1.f);
f(1.f);
f(atom("a"), atom("b"), atom("c"), 23.f);
bool matched_pattern[3] = {false, false, false};
int i = 0;
CAF_CHECKPOINT();
for (i = 0; i < 3; ++i) {
self->receive(
//}
// self->receive_for(i, 3) (
on(atom("foo"), arg_match) >> [&](uint32_t value) {
CAF_CHECKPOINT();
matched_pattern[0] = true;
CAF_CHECK_EQUAL(value, 42);
},
on(atom(":Attach"), atom(":Baz"), arg_match) >>
[&](const string& str) {
on(atom(":Attach"), atom(":Baz"), arg_match) >> [&](const string& str) {
CAF_CHECKPOINT();
matched_pattern[1] = true;
CAF_CHECK_EQUAL(str, "cstring");
......@@ -83,6 +102,17 @@ int main() {
self->receive(
// "erase" message { atom("b"), atom("a"), atom("c"), 23.f }
others() >> CAF_CHECKPOINT_CB(),
after(std::chrono::seconds(0)) >> CAF_UNEXPECTED_TOUT_CB());
after(std::chrono::seconds(0)) >> CAF_UNEXPECTED_TOUT_CB()
);
atom_value x = atom("abc");
atom_value y = abc_atom();
CAF_CHECK_EQUAL(x, y);
auto msg = make_message(abc_atom());
self->send(self, msg);
self->receive(
on(atom("abc")) >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
test_typed_atom_interface();
return CAF_TEST_RESULT();
}
......@@ -24,6 +24,8 @@
#include "caf/all.hpp"
#include "caf/io/all.hpp"
#include "caf/string_algorithms.hpp"
using namespace std;
using namespace caf;
using namespace caf::io;
......@@ -136,40 +138,34 @@ behavior peer_acceptor_fun(broker* self, const actor& buddy) {
self->fork(peer_fun, msg.handle, buddy);
self->quit();
},
on(atom("publish")) >> [=] {
return self->add_tcp_doorman(0, "127.0.0.1").second;
},
others() >> CAF_UNEXPECTED_MSG_CB(self)
};
}
void run_server(bool spawn_client, const char* bin_path) {
auto p = spawn(pong);
uint16_t port = 4242;
bool done = false;
while (!done) {
try {
io::spawn_io_server(peer_acceptor_fun, port, p);
scoped_actor self;
auto serv = io::spawn_io(peer_acceptor_fun, spawn(pong));
self->sync_send(serv, atom("publish")).await(
[&](uint16_t port) {
CAF_CHECKPOINT();
cout << "server is running on port " << port << endl;
if (spawn_client) {
auto child = run_program(self, bin_path, "-c", port);
CAF_CHECKPOINT();
child.join();
}
}
catch (bind_failure&) {
// try next port
++port;
);
self->await_all_other_actors_done();
self->receive(
[](const std::string& output) {
cout << endl << endl << "*** output of client program ***"
<< endl << output << endl;
}
done = true;
}
CAF_CHECKPOINT();
cout << "server is running on port " << port << endl;
if (spawn_client) {
ostringstream oss;
oss << bin_path << " -c " << port << to_dev_null;
thread child{[&oss] {
CAF_LOGC_TRACE("NONE", "main$thread_launcher", "");
auto cmdstr = oss.str();
if (system(cmdstr.c_str()) != 0) {
CAF_PRINTERR("FATAL: command failed: " << cmdstr);
abort();
}
}};
CAF_CHECKPOINT();
child.join();
}
);
}
int main(int argc, char** argv) {
......@@ -189,7 +185,6 @@ int main(int argc, char** argv) {
},
on() >> [&] {
run_server(true, argv[0]);
},
others() >> [&] {
cerr << "usage: " << argv[0] << " [-c PORT]" << endl;
......
#include "test.hpp"
#include "caf/all.hpp"
using namespace caf;
behavior simple_mirror(event_based_actor* self) {
return {
others() >> [=] {
return self->last_dequeued();
}
};
}
void test_continuation() {
auto mirror = spawn(simple_mirror);
spawn([=](event_based_actor* self) {
self->sync_send(mirror, 42).then(
on(42) >> [] {
return "fourty-two";
}
).continue_with(
[=](const std::string& ref) {
CAF_CHECK_EQUAL(ref, "fourty-two");
return 4.2f;
}
).continue_with(
[=](float f) {
CAF_CHECK_EQUAL(f, 4.2f);
self->send_exit(mirror, exit_reason::user_shutdown);
self->quit();
}
);
});
await_all_actors_done();
}
int main() {
CAF_TEST(test_continuation);
test_continuation();
return CAF_TEST_RESULT();
}
......@@ -6,6 +6,7 @@ using namespace caf;
class exception_testee : public event_based_actor {
public:
~exception_testee();
exception_testee() {
set_exception_handler([](const std::exception_ptr&) -> optional<uint32_t> {
return exit_reason::user_defined + 2;
......@@ -20,6 +21,11 @@ class exception_testee : public event_based_actor {
}
};
exception_testee::~exception_testee() {
// avoid weak-vtables warning
}
void test_custom_exception_handler() {
auto handler = [](const std::exception_ptr& eptr) -> optional<uint32_t> {
try {
......
#include "test.hpp"
#include "caf/all.hpp"
using namespace caf;
using foo = typed_actor<replies_to<int>::with_either<int>::or_else<float>>;
foo::behavior_type my_foo() {
return {
[](int arg) -> either<int>::or_else<float> {
if (arg == 42) {
return 42;
}
return static_cast<float>(arg);
}
};
}
void test_either() {
auto f1 = []() -> either<int>::or_else<float> {
return 42;
};
auto f2 = []() -> either<int>::or_else<float> {
return 42.f;
};
auto f3 = [](bool flag) -> either<int, int>::or_else<float, float> {
if (flag) {
return {1, 2};
}
return {3.f, 4.f};
};
f1();
f2();
f3(true);
f3(false);
either<int>::or_else<float> x1{4};
either<int>::or_else<float> x2{4.f};
auto mf = spawn_typed(my_foo);
scoped_actor self;
self->sync_send(mf, 42).await(
[](int val) {
CAF_CHECK_EQUAL(val, 42);
},
[](float) {
CAF_FAILURE("expected an integer");
}
);
self->sync_send(mf, 10).await(
[](int) {
CAF_FAILURE("expected a float");
},
[](float val) {
CAF_CHECK_EQUAL(val, 10.f);
}
);
}
int main() {
CAF_TEST(test_either);
test_either();
return CAF_TEST_RESULT();
}
#include <tuple>
#include <vector>
#include "test.hpp"
......@@ -12,11 +13,11 @@ class fixed_stack : public sb_actor<fixed_stack> {
fixed_stack(size_t max) : max_size(max) {
full = (
on(atom("push"), arg_match) >> [=](int) { /* discard */ },
on(atom("pop")) >> [=]() -> cow_tuple<atom_value, int> {
on(atom("pop")) >> [=]() -> message {
auto result = data.back();
data.pop_back();
become(filled);
return {atom("ok"), result};
return make_message(atom("ok"), result);
}
);
filled = (
......@@ -24,11 +25,11 @@ class fixed_stack : public sb_actor<fixed_stack> {
data.push_back(what);
if (data.size() == max_size) become(full);
},
on(atom("pop")) >> [=]() -> cow_tuple<atom_value, int> {
on(atom("pop")) >> [=]() -> message {
auto result = data.back();
data.pop_back();
if (data.empty()) become(empty);
return {atom("ok"), result};
return make_message(atom("ok"), result);
}
);
empty = (
......@@ -41,6 +42,9 @@ class fixed_stack : public sb_actor<fixed_stack> {
}
);
}
~fixed_stack();
private:
size_t max_size = 10;
std::vector<int> data;
......@@ -50,6 +54,11 @@ class fixed_stack : public sb_actor<fixed_stack> {
behavior& init_state = empty;
};
fixed_stack::~fixed_stack() {
// avoid weak-vtables warning
}
void test_fixed_stack_actor() {
scoped_actor self;
auto st = spawn<fixed_stack>(size_t{10});
......
......@@ -7,11 +7,10 @@
#include "caf/uniform_type_info.hpp"
#include "caf/detail/ctm.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/demangle.hpp"
using std::cout;
using std::endl;
using std::is_same;
......@@ -29,6 +28,26 @@ int main() {
CAF_TEST(test_metaprogramming);
CAF_CHECK((ctm<type_list<int, float, double>, type_list<double, int, float>>::value));
CAF_CHECK((! ctm<type_list<int, float, double>, type_list<double, int, float, int>>::value));
CAF_CHECK((! ctm<type_list<int, float, double>, type_list<>>::value));
CAF_CHECK((! ctm<type_list<>, type_list<double, int, float, int>>::value));
using if1 = type_list<replies_to<int, double>::with<void>,
replies_to<int>::with<int>>;
using if2 = type_list<replies_to<int>::with<int>,
replies_to<int, double>::with<void>>;
using if3 = type_list<replies_to<int, double>::with<void>>;
using if4 = type_list<replies_to<int>::with<skip_message_t>,
replies_to<int, double>::with<void>>;
CAF_CHECK((ctm<if1, if2>::value));
CAF_CHECK((! ctm<if1, if3>::value));
CAF_CHECK((! ctm<if2, if3>::value));
CAF_CHECK((ctm<if1, if4>::value));
CAF_CHECK((ctm<if2, if4>::value));
using l1 = type_list<int, float, std::string>;
using r1 = typename tl_reverse<l1>::type;
......@@ -53,10 +72,7 @@ int main() {
using il0 = int_list<0, 1, 2, 3, 4, 5>;
using il1 = int_list<4, 5>;
using il2 = typename il_right<il0, 2>::type;
CAF_CHECK_VERBOSE((is_same<il2, il1>::value),
"il_right<il0, 2> returned "
<< detail::demangle<il2>()
<< "expected: " << detail::demangle<il1>());
CAF_CHECK((is_same<il2, il1>::value));
/* test tl_is_strict_subset */ {
using list_a = type_list<int, float, double>;
......
......@@ -51,6 +51,44 @@ void spawn5_server_impl(event_based_actor* self, actor client, group grp) {
CAF_PRINT("monitor actor: " << to_string(a));
self->monitor(a);
}
CAF_PRINT("wait for reflected messages");
// receive seven reply messages (2 local, 5 remote)
auto replies = std::make_shared<int>(0);
self->become(
on("Hello reflectors!", 5.0) >> [=] {
if (++*replies == 7) {
CAF_PRINT("wait for DOWN messages");
auto downs = std::make_shared<int>(0);
self->become(
[=](const down_msg& dm) {
if (dm.reason != exit_reason::normal) {
CAF_PRINTERR("reflector exited for non-normal exit reason!");
}
if (++*downs == 5) {
CAF_CHECKPOINT();
self->send(client, atom("Spawn5Done"));
self->quit();
}
},
others() >> [=] {
CAF_UNEXPECTED_MSG(self);
self->quit(exit_reason::user_defined);
},
after(chrono::seconds(2)) >> [=] {
CAF_UNEXPECTED_TOUT();
CAF_LOGF_ERROR("did only receive " << *downs << " down messages");
self->quit(exit_reason::user_defined);
}
);
}
},
after(std::chrono::seconds(2)) >> [=] {
CAF_UNEXPECTED_TOUT();
CAF_LOGF_ERROR("did only receive " << *replies
<< " responses to 'Hello reflectors!'");
self->quit(exit_reason::user_defined);
}
);
},
others() >> [=] {
CAF_UNEXPECTED_MSG(self);
......@@ -58,48 +96,9 @@ void spawn5_server_impl(event_based_actor* self, actor client, group grp) {
},
after(chrono::seconds(10)) >> [=] {
CAF_UNEXPECTED_TOUT();
self->quit(exit_reason::user_defined);
}
).continue_with([=] {
CAF_PRINT("wait for reflected messages");
// receive seven reply messages (2 local, 5 remote)
auto replies = std::make_shared<int>(0);
self->become(
on("Hello reflectors!", 5.0) >> [=] {
if (++*replies == 7) {
CAF_PRINT("wait for DOWN messages");
auto downs = std::make_shared<int>(0);
self->become(
[=](const down_msg& dm) {
if (dm.reason != exit_reason::normal) {
CAF_PRINTERR("reflector exited for non-normal exit reason!");
}
if (++*downs == 5) {
CAF_CHECKPOINT();
self->send(client, atom("Spawn5Done"));
self->quit();
}
},
others() >> [=] {
CAF_UNEXPECTED_MSG(self);
self->quit(exit_reason::user_defined);
},
after(chrono::seconds(2)) >> [=] {
CAF_UNEXPECTED_TOUT();
CAF_LOGF_ERROR("did only receive " << *downs << " down messages");
self->quit(exit_reason::user_defined);
}
);
}
},
after(std::chrono::seconds(2)) >> [=] {
CAF_UNEXPECTED_TOUT();
CAF_LOGF_ERROR("did only receive " << *replies
<< " responses to 'Hello reflectors!'");
self->quit(exit_reason::user_defined);
}
);
});
self->quit(exit_reason::user_defined);
}
);
}
// receive seven reply messages (2 local, 5 remote)
......@@ -328,60 +327,31 @@ class server : public event_based_actor {
};
template <class F>
uint16_t at_some_port(uint16_t first_port, F fun) {
auto port = first_port;
for (;;) {
try {
fun(port);
return port;
}
catch (bind_failure&) {
// try next port
++port;
}
}
}
void test_remote_actor(std::string app_path, bool run_remote_actor) {
void test_remote_actor(const char* app_path, bool run_remote_actor) {
scoped_actor self;
auto serv = self->spawn<server, monitored>();
auto publish_serv = [=](uint16_t p) {
io::publish(serv, p, "127.0.0.1");
};
auto publish_groups = [](uint16_t p) {
io::publish_local_groups(p);
};
// publish on two distinct ports and use the latter one afterwards
auto port0 = at_some_port(4242, publish_serv);
CAF_LOGF_INFO("first publish succeeded on port " << port0);
auto port = at_some_port(port0 + 1, publish_serv);
CAF_PRINT("running on port " << port);
CAF_LOGF_INFO("running on port " << port);
auto port1 = io::publish(serv, 0, "127.0.0.1");
CAF_CHECK(port1 > 0);
CAF_PRINT("first publish succeeded on port " << port1);
auto port2 = io::publish(serv, 0, "127.0.0.1");
CAF_CHECK(port2 > 0);
CAF_PRINT("second publish succeeded on port " << port1);
CAF_LOGF_INFO("running on port " << port2);
// publish local groups as well
auto gport = at_some_port(port + 1, publish_groups);
auto gport = io::publish_local_groups(0);
CAF_CHECK(gport > 0);
// check whether accessing local actors via io::remote_actors works correctly,
// i.e., does not return a proxy instance
auto serv2 = io::remote_actor("127.0.0.1", port);
auto serv2 = io::remote_actor("127.0.0.1", port2);
CAF_CHECK(serv2 != invalid_actor && !serv2->is_remote());
CAF_CHECK(serv == serv2);
thread child;
ostringstream oss;
oss << app_path << " -c " << port << " " << port0 << " " << gport;
if (run_remote_actor) {
oss << to_dev_null;
// execute client_part() in a separate process,
// connected via localhost socket
child = thread([&oss]() {
CAF_LOGC_TRACE("NONE", "main$thread_launcher", "");
string cmdstr = oss.str();
if (system(cmdstr.c_str()) != 0) {
CAF_PRINTERR("FATAL: command \"" << cmdstr << "\" failed!");
abort();
}
});
child = run_program(self, app_path, "-c", port2, port1, gport);
} else {
CAF_PRINT("please run client: " << oss.str());
CAF_PRINT("please run client with: "
<< "-c " << port2 << " " << port1 << " " << gport);
}
CAF_CHECKPOINT();
self->receive(
......@@ -392,16 +362,24 @@ void test_remote_actor(std::string app_path, bool run_remote_actor) {
);
// wait until separate process (in sep. thread) finished execution
CAF_CHECKPOINT();
if (run_remote_actor) child.join();
CAF_CHECKPOINT();
self->await_all_other_actors_done();
CAF_CHECKPOINT();
if (run_remote_actor) {
child.join();
self->receive(
[](const std::string& output) {
cout << endl << endl << "*** output of client program ***"
<< endl << output << endl;
}
);
}
}
} // namespace <anonymous>
int main(int argc, char** argv) {
CAF_TEST(test_remote_actor);
announce<actor_vector>();
announce<actor_vector>("actor_vector");
cout << "this node is: " << to_string(caf::detail::singletons::get_node_id())
<< endl;
message_builder{argv + 1, argv + argc}.apply({
......
......@@ -77,6 +77,10 @@ bool operator==(const raw_struct& lhs, const raw_struct& rhs) {
}
struct raw_struct_type_info : detail::abstract_uniform_type_info<raw_struct> {
using super = detail::abstract_uniform_type_info<raw_struct>;
raw_struct_type_info() : super("raw_struct") {
// nop
}
void serialize(const void* ptr, serializer* sink) const override {
auto rs = reinterpret_cast<const raw_struct*>(ptr);
sink->write_value(static_cast<uint32_t>(rs->str.size()));
......@@ -92,7 +96,6 @@ struct raw_struct_type_info : detail::abstract_uniform_type_info<raw_struct> {
bool equals(const void* lhs, const void* rhs) const override {
return deref(lhs) == deref(rhs);
}
};
void test_ieee_754() {
......@@ -121,7 +124,7 @@ enum class test_enum {
int main() {
CAF_TEST(test_serialization);
announce<test_enum>();
announce<test_enum>("test_enum");
test_ieee_754();
......@@ -133,8 +136,7 @@ int main() {
CAF_CHECK_EQUAL(detail::impl_id<strmap>(), 2);
CAF_CHECK_EQUAL(token::value, 2);
announce(typeid(raw_struct),
uniform_type_info_ptr{new raw_struct_type_info});
announce(typeid(raw_struct), uniform_type_info_ptr{new raw_struct_type_info});
auto nid = detail::singletons::get_node_id();
auto nid_str = to_string(nid);
......
......@@ -166,28 +166,26 @@ void testee1(event_based_actor* self) {
});
}
template <class Testee>
string behavior_test(scoped_actor& self, actor et) {
string testee_name = detail::to_uniform_name(typeid(Testee));
CAF_LOGF_TRACE(CAF_TARG(et, to_string) << ", " << CAF_ARG(testee_name));
CAF_LOGF_TRACE(CAF_TARG(et, to_string));
string result;
self->send(et, 1);
self->send(et, 2);
self->send(et, 3);
self->send(et, .1f);
self->send(et, "hello " + testee_name);
self->send(et, "hello");
self->send(et, .2f);
self->send(et, .3f);
self->send(et, "hello again " + testee_name);
self->send(et, "goodbye " + testee_name);
self->send(et, "hello again");
self->send(et, "goodbye");
self->send(et, atom("get_state"));
self->receive (
[&](const string& str) {
result = str;
},
after(chrono::minutes(1)) >> [&]() {
CAF_LOGF_ERROR(testee_name << " does not reply");
throw runtime_error(testee_name + " does not reply");
CAF_LOGF_ERROR("actor does not reply");
throw runtime_error("actor does not reply");
}
);
self->send_exit(et, exit_reason::user_shutdown);
......@@ -373,13 +371,13 @@ void test_spawn() {
CAF_CHECKPOINT();
CAF_PRINT("test delayed_send()");
self->delayed_send(self, chrono::seconds(1), 1, 2, 3);
self->delayed_send(self, chrono::milliseconds(1), 1, 2, 3);
self->receive(on(1, 2, 3) >> [] { });
self->await_all_other_actors_done();
CAF_CHECKPOINT();
CAF_PRINT("test timeout");
self->receive(after(chrono::seconds(1)) >> [] { });
self->receive(after(chrono::milliseconds(1)) >> [] { });
CAF_CHECKPOINT();
spawn(testee1);
......@@ -434,7 +432,7 @@ void test_spawn() {
);
self->receive (
on("goodbye!") >> CAF_CHECKPOINT_CB(),
after(std::chrono::seconds(5)) >> CAF_UNEXPECTED_TOUT_CB()
after(std::chrono::seconds(1)) >> CAF_UNEXPECTED_TOUT_CB()
);
self->receive (
[&](const down_msg& dm) {
......@@ -448,7 +446,7 @@ void test_spawn() {
self->sync_send(sync_testee, "!?").await(
on<sync_exited_msg>() >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self),
after(chrono::milliseconds(5)) >> CAF_UNEXPECTED_TOUT_CB()
after(chrono::milliseconds(1)) >> CAF_UNEXPECTED_TOUT_CB()
);
CAF_CHECKPOINT();
......@@ -506,7 +504,7 @@ void test_spawn() {
auto f = [](const string& name) -> behavior {
return (
on(atom("get_name")) >> [name] {
return make_cow_tuple(atom("name"), name);
return make_message(atom("name"), name);
}
);
};
......@@ -529,9 +527,9 @@ void test_spawn() {
self->await_all_other_actors_done();
CAF_CHECKPOINT();
auto res1 = behavior_test<testee_actor>(self, spawn<blocking_api>(testee_actor{}));
auto res1 = behavior_test(self, spawn<blocking_api>(testee_actor{}));
CAF_CHECK_EQUAL("wait4int", res1);
CAF_CHECK_EQUAL(behavior_test<event_testee>(self, spawn<event_testee>()), "wait4int");
CAF_CHECK_EQUAL(behavior_test(self, spawn<event_testee>()), "wait4int");
self->await_all_other_actors_done();
CAF_CHECKPOINT();
......@@ -553,7 +551,7 @@ void test_spawn() {
self->link_to(pong_actor);
int i = 0;
int flags = 0;
self->delayed_send(self, chrono::seconds(1), atom("FooBar"));
self->delayed_send(self, chrono::milliseconds(10), atom("FooBar"));
// wait for DOWN and EXIT messages of pong
self->receive_for(i, 4) (
[&](const exit_msg& em) {
......@@ -578,7 +576,7 @@ void test_spawn() {
others() >> [&]() {
CAF_FAILURE("unexpected message: " << to_string(self->last_dequeued()));
},
after(chrono::seconds(5)) >> [&]() {
after(chrono::milliseconds(500)) >> [&]() {
CAF_FAILURE("timeout in file " << __FILE__ << " in line " << __LINE__);
}
);
......@@ -632,6 +630,141 @@ void counting_actor(event_based_actor* self) {
CAF_CHECK_EQUAL(self->mailbox().count(), 200);
}
// tests attach_functor() inside of an actor's constructor
void test_constructor_attach() {
class testee : public event_based_actor {
public:
testee(actor buddy) : m_buddy(buddy) {
attach_functor([=](uint32_t reason) {
send(m_buddy, atom("done"), reason);
});
}
behavior make_behavior() {
return {
on(atom("die")) >> [=] {
quit(exit_reason::user_shutdown);
}
};
}
private:
actor m_buddy;
};
class spawner : public event_based_actor {
public:
spawner() : m_downs(0) {
}
behavior make_behavior() {
m_testee = spawn<testee, monitored>(this);
return {
[=](const down_msg& msg) {
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
if (++m_downs == 2) {
quit(msg.reason);
}
},
on(atom("done"), arg_match) >> [=](uint32_t reason) {
CAF_CHECK_EQUAL(reason, exit_reason::user_shutdown);
if (++m_downs == 2) {
quit(reason);
}
},
others() >> [=] {
forward_to(m_testee);
}
};
}
private:
int m_downs;
actor m_testee;
};
anon_send(spawn<spawner>(), atom("die"));
}
class exception_testee : public event_based_actor {
public:
exception_testee() {
set_exception_handler([](const std::exception_ptr&) -> optional<uint32_t> {
return exit_reason::user_defined + 2;
});
}
behavior make_behavior() override {
return {
others() >> [] {
throw std::runtime_error("whatever");
}
};
}
};
void test_custom_exception_handler() {
auto handler = [](const std::exception_ptr& eptr) -> optional<uint32_t> {
try {
std::rethrow_exception(eptr);
}
catch (std::runtime_error&) {
return exit_reason::user_defined;
}
catch (...) {
// "fall through"
}
return exit_reason::user_defined + 1;
};
scoped_actor self;
auto testee1 = self->spawn<monitored>([=](event_based_actor* eb_self) {
eb_self->set_exception_handler(handler);
throw std::runtime_error("ping");
});
auto testee2 = self->spawn<monitored>([=](event_based_actor* eb_self) {
eb_self->set_exception_handler(handler);
throw std::logic_error("pong");
});
auto testee3 = self->spawn<exception_testee, monitored>();
self->send(testee3, "foo");
// receive all down messages
auto i = 0;
self->receive_for(i, 3)(
[&](const down_msg& dm) {
if (dm.source == testee1) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_defined);
}
else if (dm.source == testee2) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_defined + 1);
}
else if (dm.source == testee3) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_defined + 2);
}
else {
CAF_CHECK(false); // report error
}
}
);
}
using abc_atom = atom_constant<atom("abc")>;
using typed_testee = typed_actor<replies_to<abc_atom>::with<std::string>>;
typed_testee::behavior_type testee() {
return {
[](abc_atom) {
CAF_PRINT("received abc_atom");
return "abc";
}
};
}
void test_typed_testee() {
CAF_PRINT("test_typed_testee");
scoped_actor self;
auto x = spawn_typed(testee);
self->sync_send(x, abc_atom()).await(
[](const std::string& str) {
CAF_CHECK_EQUAL(str, "abc");
}
);
self->send_exit(x, exit_reason::user_shutdown);
}
} // namespace <anonymous>
int main() {
......@@ -643,6 +776,14 @@ int main() {
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
test_typed_testee();
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
test_constructor_attach();
CAF_CHECKPOINT();
test_custom_exception_handler();
CAF_CHECKPOINT();
// test setting exit reasons for scoped actors
{ // lifetime scope of self
scoped_actor self;
......
......@@ -35,7 +35,7 @@ struct float_or_int : event_based_actor {
struct popular_actor : event_based_actor { // popular actors have a buddy
actor m_buddy;
popular_actor(const actor& buddy) : m_buddy(buddy) {
popular_actor(const actor& buddy_arg) : m_buddy(buddy_arg) {
// nop
}
inline const actor& buddy() const {
......@@ -62,7 +62,7 @@ struct popular_actor : event_based_actor { // popular actors have a buddy
\ ******************************************************************************/
struct A : popular_actor {
A(const actor& buddy) : popular_actor(buddy) {
A(const actor& buddy_arg) : popular_actor(buddy_arg) {
// nop
}
behavior make_behavior() override {
......@@ -85,7 +85,7 @@ struct A : popular_actor {
};
struct B : popular_actor {
B(const actor& buddy) : popular_actor(buddy) {
B(const actor& buddy_arg) : popular_actor(buddy_arg) {
// nop
}
behavior make_behavior() override {
......@@ -127,7 +127,7 @@ struct C : event_based_actor {
\ ******************************************************************************/
struct D : popular_actor {
D(const actor& buddy) : popular_actor(buddy) {}
D(const actor& buddy_arg) : popular_actor(buddy_arg) {}
behavior make_behavior() override {
return {
others() >> [=] {
......
......@@ -70,24 +70,14 @@ void run_client(const char* host, uint16_t port) {
}
uint16_t run_server() {
auto ref = spawn_typed(server);
uint16_t port = 4242;
for (;;) {
try {
io::typed_publish(ref, port, "127.0.0.1");
CAF_PRINT("running on port " << port);
return port;
}
catch (bind_failure&) {
// try next port
++port;
}
}
auto port = io::typed_publish(spawn_typed(server), 0, "127.0.0.1");
CAF_PRINT("running on port " << port);
return port;
}
int main(int argc, char** argv) {
announce<ping>(&ping::value);
announce<pong>(&pong::value);
announce<ping>("ping", &ping::value);
announce<pong>("pong", &pong::value);
message_builder{argv + 1, argv + argc}.apply({
on("-c", spro<uint16_t>)>> [](uint16_t port) {
CAF_PRINT("run in client mode");
......@@ -99,24 +89,21 @@ int main(int argc, char** argv) {
on() >> [&] {
auto port = run_server();
CAF_CHECKPOINT();
ostringstream oss;
oss << argv[0] << " -c " << port << to_dev_null;
// execute client_part() in a separate process,
// connected via localhost socket
auto child = thread([&oss]() {
CAF_LOGC_TRACE("NONE", "main$thread_launcher", "");
string cmdstr = oss.str();
if (system(cmdstr.c_str()) != 0) {
CAF_PRINTERR("FATAL: command \"" << cmdstr << "\" failed!");
abort();
}
});
scoped_actor self;
auto child = run_program(self, argv[0], "-c", port);
CAF_CHECKPOINT();
child.join();
self->await_all_other_actors_done();
self->receive(
[](const std::string& output) {
cout << endl << endl << "*** output of client program ***"
<< endl << output << endl;
}
);
}
});
CAF_CHECKPOINT();
await_all_actors_done();
shutdown();
return CAF_TEST_RESULT();
}
......@@ -66,18 +66,17 @@ class typed_server3 : public server_type::base {
};
void client(event_based_actor* self, actor parent, server_type serv) {
self->sync_send(serv, my_request{0, 0})
.then([](bool value)->int {
CAF_CHECK_EQUAL(value, true);
return 42;
})
.continue_with([=](int ival) {
CAF_CHECK_EQUAL(ival, 42);
self->sync_send(serv, my_request{10, 20}).then([=](bool value) {
CAF_CHECK_EQUAL(value, false);
self->send(parent, atom("passed"));
});
});
self->sync_send(serv, my_request{0, 0}).then(
[=](bool val1) {
CAF_CHECK_EQUAL(val1, true);
self->sync_send(serv, my_request{10, 20}).then(
[=](bool val2) {
CAF_CHECK_EQUAL(val2, false);
self->send(parent, atom("passed"));
}
);
}
);
}
void test_typed_spawn(server_type ts) {
......@@ -177,7 +176,7 @@ void test_event_testee() {
self->send(et, "goodbye event testee!");
typed_actor<replies_to<get_state_msg>::with<string>> sub_et = et;
// $:: is the anonymous namespace
set<string> iface{"caf::replies_to<$::get_state_msg>::with<@str>",
set<string> iface{"caf::replies_to<get_state_msg>::with<@str>",
"caf::replies_to<@str>::with<void>",
"caf::replies_to<float>::with<void>",
"caf::replies_to<@i32>::with<@i32>"};
......@@ -311,9 +310,9 @@ void test_sending_typed_actors_and_down_msg() {
int main() {
CAF_TEST(test_typed_spawn);
// announce stuff
announce<get_state_msg>();
announce<int_actor>();
announce<my_request>(&my_request::a, &my_request::b);
announce<get_state_msg>("get_state_msg");
announce<int_actor>("int_actor");
announce<my_request>("my_request", &my_request::a, &my_request::b);
// run test series with typed_server(1|2)
test_typed_spawn(spawn_typed(typed_server1));
await_all_actors_done();
......
......@@ -107,14 +107,14 @@ T& append(T& storage, U&& u, Us&&... us) {
int main() {
CAF_TEST(test_uniform_type);
auto announce1 = announce<foo>(&foo::value);
auto announce2 = announce<foo>(&foo::value);
auto announce3 = announce<foo>(&foo::value);
auto announce4 = announce<foo>(&foo::value);
auto announce1 = announce<foo>("foo", &foo::value);
auto announce2 = announce<foo>("foo", &foo::value);
auto announce3 = announce<foo>("foo", &foo::value);
auto announce4 = announce<foo>("foo", &foo::value);
CAF_CHECK(announce1 == announce2);
CAF_CHECK(announce1 == announce3);
CAF_CHECK(announce1 == announce4);
CAF_CHECK_EQUAL(announce1->name(), "$::foo");
CAF_CHECK_EQUAL(announce1->name(), "foo");
{
auto uti = uniform_typeid<atom_value>();
CAF_CHECK(uti != nullptr);
......@@ -124,7 +124,7 @@ int main() {
// the uniform_type_info implementation is correct
std::set<std::string> expected = {
// local types
"$::foo", // <anonymous namespace>::foo
"foo", // <anonymous namespace>::foo
// primitive types
"bool", "@i8", "@i16",
"@i32", "@i64", // signed integer names
......@@ -166,7 +166,7 @@ int main() {
"caf::io::new_data_msg"));
}
// check whether enums can be announced as members
announce<test_enum>();
announce<test_struct>(&test_struct::test_value);
announce<test_enum>("test_enum");
announce<test_struct>("test_struct", &test_struct::test_value);
return CAF_TEST_RESULT();
}
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