Commit 4551cc9b authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'topic/actor-system' into develop

parents 13285435 5aec0526
*.DS_Store
Doxyfile
cppa.creator.user*
html/
build/*
cli_build/*
......@@ -9,11 +8,7 @@ build-gcc/*
Makefile
bin/*
lib/*
manual/build-pdf/*
manual/build-html/*
manual/*.toc
manual/manual.html
manual/variables.tex
manual/*
*.swp
bii/*
libcaf_core/caf/detail/build_config.hpp
......@@ -13,22 +13,37 @@ endif()
if(NOT CAF_ENABLE_RUNTIME_CHECKS)
set(CAF_ENABLE_RUNTIME_CHECKS no)
endif()
if(NOT CAF_NO_MEM_MANAGEMENT)
set(CAF_NO_MEM_MANAGEMENT no)
endif()
if(NOT CAF_BUILD_STATIC_ONLY)
set(CAF_BUILD_STATIC_ONLY no)
endif()
if(NOT CAF_BUILD_STATIC)
set(CAF_BUILD_STATIC no)
endif()
if(NOT CAF_NO_OPENCL)
set(CAF_NO_OPENCL no)
endif()
if(NOT CAF_NO_TOOLS)
set(CAF_NO_TOOLS no)
endif()
if(NOT CAF_NO_SUMMARY)
set(CAF_NO_SUMMARY no)
endif()
if(NOT CAF_NO_IO)
set(CAF_NO_IO no)
else()
set(CAF_NO_RIAC yes)
set(CAF_NO_TOOLS yes)
endif()
################################################################################
# get version of CAF #
......@@ -109,7 +124,7 @@ set(EXTRA_FLAGS "")
# increase max. template depth on GCC and Clang
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
set(EXTRA_FLAGS "-ftemplate-depth=512")
set(EXTRA_FLAGS "-ftemplate-depth=512 -ftemplate-backtrace-limit=0")
endif()
# add "-Werror" flag if --pedantic-build is used
if(CAF_CXX_WARNINGS_AS_ERRORS)
......@@ -429,7 +444,7 @@ endmacro()
# build core and I/O library
add_caf_lib(core)
add_caf_lib(io)
add_optional_caf_lib(io)
# build opencl library if not told otherwise and OpenCL package was found
if(NOT CAF_NO_OPENCL)
......@@ -446,6 +461,9 @@ add_optional_caf_lib(riac)
# build examples if not being told otherwise
add_optional_caf_binaries(examples)
# build tools if not being told otherwise
add_optional_caf_binaries(tools)
# build nexus if not being told otherwise
add_optional_caf_binaries(nexus CAF_NO_RIAC)
......@@ -492,7 +510,7 @@ if(NOT CAF_NO_UNIT_TESTS)
# add some extra unit testing options when testing ASIO as well
if(CAF_USE_ASIO AND "${suite}" MATCHES "^io_.+$")
add_test(${test_name}_asio ${caf_test} -n -v 5 -s
"${suite}" ${ARGN} -- --use-asio)
"${suite}" ${ARGN} -- "--caf#middleman.network-backend=asio")
endif()
endmacro ()
list(LENGTH suites num_suites)
......@@ -503,15 +521,6 @@ if(NOT CAF_NO_UNIT_TESTS)
endif()
################################################################################
# LateX setup #
################################################################################
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/manual/variables.tex.in"
"${CMAKE_CURRENT_SOURCE_DIR}/manual/variables.tex"
@ONLY)
################################################################################
# Doxygen setup #
################################################################################
......@@ -567,7 +576,9 @@ macro(invertYesNo in out)
endif()
endmacro()
# invert CAF_NO_* variables for nicer output
invertYesNo(CAF_NO_IO CAF_BUILD_IO)
invertYesNo(CAF_NO_EXAMPLES CAF_BUILD_EXAMPLES)
invertYesNo(CAF_NO_TOOLS CAF_BUILD_TOOLS)
invertYesNo(CAF_NO_UNIT_TESTS CAF_BUILD_UNIT_TESTS)
invertYesNo(CAF_NO_RIAC CAF_BUILD_RIAC)
invertYesNo(CAF_NO_NEXUS CAF_BUILD_NEXUS)
......@@ -592,6 +603,8 @@ if(NOT CAF_NO_SUMMARY)
"\nLog level: ${LOG_LEVEL_STR}"
"\nWith mem. mgmt.: ${CAF_BUILD_MEM_MANAGEMENT}"
"\n"
"\nBuild I/O module: ${CAF_BUILD_IO}"
"\nBuild tools: ${CAF_BUILD_TOOLS}"
"\nBuild examples: ${CAF_BUILD_EXAMPLES}"
"\nBuild unit tests: ${CAF_BUILD_UNIT_TESTS}"
"\nBuild riac: ${CAF_BUILD_RIAC}"
......
......@@ -70,39 +70,6 @@ Alternatively, you can use the development branch by using:
brew install caf --HEAD
```
### Biicode
The official CAF channel is published under
[caf_bot/actor-framework](https://www.biicode.com/caf_bot/actor-framework)
and includes the following blocks:
* libcaf_core
* libcaf_io
* libcaf_riac
* libcaf_opencl (*depends on OpenCL which is not distributed as part of CAF*)
**NOTE:** You do not need to have CAF installed on your
machine. Biicode will automatically do that for you during the build process.
Visit this the [bii guide](http://docs.biicode.com/c++/gettingstarted.html)
for more information.
To use actor-framework in your project, reference the header file as:
`#include "caf_bot/actor-framework/libcaf_core/caf/all.hpp"`. Then run
`bii find` to resolve and download the files and `bii build`
to compile your code.
To avoid specifying the block name in your includes add the following to your
`bii.conf` file to allow Biicode to associate all `#include "caf/*.hpp"`
with the actor-framework block:
```ini
[includes]
caf/riac/*.hpp : caf_bot/actor-framework/libcaf_riac
caf/opencl/*.hpp : caf_bot/actor-framework/libcaf_opencl
caf/io/*.hpp : caf_bot/actor-framework/libcaf_io
caf/*.hpp : caf_bot/actor-framework/libcaf_core
```
## Get the Sources
* git clone https://github.com/actor-framework/actor-framework.git
......
Subproject commit b300db01ac8bfbb396b62163fe36da39a8f12469
Subproject commit bc426c71c2338d78d6f871913bf7692e4dd1d9bf
# Biicode configuration file
[requirements]
[paths]
# Local directories to look for headers (within block)
libcaf_core
libcaf_io
libcaf_opencl
libcaf_riac
libcaf_test
[dependencies]
# Manual adjust file implicit dependencies, add (+), remove (-), or overwrite (=)
# Nothing depends on the tests
libcaf_core/src/* - libcaf_core/test/*
libcaf_io/src/* - libcaf_io/test/*
libcaf_opencl/src/* - libcaf_opencl/unit_testing/* libcaf_opencl/examples/*
# The test runner depends on all the tests
libcaf_test/src/caf-test.cpp - libcaf_opencl/unit_testing/* libcaf_opencl/examples/*
libcaf_test/src/caf-test.cpp + libcaf_core/test/* libcaf_io/test/*
# Always include the license files when any hpp file is referenced
*.hpp + !LICENSE !LICENSE_ALTERNATIVE !README.md
# Include this helper module so libcaf's makefile can figure out compiler version
* + !cmake/get_compiler_version.cpp
[mains]
# Manual adjust of files that define an executable
# !main.cpp # Do not build executable from this file
# main2.cpp # Build it (it doesnt have a main() function, but maybe it includes it)
# Don't build this helper module from biicode
!cmake/get_compiler_version.cpp
# File has a main method for the tests, but looks can be deceiving
!libcaf_test/caf/test/unit_test_impl.hpp
# It doesn't look like it but this file is the test runner we should build
libcaf_test/src/caf-test.cpp
[tests]
# Manual adjust of files that define a CTest test
# test/* pattern to evaluate this test/ folder sources like tests
libcaf_test/src/caf-test.cpp
libcaf_opencl/unit_testing/*
libcaf_opencl/examples/*
[hooks]
# These are defined equal to [dependencies],files names matching bii*stage*hook.py
# will be launched as python scripts at stage = {post_process, clean}
# CMakeLists.txt + bii/my_post_process1_hook.py bii_clean_hook.py
[includes]
# Mapping of include patterns to external blocks
# hello*.h: user3/depblock # includes will be processed as user3/depblock/hello*.h
[data]
# Manually define data files dependencies, that will be copied to bin for execution
# By default they are copied to bin/user/block/... which should be taken into account
# when loading from disk such data
# image.cpp + image.jpg # code should write open("user/block/image.jpg")
[parent]
caf_bot/actor-framework: 5
Subproject commit 08cc54612466e68f35b9b2f6ecaa8853914d3574
Subproject commit 7ad056b7c230a85942511cb6d1633bf44ae4b26b
......@@ -60,11 +60,13 @@ Usage: $0 [OPTION]... [VAR=VALUE]...
--no-protobuf-examples build without Google Protobuf examples
--no-curl-examples build without libcurl examples
--no-unit-tests build without unit tests
--no-opencl build without opencl
--no-opencl build without OpenCL module
--no-nexus build without nexus
--no-cash build without cash
--no-benchmarks build without benchmarks
--no-riac build without riac
--no-riac build without RIAC module
--no-tools build without CAF tools such as caf-run
--no-io build without I/O module
--no-summary do not print configuration before building
Testing:
......@@ -350,6 +352,12 @@ while [ $# -ne 0 ]; do
--no-riac)
append_cache_entry CAF_NO_RIAC BOOL yes
;;
--no-tools)
append_cache_entry CAF_NO_TOOLS BOOL yes
;;
--no-io)
append_cache_entry CAF_NO_IO BOOL yes
;;
--no-summary)
append_cache_entry CAF_NO_SUMMARY BOOL yes
;;
......
......@@ -17,10 +17,10 @@
MARKDOWN_SUPPORT = YES
#---------------------------------------------------------------------------
# Project related configuration options
# Project related actor_system& system, const uration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the config file
# This tag specifies the encoding used for all characters in the actor_system& system, const file
# that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
......@@ -185,7 +185,7 @@ TAB_SIZE = 2
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
ALIASES = experimental="@attention This feature is **experimental**."
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
# sources only. Doxygen will then generate output that is more tailored for C.
......@@ -280,7 +280,7 @@ SUBGROUPING = YES
TYPEDEF_HIDES_STRUCT = NO
#---------------------------------------------------------------------------
# Build related configuration options
# Build related actor_system& system, const uration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
......@@ -490,7 +490,7 @@ FILE_VERSION_FILTER =
LAYOUT_FILE =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
# actor_system& system, const uration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
......@@ -541,7 +541,7 @@ WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
# actor_system& system, const uration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
......@@ -657,7 +657,7 @@ FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
# actor_system& system, const uration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
......@@ -713,7 +713,7 @@ USE_HTAGS = NO
VERBATIM_HEADERS = NO
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
# actor_system& system, const uration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
......@@ -736,7 +736,7 @@ COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
# actor_system& system, const uration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
......@@ -947,7 +947,7 @@ TREEVIEW_WIDTH = 250
FORMULA_FONTSIZE = 10
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
# actor_system& system, const uration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
......@@ -1023,7 +1023,7 @@ LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
# actor_system& system, const uration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
......@@ -1054,18 +1054,18 @@ COMPACT_RTF = NO
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assignments. You only have to provide
# actor_system& system, const file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
# Syntax is similar to doxygen's actor_system& system, const file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
# actor_system& system, const uration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
......@@ -1093,7 +1093,7 @@ MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
# actor_system& system, const uration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
......@@ -1116,7 +1116,7 @@ XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
# actor_system& system, const uration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
......@@ -1128,7 +1128,7 @@ XML_PROGRAMLISTING = YES
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
# actor_system& system, const uration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES Doxygen will
......
......@@ -11,7 +11,7 @@ else ()
set(WSLIB)
endif()
macro(add name folder)
macro(add folder name)
add_executable(${name} ${folder}/${name}.cpp ${ARGN})
target_link_libraries(${name}
${LD_FLAGS}
......@@ -22,27 +22,47 @@ macro(add name folder)
add_dependencies(${name} all_examples)
endmacro()
add(announce_1 type_system)
add(announce_2 type_system)
add(announce_3 type_system)
add(announce_4 type_system)
add(announce_5 type_system)
add(dancing_kirby message_passing)
add(dining_philosophers message_passing)
add(hello_world .)
add(aout .)
add(calculator message_passing)
add(typed_calculator message_passing)
add(distributed_calculator remote_actors)
add(group_server remote_actors)
add(group_chat remote_actors)
add(simple_broker brokers)
add(simple_http_broker brokers)
# introductionary applications
add(. aout)
add(. hello_world)
# basic message passing primitives
add(message_passing cell)
add(message_passing divider)
add(message_passing request)
add(message_passing promises)
add(message_passing calculator)
add(message_passing delegating)
add(message_passing fixed_stack)
add(message_passing prioritizing)
add(message_passing dancing_kirby)
# dynamic behavior changes using 'become'
add(dynamic_behavior skip_messages)
add(dynamic_behavior dining_philosophers)
# composing actors using states or other actors
add(composition calculator_behavior)
add(composition dictionary_behavior)
# adding custom message types
add(custom_type custom_types_1)
add(custom_type custom_types_2)
add(custom_type custom_types_3)
# basic remoting
add(remoting group_chat)
add(remoting group_server)
add(remoting distributed_calculator)
# basic I/O with brokers
add(broker simple_broker)
add(broker simple_http_broker)
if(NOT CAF_NO_PROTOBUF_EXAMPLES)
find_package(Protobuf)
if(PROTOBUF_FOUND AND PROTOBUF_PROTOC_EXECUTABLE)
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders "${CMAKE_CURRENT_SOURCE_DIR}/remote_actors/pingpong.proto")
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders "${CMAKE_CURRENT_SOURCE_DIR}/remoting/pingpong.proto")
include_directories(${PROTOBUF_INCLUDE_DIR})
# add binary dir as include path as generated headers will be located there
include_directories(${CMAKE_CURRENT_BINARY_DIR})
......
/******************************************************************************\
* This example illustrates how to use aout. *
\******************************************************************************/
#include <random>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
using namespace caf;
using std::endl;
int main() {
void caf_main(actor_system& system) {
for (int i = 1; i <= 50; ++i) {
spawn<blocking_api>([i](blocking_actor* self) {
system.spawn([i](blocking_actor* self) {
aout(self) << "Hi there! This is actor nr. "
<< i << "!" << endl;
std::random_device rd;
......@@ -29,8 +26,6 @@ int main() {
);
});
}
// wait until all other actors we've spawned are done
await_all_actors_done();
// done
shutdown();
}
CAF_MAIN()
......@@ -18,7 +18,7 @@ using namespace caf;
using namespace caf::io;
void print_on_exit(const actor& hdl, const std::string& name) {
hdl->attach_functor([=](abstract_actor* ptr, uint32_t reason) {
hdl->attach_functor([=](abstract_actor* ptr, exit_reason reason) {
aout(ptr) << name << " exited with reason " << reason << endl;
});
}
......@@ -144,20 +144,29 @@ maybe<uint16_t> as_u16(const std::string& str) {
}
int main(int argc, char** argv) {
actor_system_config cfg;
cfg.load<io::middleman>();
actor_system system{cfg};
message_builder{argv + 1, argv + argc}.apply({
on("-s", as_u16) >> [&](uint16_t port) {
cout << "run in server mode" << endl;
auto pong_actor = spawn(pong);
auto sever_actor = spawn_io_server(server, port, pong_actor);
print_on_exit(sever_actor, "server");
print_on_exit(pong_actor, "pong");
auto pong_actor = system.spawn(pong);
auto server_actor = system.middleman().spawn_server(server, port,
pong_actor);
if (server_actor) {
print_on_exit(*server_actor, "server");
print_on_exit(pong_actor, "pong");
}
},
on("-c", val<string>, as_u16) >> [&](const string& host, uint16_t port) {
auto ping_actor = spawn(ping, 20);
auto io_actor = spawn_io_client(protobuf_io, host, port, ping_actor);
print_on_exit(io_actor, "protobuf_io");
print_on_exit(ping_actor, "ping");
send_as(io_actor, ping_actor, atom("kickoff"), io_actor);
auto ping_actor = system.spawn(ping, 20);
auto io_actor = system.middleman().spawn_client(protobuf_io, host,
port, ping_actor);
if (io_actor) {
print_on_exit(ping_actor, "ping");
print_on_exit(*io_actor, "protobuf_io");
send_as(*io_actor, ping_actor, atom("kickoff"), *io_actor);
}
},
others >> [] {
cerr << "use with eihter '-s PORT' as server or "
......@@ -165,6 +174,4 @@ int main(int argc, char** argv) {
<< endl;
}
});
await_all_actors_done();
shutdown();
}
......@@ -7,6 +7,10 @@
* - ./build/bin/broker -c localhost 4242 *
\ ******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 46-50 (Actors.tex)
#include "caf/config.hpp"
#ifdef WIN32
......@@ -40,8 +44,8 @@ using kickoff_atom = atom_constant<atom("kickoff")>;
// utility function to print an exit message with custom name
void print_on_exit(const actor& hdl, const std::string& name) {
hdl->attach_functor([=](abstract_actor* ptr, uint32_t reason) {
aout(ptr) << name << " exited with reason " << reason << endl;
hdl->attach_functor([=](const error& reason) {
cout << name << " exited: " << to_string(reason) << endl;
});
}
......@@ -51,9 +55,9 @@ behavior ping(event_based_actor* self, size_t num_pings) {
[=](kickoff_atom, const actor& pong) {
self->send(pong, ping_atom::value, int32_t(1));
self->become (
[=](pong_atom, int32_t value) -> message {
[=](pong_atom, int32_t value) -> result<ping_atom, int32_t> {
if (++*count >= num_pings) self->quit();
return make_message(ping_atom::value, value + 1);
return {ping_atom::value, value + 1};
}
);
}
......@@ -62,8 +66,8 @@ behavior ping(event_based_actor* self, size_t num_pings) {
behavior pong() {
return {
[](ping_atom, int32_t value) {
return make_message(pong_atom::value, value);
[](ping_atom, int32_t value) -> result<pong_atom, int32_t> {
return {pong_atom::value, value};
}
};
}
......@@ -106,6 +110,13 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
assert(self->num_connections() == 1);
// monitor buddy to quit broker if buddy is done
self->monitor(buddy);
self->set_down_handler([=](down_msg& dm) {
if (dm.source == buddy) {
aout(self) << "our buddy is down" << endl;
// quit for same reason
self->quit(dm.reason);
}
});
// setup: we are exchanging only messages consisting of an atom
// (as uint64_t) and an integer value (int32_t)
self->configure_read(hdl, receive_policy::exactly(sizeof(uint64_t) +
......@@ -130,13 +141,6 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
write_int(self, hdl, static_cast<uint64_t>(av));
write_int(self, hdl, i);
},
[=](const down_msg& dm) {
if (dm.source == buddy) {
aout(self) << "our buddy is down" << endl;
// quit for same reason
self->quit(dm.reason);
}
},
[=](const new_data_msg& msg) {
// read the atom value as uint64_t from buffer
uint64_t atm_val;
......@@ -152,10 +156,6 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
<< endl;
// send composed message to our buddy
self->send(buddy, atm, ival);
},
others >> [=] {
aout(self) << "unexpected: "
<< to_string(self->current_message()) << endl;
}
};
}
......@@ -171,62 +171,44 @@ behavior server(broker* self, const actor& buddy) {
print_on_exit(impl, "broker_impl");
aout(self) << "quit server (only accept 1 connection)" << endl;
self->quit();
},
others >> [=] {
aout(self) << "unexpected: "
<< to_string(self->current_message()) << endl;
}
};
}
maybe<uint16_t> as_u16(const std::string& str) {
optional<uint16_t> as_u16(const std::string& str) {
return static_cast<uint16_t>(stoul(str));
}
int main(int argc, char** argv) {
using std::string;
class config : public actor_system_config {
public:
uint16_t port = 0;
string host = "localhost";
auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"server,s", "run in server mode"},
{"client,c", "run in client mode"},
{"port,p", "set port", port},
{"host,H", "set host (client mode only"}
});
if (! res.error.empty()) {
cerr << res.error << endl;
return 1;
}
if (res.opts.count("help") > 0) {
cout << res.helptext << endl;
return 0;
}
if (! res.remainder.empty()) {
// not all CLI arguments could be consumed
cerr << "*** too many arguments" << endl << res.helptext << endl;
return 1;
std::string host = "localhost";
bool server_mode = false;
void init() override {
opt_group{custom_options_, "global"}
.add(port, "port,p", "set port")
.add(host, "host,H", "set host (ignored in server mode)")
.add(server_mode, "server-mode,s", "enable server mode");
}
if (res.opts.count("port") == 0) {
cerr << "*** no port given" << endl << res.helptext << endl;
return 1;
}
if (res.opts.count("server") > 0) {
};
void caf_main(actor_system& system, const config& cfg) {
if (cfg.server_mode) {
cout << "run in server mode" << endl;
auto pong_actor = spawn(pong);
auto server_actor = spawn_io_server(server, port, pong_actor);
auto pong_actor = system.spawn(pong);
auto server_actor = system.middleman().spawn_server(server, cfg.port,
pong_actor);
print_on_exit(server_actor, "server");
print_on_exit(pong_actor, "pong");
} else if (res.opts.count("client") > 0) {
auto ping_actor = spawn(ping, size_t{20});
auto io_actor = spawn_io_client(broker_impl, host, port, ping_actor);
print_on_exit(io_actor, "protobuf_io");
print_on_exit(ping_actor, "ping");
send_as(io_actor, ping_actor, kickoff_atom::value, io_actor);
} else {
cerr << "*** neither client nor server mode set" << endl
<< res.helptext << endl;
return 1;
return;
}
await_all_actors_done();
shutdown();
auto ping_actor = system.spawn(ping, size_t{20});
auto io_actor = system.middleman().spawn_client(broker_impl, cfg.host,
cfg.port, ping_actor);
print_on_exit(ping_actor, "ping");
print_on_exit(io_actor, "protobuf_io");
send_as(io_actor, ping_actor, kickoff_atom::value, io_actor);
}
CAF_MAIN(io::middleman)
......@@ -46,6 +46,9 @@ behavior connection_worker(broker* self, connection_handle hdl) {
behavior server(broker* self) {
auto counter = std::make_shared<int>(0);
self->set_down_handler([=](down_msg&) {
++*counter;
});
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value);
return {
[=](const new_connection_msg& ncm) {
......@@ -53,54 +56,33 @@ behavior server(broker* self) {
self->monitor(worker);
self->link_to(worker);
},
[=](const down_msg&) {
++*counter;
},
[=](tick_atom) {
aout(self) << "Finished " << *counter << " requests per second." << endl;
*counter = 0;
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value);
},
others >> [=] {
aout(self) << "unexpected: " << to_string(self->current_message()) << endl;
}
};
}
maybe<uint16_t> as_u16(const std::string& str) {
return static_cast<uint16_t>(stoul(str));
}
int main(int argc, const char** argv) {
class config : public actor_system_config {
public:
uint16_t port = 0;
auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"port,p", "set port", port},
});
if (! res.error.empty()) {
cerr << res.error << endl;
return 1;
}
if (res.opts.count("help") > 0) {
cout << res.helptext << endl;
return 0;
}
if (! res.remainder.empty()) {
// not all CLI arguments could be consumed
cerr << "*** too many arguments" << endl << res.helptext << endl;
return 1;
}
if (res.opts.count("port") == 0) {
cerr << "*** no port given" << endl << res.helptext << endl;
return 1;
void init() override {
opt_group{custom_options_, "global"}
.add(port, "port,p", "set port");
}
cout << "*** run in server mode listen on: " << port << endl;
};
void caf_main(actor_system& system, const config& cfg) {
cout << "*** run in server mode listen on: " << cfg.port << endl;
cout << "*** to quit the program, simply press <enter>" << endl;
auto server_actor = spawn_io_server(server, port);
auto server_actor = system.middleman().spawn_server(server, cfg.port);
// wait for any input
std::string dummy;
std::getline(std::cin, dummy);
// kill server
anon_send_exit(server_actor, exit_reason::user_shutdown);
await_all_actors_done();
shutdown();
}
CAF_MAIN(io::middleman)
; This file shows all possible parameters with defaults.
; Values enclosed in <> are detected at runtime unless defined by the user.
; when using the default scheduler
[scheduler]
; accepted alternative: 'sharing'
policy='stealing'
; configures whether the scheduler generates profiling output
enable-profiling=false
; can be overriden to force a fixed number of threads
max-threads=<number of cores>
; configures the maximum number of messages actors can consume in one run
max-throughput=<infinite>
; measurement resolution in milliseconds (only if profiling is enabled)
profiling-ms-resolution=100
; output file for profiler data (only if profiling is enabled)
profiling-output-file="/dev/null"
; when loading io::middleman
[middleman]
; configures whether MMs try to span a full mesh
enable-automatic-connections=false
; accepted alternative: 'asio' (only when compiling CAF with ASIO)
network-backend='default'
; sets the maximum number of consecutive I/O reads per broker
max-consecutive-reads=50
; heartbeat message interval in ms (0 disables heartbeating)
heartbeat-interval=0
; when loading riac::probe
[probe]
; denotes the hostname or IP address to reach the Nexus
nexus-host=""
; denotes the port where the Nexus actor is published
nexus-port=0
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* using composable states. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-50 (Actor.tex)
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using namespace caf;
namespace {
using add_atom = atom_constant<atom("add")>;
using multiply_atom = atom_constant<atom("multiply")>;
using adder = typed_actor<replies_to<add_atom, int, int>::with<int>>;
using multiplier = typed_actor<replies_to<multiply_atom, int, int>::with<int>>;
class adder_bhvr : public composable_behavior<adder> {
public:
result<int> operator()(add_atom, int x, int y) override {
return x + y;
}
};
class multiplier_bhvr : public composable_behavior<multiplier> {
public:
result<int> operator()(multiply_atom, int x, int y) override {
return x * y;
}
};
// calculator_bhvr can be inherited from or composed further
using calculator_bhvr = composed_behavior<adder_bhvr, multiplier_bhvr>;
} // namespace <anonymous>
void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn<calculator_bhvr>());
cout << "10 + 20 = " << f(add_atom::value, 10, 20) << endl;
cout << "7 * 9 = " << f(multiply_atom::value, 7, 9) << endl;
}
CAF_MAIN()
/******************************************************************************\
* This example is a simple dictionary implemented * using composable states. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 22-44 (Actor.tex)
#include <string>
#include <iostream>
#include <unordered_map>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::string;
using namespace caf;
namespace {
using dict = typed_actor<reacts_to<put_atom, string, string>,
replies_to<get_atom, string>::with<string>>;
class dict_behavior : public composable_behavior<dict> {
public:
result<string> operator()(get_atom, param<string> key) override {
auto i = values_.find(key);
if (i == values_.end())
return "";
return i->second;
}
result<void> operator()(put_atom, param<string> key,
param<string> value) override {
if (values_.count(key) != 0)
return unit;
values_.emplace(key.move(), value.move());
return unit;
}
protected:
std::unordered_map<string, string> values_;
};
} // namespace <anonymous>
void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn<dict_behavior>());
f(put_atom::value, "CAF", "success");
cout << "CAF is the key to " << f(get_atom::value, "CAF") << endl;
}
CAF_MAIN()
......@@ -43,6 +43,7 @@
// CAF
#include "caf/all.hpp"
#include "caf/io/all.hpp"
CAF_PUSH_WARNINGS
#include <curl/curl.h>
......@@ -111,8 +112,7 @@ struct base_state {
return aout(self) << color << name << " (id = " << self->id() << "): ";
}
virtual void init(actor m_parent, std::string m_name, std::string m_color) {
parent = std::move(m_parent);
virtual void init(std::string m_name, std::string m_color) {
name = std::move(m_name);
color = std::move(m_color);
print() << "started" << color::reset_endl;
......@@ -123,15 +123,14 @@ struct base_state {
}
local_actor* self;
actor parent;
std::string name;
std::string color;
};
// encapsulates an HTTP request
behavior client_job(stateful_actor<base_state>* self, actor parent) {
self->state.init(std::move(parent), "client-job", color::blue);
self->send(self->state.parent, read_atom::value,
self->state.init("client-job", color::blue);
self->send(parent, read_atom::value,
"http://www.example.com/index.html",
uint64_t{0}, uint64_t{4095});
return {
......@@ -166,7 +165,7 @@ struct client_state : base_state {
behavior client(stateful_actor<client_state>* self, actor parent) {
using std::chrono::milliseconds;
self->link_to(parent);
self->state.init(std::move(parent), "client", color::green);
self->state.init("client", color::green);
self->send(self, next_atom::value);
return {
[=](next_atom) {
......@@ -175,7 +174,7 @@ behavior client(stateful_actor<client_state>* self, actor parent) {
<< color::reset_endl;
// client_job will use IO
// and should thus be spawned in a separate thread
self->spawn<detached+linked>(client_job, st.parent);
self->spawn<detached+linked>(client_job, parent);
// compute random delay until next job is launched
auto delay = st.dist(st.re);
self->delayed_send(self, milliseconds(delay), next_atom::value);
......@@ -202,14 +201,13 @@ struct curl_state : base_state {
return size;
}
void init(actor m_parent, std::string m_name, std::string m_color) override {
void init(std::string m_name, std::string m_color) override {
curl = curl_easy_init();
if (! curl)
throw std::runtime_error("Unable initialize CURL.");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_state::callback);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
base_state::init(std::move(m_parent), std::move(m_name),
std::move(m_color));
base_state::init(std::move(m_name), std::move(m_color));
}
CURL* curl = nullptr;
......@@ -218,7 +216,7 @@ struct curl_state : base_state {
// manages a CURL session
behavior curl_worker(stateful_actor<curl_state>* self, actor parent) {
self->state.init(std::move(parent), "curl-worker", color::yellow);
self->state.init("curl-worker", color::yellow);
return {
[=](read_atom, const std::string& fname, uint64_t offset, uint64_t range)
-> message {
......@@ -259,7 +257,7 @@ behavior curl_worker(stateful_actor<curl_state>* self, actor parent) {
<< hc
<< color::reset_endl;
// tell parent that this worker is done
self->send(st.parent, finished_atom::value);
self->send(parent, finished_atom::value);
return make_message(reply_atom::value, std::move(st.buf));
case 404: // file does not exist
st.print() << "http error: download failed with "
......@@ -284,7 +282,7 @@ struct master_state : base_state {
};
behavior curl_master(stateful_actor<master_state>* self) {
self->state.init(invalid_actor, "curl-master", color::magenta);
self->state.init("curl-master", color::magenta);
// spawn workers
for(size_t i = 0; i < num_curl_workers; ++i)
self->state.idle.push_back(self->spawn<detached+linked>(curl_worker, self));
......@@ -301,14 +299,14 @@ behavior curl_master(stateful_actor<master_state>* self) {
self->state.print() << "spawned " << self->state.idle.size()
<< " worker(s)" << color::reset_endl;
return {
[=](read_atom, const std::string&, uint64_t, uint64_t) {
[=](read_atom rd, std::string& str, uint64_t x, uint64_t y) {
auto& st = self->state;
st.print() << "received {'read'}" << color::reset_endl;
// forward job to an idle worker
actor worker = st.idle.back();
st.idle.pop_back();
st.busy.push_back(worker);
self->forward_to(worker);
self->delegate(worker, rd, std::move(str), x, y);
st.print() << st.busy.size() << " active jobs" << color::reset_endl;
if (st.idle.empty()) {
// wait until at least one worker finished its job
......@@ -332,8 +330,7 @@ std::atomic<bool> shutdown_flag{false};
} // namespace <anonymous>
int main() {
// random number setup
void caf_main(actor_system& system) {
// install signal handler
struct sigaction act;
act.sa_handler = [](int) { shutdown_flag = true; };
......@@ -346,28 +343,27 @@ int main() {
set_sighandler();
// initialize CURL
curl_global_init(CURL_GLOBAL_DEFAULT);
{ // lifetime scope of self
scoped_actor self;
// spawn client and curl_master
auto master = self->spawn<detached>(curl_master);
self->spawn<detached>(client, master);
// poll CTRL+C flag every second
while (! shutdown_flag)
std::this_thread::sleep_for(std::chrono::seconds(1));
aout(self) << color::cyan << "received CTRL+C" << color::reset_endl;
// shutdown actors
anon_send_exit(master, exit_reason::user_shutdown);
// await actors
act.sa_handler = [](int) { abort(); };
set_sighandler();
aout(self) << color::cyan
<< "await CURL; this may take a while "
"(press CTRL+C again to abort)"
<< color::reset_endl;
self->await_all_other_actors_done();
}
// shutdown libcaf
shutdown();
// get a scoped actor for the communication with our CURL actors
scoped_actor self{system};
// spawn client and curl_master
auto master = self->spawn<detached>(curl_master);
self->spawn<detached>(client, master);
// poll CTRL+C flag every second
while (! shutdown_flag)
std::this_thread::sleep_for(std::chrono::seconds(1));
aout(self) << color::cyan << "received CTRL+C" << color::reset_endl;
// shutdown actors
anon_send_exit(master, exit_reason::user_shutdown);
// await actors
act.sa_handler = [](int) { abort(); };
set_sighandler();
aout(self) << color::cyan
<< "await CURL; this may take a while "
"(press CTRL+C again to abort)"
<< color::reset_endl;
self->await_all_other_actors_done();
// shutdown CURL
curl_global_cleanup();
}
CAF_MAIN(io::middleman)
// Showcases how to add custom POD message types.
// This file is referenced in the manual, do not modify without updating refs!
// ConfiguringActorApplications: 23-26, 29-33, 87-90, 93-96
#include <tuple>
#include <string>
#include <vector>
#include <cassert>
#include <utility>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::cerr;
using std::endl;
using std::vector;
using namespace caf;
// POD struct foo
struct foo {
std::vector<int> a;
int b;
};
// foo needs to be serializable
template <class Processor>
void serialize(Processor& proc, foo& x, const unsigned int) {
proc & x.a;
proc & x.b;
}
// also, CAF gives us `deep_to_string` for implementing `to_string` easily
std::string to_string(const foo& x) {
// `to_string(foo{{1, 2, 3}, 4})` prints: "foo([1, 2, 3], 4)"
return "foo" + deep_to_string_as_tuple(x.a, x.b);
}
// a pair of two ints
using foo_pair = std::pair<int, int>;
// another alias for pairs of two ints
using foo_pair2 = std::pair<int, int>;
// a struct with a nested container
struct foo2 {
int a;
vector<vector<double>> b;
};
// foo2 also needs to be serializable
template <class Processor>
void serialize(Processor& proc, foo2& x, const unsigned int) {
proc & x.a;
proc & x.b; // traversed automatically and recursively
}
// `deep_to_string` also traverses nested containers
std::string to_string(const foo2& x) {
return "foo" + deep_to_string_as_tuple(x.a, x.b);
}
// receives our custom message types
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1)
testee(self, remaining - 1);
else
self->quit();
};
self->become (
// note: we sent a foo_pair2, but match on foo_pair
// that works because both are aliases for std::pair<int, int>
[=](const foo_pair& val) {
aout(self) << "foo_pair" << deep_to_string(val) << endl;
set_next_behavior();
},
[=](const foo& val) {
aout(self) << to_string(val) << endl;
set_next_behavior();
}
);
}
class config : public actor_system_config {
public:
void init() override {
add_message_type<foo>("foo");
add_message_type<foo2>("foo2");
add_message_type<foo_pair>("foo_pair");
}
};
void caf_main(actor_system& system, const config&) {
// two variables for testing serialization
foo2 f1;
foo2 f2;
// init some test data
f1.a = 5;
f1.b.resize(1);
f1.b.back().push_back(42);
// I/O buffer
vector<char> buf;
// write f1 to buffer
binary_serializer bs{system, buf};
bs << f1;
// read f2 back from buffer
binary_deserializer bd{system, buf};
bd >> f2;
// must be equal
assert(to_string(f1) == to_string(f2));
// spawn a testee that receives two messages of user-defined type
auto t = system.spawn(testee, 2);
scoped_actor self{system};
// send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2
self->send(t, foo_pair2{3, 4});
self->await_all_other_actors_done();
}
CAF_MAIN()
// showcases how to add custom message types to CAF
// if friend access for serialization is available
#include <utility>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// a simple class using getter and setter member functions
class foo {
public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
// nop
}
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const {
return a_;
}
void set_a(int val) {
a_ = val;
}
int b() const {
return b_;
}
void set_b(int val) {
b_ = val;
}
template <class Processor>
friend void serialize(Processor& proc, foo& x, const unsigned int) {
proc & x.a_;
proc & x.b_;
}
friend std::string to_string(const foo& x) {
return "foo" + deep_to_string_as_tuple(x.a_, x.b_);
}
private:
int a_;
int b_;
};
behavior testee(event_based_actor* self) {
return {
[=](const foo& x) {
aout(self) << to_string(x) << endl;
}
};
}
class config : public actor_system_config {
public:
void init() override {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2});
}
CAF_MAIN()
// Showcases how to add custom message types to CAF
// if no friend access for serialization is possible.
// This file is referenced in the manual, do not modify without updating refs!
// ConfiguringActorApplications: 57-59, 64-66
#include <utility>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// identical to our second custom type example,
// but without friend declarations
class foo {
public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
// nop
}
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const {
return a_;
}
void set_a(int val) {
a_ = val;
}
int b() const {
return b_;
}
void set_b(int val) {
b_ = val;
}
private:
int a_;
int b_;
};
// to_string is straightforward ...
std::string to_string(const foo& x) {
return "foo" + deep_to_string_as_tuple(x.a(), x.b());
}
// ... but we need to split serialization into a saving ...
template <class T>
typename std::enable_if<T::is_saving::value>::type
serialize(T& out, const foo& x, const unsigned int) {
out << x.a() << x.b();
}
// ... and a loading function
template <class T>
typename std::enable_if<T::is_loading::value>::type
serialize(T& in, foo& x, const unsigned int) {
int tmp;
in >> tmp;
x.set_a(tmp);
in >> tmp;
x.set_b(tmp);
}
behavior testee(event_based_actor* self) {
return {
[=](const foo& x) {
aout(self) << to_string(x) << endl;
}
};
}
class config : public actor_system_config {
public:
void init() override {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2});
}
CAF_MAIN()
/******************************************************************************\
* This example is an implementation of the classical Dining Philosophers *
* exercise using only libcaf's event-based actor implementation. *
\ ******************************************************************************/
#include <map>
#include <thread>
#include <vector>
#include <chrono>
#include <sstream>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::cerr;
using std::endl;
using std::chrono::seconds;
using namespace caf;
namespace {
// atoms for chopstick interface
using put_atom = atom_constant<atom("put")>;
using take_atom = atom_constant<atom("take")>;
using taken_atom = atom_constant<atom("taken")>;
// atoms for philosopher interface
using eat_atom = atom_constant<atom("eat")>;
using think_atom = atom_constant<atom("think")>;
// a chopstick
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>,
reacts_to<put_atom>>;
chopstick::behavior_type taken_chopstick(chopstick::pointer, strong_actor_ptr);
// either taken by a philosopher or available
chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return {
[=](take_atom) -> std::tuple<taken_atom, bool> {
self->become(taken_chopstick(self, self->current_sender()));
return std::make_tuple(taken_atom::value, true);
},
[](put_atom) {
cerr << "chopstick received unexpected 'put'" << endl;
}
};
}
chopstick::behavior_type taken_chopstick(chopstick::pointer self,
strong_actor_ptr user) {
return {
[](take_atom) -> std::tuple<taken_atom, bool> {
return std::make_tuple(taken_atom::value, false);
},
[=](put_atom) {
if (self->current_sender() == user)
self->become(available_chopstick(self));
}
};
}
/* Based on: http://www.dalnefre.com/wp/2010/08/dining-philosophers-in-humus/
*
*
* +-------------+ {busy|taken}
* /-------->| thinking |<------------------\
* | +-------------+ |
* | | |
* | | {eat} |
* | | |
* | V |
* | +-------------+ {busy} +-------------+
* | | hungry |----------->| denied |
* | +-------------+ +-------------+
* | |
* | | {taken}
* | |
* | V
* | +-------------+
* | | granted |
* | +-------------+
* | | |
* | {busy} | | {taken}
* \-----------/ |
* | V
* | {think} +-------------+
* \---------| eating |
* +-------------+
*/
class philosopher : public event_based_actor {
public:
philosopher(actor_config& cfg,
const std::string& n,
const chopstick& l,
const chopstick& r)
: event_based_actor(cfg),
name_(n),
left_(l),
right_(r) {
// we only accept one message per state and skip others in the meantime
set_default_handler(skip);
// a philosopher that receives {eat} stops thinking and becomes hungry
thinking_.assign(
[=](eat_atom) {
become(hungry_);
send(left_, take_atom::value);
send(right_, take_atom::value);
}
);
// wait for the first answer of a chopstick
hungry_.assign(
[=](taken_atom, bool result) {
if (result)
become(granted_);
else
become(denied_);
}
);
// philosopher was able to obtain the first chopstick
granted_.assign(
[=](taken_atom, bool result) {
if (result) {
aout(this) << name_
<< " has picked up chopsticks with IDs "
<< left_->id() << " and " << right_->id()
<< " and starts to eat\n";
// eat some time
delayed_send(this, seconds(5), think_atom::value);
become(eating_);
} else {
send(current_sender() == left_ ? right_ : left_, put_atom::value);
send(this, eat_atom::value);
become(thinking_);
}
}
);
// philosopher was *not* able to obtain the first chopstick
denied_.assign(
[=](taken_atom, bool result) {
if (result)
send(current_sender() == left_ ? left_ : right_, put_atom::value);
send(this, eat_atom::value);
become(thinking_);
}
);
// philosopher obtained both chopstick and eats (for five seconds)
eating_.assign(
[=](think_atom) {
send(left_, put_atom::value);
send(right_, put_atom::value);
delayed_send(this, seconds(5), eat_atom::value);
aout(this) << name_
<< " puts down his chopsticks and starts to think\n";
become(thinking_);
}
);
}
const char* name() const override {
return name_.c_str();
}
protected:
behavior make_behavior() override {
// start thinking
send(this, think_atom::value);
// philosophers start to think after receiving {think}
return (
[=](think_atom) {
aout(this) << name_ << " starts to think\n";
delayed_send(this, seconds(5), eat_atom::value);
become(thinking_);
}
);
}
private:
std::string name_; // the name of this philosopher
chopstick left_; // left chopstick
chopstick right_; // right chopstick
behavior thinking_; // initial behavior
behavior hungry_; // tries to take chopsticks
behavior granted_; // has one chopstick and waits for the second one
behavior denied_; // could not get first chopsticks
behavior eating_; // wait for some time, then go thinking again
};
} // namespace <anonymous>
void caf_main(actor_system& system) {
scoped_actor self{system};
// create five chopsticks
aout(self) << "chopstick ids are:";
std::vector<chopstick> chopsticks;
for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(self->spawn(available_chopstick));
aout(self) << " " << chopsticks.back()->id();
}
aout(self) << endl;
// spawn five philosophers
std::vector<std::string> names {"Plato", "Hume", "Kant",
"Nietzsche", "Descartes"};
for (size_t i = 0; i < 5; ++i)
self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
}
CAF_MAIN()
#include "caf/all.hpp"
using namespace caf;
using idle_atom = atom_constant<atom("idle")>;
using request_atom = atom_constant<atom("request")>;
using response_atom = atom_constant<atom("response")>;
behavior server(event_based_actor* self) {
return {
[=](idle_atom, const actor& worker) {
self->become (
keep_behavior,
[=](request_atom atm) {
self->delegate(worker, atm);
self->unbecome();
},
[=](idle_atom) {
return skip();
}
);
},
[=](request_atom) {
return skip();
}
};
}
behavior client(event_based_actor* self, const actor& serv) {
self->link_to(serv);
self->send(serv, idle_atom::value, self);
return {
[=](request_atom) {
self->send(serv, idle_atom::value, self);
return response_atom::value;
}
};
}
void caf_main(actor_system& system) {
auto serv = system.spawn(server);
auto worker = system.spawn(client, serv);
scoped_actor self{system};
self->request(serv, std::chrono::seconds(10),
request_atom::value).receive([&](response_atom) {
aout(self) << "received response from "
<< (self->current_sender() == worker ? "worker\n" : "server\n");
});
self->send_exit(serv, exit_reason::user_shutdown);
}
CAF_MAIN()
......@@ -16,8 +16,6 @@ behavior mirror(event_based_actor* self) {
[=](const string& what) -> string {
// prints "Hello World!" via aout (thread-safe cout wrapper)
aout(self) << what << endl;
// terminates this actor ('become' otherwise loops forever)
self->quit();
// reply "!dlroW olleH"
return string(what.rbegin(), what.rend());
}
......@@ -26,8 +24,8 @@ behavior mirror(event_based_actor* self) {
void hello_world(event_based_actor* self, const actor& buddy) {
// send "Hello World!" to our buddy ...
self->sync_send(buddy, "Hello World!").then(
// ... wait for a response ...
self->request(buddy, std::chrono::seconds(10), "Hello World!").then(
// ... wait up to 10s for a response ...
[=](const string& what) {
// ... and print it
aout(self) << what << endl;
......@@ -36,12 +34,11 @@ void hello_world(event_based_actor* self, const actor& buddy) {
}
int main() {
// our CAF environment
actor_system system;
// create a new actor that calls 'mirror()'
auto mirror_actor = spawn(mirror);
auto mirror_actor = system.spawn(mirror);
// create another actor that calls 'hello_world(mirror_actor)';
spawn(hello_world, mirror_actor);
// wait until all other actors we have spawned are done
await_all_actors_done();
// run cleanup code before exiting main
shutdown();
system.spawn(hello_world, mirror_actor);
// system will wait until both actors are destroyed before leaving main
}
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* for both the blocking and the event-based API. *
\ ******************************************************************************/
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 17-21, 31-65, 67-101, and 134-139 (Actor.tex)
#include <tuple>
#include <cassert>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::cerr;
using std::endl;
using namespace caf;
using plus_atom = atom_constant<atom("plus")>;
using minus_atom = atom_constant<atom("minus")>;
using result_atom = atom_constant<atom("result")>;
using add_atom = atom_constant<atom("add")>;
using sub_atom = atom_constant<atom("sub")>;
using calculator_actor =
typed_actor<replies_to<plus_atom, int, int>::with<result_atom, int>,
replies_to<minus_atom, int, int>::with<result_atom, int>>;
using calculator_actor = typed_actor<replies_to<add_atom, int, int>::with<int>,
replies_to<sub_atom, int, int>::with<int>>;
// implementation using the blocking API
void blocking_calculator(blocking_actor* self) {
self->receive_loop (
[](plus_atom, int a, int b) {
return std::make_tuple(result_atom::value, a + b);
},
[](minus_atom, int a, int b) {
return std::make_tuple(result_atom::value, a - b);
// prototypes and forward declarations
behavior calculator_fun(event_based_actor* self);
void blocking_calculator_fun(blocking_actor* self);
calculator_actor::behavior_type typed_calculator_fun();
class calculator;
class blocking_calculator;
class typed_calculator;
// function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) {
return behavior{
[](add_atom, int a, int b) {
return a + b;
},
others >> [=] {
cout << "unexpected: " << to_string(self->current_message()) << endl;
[](sub_atom, int a, int b) {
return a - b;
}
);
};
}
// implementation using the event-based API
behavior calculator(event_based_actor* self) {
return behavior{
[](plus_atom, int a, int b) {
return std::make_tuple(result_atom::value, a + b);
},
[](minus_atom, int a, int b) {
return std::make_tuple(result_atom::value, a - b);
// function-based, dynamically typed, blocking API
void blocking_calculator_fun(blocking_actor* self) {
self->receive_loop (
[](add_atom, int a, int b) {
return a + b;
},
others >> [=] {
cerr << "unexpected: " << to_string(self->current_message()) << endl;
[](sub_atom, int a, int b) {
return a - b;
}
};
);
}
// implementation using the statically typed API
calculator_actor::behavior_type typed_calculator() {
// function-based, statically typed, event-based API
calculator_actor::behavior_type typed_calculator_fun() {
return {
[](plus_atom, int a, int b) {
return std::make_tuple(result_atom::value, a + b);
[](add_atom, int a, int b) {
return a + b;
},
[](minus_atom, int a, int b) {
return std::make_tuple(result_atom::value, a - b);
[](sub_atom, int a, int b) {
return a - b;
}
};
}
// class-based, dynamically typed, event-based API
class calculator : public event_based_actor {
public:
calculator(actor_config& cfg) : event_based_actor(cfg) {
// nop
}
behavior make_behavior() override {
return calculator_fun(this);
}
};
// class-based, dynamically typed, blocking API
class blocking_calculator : public blocking_actor {
public:
blocking_calculator(actor_config& cfg) : blocking_actor(cfg) {
// nop
}
void act() override {
blocking_calculator_fun(this);
}
};
// class-based, statically typed, event-based API
class typed_calculator : public calculator_actor::base {
public:
typed_calculator(actor_config& cfg) : calculator_actor::base(cfg) {
// nop
}
behavior_type make_behavior() override {
return typed_calculator_fun();
}
};
void tester(scoped_actor&) {
// end of recursion
}
// tests a calculator instance
template <class Handle>
void tester(event_based_actor* self, const Handle& testee, int x, int y) {
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([=] {
aout(self) << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
self->sync_send(testee, plus_atom::value, x, y).then(
[=](result_atom, int res1) {
template <class Handle, class... Ts>
void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) {
// first test: x + y = z
self->request(hdl, infinite, add_atom::value, x, y).receive(
[&](int res1) {
aout(self) << x << " + " << y << " = " << res1 << endl;
self->sync_send(testee, minus_atom::value, x, y).then(
[=](result_atom, int res2) {
// both tests succeeded
aout(self) << x << " - " << y << " = " << res2 << endl;
self->quit(exit_reason::user_shutdown);
// second test: x - y = z
self->request(hdl, infinite, sub_atom::value, x, y).receive(
[&](int res2) {
aout(self) << x << " - " << y << " = " << res2 << endl;
}
);
},
[&](const error& err) {
aout(self) << "AUT (actor under test) failed: "
<< self->system().render(err) << endl;
self->quit(exit_reason::user_shutdown);
}
);
tester(self, std::forward<Ts>(xs)...);
}
void test_calculators() {
scoped_actor self;
aout(self) << "blocking actor:" << endl;
self->spawn(tester<actor>, spawn<blocking_api>(blocking_calculator), 1, 2);
self->await_all_other_actors_done();
aout(self) << "event-based actor:" << endl;
self->spawn(tester<actor>, spawn(calculator), 3, 4);
self->await_all_other_actors_done();
aout(self) << "typed actor:" << endl;
self->spawn(tester<calculator_actor>, spawn(typed_calculator), 5, 6);
self->await_all_other_actors_done();
void caf_main(actor_system& system) {
auto a1 = system.spawn(blocking_calculator_fun);
auto a2 = system.spawn(calculator_fun);
auto a3 = system.spawn(typed_calculator_fun);
auto a4 = system.spawn<blocking_calculator>();
auto a5 = system.spawn<calculator>();
auto a6 = system.spawn<typed_calculator>();
scoped_actor self{system};
tester(self, a1, 1, 2, a2, 3, 4, a3, 5, 6, a4, 7, 8, a5, 9, 10, a6, 11, 12);
}
int main() {
test_calculators();
shutdown();
}
CAF_MAIN()
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* for both the blocking and the event-based API. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-44, and 50-51 (Actor.tex)
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>,
replies_to<get_atom>::with<int>>;
struct cell_state {
int value = 0;
};
cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) {
return {
[=](put_atom, int val) {
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
}
behavior unchecked_cell(stateful_actor<cell_state>* self) {
return {
[=](put_atom, int val) {
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
}
void caf_main(actor_system& system) {
// create one cell for each implementation
auto cell1 = system.spawn(type_checked_cell);
auto cell2 = system.spawn(unchecked_cell);
auto f = make_function_view(cell1);
cout << "cell value: " << f(get_atom::value) << endl;
f(put_atom::value, 20);
cout << "cell value (after setting to 20): " << f(get_atom::value) << endl;
// get an unchecked cell and send it some garbage
anon_send(cell2, "hello there!");
}
CAF_MAIN()
......@@ -7,6 +7,10 @@
#include <algorithm>
#include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 56-75 (MessagePassing.tex)
using std::cout;
using std::endl;
using std::pair;
......@@ -70,8 +74,8 @@ void dancing_kirby(event_based_actor* self) {
);
}
int main() {
spawn(dancing_kirby);
await_all_actors_done();
shutdown();
void caf_main(actor_system& system) {
system.spawn(dancing_kirby);
}
CAF_MAIN()
#include <iostream>
#include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 15-42 (MessagePassing.tex)
using std::endl;
using namespace caf;
using add_atom = atom_constant<atom("add")>;
using calc = typed_actor<replies_to<add_atom, int, int>::with<int>>;
void actor_a(event_based_actor* self, calc worker) {
self->request(worker, std::chrono::seconds(10), add_atom::value, 1, 2).then(
[=](int result) {
aout(self) << "1 + 2 = " << result << endl;
}
);
}
calc::behavior_type actor_b(calc::pointer self, calc worker) {
return {
[=](add_atom add, int x, int y) {
return self->delegate(worker, add, x, y);
}
};
}
calc::behavior_type actor_c() {
return {
[](add_atom, int x, int y) {
return x + y;
}
};
}
void caf_main(actor_system& system) {
system.spawn(actor_a, system.spawn(actor_b, system.spawn(actor_c)));
}
CAF_MAIN()
......@@ -4,11 +4,22 @@
\ ******************************************************************************/
#include <map>
#include <thread>
#include <vector>
#include <chrono>
#include <sstream>
#include <iostream>
namespace std {
string to_string(const thread::id& x) {
ostringstream os;
os << x;
return os.str();
}
}
#include "caf/all.hpp"
using std::cout;
......@@ -23,7 +34,6 @@ namespace {
// atoms for chopstick interface
using put_atom = atom_constant<atom("put")>;
using take_atom = atom_constant<atom("take")>;
using busy_atom = atom_constant<atom("busy")>;
using taken_atom = atom_constant<atom("taken")>;
// atoms for philosopher interface
......@@ -31,9 +41,7 @@ using eat_atom = atom_constant<atom("eat")>;
using think_atom = atom_constant<atom("think")>;
// a chopstick
using chopstick = typed_actor<replies_to<take_atom>
::with_either<taken_atom>
::or_else<busy_atom>,
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>,
reacts_to<put_atom>>;
chopstick::behavior_type taken_chopstick(chopstick::pointer self, actor_addr);
......@@ -41,9 +49,9 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self, actor_addr);
// either taken by a philosopher or available
chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return {
[=](take_atom) {
[=](take_atom) -> std::tuple<taken_atom, bool> {
self->become(taken_chopstick(self, self->current_sender()));
return taken_atom::value;
return std::make_tuple(taken_atom::value, true);
},
[](put_atom) {
cerr << "chopstick received unexpected 'put'" << endl;
......@@ -54,13 +62,12 @@ chopstick::behavior_type available_chopstick(chopstick::pointer self) {
chopstick::behavior_type taken_chopstick(chopstick::pointer self,
actor_addr user) {
return {
[](take_atom) {
return busy_atom::value;
[](take_atom) -> std::tuple<taken_atom, bool> {
return std::make_tuple(taken_atom::value, false);
},
[=](put_atom) {
if (self->current_sender() == user) {
if (self->current_sender() == user)
self->become(available_chopstick(self));
}
}
};
}
......@@ -96,8 +103,12 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self,
class philosopher : public event_based_actor {
public:
philosopher(const std::string& n, const chopstick& l, const chopstick& r)
: name(n),
philosopher(actor_config& cfg,
const std::string& n,
const chopstick& l,
const chopstick& r)
: event_based_actor(cfg),
name(n),
left(l),
right(r) {
// a philosopher that receives {eat} stops thinking and becomes hungry
......@@ -110,38 +121,36 @@ public:
);
// wait for the first answer of a chopstick
hungry.assign(
[=](taken_atom) {
become(granted);
},
[=](busy_atom) {
become(denied);
[=](taken_atom, bool result) {
if (result)
become(granted);
else
become(denied);
}
);
// philosopher was able to obtain the first chopstick
granted.assign(
[=](taken_atom) {
aout(this) << name
<< " has picked up chopsticks with IDs "
<< left->id() << " and " << right->id()
<< " and starts to eat\n";
// eat some time
delayed_send(this, seconds(5), think_atom::value);
become(eating);
},
[=](busy_atom) {
send(current_sender() == left ? right : left, put_atom::value);
send(this, eat_atom::value);
become(thinking);
[=](taken_atom, bool result) {
if (result) {
aout(this) << name
<< " has picked up chopsticks with IDs "
<< left->id() << " and " << right->id()
<< " and starts to eat\n";
// eat some time
delayed_send(this, seconds(5), think_atom::value);
become(eating);
} else {
send(current_sender() == left ? right : left, put_atom::value);
send(this, eat_atom::value);
become(thinking);
}
}
);
// philosopher was *not* able to obtain the first chopstick
denied.assign(
[=](taken_atom) {
send(current_sender() == left ? left : right, put_atom::value);
send(this, eat_atom::value);
become(thinking);
},
[=](busy_atom) {
[=](taken_atom, bool result) {
if (result)
send(current_sender() == left ? left : right, put_atom::value);
send(this, eat_atom::value);
become(thinking);
}
......@@ -180,32 +189,25 @@ private:
behavior hungry; // tries to take chopsticks
behavior granted; // has one chopstick and waits for the second one
behavior denied; // could not get first chopsticks
behavior eating; // waits for some time, then go thinking again
behavior eating; // wait for some time, then go thinking again
};
void dining_philosophers() {
scoped_actor self;
} // namespace <anonymous>
int main(int, char**) {
actor_system system;
scoped_actor self{system};
// create five chopsticks
aout(self) << "chopstick ids are:";
std::vector<chopstick> chopsticks;
for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(spawn(available_chopstick));
chopsticks.push_back(self->spawn(available_chopstick));
aout(self) << " " << chopsticks.back()->id();
}
aout(self) << endl;
// spawn five philosophers
std::vector<std::string> names {"Plato", "Hume", "Kant",
"Nietzsche", "Descartes"};
for (size_t i = 0; i < 5; ++i) {
spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
}
}
} // namespace <anonymous>
int main(int, char**) {
dining_philosophers();
// real philosophers are never done
await_all_actors_done();
shutdown();
"Nietzsche", "Descartes"};
for (size_t i = 0; i < 5; ++i)
self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
}
/******************************************************************************\
* A very basic, interactive divider. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 19-25, 35-48, and 63-73 (MessagePassing.tex);
// lines 19-34, and 51-56 (Error.tex)
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::flush;
using namespace caf;
enum class math_error : uint8_t {
division_by_zero = 1
};
error make_error(math_error x) {
return {static_cast<uint8_t>(x), atom("math")};
}
std::string to_string(math_error x) {
switch (x) {
case math_error::division_by_zero:
return "division_by_zero";
default:
return "-unknown-error-";
}
}
using div_atom = atom_constant<atom("add")>;
using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>;
divider::behavior_type divider_impl() {
return {
[](div_atom, double x, double y) -> result<double> {
if (y == 0.0)
return math_error::division_by_zero;
return x / y;
}
};
}
class config : public actor_system_config {
public:
void init() override {
auto renderer = [](uint8_t x, atom_value, const message&) {
return "math_error" + deep_to_string_as_tuple(static_cast<math_error>(x));
};
add_error_category(atom("math"), renderer);
}
};
void caf_main(actor_system& system, const config&) {
double x;
double y;
cout << "x: " << flush;
std::cin >> x;
cout << "y: " << flush;
std::cin >> y;
auto div = system.spawn(divider_impl);
scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom::value, x, y).receive(
[&](double z) {
aout(self) << x << " / " << y << " = " << z << endl;
},
[&](const error& err) {
aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< system.render(err) << endl;
}
);
}
CAF_MAIN()
#include <cstdint>
#include <iostream>
#include "caf/all.hpp"
using std::endl;
using namespace caf;
using pop_atom = atom_constant<atom("pop")>;
using push_atom = atom_constant<atom("push")>;
enum class fixed_stack_errc : uint8_t { push_to_full = 1, pop_from_empty };
error make_error(fixed_stack_errc x) {
return error{static_cast<uint8_t>(x), atom("FixedStack")};
}
class fixed_stack : public event_based_actor {
public:
fixed_stack(actor_config& cfg, size_t stack_size)
: event_based_actor(cfg),
size_(stack_size) {
full_.assign(
[=](push_atom, int) -> error {
return fixed_stack_errc::push_to_full;
},
[=](pop_atom) -> int {
auto result = data_.back();
data_.pop_back();
become(filled_);
return result;
}
);
filled_.assign(
[=](push_atom, int what) {
data_.push_back(what);
if (data_.size() == size_)
become(full_);
},
[=](pop_atom) -> int {
auto result = data_.back();
data_.pop_back();
if (data_.empty())
become(empty_);
return result;
}
);
empty_.assign(
[=](push_atom, int what) {
data_.push_back(what);
become(filled_);
},
[=](pop_atom) -> error {
return fixed_stack_errc::pop_from_empty;
}
);
}
behavior make_behavior() override {
if (size_ < 2)
throw std::runtime_error("size < 2 is not supported for fixed_stack");
return empty_;
}
private:
size_t size_;
std::vector<int> data_;
behavior full_;
behavior filled_;
behavior empty_;
};
void caf_main(actor_system& system) {
scoped_actor self{system};
auto st = self->spawn<fixed_stack>(5);
// fill stack
for (int i = 0; i < 10; ++i)
self->send(st, push_atom::value, i);
// drain stack
aout(self) << "stack: { ";
bool stack_empty = false;
while (! stack_empty) {
self->request(st, std::chrono::seconds(10), pop_atom::value).receive(
[&](int x) {
aout(self) << x << " ";
},
[&](const error&) {
stack_empty = true;
}
);
}
aout(self) << "}" << endl;
self->send_exit(st, exit_reason::user_shutdown);
}
CAF_MAIN()
#include "caf/all.hpp"
using std::endl;
using namespace caf;
behavior foo(event_based_actor* self) {
self->send(self, "world");
self->send<message_priority::high>(self, "hello");
// when spawning `foo` with priority_aware flag, it will print "hello" first
return {
[=](const std::string& str) {
aout(self) << str << endl;
}
};
}
void caf_main(actor_system& system) {
scoped_actor self{system};
aout(self) << "spawn foo" << endl;
self->spawn(foo);
self->await_all_other_actors_done();
aout(self) << "spawn foo again with priority_aware flag" << endl;
self->spawn<priority_aware>(foo);
}
CAF_MAIN()
/******************************************************************************\
* Illustrates response promises. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-43 (MessagePassing.tex)
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using namespace caf;
using add_atom = atom_constant<atom("add")>;
using adder = typed_actor<replies_to<add_atom, int, int>::with<int>>;
// function-based, statically typed, event-based API
adder::behavior_type worker() {
return {
[](add_atom, int a, int b) {
return a + b;
}
};
}
// function-based, statically typed, event-based API
adder::behavior_type calculator_master(adder::pointer self) {
auto w = self->spawn(worker);
return {
[=](add_atom x, int y, int z) -> result<int> {
auto rp = self->make_response_promise<int>();
self->request(w, infinite, x, y, z).then([=](int result) mutable {
rp.deliver(result);
});
return rp;
}
};
}
void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn(calculator_master));
cout << "12 + 13 = " << f(add_atom::value, 12, 13) << endl;
}
CAF_MAIN()
/******************************************************************************\
* Illustrates semantics of request().{then|await|receive}. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-58, 62-64 (MessagePassing.tex)
#include <vector>
#include <chrono>
#include <iostream>
#include "caf/all.hpp"
using std::endl;
using std::vector;
using std::chrono::seconds;
using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>,
replies_to<get_atom>::with<int>>;
struct cell_state {
int value = 0;
};
cell::behavior_type cell_impl(cell::stateful_pointer<cell_state> self, int x0) {
self->state.value = x0;
return {
[=](put_atom, int val) {
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
}
void waiting_testee(event_based_actor* self, vector<cell> cells) {
for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).await([=](int y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl;
});
}
void multiplexed_testee(event_based_actor* self, vector<cell> cells) {
for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).then([=](int y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl;
});
}
void blocking_testee(blocking_actor* self, vector<cell> cells) {
for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).receive([&](int y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl;
});
}
void caf_main(actor_system& system) {
vector<cell> cells;
for (auto i = 0; i < 5; ++i)
cells.emplace_back(system.spawn(cell_impl, i * i));
scoped_actor self{system};
aout(self) << "waiting_testee" << endl;
auto x1 = self->spawn(waiting_testee, cells);
self->wait_for(x1);
aout(self) << "multiplexed_testee" << endl;
auto x2 = self->spawn(multiplexed_testee, cells);
self->wait_for(x2);
aout(self) << "blocking_testee" << endl;
system.spawn(blocking_testee, cells);
}
CAF_MAIN()
......@@ -20,7 +20,7 @@ using calculator_type =
typed_actor<replies_to<plus_atom, int, int>::with<result_atom, int>,
replies_to<minus_atom, int, int>::with<result_atom, int>>;
calculator_type::behavior_type typed_calculator(calculator_type::pointer) {
calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) {
return {
[](plus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x + y);
......@@ -33,6 +33,10 @@ calculator_type::behavior_type typed_calculator(calculator_type::pointer) {
class typed_calculator_class : public calculator_type::base {
protected:
typed_calculator_class(actor_config& cfg) : calculator_type::base(cfg) {
// nop
}
behavior_type make_behavior() override {
return {
[](plus_atom, int x, int y) {
......@@ -47,16 +51,11 @@ protected:
void tester(event_based_actor* self, const calculator_type& testee) {
self->link_to(testee);
// will be invoked if we receive an unexpected response message
self->on_sync_failure([=] {
aout(self) << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
self->sync_send(testee, plus_atom::value, 2, 1).then(
self->request(testee, plus_atom::value, 2, 1).then(
[=](result_atom, int r1) {
// second test: 2 - 1 = 1
self->sync_send(testee, minus_atom::value, 2, 1).then(
self->request(testee, minus_atom::value, 2, 1).then(
[=](result_atom, int r2) {
// both tests succeeded
if (r1 == 3 && r2 == 1) {
......@@ -66,6 +65,11 @@ void tester(event_based_actor* self, const calculator_type& testee) {
self->send_exit(testee, exit_reason::user_shutdown);
}
);
},
[=](const error& err) {
aout(self) << "AUT (actor under test) failed: "
<< self->system().render(err) << endl;
self->quit(exit_reason::user_shutdown);
}
);
}
......@@ -73,12 +77,10 @@ void tester(event_based_actor* self, const calculator_type& testee) {
} // namespace <anonymous>
int main() {
actor_system system;
// test function-based impl
spawn(tester, spawn(typed_calculator));
await_all_actors_done();
system.spawn(tester, system.spawn(typed_calculator_fun));
system.await_all_actors_done();
// test class-based impl
spawn(tester, spawn<typed_calculator_class>());
await_all_actors_done();
// done
shutdown();
system.spawn(tester, system.spawn<typed_calculator_class>());
}
......@@ -57,58 +57,51 @@ void client(event_based_actor* self, const string& name) {
},
[=](const group_down_msg& g) {
cout << "*** chatroom offline: " << to_string(g.source) << endl;
},
others >> [=]() {
cout << "unexpected: " << to_string(self->current_message()) << endl;
}
);
}
int main(int argc, char** argv) {
string name;
string group_id;
auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"name,n", "set name", name},
{"group,g", "join group", group_id}
});
if (! res.error.empty()) {
cerr << res.error << endl;
return 1;
}
if (! res.remainder.empty()) {
std::cout << res.helptext << std::endl;
return 1;
}
if (res.opts.count("help") > 0) {
cout << res.helptext << endl;
return 0;
class config : public actor_system_config {
public:
std::string name;
std::string group_id;
void init() override {
opt_group{custom_options_, "global"}
.add(name, "name,n", "set name")
.add(group_id, "group,g", "join group");
}
};
void caf_main(actor_system& system, const config& cfg) {
auto name = cfg.name;
while (name.empty()) {
cout << "please enter your name: " << flush;
if (! getline(cin, name)) {
cerr << "*** no name given... terminating" << endl;
return 1;
return;
}
}
auto client_actor = spawn(client, name);
auto client_actor = system.spawn(client, name);
// evaluate group parameters
if (! group_id.empty()) {
auto p = group_id.find(':');
if (! cfg.group_id.empty()) {
auto p = cfg.group_id.find(':');
if (p == std::string::npos) {
cerr << "*** error parsing argument " << group_id
cerr << "*** error parsing argument " << cfg.group_id
<< ", expected format: <module_name>:<group_id>";
} else {
try {
auto module = group_id.substr(0, p);
auto group_uri = group_id.substr(p + 1);
auto g = (module == "remote") ? io::remote_group(group_uri)
: group::get(module, group_uri);
auto module = cfg.group_id.substr(0, p);
auto group_uri = cfg.group_id.substr(p + 1);
auto g = (module == "remote")
? system.middleman().remote_group(group_uri)
: system.groups().get(module, group_uri);
anon_send(client_actor, join_atom::value, g);
}
catch (exception& e) {
cerr << "*** exception: group::get(\"" << group_id.substr(0, p)
<< "\", \"" << group_id.substr(p + 1) << "\") failed; "
<< to_verbose_string(e) << endl;
cerr << "*** exception: group::get(\"" << cfg.group_id.substr(0, p)
<< "\", \"" << cfg.group_id.substr(p + 1) << "\") failed: "
<< e.what() << endl;
}
}
}
......@@ -117,22 +110,21 @@ int main(int argc, char** argv) {
vector<string> words;
for (istream_iterator<line> i(cin); i != eof; ++i) {
auto send_input = [&] {
if (!i->str.empty()) {
if (! i->str.empty())
anon_send(client_actor, broadcast_atom::value, i->str);
}
};
words.clear();
split(words, i->str, is_any_of(" "));
message_builder(words.begin(), words.end()).apply({
auto res = message_builder(words.begin(), words.end()).apply({
[&](const string& cmd, const string& mod, const string& id) {
if (cmd == "/join") {
try {
group grp = (mod == "remote") ? io::remote_group(id)
: group::get(mod, id);
group grp = (mod == "remote") ? system.middleman().remote_group(id)
: system.groups().get(mod, id);
anon_send(client_actor, join_atom::value, grp);
}
catch (exception& e) {
cerr << "*** exception: " << to_verbose_string(e) << endl;
cerr << "*** exception: " << e.what() << endl;
}
}
else {
......@@ -152,12 +144,13 @@ int main(int argc, char** argv) {
else {
send_input();
}
},
others >> send_input
}
});
if (! res)
send_input();
}
// force actor to quit
anon_send_exit(client_actor, exit_reason::user_shutdown);
await_all_actors_done();
shutdown();
}
CAF_MAIN(io::middleman)
......@@ -18,55 +18,25 @@
using namespace std;
using namespace caf;
int main(int argc, char** argv) {
class config : public actor_system_config {
public:
uint16_t port = 0;
auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"port,p", "set port", port}
});
if (! res.error.empty()) {
cerr << res.error << endl;
return 1;
}
if (res.opts.count("help") > 0) {
cout << res.helptext << endl;
return 0;
}
if (! res.remainder.empty()) {
// not all CLI arguments could be consumed
cerr << "*** too many arguments" << endl << res.helptext << endl;
return 1;
}
if (res.opts.count("port") == 0 || port <= 1024) {
cerr << "*** no valid port (>1024) given" << endl << res.helptext << endl;
return 1;
}
try {
// try to bind the group server to the given port,
// this allows other nodes to access groups of this server via
// group::get("remote", "<group>@<host>:<port>");
// note: it is not needed to explicitly create a <group> on the server,
// as groups are created on-the-fly on first usage
io::publish_local_groups(port);
}
catch (bind_failure& e) {
// thrown if <port> is already in use
cerr << "*** bind_failure: " << e.what() << endl;
return 2;
}
catch (network_error& e) {
// thrown on errors in the socket API
cerr << "*** network error: " << e.what() << endl;
return 2;
void init() override {
opt_group{custom_options_, "global"}
.add(port, "port,p", "set port");
}
};
void caf_main(actor_system& system, const config& cfg) {
system.middleman().publish_local_groups(cfg.port);
cout << "type 'quit' to shutdown the server" << endl;
string line;
while (getline(cin, line)) {
if (line == "quit") {
return 0;
}
else {
while (getline(cin, line))
if (line == "quit")
return;
else
cerr << "illegal command" << endl;
}
}
shutdown();
}
CAF_MAIN(io::middleman)
#include <vector>
#include <cassert>
#include <utility>
#include <iostream>
#include "caf/all.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/binary_deserializer.hpp"
using std::cout;
using std::cerr;
using std::endl;
using std::vector;
using namespace caf;
// POD struct
struct foo {
std::vector<int> a;
int b;
};
// announce requires foo to have the equal operator implemented
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a == rhs.a && lhs.b == rhs.b;
}
// a pair of two ints
using foo_pair = std::pair<int, int>;
// another pair of two ints
using foo_pair2 = std::pair<int, int>;
// a struct with member vector<vector<...>>
struct foo2 {
int a;
vector<vector<double>> b;
};
bool operator==(const foo2& lhs, const foo2& rhs) {
return lhs.a == rhs.a && lhs.b == rhs.b;
}
// receives `remaining` messages
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
// note: we sent a foo_pair2, but match on foo_pair
// that's safe because both are aliases for std::pair<int, int>
[=](const foo_pair& val) {
cout << "foo_pair("
<< val.first << ", "
<< val.second << ")"
<< endl;
set_next_behavior();
},
[=](const foo& val) {
cout << "foo({";
auto i = val.a.begin();
auto end = val.a.end();
if (i != end) {
cout << *i;
while (++i != end) {
cout << ", " << *i;
}
}
cout << "}, " << val.b << ")" << endl;
set_next_behavior();
}
);
}
int main(int, char**) {
// announces foo to the libcaf type system;
// the function expects member pointers to all elements of foo
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", &foo2::a, &foo2::b);
// serialization can throw if types are not announced properly
try {
// init some test data
foo2 vd;
vd.a = 5;
vd.b.resize(1);
vd.b.back().push_back(42);
// serialize test data
vector<char> buf;
binary_serializer bs(std::back_inserter(buf));
bs << vd;
// deserialize written test data from buffer
binary_deserializer bd(buf.data(), buf.size());
foo2 vd2;
uniform_typeid<foo2>()->deserialize(&vd2, &bd);
// deserialized data must be equal to original input
assert(vd == vd2);
// 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 the type aliases foo_pair and foo_pair2
assert(uniform_typeid<foo_pair>() == uniform_typeid<foo_pair2>());
}
catch (std::exception& e) {
cerr << "error during type (de)serialization: " << e.what() << endl;
return -1;
}
// spawn a testee that receives two messages of user-defined type
auto t = spawn(testee, size_t{2});
{ // lifetime scope of self
scoped_actor self;
// send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2
self->send(t, foo_pair2{3, 4});
}
await_all_actors_done();
shutdown();
}
#include <utility>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// a simple class using getter and setter member functions
class foo {
int a_;
int b_;
public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) { }
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return a_; }
void set_a(int val) { a_ = val; }
int b() const { return b_; }
void set_b(int val) { b_ = val; }
};
// announce requires foo to be comparable
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
void testee(event_based_actor* self) {
self->become (
[=](const foo& val) {
aout(self) << "foo("
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit();
}
);
}
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>("foo", make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
{
scoped_actor self;
auto t = spawn(testee);
self->send(t, foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
}
#include <utility>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// a simple class using overloaded getter and setter member functions
class foo {
int a_;
int b_;
public:
foo() : a_(0), b_(0) { }
foo(int a0, int b0) : a_(a0), b_(b0) { }
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return a_; }
void a(int val) { a_ = val; }
int b() const { return b_; }
void b(int val) { b_ = val; }
};
// announce requires foo to have the equal operator implemented
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
// a member function pointer to get an attribute of foo
using foo_getter = int (foo::*)() const;
// a member function pointer to set an attribute of foo
using foo_setter = void (foo::*)(int);
void testee(event_based_actor* self) {
self->become (
[=](const foo& val) {
aout(self) << "foo("
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit();
}
);
}
int main(int, char**) {
// since the member function "a" is ambiguous, the compiler
// also needs a type to select the correct overload
foo_getter g1 = &foo::a;
foo_setter s1 = &foo::a;
// same is true for b
foo_getter g2 = &foo::b;
foo_setter s2 = &foo::b;
// equal to example 3
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>("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
{
scoped_actor self;
self->send(spawn(testee), foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
}
#include <iostream>
#include "caf/all.hpp"
#include "caf/to_string.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// the foo class from example 3
class foo {
int a_;
int b_;
public:
foo() : a_(0), b_(0) { }
foo(int a0, int b0) : a_(a0), b_(b0) { }
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return a_; }
void set_a(int val) { a_ = val; }
int b() const { return b_; }
void set_b(int val) { b_ = val; }
};
// needed for operator==() of bar
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
// simple struct that has foo as a member
struct bar {
foo f;
int i;
};
// announce requires bar to have the equal operator implemented
bool operator==(const bar& lhs, const bar& rhs) {
return lhs.f == rhs.f
&& lhs.i == rhs.i;
}
// "worst case" class ... not a good software design at all ;)
class baz {
foo f_;
public:
bar b;
// announce requires a default constructor
baz() = default;
inline baz(const foo& mf, const bar& mb) : f_(mf), b(mb) { }
const foo& f() const { return f_; }
void set_f(const foo& val) { f_ = val; }
};
// even worst case classes have to implement operator==
bool operator==(const baz& lhs, const baz& rhs) {
return lhs.f() == rhs.f()
&& lhs.b == rhs.b;
}
// receives `remaining` messages
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
[=](const bar& val) {
aout(self) << "bar(foo("
<< val.f.a() << ", "
<< val.f.b() << "), "
<< val.i << ")"
<< endl;
set_next_behavior();
},
[=](const baz& val) {
// prints: baz ( foo ( 1, 2 ), bar ( foo ( 3, 4 ), 5 ) )
aout(self) << to_string(make_message(val)) << endl;
set_next_behavior();
}
);
}
int main(int, char**) {
// bar has a non-trivial data member f, thus, we have to told
// announce how to serialize/deserialize this member;
// this is was the compound_member function is for;
// 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
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>("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, size_t{2});
{
scoped_actor self;
self->send(t, bar{foo{1, 2}, 3});
self->send(t, baz{foo{1, 2}, bar{foo{3, 4}, 5}});
}
await_all_actors_done();
shutdown();
return 0;
}
/******************************************************************************
* This example shows how to implement serialize/deserialize to announce *
* non-trivial data structures to the libcaf type system. *
* *
* Announce() auto-detects STL compliant containers and provides *
* an easy way to tell libcaf how to serialize user defined types. *
* See announce_example 1-4 for usage examples. *
* *
* You should use "hand written" serialize/deserialize implementations *
* if and only if there is no other way. *
******************************************************************************/
#include <cstdint>
#include <iostream>
#include "caf/all.hpp"
#include "caf/to_string.hpp"
using std::cout;
using std::endl;
using namespace caf;
namespace {
// a node containing an integer and a vector of children
struct tree_node {
std::uint32_t value;
std::vector<tree_node> children;
explicit tree_node(std::uint32_t v = 0) : value(v) {
// nop
}
tree_node& add_child(std::uint32_t v = 0) {
children.emplace_back(v);
return *this;
}
tree_node(const tree_node&) = default;
// recursively print this node and all of its children to stdout
void print() const {
// format is: value { children0, children1, ..., childrenN }
// e.g., 10 { 20 { 21, 22 }, 30 }
cout << value;
if (children.empty() == false) {
cout << " { ";
auto begin = children.begin();
auto end = children.end();
for (auto i = begin; i != end; ++i) {
if (i != begin) {
cout << ", ";
}
i->print();
}
cout << " } ";
}
}
};
// a very primitive tree implementation
struct tree {
tree_node root;
// print tree to stdout
void print() const {
cout << "tree::print: ";
root.print();
cout << endl;
}
};
// tree nodes are equals if values and all values of all children are equal
bool operator==(const tree_node& lhs, const tree_node& rhs) {
return (lhs.value == rhs.value) && (lhs.children == rhs.children);
}
bool operator==(const tree& lhs, const tree& rhs) {
return lhs.root == rhs.root;
}
// abstract_uniform_type_info implements all functions of uniform_type_info
// except for serialize() and deserialize() if the template parameter T:
// - does have a default constructor
// - does have a copy constructor
// - does provide operator==
class tree_type_info : public abstract_uniform_type_info<tree> {
public:
tree_type_info() : 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);
// recursively serialize nodes, beginning with root
serialize_node(tree_ptr->root, sink);
}
void deserialize(void* ptr, deserializer* source) const {
// ptr is guaranteed to be a pointer of type tree
auto tree_ptr = reinterpret_cast<tree*>(ptr);
tree_ptr->root.children.clear();
// recursively deserialize nodes, beginning with root
deserialize_node(tree_ptr->root, source);
}
private:
void serialize_node(const tree_node& node, serializer* sink) const {
// value, ... children ...
sink->write_value(node.value);
sink->begin_sequence(node.children.size());
for (const tree_node& subnode : node.children) {
serialize_node(subnode, sink);
}
sink->end_sequence();
}
void deserialize_node(tree_node& node, deserializer* source) const {
// value, ... children ...
auto value = source->read<std::uint32_t>();
node.value = value;
auto num_children = source->begin_sequence();
for (size_t i = 0; i < num_children; ++i) {
node.add_child();
deserialize_node(node.children.back(), source);
}
source->end_sequence();
}
};
using tree_vector = std::vector<tree>;
// receives `remaining` messages
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
[=](const tree& tmsg) {
// prints the tree in its serialized format:
// @<> ( { tree ( 0, { 10, { 11, { }, 12, { }, 13, { } }, 20, { 21, { }, 22, { } } } ) } )
cout << "to_string(self->current_message()): "
<< to_string(self->current_message())
<< endl;
// prints the tree using the print member function:
// 0 { 10 { 11, 12, 13 } , 20 { 21, 22 } }
tmsg.print();
set_next_behavior();
},
[=](const tree_vector& trees) {
// prints "received 2 trees"
cout << "received " << trees.size() << " trees" << endl;
// prints:
// @<> ( {
// std::vector<tree, std::allocator<tree>> ( {
// tree ( 0, { 10, { 11, { }, 12, { }, 13, { } }, 20, { 21, { }, 22, { } } } ),
// tree ( 0, { 10, { 11, { }, 12, { }, 13, { } }, 20, { 21, { }, 22, { } } } )
// )
// } )
cout << "to_string: " << to_string(self->current_message()) << endl;
set_next_behavior();
}
);
}
} // namespace <anonymous>
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>("tree_vector");
tree t0; // create a tree and fill it with some data
t0.root.add_child(10);
t0.root.children.back().add_child(11).add_child(12).add_child(13);
t0.root.add_child(20);
t0.root.children.back().add_child(21).add_child(22);
/*
tree t is now:
0
/ \
/ \
/ \
10 20
/ |\ / \
/ | \ / \
11 12 13 21 22
*/
{ // lifetime scope of self
scoped_actor self;
// spawn a testee that receives two messages
auto t = spawn(testee, size_t{2});
// send a tree
self->send(t, t0);
// send a vector of trees
tree_vector tvec;
tvec.push_back(t0);
tvec.push_back(t0);
self->send(t, tvec);
}
await_all_actors_done();
shutdown();
return 0;
}
build/**
benchmarks/**
cash/**
examples/**
nexus/**
manual/**
......@@ -13,33 +13,38 @@ file(GLOB_RECURSE LIBCAF_CORE_HDRS "caf/*.hpp")
set (LIBCAF_CORE_SRCS
src/abstract_actor.cpp
src/abstract_channel.cpp
src/abstract_composable_behavior.cpp
src/abstract_group.cpp
src/abstract_coordinator.cpp
src/actor.cpp
src/actor_addr.cpp
src/actor_config.cpp
src/actor_control_block.cpp
src/actor_companion.cpp
src/actor_namespace.cpp
src/actor_ostream.cpp
src/actor_pool.cpp
src/actor_proxy.cpp
src/actor_registry.cpp
src/actor_system.cpp
src/actor_system_config.cpp
src/adapter.cpp
src/atom.cpp
src/attachable.cpp
src/announce_actor_type.cpp
src/behavior.cpp
src/behavior_stack.cpp
src/behavior_impl.cpp
src/binary_deserializer.cpp
src/binary_serializer.cpp
src/blocking_actor.cpp
src/channel.cpp
src/concatenated_tuple.cpp
src/config_option.cpp
src/continue_helper.cpp
src/decorated_tuple.cpp
src/deep_to_string.cpp
src/default_attachable.cpp
src/deserializer.cpp
src/detached_threads.cpp
src/duration.cpp
src/either.cpp
src/dynamic_message_data.cpp
src/error.cpp
src/event_based_actor.cpp
src/exception.cpp
src/execution_unit.cpp
......@@ -50,9 +55,12 @@ set (LIBCAF_CORE_SRCS
src/get_root_uuid.cpp
src/group.cpp
src/group_manager.cpp
src/invoke_result_visitor.cpp
src/match_case.cpp
src/merged_tuple.cpp
src/monitorable_actor.cpp
src/local_actor.cpp
src/logging.cpp
src/logger.cpp
src/mailbox_element.cpp
src/memory.cpp
src/memory_managed.cpp
......@@ -61,26 +69,26 @@ set (LIBCAF_CORE_SRCS
src/message_data.cpp
src/message_handler.cpp
src/node_id.cpp
src/parse_config.cpp
src/parse_ini.cpp
src/ref_counted.cpp
src/proxy_registry.cpp
src/response_promise.cpp
src/replies_to.cpp
src/resumable.cpp
src/ripemd_160.cpp
src/run_sub_unit_test.cpp
src/scoped_actor.cpp
src/set_scheduler.cpp
src/scoped_execution_unit.cpp
src/sec.cpp
src/serializer.cpp
src/sequencer.cpp
src/shared_spinlock.cpp
src/shutdown.cpp
src/singletons.cpp
src/string_serialization.cpp
src/skip.cpp
src/splitter.cpp
src/sync_request_bouncer.cpp
src/try_match.cpp
src/uniform_type_info.cpp
src/uniform_type_info_map.cpp
src/whereis.cpp)
src/type_erased_value.cpp
src/type_erased_tuple.cpp
src/uniform_type_info_map.cpp)
add_custom_target(libcaf_core)
......
This diff is collapsed.
......@@ -23,44 +23,33 @@
#include <atomic>
#include "caf/fwd.hpp"
#include "caf/node_id.hpp"
#include "caf/message_id.hpp"
#include "caf/ref_counted.hpp"
namespace caf {
/// Interface for all message receivers. * This interface describes an
/// entity that can receive messages and is implemented by {@link actor}
/// and {@link group}.
class abstract_channel : public ref_counted {
class abstract_channel {
public:
friend class abstract_actor;
friend class abstract_group;
virtual ~abstract_channel();
/// Enqueues a new message to the channel.
virtual void enqueue(const actor_addr& sender, message_id mid,
message content, execution_unit* host) = 0;
/// Enqueues a new message without forwarding stack to the channel.
virtual void enqueue(strong_actor_ptr sender, message_id mid, message content,
execution_unit* host = nullptr) = 0;
/// Enqueues a new message wrapped in a `mailbox_element` to the channel.
/// This variant is used by actors whenever it is possible to allocate
/// mailbox element and message on the same memory block and is thus
/// more efficient. Non-actors use the default implementation which simply
/// calls the pure virtual version.
virtual void enqueue(mailbox_element_ptr what, execution_unit* host);
static constexpr int is_abstract_actor_flag = 0x01000000;
/// Returns the ID of the node this actor is running on.
inline node_id node() const {
return node_;
}
static constexpr int is_abstract_group_flag = 0x02000000;
/// Returns true if {@link node_ptr} returns
bool is_remote() const;
static constexpr int is_actor_bind_decorator_flag = 0x04000000;
static constexpr int is_abstract_actor_flag = 0x100000;
static constexpr int is_actor_dot_decorator_flag = 0x08000000;
static constexpr int is_abstract_group_flag = 0x200000;
static constexpr int is_actor_decorator_mask = 0x0C000000;
inline bool is_abstract_actor() const {
return static_cast<bool>(flags() & is_abstract_actor_flag);
......@@ -70,6 +59,10 @@ public:
return static_cast<bool>(flags() & is_abstract_group_flag);
}
inline bool is_actor_decorator() const {
return static_cast<bool>(flags() & is_actor_decorator_mask);
}
protected:
// note: *both* operations use relaxed memory order, this is because
// only the actor itself is granted write access while all access
......@@ -87,23 +80,14 @@ protected:
private:
// can only be called from abstract_actor and abstract_group
explicit abstract_channel(int init_flags);
abstract_channel(int init_flags, node_id nid);
/*
* Accumulates several state and type flags. Subtypes may use only the
* first 20 bits, i.e., the bitmask 0xFFF00000 is reserved for
* channel-related flags.
*/
abstract_channel(int init_flags);
// Accumulates several state and type flags. Subtypes may use only the
// first 20 bits, i.e., the bitmask 0xFFF00000 is reserved for
// channel-related flags.
std::atomic<int> flags_;
// identifies the node of this channel
node_id node_;
};
/// A smart pointer to an abstract channel.
/// @relates abstract_channel_ptr
using abstract_channel_ptr = intrusive_ptr<abstract_channel>;
} // namespace caf
#endif // CAF_ABSTRACT_CHANNEL_HPP
......@@ -17,36 +17,29 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_ANYTHING_HPP
#define CAF_ANYTHING_HPP
#ifndef CAF_ABSTRACT_COMPOSABLE_STATE_HPP
#define CAF_ABSTRACT_COMPOSABLE_STATE_HPP
#include <type_traits>
#include <utility>
namespace caf {
#include "caf/fwd.hpp"
#include "caf/sec.hpp"
#include "caf/message.hpp"
/// Acts as wildcard expression in patterns.
struct anything {
constexpr anything() {
// nop
}
};
#include "caf/detail/int_list.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/pseudo_tuple.hpp"
/// @relates anything
inline bool operator==(const anything&, const anything&) {
return true;
}
namespace caf {
/// @relates anything
inline bool operator!=(const anything&, const anything&) {
return false;
}
/// Marker type that allows CAF to spawn actors from composable states.
class abstract_composable_behavior {
public:
virtual ~abstract_composable_behavior();
/// @relates anything
template <class T>
struct is_anything : std::is_same<T, anything> {
// no content
virtual void init_behavior(behavior& x) = 0;
};
} // namespace caf
#endif // CAF_ANYTHING_HPP
#endif // CAF_ABSTRACT_COMPOSABLE_STATE_HPP
......@@ -32,7 +32,7 @@
namespace caf {
/// A multicast group.
class abstract_group : public abstract_channel {
class abstract_group : public ref_counted, public abstract_channel {
public:
friend class local_actor;
friend class subscription;
......@@ -43,13 +43,17 @@ public:
/// Interface for user-defined multicast implementations.
class module {
public:
module(std::string module_name);
module(actor_system& sys, std::string module_name);
virtual ~module();
/// Stops all groups from this module.
virtual void stop() = 0;
inline actor_system& system() const {
return system_;
}
/// Returns the name of this module implementation.
/// @threadsafe
const std::string& name() const;
......@@ -58,16 +62,17 @@ public:
/// @threadsafe
virtual group get(const std::string& group_name) = 0;
virtual group deserialize(deserializer* source) = 0;
virtual group load(deserializer& source) = 0;
private:
actor_system& system_;
std::string name_;
};
using module_ptr = module*;
using unique_module_ptr = std::unique_ptr<module>;
virtual void serialize(serializer* sink) = 0;
virtual void save(serializer& sink) const = 0;
/// Returns a string representation of the group identifier, e.g.,
/// "224.0.0.1" for IPv4 multicast or a user-defined string for local groups.
......@@ -80,19 +85,38 @@ public:
/// Subscribes `who` to this group and returns `true` on success
/// or `false` if `who` is already subscribed.
virtual bool subscribe(const actor_addr& who) = 0;
virtual bool subscribe(strong_actor_ptr who) = 0;
/// Stops any background actors or threads and IO handles.
virtual void stop() = 0;
inline actor_system& system() {
return system_;
}
/// @cond PRIVATE
template <class... Ts>
void eq_impl(message_id mid, strong_actor_ptr sender,
execution_unit* ctx, Ts&&... xs) {
CAF_ASSERT(! mid.is_request());
enqueue(std::move(sender), mid,
make_message(std::forward<Ts>(xs)...), ctx);
}
/// @endcond
protected:
abstract_group(module_ptr module, std::string group_id, const node_id& nid);
abstract_group(actor_system& sys, module_ptr module,
std::string group_id, const node_id& nid);
// called by local_actor
virtual void unsubscribe(const actor_addr& who) = 0;
virtual void unsubscribe(const actor_control_block* who) = 0;
actor_system& system_;
module_ptr module_;
std::string identifier_;
node_id origin_;
};
/// A smart pointer type that manages instances of {@link group}.
......
......@@ -20,84 +20,82 @@
#ifndef CAF_ACTOR_HPP
#define CAF_ACTOR_HPP
#include <string>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <type_traits>
#include "caf/intrusive_ptr.hpp"
#include "caf/fwd.hpp"
#include "caf/message.hpp"
#include "caf/actor_marker.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/unsafe_actor_handle_init.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
class scoped_actor;
struct invalid_actor_t {
constexpr invalid_actor_t() {
// nop
}
};
/// Identifies an invalid {@link actor}.
/// @relates actor
constexpr invalid_actor_t invalid_actor = invalid_actor_t{};
template <class T>
struct is_convertible_to_actor {
using type = typename std::remove_pointer<T>::type;
static constexpr bool value =
! std::is_base_of<statically_typed_actor_base, type>::value
&& (std::is_base_of<actor_proxy, type>::value
|| std::is_base_of<local_actor, type>::value
|| std::is_same<scoped_actor, type>::value);
! std::is_base_of<statically_typed_actor_base, T>::value
&& (std::is_base_of<actor_proxy, T>::value
|| std::is_base_of<local_actor, T>::value);
};
template <>
struct is_convertible_to_actor<scoped_actor> : std::true_type {
// nop
};
/// Identifies an untyped actor. Can be used with derived types
/// of `event_based_actor`, `blocking_actor`, and `actor_proxy`.
class actor : detail::comparable<actor>,
detail::comparable<actor, actor_addr>,
detail::comparable<actor, invalid_actor_t>,
detail::comparable<actor, invalid_actor_addr_t> {
detail::comparable<actor, strong_actor_ptr> {
public:
// grant access to private ctor
// -- friend types that need access to private ctors
friend class local_actor;
template <class>
friend class data_processor;
template <class>
friend class type_erased_value_impl;
using signatures = none_t;
// allow conversion via actor_cast
template <class T, typename U>
friend T actor_cast(const U&);
template <class, class, int>
friend class actor_cast_access;
// tell actor_cast which semantic this type uses
static constexpr bool has_weak_ptr_semantics = false;
// tell actor_cast this is a non-null handle type
static constexpr bool has_non_null_guarantee = true;
actor() = default;
actor(actor&&) = default;
actor(const actor&) = default;
actor& operator=(actor&&) = default;
actor& operator=(const actor&) = default;
template <class T,
class Enable =
typename std::enable_if<
is_convertible_to_actor<T>::value
>::type>
actor(intrusive_ptr<T> ptr) : ptr_(std::move(ptr)) {
// nop
}
actor(const scoped_actor&);
explicit actor(const unsafe_actor_handle_init_t&);
template <class T,
class Enable =
typename std::enable_if<
is_convertible_to_actor<T>::value
>::type>
actor(T* ptr) : ptr_(ptr) {
// nop
class = typename std::enable_if<
std::is_base_of<dynamically_typed_actor_base, T>::value
>::type>
actor(T* ptr) : ptr_(ptr->ctrl()) {
CAF_ASSERT(ptr != nullptr);
}
actor(const invalid_actor_t&);
template <class T>
typename std::enable_if<is_convertible_to_actor<T>::value, actor&>::type
operator=(intrusive_ptr<T> ptr) {
......@@ -114,65 +112,109 @@ public:
return *this;
}
actor& operator=(const invalid_actor_t&);
actor& operator=(const scoped_actor& x);
/// Returns the address of the stored actor.
actor_addr address() const noexcept;
/// Returns `*this != invalid_actor`.
inline operator bool() const noexcept {
return static_cast<bool>(ptr_);
/// Returns the origin node of this actor.
inline node_id node() const noexcept {
return ptr_->node();
}
/// Returns `*this == invalid_actor`.
inline bool operator!() const noexcept {
return ! ptr_;
}
/// Returns whether this is an handle to a remote actor.
bool is_remote() const noexcept;
/// Returns the ID of this actor.
actor_id id() const noexcept;
inline actor_id id() const noexcept {
return ptr_->id();
}
/// Exchange content of `*this` and `other`.
void swap(actor& other) noexcept;
/// Returns the interface definition for this actor handle.
static std::set<std::string> message_types() {
return std::set<std::string>{};
/// Create a new actor decorator that presets or reorders inputs.
template <class... Ts>
actor bind(Ts&&... xs) const {
return bind_impl(make_message(std::forward<Ts>(xs)...));
}
/// Queries whether this object was constructed using
/// `unsafe_actor_handle_init` or is in moved-from state.
bool unsafe() const {
return ! ptr_;
}
/// @cond PRIVATE
inline abstract_actor* operator->() const noexcept {
return ptr_.get();
CAF_ASSERT(ptr_);
return ptr_->get();
}
intptr_t compare(const actor&) const noexcept;
intptr_t compare(const actor_addr&) const noexcept;
inline intptr_t compare(const invalid_actor_t&) const noexcept {
return ptr_ ? 1 : 0;
intptr_t compare(const strong_actor_ptr&) const noexcept;
static actor splice_impl(std::initializer_list<actor> xs);
actor(actor_control_block*, bool);
template <class Processor>
friend void serialize(Processor& proc, actor& x, const unsigned int v) {
serialize(proc, x.ptr_, v);
}
inline intptr_t compare(const invalid_actor_addr_t&) const noexcept {
return compare(invalid_actor);
friend inline std::string to_string(const actor& x) {
return to_string(x.ptr_);
}
/// @endcond
/// Releases the reference held by handle `x`. Using the
/// handle after invalidating it is undefined behavior.
friend void invalidate(actor& x) {
x.ptr_.reset();
}
private:
inline abstract_actor* get() const noexcept {
actor() = default;
actor bind_impl(message msg) const;
inline actor_control_block* get() const noexcept {
return ptr_.get();
}
actor(abstract_actor*);
inline actor_control_block* release() noexcept {
return ptr_.release();
}
actor(actor_control_block*);
abstract_actor_ptr ptr_;
strong_actor_ptr ptr_;
};
/// Combine `f` and `g` so that `(f*g)(x) = f(g(x))`.
actor operator*(actor f, actor g);
/// @relates actor
template <class... Ts>
actor splice(const actor& x, const actor& y, const Ts&... zs) {
return actor::splice_impl({x, y, zs...});
}
/// @relates actor
bool operator==(const actor& lhs, abstract_actor* rhs);
/// @relates actor
bool operator==(abstract_actor* lhs, const actor& rhs);
/// @relates actor
bool operator!=(const actor& lhs, abstract_actor* rhs);
/// @relates actor
bool operator!=(abstract_actor* lhs, const actor& rhs);
} // namespace caf
// allow actor to be used in hash maps
......@@ -180,9 +222,8 @@ namespace std {
template <>
struct hash<caf::actor> {
inline size_t operator()(const caf::actor& ref) const {
return ref ? static_cast<size_t>(ref->id()) : 0;
return static_cast<size_t>(ref->id());
}
};
} // namespace std
......
......@@ -25,59 +25,52 @@
#include <functional>
#include <type_traits>
#include "caf/intrusive_ptr.hpp"
#include "caf/fwd.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/unsafe_actor_handle_init.hpp"
#include "caf/detail/comparable.hpp"
namespace caf {
struct invalid_actor_addr_t {
constexpr invalid_actor_addr_t() {}
};
/// Identifies an invalid {@link actor_addr}.
/// @relates actor_addr
constexpr invalid_actor_addr_t invalid_actor_addr = invalid_actor_addr_t{};
/// Stores the address of typed as well as untyped actors.
class actor_addr : detail::comparable<actor_addr>,
detail::comparable<actor_addr, weak_actor_ptr>,
detail::comparable<actor_addr, strong_actor_ptr>,
detail::comparable<actor_addr, abstract_actor*>,
detail::comparable<actor_addr, abstract_actor_ptr> {
detail::comparable<actor_addr, actor_control_block*> {
public:
// -- friend types that need access to private ctors
template <class>
friend class type_erased_value_impl;
template <class>
friend class data_processor;
// grant access to private ctor
friend class actor;
friend class abstract_actor;
friend class down_msg;
friend class exit_msg;
template <class T, typename U>
friend T actor_cast(const U&);
// allow conversion via actor_cast
template <class, class, int>
friend class actor_cast_access;
// tell actor_cast which semantic this type uses
static constexpr bool has_weak_ptr_semantics = true;
// tell actor_cast this is a nullable handle type
static constexpr bool has_non_null_guarantee = true;
actor_addr() = default;
actor_addr(actor_addr&&) = default;
actor_addr(const actor_addr&) = default;
actor_addr& operator=(actor_addr&&) = default;
actor_addr& operator=(const actor_addr&) = default;
actor_addr(const invalid_actor_addr_t&);
actor_addr operator=(const invalid_actor_addr_t&);
/// Returns `*this != invalid_actor_addr`.
inline explicit operator bool() const noexcept {
return static_cast<bool>(ptr_);
}
/// Returns `*this == invalid_actor_addr`.
inline bool operator!() const noexcept {
return !ptr_;
}
/// Returns whether this is an handle to a remote actor.
bool is_remote() const noexcept;
actor_addr(const unsafe_actor_handle_init_t&);
/// Returns the ID of this actor.
actor_id id() const noexcept;
......@@ -85,35 +78,71 @@ public:
/// Returns the origin node of this actor.
node_id node() const noexcept;
/// Returns the hosting actor system.
actor_system* home_system() const noexcept;
/// Exchange content of `*this` and `other`.
void swap(actor_addr& other) noexcept;
/// @cond PRIVATE
inline abstract_actor* operator->() const noexcept {
return ptr_.get();
}
static intptr_t compare(const actor_control_block* lhs,
const actor_control_block* rhs);
intptr_t compare(const actor_addr& other) const noexcept;
intptr_t compare(const abstract_actor* other) const noexcept;
inline intptr_t compare(const abstract_actor_ptr& other) const noexcept {
intptr_t compare(const actor_control_block* other) const noexcept;
inline intptr_t compare(const weak_actor_ptr& other) const noexcept {
return compare(other.get());
}
inline intptr_t compare(const strong_actor_ptr& other) const noexcept {
return compare(other.get());
}
template <class Processor>
friend void serialize(Processor& proc, actor_addr& x, const unsigned int v) {
serialize(proc, x.ptr_, v);
}
friend inline std::string to_string(const actor_addr& x) {
return to_string(x.ptr_);
}
/// Releases the reference held by handle `x`. Using the
/// handle after invalidating it is undefined behavior.
friend void invalidate(actor_addr& x) {
x.ptr_.reset();
}
actor_addr(actor_control_block*, bool);
/// @endcond
private:
inline abstract_actor* get() const noexcept {
actor_addr() = default;
inline actor_control_block* get() const noexcept {
return ptr_.get();
}
explicit actor_addr(abstract_actor*);
inline actor_control_block* release() noexcept {
return ptr_.release();
}
abstract_actor_ptr ptr_;
inline actor_control_block* get_locked() const noexcept {
return ptr_.get_locked();
}
actor_addr(actor_control_block*);
weak_actor_ptr ptr_;
};
} // namespace caf
// allow actor_addr to be used in hash maps
......@@ -123,7 +152,6 @@ struct hash<caf::actor_addr> {
inline size_t operator()(const caf::actor_addr& ref) const {
return static_cast<size_t>(ref.id());
}
};
} // namespace std
......
......@@ -20,12 +20,182 @@
#ifndef CAF_ACTOR_CAST_HPP
#define CAF_ACTOR_CAST_HPP
#include <type_traits>
#include "caf/fwd.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/actor_control_block.hpp"
namespace caf {
/// Converts actor handle `what` to a different actor handle of type `T`.
template <class T, typename U>
T actor_cast(const U& what) {
return what.get();
namespace {
// The function actor_cast<> computes the type of the cast for
// actor_cast_access via the following formula:
// x = 0 if To is a raw poiner
// = 1 if To is a strong pointer
// = 2 if To is a weak pointer
// y = 0 if From is a raw poiner
// = 6 if From is a strong pointer
// = 2 if From is a weak pointer
// the result of x * y + z then denotes which operation the cast is performing:
// raw <- raw = 0
// raw <- weak = 0
// raw <- strong = 0
// weak <- raw = 0
// weak <- weak = 6
// weak <- strong = 12
// strong <- raw = 0
// strong <- weak = 3
// strong <- strong = 6
// x * y is then interpreted as follows:
// - 0 is a conversion to or from a raw pointer
// - 6 is a conversion between pointers with same semantics
// - 3 is a conversion from a weak pointer to a strong pointer
// - 12 is a conversion from a strong pointer to a weak pointer
constexpr int raw_ptr_cast = 0; // either To or From is a raw pointer
constexpr int weak_ptr_downgrade_cast = 12; // To is weak, From is strong
constexpr int weak_ptr_upgrade_cast = 3; // To is strong, From is weak
constexpr int neutral_cast = 6; // To and From are both weak or both strong
template <class T>
struct is_weak_ptr {
static constexpr bool value = T::has_weak_ptr_semantics;
};
template <class T>
struct is_weak_ptr<T*> {
static constexpr bool value = false;
};
template <class T>
struct is_non_null_handle {
static constexpr bool value = T::has_non_null_guarantee;
};
template <class T>
struct is_non_null_handle<T*> {
static constexpr bool value = false;
};
template <class T>
struct is_strong_non_null_handle {
static constexpr bool value = ! is_weak_ptr<T>::value
&& is_non_null_handle<T>::value;
};
} // namespace <anonymous>
template <class To, class From, int>
class actor_cast_access;
template <class To, class From>
class actor_cast_access<To, From, raw_ptr_cast> {
public:
To operator()(actor_control_block* x) const {
return x;
}
To operator()(abstract_actor* x) const {
return x->ctrl();
}
template <class T,
class = typename std::enable_if<! std::is_pointer<T>::value>::type>
To operator()(const T& x) const {
return x.get();
}
};
template <class To, class From>
class actor_cast_access<To*, From, raw_ptr_cast> {
public:
To* operator()(actor_control_block* x) const {
return static_cast<To*>(x->get());
}
To* operator()(abstract_actor* x) const {
return static_cast<To*>(x);
}
template <class T,
class = typename std::enable_if<! std::is_pointer<T>::value>::type>
To* operator()(const T& x) const {
return (*this)(x.get());
}
};
template <class From>
class actor_cast_access<actor_control_block*, From, raw_ptr_cast> {
public:
actor_control_block* operator()(actor_control_block* x) const {
return x;
}
actor_control_block* operator()(abstract_actor* x) const {
return x->ctrl();
}
template <class T,
class = typename std::enable_if<! std::is_pointer<T>::value>::type>
actor_control_block* operator()(const T& x) const {
return x.get();
}
};
template <class To, class From>
class actor_cast_access<To, From, weak_ptr_downgrade_cast> {
public:
To operator()(const From& x) const {
return x.get();
}
};
template <class To, class From>
class actor_cast_access<To, From, weak_ptr_upgrade_cast> {
public:
To operator()(const From& x) const {
return {x.get_locked(), false};
}
};
template <class To, class From>
class actor_cast_access<To, From, neutral_cast> {
public:
To operator()(const From& x) const {
return x.get();
}
To operator()(From&& x) const {
return {x.release(), false};
}
};
/// Converts actor handle `what` to a different actor
/// handle or raw pointer of type `T`.
template <class T, class U>
T actor_cast(U&& what) {
using from_type =
typename std::remove_const<
typename std::remove_reference<U>::type
>::type;
// query traits for T
constexpr bool to_raw = std::is_pointer<T>::value;
constexpr bool to_weak = is_weak_ptr<T>::value;
// query traits for U
constexpr bool from_raw = std::is_pointer<from_type>::value;
constexpr bool from_weak = is_weak_ptr<from_type>::value;
// check whether this cast is legal
static_assert(! from_weak || ! is_strong_non_null_handle<T>::value,
"casts from actor_addr to actor or typed_actor are prohibited");
// calculate x and y
constexpr int x = to_raw ? 0 : (to_weak ? 2 : 1);
constexpr int y = from_raw ? 0 : (from_weak ? 3 : 6);
// perform cast
actor_cast_access<T, from_type, x * y> f;
return f(std::forward<U>(what));
}
} // namespace caf
......
......@@ -23,11 +23,12 @@
#include <memory>
#include <functional>
#include "caf/extend.hpp"
#include "caf/local_actor.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/abstract_event_based_actor.hpp"
#include "caf/mixin/sync_sender.hpp"
#include "caf/mixin/sender.hpp"
#include "caf/mixin/behavior_changer.hpp"
#include "caf/detail/disposer.hpp"
#include "caf/detail/shared_spinlock.hpp"
......@@ -38,7 +39,8 @@ namespace caf {
/// callback to another object, thus serving as gateway to
/// allow any object to interact with other actors.
/// @extends local_actor
class actor_companion : public abstract_event_based_actor<behavior, true> {
class actor_companion : public extend<local_actor, actor_companion>::
with<mixin::sender> {
public:
using lock_type = detail::shared_spinlock;
using message_pointer = std::unique_ptr<mailbox_element, detail::disposer>;
......@@ -46,7 +48,7 @@ public:
/// Removes the handler for incoming messages and terminates
/// the companion for exit reason @ rsn.
void disconnect(std::uint32_t rsn = exit_reason::normal);
void disconnect(exit_reason rsn = exit_reason::normal);
/// Sets the handler for incoming messages.
/// @warning `handler` needs to be thread-safe
......@@ -54,8 +56,8 @@ public:
void enqueue(mailbox_element_ptr what, execution_unit* host) override;
void enqueue(const actor_addr& sender, message_id mid,
message content, execution_unit* host) override;
void enqueue(strong_actor_ptr sender, message_id mid, message content,
execution_unit* host) override;
void initialize() override;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -25,7 +25,7 @@
#include <utility>
#include <algorithm>
#include "caf/maybe.hpp"
#include "caf/optional.hpp"
#include "caf/config.hpp"
#include "caf/behavior.hpp"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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