Commit 009b5086 authored by Dominik Charousset's avatar Dominik Charousset Committed by Dominik Charousset

Integrate manual into the main repository

parent b055e589
......@@ -597,78 +597,58 @@ if(NOT CAF_NO_UNIT_TESTS)
endif()
################################################################################
# Doxygen setup #
################################################################################
# -- Fetch branch name and SHA if available ------------------------------------
# check for doxygen and add custom "doc" target to Makefile
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in"
"${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile"
@ONLY)
add_custom_target(doc "${DOXYGEN_EXECUTABLE}"
"${CMAKE_HOME_DIRECTORY}/Doxyfile"
WORKING_DIRECTORY "${CMAKE_HOME_DIRECTORY}"
COMMENT "Generating API documentation with Doxygen"
VERBATIM)
endif(DOXYGEN_FOUND)
find_package(Git QUIET)
################################################################################
# Manual setup #
################################################################################
set(CAF_RELEASE "${CAF_VERSION}")
set(MANUAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/manual")
if(EXISTS "${MANUAL_DIR}"
AND EXISTS "${MANUAL_DIR}/variables.tex.in"
AND EXISTS "${MANUAL_DIR}/conf.py.in")
if(GIT_FOUND)
# retrieve current branch name for CAF
execute_process(COMMAND git rev-parse --abbrev-ref HEAD
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
RESULT_VARIABLE gitBranchResult
OUTPUT_VARIABLE gitBranch
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
# retrieve current SHA1 hash for CAF
execute_process(COMMAND git log --pretty=format:%h -n 1
execute_process(COMMAND ${GIT_EXECUTABLE} log --pretty=format:%h -n 1
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE CAF_SHA
OUTPUT_STRIP_TRAILING_WHITESPACE)
# retrieve current SHA1 hash for the Manual
execute_process(COMMAND git log --pretty=format:%h -n 1
WORKING_DIRECTORY ${MANUAL_DIR}
OUTPUT_VARIABLE CAF_MANUAL_SHA
RESULT_VARIABLE gitShaResult
OUTPUT_VARIABLE gitSha
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(${GIT_BRANCH} STREQUAL "master")
if(gitBranchResult EQUAL 0 AND gitShaResult EQUAL 0)
# generate semantic CAF version for manual
set(CAF_RELEASE "${CAF_VERSION}+exp.sha.${gitSha}")
# check whether we're building the manual for a stable release
if(gitBranch STREQUAL "master")
# retrieve current tag name for CAF
execute_process(COMMAND git describe --tags --contains ${CAF_SHA}
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --contains ${gitSha}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE GIT_TAG_RES
OUTPUT_VARIABLE GIT_TAG
RESULT_VARIABLE gitTagResult
OUTPUT_VARIABLE gitTag
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
# prefer tag name over auto-generated version
if(GIT_TAG_RES STREQUAL "0")
set(CAF_RELEASE "${GIT_TAG}")
else()
set(CAF_RELEASE "${CAF_VERSION}")
# tags indicate stable releases -> use tag name as release version
if(gitTagResult EQUAL 0)
set(CAF_RELEASE "${gitTag}")
endif()
endif()
else()
set(CAF_RELEASE "${CAF_VERSION}+exp.sha.${CAF_SHA}")
endif()
MESSAGE(STATUS "Set manual version to ${CAF_RELEASE}")
configure_file("${MANUAL_DIR}/variables.tex.in"
"${MANUAL_DIR}/variables.tex"
IMMEDIATE @ONLY)
configure_file("${MANUAL_DIR}/conf.py.in"
"${MANUAL_DIR}/conf.py"
IMMEDIATE @ONLY)
configure_file("${MANUAL_DIR}/index_header.rst.in"
"${MANUAL_DIR}/index_header.rst"
IMMEDIATE @ONLY)
configure_file("${MANUAL_DIR}/index_footer.rst.in"
"${MANUAL_DIR}/index_footer.rst"
IMMEDIATE @ONLY)
endif()
message(STATUS "Set release version for all documentation to ${CAF_RELEASE}.")
# -- Setup for building manual and API documentation ---------------------------
# we need the examples and some headers relative to the build/doc/tex directory
file(COPY examples/ DESTINATION examples)
file(COPY libcaf_core/caf/exit_reason.hpp DESTINATION libcaf_core/caf/)
file(COPY libcaf_core/caf/sec.hpp DESTINATION libcaf_core/caf/)
add_subdirectory(doc)
################################################################################
# Add additional project files to GUI #
################################################################################
......
......@@ -119,6 +119,12 @@ A SNocs workspace is provided by GitHub user
* FreeBSD 10
* Windows >= 7 (currently static builds only)
## Optional Dependencies
* Doxygen (for the `doxygen` target)
* LaTeX (for the `manual` target)
* Pandoc and Python with pandocfilters (for the `rst` target)
## Scientific Use
If you use CAF in a scientific context, please use one of the following citations:
......
cmake_minimum_required(VERSION 3.10)
project(doc NONE)
# -- list all .tex source files ------------------------------------------------
set(sources
tex/Actors.tex
tex/Brokers.tex
tex/CommonPitfalls.tex
tex/ConfiguringActorApplications.tex
tex/Error.tex
tex/FAQ.tex
tex/FirstSteps.tex
tex/GroupCommunication.tex
tex/Introduction.tex
tex/ManagingGroupsOfWorkers.tex
tex/MessageHandlers.tex
tex/MessagePassing.tex
tex/Messages.tex
tex/MigrationGuides.tex
tex/NetworkTransparency.tex
tex/OpenCL.tex
tex/ReferenceCounting.tex
tex/Registry.tex
tex/RemoteSpawn.tex
tex/Scheduler.tex
tex/Streaming.tex
tex/TypeInspection.tex
tex/UsingAout.tex
tex/Utility.tex
)
# -- process .in files -----------------------------------------------------
configure_file("cmake/Doxyfile.in"
"${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
@ONLY)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tex")
configure_file("cmake/variables.tex.in"
"${CMAKE_CURRENT_BINARY_DIR}/tex/variables.tex"
@ONLY)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rst")
configure_file("cmake/conf.py.in"
"${CMAKE_CURRENT_BINARY_DIR}/rst/conf.py"
@ONLY)
configure_file("cmake/index_footer.rst.in"
"${CMAKE_CURRENT_BINARY_DIR}/rst/index_footer.rst"
@ONLY)
configure_file("cmake/index_header.rst.in"
"${CMAKE_CURRENT_BINARY_DIR}/rst/index_header.rst"
@ONLY)
# -- Doxygen setup -------------------------------------------------------------
find_package(Doxygen)
if(NOT DOXYGEN_FOUND)
message(STATUS "Doxygen not found, skip building API documentation.")
else()
message(STATUS "Add optional target: doxygen.")
add_custom_target(doxygen "${DOXYGEN_EXECUTABLE}"
"${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Generating API documentation with Doxygen"
VERBATIM)
endif()
# -- Pandoc utility macro ------------------------------------------------------
macro(generate_rst texfile)
get_filename_component(rstfile_we "${texfile}" NAME_WE)
set(rstfile "${rstfile_we}.rst")
set(bin_texfile "${CMAKE_CURRENT_BINARY_DIR}/${texfile}")
add_custom_target("${rstfile}"
DEPENDS "${bin_texfile}"
COMMAND ${PANDOC_EXECUTABLE}
"--filter=${CMAKE_SOURCE_DIR}/scripts/pandoc-filter.py"
--wrap=none -f latex
-o "${CMAKE_CURRENT_BINARY_DIR}/rst/${rstfile}"
"${bin_texfile}"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rst")
add_dependencies(rst "${rstfile}")
endmacro()
# -- LaTeX setup ---------------------------------------------------------------
find_package(LATEX QUIET)
if(NOT LATEX_FOUND)
message(STATUS "LaTeX not found, skip building manual.")
else()
message(STATUS "Add optional target: manual.")
include("cmake/UseLATEX.cmake")
# enable synctex for convenient editing
set(LATEX_USE_SYNCTEX yes)
# add manual.pdf as target
add_latex_document(tex/manual.tex
INPUTS ${sources} "tex/variables.tex"
IMAGE_DIRS "pdf"
FORCE_PDF
TARGET_NAME manual)
find_program(PANDOC_EXECUTABLE pandoc)
if(NOT EXISTS ${PANDOC_EXECUTABLE})
message(STATUS "Pandoc not found, skip generating reFormattedText version of the manual.")
else()
execute_process(COMMAND "python" "-c"
"from pandocfilters import toJSONFilter; print('ok')"
RESULT_VARIABLE has_pandocfilters
OUTPUT_QUIET
ERROR_QUIET)
if(NOT ${has_pandocfilters} EQUAL 0)
message(STATUS "Python with pandocfilters not found, skip generating reFormattedText version of the manual.")
else()
message(STATUS "Add optional target: rst.")
add_custom_target(rst)
foreach(texfile ${sources})
generate_rst(${texfile})
endforeach()
endif()
endif()
endif()
This diff is collapsed.
This diff is collapsed.
# -*- coding: utf-8 -*-
#
# CAF documentation build configuration file, created by
# sphinx-quickstart on Fri Jun 3 11:27:36 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
import sphinx_rtd_theme
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The encoding of source files.
#
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'CAF'
copyright = u'2016, Dominik Charousset'
author = u'Dominik Charousset'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = u'@CAF_VERSION@'
# The full version, including alpha/beta/rc tags.
release = u'@CAF_RELEASE@'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#
# today = ''
#
# Else, today_fmt is used as the format for a strftime call.
#
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The reST default role (used for this markup: `text`) to use for all
# documents.
#
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
highlight_language = 'C++'
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# The name for this set of Sphinx documents.
# "<project> v<release> documentation" by default.
#
# html_title = u'CAF v0.15'
# A shorter title for the navigation bar. Default is the same as html_title.
#
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#
# html_logo = None
# The name of an image file (relative to this directory) to use as a favicon of
# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#
# html_extra_path = []
# If not None, a 'Last updated on:' timestamp is inserted at every page
# bottom, using the given strftime format.
# The empty string is equivalent to '%b %d, %Y'.
#
# html_last_updated_fmt = None
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#
# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#
# html_additional_pages = {}
# If false, no module index is generated.
#
# html_domain_indices = True
# If false, no index is generated.
#
# html_use_index = True
# If true, the index is split into individual pages for each letter.
#
# html_split_index = False
# If true, links to the reST sources are added to the pages.
#
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
#
# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# 'ja' uses this config value.
# 'zh' user can custom change `jieba` dictionary path.
#
# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
#
# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
htmlhelp_basename = 'CAFdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'CAF.tex', u'CAF Documentation',
u'Dominik Charousset', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#
# latex_use_parts = False
# If true, show page references after internal links.
#
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
#
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
#
# latex_appendices = []
# If false, no module index is generated.
#
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'caf', u'CAF Documentation',
[author], 1)
]
# If true, show URL addresses after external links.
#
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'CAF', u'CAF Documentation',
author, 'CAF', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#
# texinfo_appendices = []
# If false, no module index is generated.
#
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
#
# texinfo_no_detailmenu = False
Version Information
===================
This version of the Manual was automatically generated from CAF commit
`@CAF_SHA@ <https://github.com/actor-framework/actor-framework/commit/@CAF_SHA@>`_.
CAF User Manual
===============
**C++ Actor Framework** version @CAF_RELEASE@.
Contents
========
\newcommand{\cafrelease}{@CAF_RELEASE@}
\newcommand{\cafsha}{@CAF_SHA@}
This diff is collapsed.
\section{Network I/O with Brokers}
\label{broker}
When communicating to other services in the network, sometimes low-level socket
I/O is inevitable. For this reason, CAF provides \emph{brokers}. A broker is
an event-based actor running in the middleman that multiplexes socket I/O. It
can maintain any number of acceptors and connections. Since the broker runs in
the middleman, implementations should be careful to consume as little time as
possible in message handlers. Brokers should outsource any considerable amount
of work by spawning new actors or maintaining worker actors.
\textit{Note that all UDP-related functionality is still \experimental.}
\subsection{Spawning Brokers}
Brokers are implemented as functions and are spawned by calling on of the three
following member functions of the middleman.
\begin{lstlisting}
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts>
typename infer_handle_from_fun<F>::type
spawn_broker(F fun, Ts&&... xs);
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts>
expected<typename infer_handle_from_fun<F>::type>
spawn_client(F fun, const std::string& host, uint16_t port, Ts&&... xs);
template <spawn_options Os = no_spawn_options,
class F = std::function<void(broker*)>, class... Ts>
expected<typename infer_handle_from_fun<F>::type>
spawn_server(F fun, uint16_t port, Ts&&... xs);
\end{lstlisting}
The function \lstinline^spawn_broker^ simply spawns a broker. The convenience
function \lstinline^spawn_client^ tries to connect to given host and port over
TCP and returns a broker managing this connection on success. Finally,
\lstinline^spawn_server^ opens a local TCP port and spawns a broker managing it
on success. There are no convenience functions spawn a UDP-based client or
server.
\subsection{Class \texttt{broker}}
\label{broker-class}
\begin{lstlisting}
void configure_read(connection_handle hdl, receive_policy::config config);
\end{lstlisting}
Modifies the receive policy for the connection identified by \lstinline^hdl^.
This will cause the middleman to enqueue the next \lstinline^new_data_msg^
according to the given \lstinline^config^ created by
\lstinline^receive_policy::exactly(x)^, \lstinline^receive_policy::at_most(x)^,
or \lstinline^receive_policy::at_least(x)^ (with \lstinline^x^ denoting the
number of bytes).
\begin{lstlisting}
void write(connection_handle hdl, size_t num_bytes, const void* buf)
void write(datagram_handle hdl, size_t num_bytes, const void* buf)
\end{lstlisting}
Writes data to the output buffer.
\begin{lstlisting}
void enqueue_datagram(datagram_handle hdl, std::vector<char> buf);
\end{lstlisting}
Enqueues a buffer to be sent as a datagram. Use of this function is encouraged
over write as it allows reuse of the buffer which can be returned to the broker
in a \lstinline^datagram_sent_msg^.
\begin{lstlisting}
void flush(connection_handle hdl);
void flush(datagram_handle hdl);
\end{lstlisting}
Sends the data from the output buffer.
\begin{lstlisting}
template <class F, class... Ts>
actor fork(F fun, connection_handle hdl, Ts&&... xs);
\end{lstlisting}
Spawns a new broker that takes ownership of a given connection.
\begin{lstlisting}
size_t num_connections();
\end{lstlisting}
Returns the number of open connections.
\begin{lstlisting}
void close(connection_handle hdl);
void close(accept_handle hdl);
void close(datagram_handle hdl);
\end{lstlisting}
Closes the endpoint related to the handle.
\begin{lstlisting}
expected<std::pair<accept_handle, uint16_t>>
add_tcp_doorman(uint16_t port = 0, const char* in = nullptr,
bool reuse_addr = false);
\end{lstlisting}
Creates new doorman that accepts incoming connections on a given port and
returns the handle to the doorman and the port in use or an error.
\begin{lstlisting}
expected<connection_handle>
add_tcp_scribe(const std::string& host, uint16_t port);
\end{lstlisting}
Creates a new scribe to connect to host:port and returns handle to it or an
error.
\begin{lstlisting}
expected<std::pair<datagram_handle, uint16_t>>
add_udp_datagram_servant(uint16_t port = 0, const char* in = nullptr,
bool reuse_addr = false);
\end{lstlisting}
Creates a datagram servant to handle incoming datagrams on a given port.
Returns the handle to the servant and the port in use or an error.
\begin{lstlisting}
expected<datagram_handle>
add_udp_datagram_servant(const std::string& host, uint16_t port);
\end{lstlisting}
Creates a datagram servant to send datagrams to host:port and returns a handle
to it or an error.
\subsection{Broker-related Message Types}
Brokers receive system messages directly from the middleman for connection and
acceptor events.
\textbf{Note:} brokers are \emph{required} to handle these messages immediately
regardless of their current state. Not handling the system messages from the
broker results in loss of data, because system messages are \emph{not}
delivered through the mailbox and thus cannot be skipped.
\begin{lstlisting}
struct new_connection_msg {
accept_handle source;
connection_handle handle;
};
\end{lstlisting}
Indicates that \lstinline^source^ accepted a new TCP connection identified by
\lstinline^handle^.
\begin{lstlisting}
struct new_data_msg {
connection_handle handle;
std::vector<char> buf;
};
\end{lstlisting}
Contains raw bytes received from \lstinline^handle^. The amount of data
received per event is controlled with \lstinline^configure_read^ (see
\ref{broker-class}). It is worth mentioning that the buffer is re-used whenever
possible.
\begin{lstlisting}
struct data_transferred_msg {
connection_handle handle;
uint64_t written;
uint64_t remaining;
};
\end{lstlisting}
This message informs the broker that the \lstinline^handle^ sent
\lstinline^written^ bytes with \lstinline^remaining^ bytes in the buffer. Note,
that these messages are not sent per default but must be explicitly enabled via
the member function \lstinline^ack_writes^.
\begin{lstlisting}
struct connection_closed_msg {
connection_handle handle;
};
struct acceptor_closed_msg {
accept_handle handle;
};
\end{lstlisting}
A \lstinline^connection_closed_msg^ or \lstinline^acceptor_closed_msg^ informs
the broker that one of its handles is no longer valid.
\begin{lstlisting}
struct connection_passivated_msg {
connection_handle handle;
};
struct acceptor_passivated_msg {
accept_handle handle;
};
\end{lstlisting}
A \lstinline^connection_passivated_msg^ or \lstinline^acceptor_passivated_msg^
informs the broker that one of its handles entered passive mode and no longer
accepts new data or connections \see{trigger}.
The following messages are related to UDP communication
(see~\sref{transport-protocols}. Since UDP is not connection oriented, there is
no equivalent to the \lstinline^new_connection_msg^ of TCP.
\begin{lstlisting}
struct new_datagram_msg {
datagram_handle handle;
network::receive_buffer buf;
};
\end{lstlisting}
Contains the raw bytes from \lstinline^handle^. The buffer always has a maximum
size of 65k to receive all regular UDP messages. The amount of bytes can be
queried via the \lstinline^.size()^ member function. Similar to TCP, the buffer
is reused when possible---please do not resize it.
\begin{lstlisting}
struct datagram_sent_msg {
datagram_handle handle;
uint64_t written;
std::vector<char> buf;
};
\end{lstlisting}
This message informs the broker that the \lstinline^handle^ sent a datagram of
\lstinline^written^ bytes. It includes the buffer that held the sent message to
allow its reuse. Note, that these messages are not sent per default but must be
explicitly enabled via the member function \lstinline^ack_writes^.
\begin{lstlisting}
struct datagram_servant_closed_msg {
std::vector<datagram_handle> handles;
};
\end{lstlisting}
A \lstinline^datagram_servant_closed_msg^ informs the broker that one of its
handles is no longer valid.
\begin{lstlisting}
struct datagram_servant_passivated_msg {
datagram_handle handle;
};
\end{lstlisting}
A \lstinline^datagram_servant_closed_msg^ informs the broker that one of its
handles entered passive mode and no longer accepts new data \see{trigger}.
\subsection{Manually Triggering Events \experimental}
\label{trigger}
Brokers receive new events as \lstinline^new_connection_msg^ and
\lstinline^new_data_msg^ as soon and as often as they occur, per default. This
means a fast peer can overwhelm a broker by sending it data faster than the
broker can process it. In particular if the broker outsources work items to
other actors, because work items can accumulate in the mailboxes of the
workers.
Calling \lstinline^self->trigger(x,y)^, where \lstinline^x^ is a connection or
acceptor handle and \lstinline^y^ is a positive integer, allows brokers to halt
activities after \lstinline^y^ additional events. Once a connection or acceptor
stops accepting new data or connections, the broker receives a
\lstinline^connection_passivated_msg^ or \lstinline^acceptor_passivated_msg^.
Brokers can stop activities unconditionally with \lstinline^self->halt(x)^ and
resume activities unconditionally with \lstinline^self->trigger(x)^.
\section{Common Pitfalls}
\label{pitfalls}
This Section highlights common mistakes or C++ subtleties that can show up when
programming in CAF.
\subsection{Defining Message Handlers}
\begin{itemize}
\item C++ evaluates comma-separated expressions from left-to-right, using only
the last element as return type of the whole expression. This means that
message handlers and behaviors must \emph{not} be initialized like this:
\begin{lstlisting}
message_handler wrong = (
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
\end{lstlisting}
The correct way to initialize message handlers and behaviors is to either
use the constructor or the member function \lstinline^assign^:
\begin{lstlisting}
message_handler ok1{
[](int i) { /*...*/ },
[](float f) { /*...*/ }
};
message_handler ok2;
// some place later
ok2.assign(
[](int i) { /*...*/ },
[](float f) { /*...*/ }
);
\end{lstlisting}
\end{itemize}
\subsection{Event-Based API}
\begin{itemize}
\item The member function \lstinline^become^ does not block, i.e., always
returns immediately. Thus, lambda expressions should \textit{always} capture
by value. Otherwise, all references on the stack will cause undefined
behavior if the lambda expression is executed.
\end{itemize}
\subsection{Requests}
\begin{itemize}
\item A handle returned by \lstinline^request^ represents \emph{exactly one}
response message. It is not possible to receive more than one response
message.
\item The handle returned by \lstinline^request^ is bound to the calling actor.
It is not possible to transfer a handle to a response to another actor.
\end{itemize}
\clearpage
\subsection{Sharing}
\begin{itemize}
\item It is strongly recommended to \textbf{not} share states between actors.
In particular, no actor shall ever access member variables or member
functions of another actor. Accessing shared memory segments concurrently
can cause undefined behavior that is incredibly hard to find and debug.
However, sharing \textit{data} between actors is fine, as long as the data
is \textit{immutable} and its lifetime is guaranteed to outlive all actors.
The simplest way to meet the lifetime guarantee is by storing the data in
smart pointers such as \lstinline^std::shared_ptr^. Nevertheless, the
recommended way of sharing informations is message passing. Sending the
same message to multiple actors does not result in copying the data several
times.
\end{itemize}
This diff is collapsed.
\section{Errors}
\label{error}
Errors in CAF have a code and a category, similar to
\lstinline^std::error_code^ and \lstinline^std::error_condition^. Unlike its
counterparts from the C++ standard library, \lstinline^error^ is
plattform-neutral and serializable. Instead of using category singletons, CAF
stores categories as atoms~\see{atom}. Errors can also include a message to
provide additional context information.
\subsection{Class Interface}
\begin{center}
\begin{tabular}{ll}
\textbf{Constructors} & ~ \\
\hline
\lstinline^(Enum x)^ & Construct error by calling \lstinline^make_error(x)^ \\
\hline
\lstinline^(uint8_t x, atom_value y)^ & Construct error with code \lstinline^x^ and category \lstinline^y^ \\
\hline
\lstinline^(uint8_t x, atom_value y, message z)^ & Construct error with code \lstinline^x^, category \lstinline^y^, and context \lstinline^z^ \\
\hline
~ & ~ \\ \textbf{Observers} & ~ \\
\hline
\lstinline^uint8_t code()^ & Returns the error code \\
\hline
\lstinline^atom_value category()^ & Returns the error category \\
\hline
\lstinline^message context()^ & Returns additional context information \\
\hline
\lstinline^explicit operator bool()^ & Returns \lstinline^code() != 0^ \\
\hline
\end{tabular}
\end{center}
\subsection{Add Custom Error Categories}
\label{custom-error}
Adding custom error categories requires three steps: (1)~declare an enum class
of type \lstinline^uint8_t^ with the first value starting at 1, (2)~implement a
free function \lstinline^make_error^ that converts the enum to an
\lstinline^error^ object, (3)~add the custom category to the actor system with
a render function. The last step is optional to allow users to retrieve a
better string representation from \lstinline^system.render(x)^ than
\lstinline^to_string(x)^ can offer. Note that any error code with value 0 is
interpreted as \emph{not-an-error}. The following example adds a custom error
category by performing the first two steps.
\cppexample[19-34]{message_passing/divider}
The implementation of \lstinline^to_string(error)^ is unable to call string
conversions for custom error categories. Hence,
\lstinline^to_string(make_error(math_error::division_by_zero))^ returns
\lstinline^"error(1, math)"^.
The following code adds a rendering function to the actor system to provide a
more satisfactory string conversion.
\cppexample[50-58]{message_passing/divider}
With the custom rendering function,
\lstinline^system.render(make_error(math_error::division_by_zero))^ returns
\lstinline^"math_error(division_by_zero)"^.
\clearpage
\subsection{System Error Codes}
\label{sec}
System Error Codes (SECs) use the error category \lstinline^"system"^. They
represent errors in the actor system or one of its modules and are defined as
follows.
\sourcefile[32-117]{libcaf_core/caf/sec.hpp}
%\clearpage
\subsection{Default Exit Reasons}
\label{exit-reason}
CAF uses the error category \lstinline^"exit"^ for default exit reasons. These
errors are usually fail states set by the actor system itself. The two
exceptions are \lstinline^exit_reason::user_shutdown^ and
\lstinline^exit_reason::kill^. The former is used in CAF to signalize orderly,
user-requested shutdown and can be used by programmers in the same way. The
latter terminates an actor unconditionally when used in \lstinline^send_exit^,
even if the default handler for exit messages~\see{exit-message} is overridden.
\sourcefile[29-49]{libcaf_core/caf/exit_reason.hpp}
\section{Frequently Asked Questions}
\label{faq}
This Section is a compilation of the most common questions via GitHub, chat,
and mailing list.
\subsection{Can I Encrypt CAF Communication?}
Yes, by using the OpenSSL module~\see{free-remoting-functions}.
\subsection{Can I Create Messages Dynamically?}
Yes.
Usually, messages are created implicitly when sending messages but can also be
created explicitly using \lstinline^make_message^. In both cases, types and
number of elements are known at compile time. To allow for fully dynamic
message generation, CAF also offers \lstinline^message_builder^:
\begin{lstlisting}
message_builder mb;
// prefix message with some atom
mb.append(strings_atom::value);
// fill message with some strings
std::vector<std::string> strings{/*...*/};
for (auto& str : strings)
mb.append(str);
// create the message
message msg = mb.to_message();
\end{lstlisting}
\subsection{What Debugging Tools Exist?}
The \lstinline^scripts/^ and \lstinline^tools/^ directories contain some useful
tools to aid in development and debugging.
\lstinline^scripts/atom.py^ converts integer atom values back into strings.
\lstinline^scripts/demystify.py^ replaces cryptic \lstinline^typed_mpi<...>^
templates with \lstinline^replies_to<...>::with<...>^ and
\lstinline^atom_constant<...>^ with a human-readable representation of the
actual atom.
\lstinline^scripts/caf-prof^ is an R script that generates plots from CAF
profiler output.
\lstinline^caf-vec^ is a (highly) experimental tool that annotates CAF logs
with vector timestamps. It gives you happens-before relations and a nice
visualization via \href{https://bestchai.bitbucket.io/shiviz/}{ShiViz}.
\section{Overview}
Compiling CAF requires CMake and a C++11-compatible compiler. To get and
compile the sources on UNIX-like systems, type the following in a terminal:
\begin{verbatim}
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
./configure
make
make install [as root, optional]
\end{verbatim}
We recommended to run the unit tests as well:
\begin{verbatim}
make test
\end{verbatim}
If the output indicates an error, please submit a bug report that includes (a)
your compiler version, (b) your OS, and (c) the content of the file
\texttt{build/Testing/Temporary/LastTest.log}.
\subsection{Features}
\begin{itemize}
\item Lightweight, fast and efficient actor implementations
\item Network transparent messaging
\item Error handling based on Erlang's failure model
\item Pattern matching for messages as internal DSL to ease development
\item Thread-mapped actors for soft migration of existing applications
\item Publish/subscribe group communication
\end{itemize}
\subsection{Minimal Compiler Versions}
\begin{itemize}
\item GCC 4.8
\item Clang 3.4
\item Visual Studio 2015, Update 3
\end{itemize}
\subsection{Supported Operating Systems}
\begin{itemize}
\item Linux
\item Mac OS X
\item Windows (static library only)
\end{itemize}
\clearpage
\subsection{Hello World Example}
\cppexample{hello_world}
\section{Group Communication}
\label{groups}
CAF supports publish/subscribe-based group communication. Dynamically typed
actors can join and leave groups and send messages to groups. The following
example showcases the basic API for retrieving a group from a module by its
name, joining, and leaving.
\begin{lstlisting}
std::string module = "local";
std::string id = "foo";
auto expected_grp = system.groups().get(module, id);
if (! expected_grp) {
std::cerr << "*** cannot load group: "
<< system.render(expected_grp.error()) << std::endl;
return;
}
auto grp = std::move(*expected_grp);
scoped_actor self{system};
self->join(grp);
self->send(grp, "test");
self->receive(
[](const std::string& str) {
assert(str == "test");
}
);
self->leave(grp);
\end{lstlisting}
It is worth mentioning that the module \lstinline`"local"` is guaranteed to
never return an error. The example above uses the general API for retrieving
the group. However, local modules can be easier accessed by calling
\lstinline`system.groups().get_local(id)`, which returns \lstinline`group`
instead of \lstinline`expected<group>`.
\subsection{Anonymous Groups}
\label{anonymous-group}
Groups created on-the-fly with \lstinline^system.groups().anonymous()^ can be
used to coordinate a set of workers. Each call to this function returns a new,
unique group instance.
\subsection{Local Groups}
\label{local-group}
The \lstinline^"local"^ group module creates groups for in-process
communication. For example, a group for GUI related events could be identified
by \lstinline^system.groups().get_local("GUI events")^. The group ID
\lstinline^"GUI events"^ uniquely identifies a singleton group instance of the
module \lstinline^"local"^.
\subsection{Remote Groups}
\label{remote-group}
Calling\lstinline^system.middleman().publish_local_groups(port, addr)^ makes
all local groups available to other nodes in the network. The first argument
denotes the port, while the second (optional) parameter can be used to
whitelist IP addresses.
After publishing the group at one node (the server), other nodes (the clients)
can get a handle for that group by using the ``remote'' module:
\lstinline^system.groups().get("remote", "<group>@<host>:<port>")^. This
implementation uses N-times unicast underneath and the group is only available
as long as the hosting server is alive.
\section{Introduction}
Before diving into the API of CAF, we discuss the concepts behind it and
explain the terminology used in this manual.
\subsection{Actor Model}
The actor model describes concurrent entities---actors---that do not share
state and communicate only via asynchronous message passing. Decoupling
concurrently running software components via message passing avoids race
conditions by design. Actors can create---spawn---new actors and monitor each
other to build fault-tolerant, hierarchical systems. Since message passing is
network transparent, the actor model applies to both concurrency and
distribution.
Implementing applications on top of low-level primitives such as mutexes and
semaphores has proven challenging and error-prone. In particular when trying to
implement applications that scale up to many CPU cores. Queueing, starvation,
priority inversion, and false sharing are only a few of the issues that can
decrease performance significantly in mutex-based concurrency models. In the
extreme, an application written with the standard toolkit can run slower when
adding more cores.
The actor model has gained momentum over the last decade due to its high level
of abstraction and its ability to scale dynamically from one core to many cores
and from one node to many nodes. However, the actor model has not yet been
widely adopted in the native programming domain. With CAF, we contribute a
library for actor programming in C++ as open-source software to ease native
development of concurrent as well as distributed systems. In this regard, CAF
follows the C++ philosophy ``building the highest abstraction possible without
sacrificing performance''.
\subsection{Terminology}
CAF is inspired by other implementations based on the actor model such as
Erlang or Akka. It aims to provide a modern C++ API allowing for type-safe as
well as dynamically typed messaging. While there are similarities to other
implementations, we made many different design decisions that lead to slight
differences when comparing CAF to other actor frameworks.
\subsubsection{Dynamically Typed Actor}
A dynamically typed actor accepts any kind of message and dispatches on its
content dynamically at the receiver. This is the ``traditional'' messaging
style found in implementations like Erlang or Akka. The upside of this approach
is (usually) faster prototyping and less code. This comes at the cost of
requiring excessive testing.
\subsubsection{Statically Typed Actor}
CAF achieves static type-checking for actors by defining abstract messaging
interfaces. Since interfaces define both input and output types, CAF is able to
verify messaging protocols statically. The upside of this approach is much
higher robustness to code changes and fewer possible runtime errors. This comes
at an increase in required source code, as developers have to define and use
messaging interfaces.
\subsubsection{Actor References}
\label{actor-reference}
CAF uses reference counting for actors. The three ways to store a reference to
an actor are addresses, handles, and pointers. Note that \emph{address} does
not refer to a \emph{memory region} in this context.
\paragraph{Address}
\label{actor-address}
Each actor has a (network-wide) unique logical address. This identifier is
represented by \lstinline^actor_addr^, which allows to identify and monitor an
actor. Unlike other actor frameworks, CAF does \emph{not} allow users to send
messages to addresses. This limitation is due to the fact that the address does
not contain any type information. Hence, it would not be safe to send it a
message, because the receiving actor might use a statically typed interface
that does not accept the given message. Because an \lstinline^actor_addr^ fills
the role of an identifier, it has \emph{weak reference semantics}
\see{reference-counting}.
\paragraph{Handle}
\label{actor-handle}
An actor handle contains the address of an actor along with its type
information and is required for sending messages to actors. The distinction
between handles and addresses---which is unique to CAF when comparing it to
other actor systems---is a consequence of the design decision to enforce static
type checking for all messages. Dynamically typed actors use \lstinline^actor^
handles, while statically typed actors use \lstinline^typed_actor<...>^
handles. Both types have \emph{strong reference semantics}
\see{reference-counting}.
\paragraph{Pointer}
\label{actor-pointer}
In a few instances, CAF uses \lstinline^strong_actor_ptr^ to refer to an actor
using \emph{strong reference semantics} \see{reference-counting} without
knowing the proper handle type. Pointers must be converted to a handle via
\lstinline^actor_cast^ \see{actor-cast} prior to sending messages. A
\lstinline^strong_actor_ptr^ can be \emph{null}.
\subsubsection{Spawning}
``Spawning'' an actor means to create and run a new actor.
\subsubsection{Monitor}
\label{monitor}
A monitored actor sends a down message~\see{down-message} to all actors
monitoring it as part of its termination. This allows actors to supervise other
actors and to take actions when one of the supervised actors fails, i.e.,
terminates with a non-normal exit reason.
\subsubsection{Link}
\label{link}
A link is a bidirectional connection between two actors. Each actor sends an
exit message~\see{exit-message} to all of its links as part of its termination.
Unlike down messages, exit messages cause the receiving actor to terminate as
well when receiving a non-normal exit reason per default. This allows
developers to create a set of actors with the guarantee that either all or no
actors are alive. Actors can override the default handler to implement error
recovery strategies.
\subsection{Experimental Features}
Sections that discuss experimental features are highlighted with \experimental.
The API of such features is not stable. This means even minor updates to CAF
can come with breaking changes to the API or even remove a feature completely.
However, we encourage developers to extensively test such features and to start
discussions to uncover flaws, report bugs, or tweaking the API in order to
improve a feature or streamline it to cover certain use cases.
\section{Managing Groups of Workers \experimental}
\label{worker-groups}
When managing a set of workers, a central actor often dispatches requests to a
set of workers. For this purpose, the class \lstinline^actor_pool^ implements a
lightweight abstraction for managing a set of workers using a dispatching
policy. Unlike groups, pools usually own their workers.
Pools are created using the static member function \lstinline^make^, which
takes either one argument (the policy) or three (number of workers, factory
function for workers, and dispatching policy). After construction, one can add
new workers via messages of the form \texttt{('SYS', 'PUT', worker)}, remove
workers with \texttt{('SYS', 'DELETE', worker)}, and retrieve the set of
workers as \lstinline^vector<actor>^ via \texttt{('SYS', 'GET')}.
An actor pool takes ownership of its workers. When forced to quit, it sends an
exit messages to all of its workers, forcing them to quit as well. The pool
also monitors all of its workers.
Pools do not cache messages, but enqueue them directly in a workers mailbox.
Consequently, a terminating worker loses all unprocessed messages. For more
advanced caching strategies, such as reliable message delivery, users can
implement their own dispatching policies.
\subsection{Dispatching Policies}
A dispatching policy is a functor with the following signature:
\begin{lstlisting}
using uplock = upgrade_lock<detail::shared_spinlock>;
using policy = std::function<void (actor_system& sys,
uplock& guard,
const actor_vec& workers,
mailbox_element_ptr& ptr,
execution_unit* host)>;
\end{lstlisting}
The argument \lstinline^guard^ is a shared lock that can be upgraded for unique
access if the policy includes a critical section. The second argument is a
vector containing all workers managed by the pool. The argument \lstinline^ptr^
contains the full message as received by the pool. Finally, \lstinline^host^ is
the current scheduler context that can be used to enqueue workers into the
corresponding job queue.
The actor pool class comes with a set predefined policies, accessible via
factory functions, for convenience.
\begin{lstlisting}
actor_pool::policy actor_pool::round_robin();
\end{lstlisting}
This policy forwards incoming requests in a round-robin manner to workers.
There is no guarantee that messages are consumed, i.e., work items are lost if
the worker exits before processing all of its messages.
\begin{lstlisting}
actor_pool::policy actor_pool::broadcast();
\end{lstlisting}
This policy forwards \emph{each} message to \emph{all} workers. Synchronous
messages to the pool will be received by all workers, but the client will only
recognize the first arriving response message---or error---and discard
subsequent messages. Note that this is not caused by the policy itself, but a
consequence of forwarding synchronous messages to more than one actor.
\begin{lstlisting}
actor_pool::policy actor_pool::random();
\end{lstlisting}
This policy forwards incoming requests to one worker from the pool chosen
uniformly at random. Analogous to \lstinline^round_robin^, this policy does not
cache or redispatch messages.
\begin{lstlisting}
using join = function<void (T&, message&)>;
using split = function<void (vector<pair<actor, message>>&, message&)>;
template <class T>
static policy split_join(join jf, split sf = ..., T init = T());
\end{lstlisting}
This policy models split/join or scatter/gather work flows, where a work item
is split into as many tasks as workers are available and then the individuals
results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together
to create a single result. The function is called with the result object
(initialed using \lstinline^init^) and the current result messages from a
worker.
The first argument of a split function is a mapping from actors (workers) to
tasks (messages). The second argument is the input message. The default split
function is a broadcast dispatching, sending each worker the original request.
\section{Message Handlers}
\label{message-handler}
Actors can store a set of callbacks---usually implemented as lambda
expressions---using either \lstinline^behavior^ or \lstinline^message_handler^.
The former stores an optional timeout, while the latter is composable.
\subsection{Definition and Composition}
As the name implies, a \lstinline^behavior^ defines the response of an actor to
messages it receives. The optional timeout allows an actor to dynamically
change its behavior when not receiving message after a certain amount of time.
\begin{lstlisting}
message_handler x1{
[](int i) { /*...*/ },
[](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ }
};
\end{lstlisting}
In our first example, \lstinline^x1^ models a behavior accepting messages that
consist of either exactly one \lstinline^int^, or one \lstinline^double^, or
three \lstinline^int^ values. Any other message is not matched and gets
forwarded to the default handler \see{default-handler}.
\begin{lstlisting}
message_handler x2{
[](double db) { /*...*/ },
[](double db) { /* - unrachable - */ }
};
\end{lstlisting}
Our second example illustrates an important characteristic of the matching
mechanism. Each message is matched against the callbacks in the order they are
defined. The algorithm stops at the first match. Hence, the second callback in
\lstinline^x2^ is unreachable.
\begin{lstlisting}
message_handler x3 = x1.or_else(x2);
message_handler x4 = x2.or_else(x1);
\end{lstlisting}
Message handlers can be combined using \lstinline^or_else^. This composition is
not commutative, as our third examples illustrates. The resulting message
handler will first try to handle a message using the left-hand operand and will
fall back to the right-hand operand if the former did not match. Thus,
\lstinline^x3^ behaves exactly like \lstinline^x1^. This is because the second
callback in \lstinline^x1^ will consume any message with a single
\lstinline^double^ and both callbacks in \lstinline^x2^ are thus unreachable.
The handler \lstinline^x4^ will consume messages with a single
\lstinline^double^ using the first callback in \lstinline^x2^, essentially
overriding the second callback in \lstinline^x1^.
\clearpage
\subsection{Atoms}
\label{atom}
Defining message handlers in terms of callbacks is convenient, but requires a
simple way to annotate messages with meta data. Imagine an actor that provides
a mathematical service for integers. It receives two integers, performs a
user-defined operation and returns the result. Without additional context, the
actor cannot decide whether it should multiply or add the integers. Thus, the
operation must be encoded into the message. The Erlang programming language
introduced an approach to use non-numerical constants, so-called
\textit{atoms}, which have an unambiguous, special-purpose type and do not have
the runtime overhead of string constants.
Atoms in CAF are mapped to integer values at compile time. This mapping is
guaranteed to be collision-free and invertible, but limits atom literals to ten
characters and prohibits special characters. Legal characters are
\lstinline^_0-9A-Za-z^ and the whitespace character. Atoms are created using
the \lstinline^constexpr^ function \lstinline^atom^, as the following example
illustrates.
\begin{lstlisting}
atom_value a1 = atom("add");
atom_value a2 = atom("multiply");
\end{lstlisting}
\textbf{Warning}: The compiler cannot enforce the restrictions at compile time,
except for a length check. The assertion \lstinline^atom("!?") != atom("?!")^
is not true, because each invalid character translates to a whitespace
character.
While the \lstinline^atom_value^ is computed at compile time, it is not
uniquely typed and thus cannot be used in the signature of a callback. To
accomplish this, CAF offers compile-time \emph{atom constants}.
\begin{lstlisting}
using add_atom = atom_constant<atom("add")>;
using multiply_atom = atom_constant<atom("multiply")>;
\end{lstlisting}
Using these constants, we can now define message passing interfaces in a
convenient way:
\begin{lstlisting}
behavior do_math{
[](add_atom, int a, int b) {
return a + b;
},
[](multiply_atom, int a, int b) {
return a * b;
}
};
// caller side: send(math_actor, add_atom::value, 1, 2)
\end{lstlisting}
Atom constants define a static member \lstinline^value^. Please note that this
static \lstinline^value^ member does \emph{not} have the type
\lstinline^atom_value^, unlike \lstinline^std::integral_constant^ for example.
This diff is collapsed.
This diff is collapsed.
\section{Migration Guides}
The guides in this section document all possibly breaking changes in the
library for that last versions of CAF.
\subsection{0.8 to 0.9}
Version 0.9 included a lot of changes and improvements in its implementation,
but it also made breaking changes to the API.
\paragraph{\lstinline^self^ has been removed}
~
This is the biggest library change since the initial release. The major problem
with this keyword-like identifier is that it must have a single type as it's
implemented as a thread-local variable. Since there are so many different kinds
of actors (event-based or blocking, untyped or typed), \lstinline^self^ needs
to perform type erasure at some point, rendering it ultimately useless. Instead
of a thread-local pointer, you can now use the first argument in functor-based
actors to "catch" the self pointer with proper type information.
\paragraph{\lstinline^actor_ptr^ has been replaced}
~
CAF now distinguishes between handles to actors, i.e.,
\lstinline^typed_actor<...>^ or simply \lstinline^actor^, and \emph{addresses}
of actors, i.e., \lstinline^actor_addr^. The reason for this change is that
each actor has a logical, (network-wide) unique address, which is used by the
networking layer of CAF. Furthermore, for monitoring or linking, the address
is all you need. However, the address is not sufficient for sending messages,
because it doesn't have any type information. The function
\lstinline^current_sender()^ now returns the \emph{address} of the sender. This
means that previously valid code such as \lstinline^send(current_sender(),...)^
will cause a compiler error. However, the recommended way of replying to
messages is to return the result from the message handler.
\paragraph{The API for typed actors is now similar to the API for untyped actors}
~
The APIs of typed and untyped actors have been harmonized. Typed actors can now
be published in the network and also use all operations untyped actors can.
\clearpage
\subsection{0.9 to 0.10 (\texttt{libcppa} to CAF)}
The first release under the new name CAF is an overhaul of the entire library.
Some classes have been renamed or relocated, others have been removed. The
purpose of this refactoring was to make the library easier to grasp and to make
its API more consistent. All classes now live in the namespace \texttt{caf} and
all headers have the top level folder \texttt{caf} instead of \texttt{cppa}.
For example, \texttt{cppa/actor.hpp} becomes \texttt{caf/actor.hpp}. Further,
the convenience header to get all parts of the user API is now
\texttt{"caf/all.hpp"}. The networking has been separated from the core
library. To get the networking components, simply include
\texttt{caf/io/all.hpp} and use the namespace \lstinline^caf::io^.
Version 0.10 still includes the header \texttt{cppa/cppa.hpp} to make the
transition process for users easier and to not break existing code right away.
The header defines the namespace \texttt{cppa} as an alias for \texttt{caf}.
Furthermore, it provides implementations or type aliases for renamed or removed
classes such as \lstinline^cow_tuple^. You won't get any warning about deprecated
headers with 0.10. However, we will add this warnings in the next library
version and remove deprecated code eventually.
Even when using the backwards compatibility header, the new library has
breaking changes. For instance, guard expressions have been removed entirely.
The reasoning behind this decision is that we already have projections to
modify the outcome of a match. Guard expressions add little expressive power to
the library but a whole lot of code that is hard to maintain in the long run
due to its complexity. Using projections to not only perform type conversions
but also to restrict values is the more natural choice.
\lstinline^any_tuple => message^
This type is only being used to pass a message from one actor to another.
Hence, \lstinline^message^ is the logical name.
\lstinline^partial_function => message_handler^
Technically, it still is a partial function. However, we wanted to put
emphasize on its use case.
\lstinline^cow_tuple => X^
We want to provide a streamlined, simple API. Shipping a full tuple abstraction
with the library does not fit into this philosophy. The removal of
\lstinline^cow_tuple^ implies the removal of related functions such as
\lstinline^tuple_cast^.
\lstinline^cow_ptr => X^
This pointer class is an implementation detail of \lstinline^message^ and
should not live in the global namespace in the first place. It also had the
wrong name, because it is intrusive.
\lstinline^X => message_builder^
This new class can be used to create messages dynamically. For example, the
content of a vector can be used to create a message using a series of
\lstinline^append^ calls.
\begin{lstlisting}
accept_handle, connection_handle, publish, remote_actor,
max_msg_size, typed_publish, typed_remote_actor, publish_local_groups,
new_connection_msg, new_data_msg, connection_closed_msg, acceptor_closed_msg
\end{lstlisting}
These classes concern I/O functionality and have thus been moved to
\lstinline^caf::io^
\subsection{0.10 to 0.11}
Version 0.11 introduced new, optional components. The core library itself,
however, mainly received optimizations and bugfixes with one exception: the
member function \lstinline^on_exit^ is no longer virtual. You can still provide
it to define a custom exit handler, but you must not use \lstinline^override^.
\subsection{0.11 to 0.12}
Version 0.12 removed two features:
\begin{itemize}
\item Type names are no longer demangled automatically. Hence, users must
explicitly pass the type name as first argument when using
\lstinline^announce^, i.e., \lstinline^announce<my_class>(...)^ becomes
\lstinline^announce<my_class>("my_class", ...)^.
\item Synchronous send blocks no longer support \lstinline^continue_with^. This
feature has been removed without substitution.
\end{itemize}
\subsection{0.12 to 0.13}
This release removes the (since 0.9 deprecated) \lstinline^cppa^ headers and
deprecates all \lstinline^*_send_tuple^ versions (simply use the function
without \lstinline^_tuple^ suffix). \lstinline^local_actor::on_exit^ once again
became virtual.
In case you were using the old \lstinline^cppa::options_description^ API, you
can migrate to the new API based on \lstinline^extract^ \see{extract-opts}.
Most importantly, version 0.13 slightly changes \lstinline^last_dequeued^ and
\lstinline^last_sender^. Both functions will now cause undefined behavior
(dereferencing a \lstinline^nullptr^) instead of returning dummy values when
accessed from outside a callback or after forwarding the current message.
Besides, these function names were not a good choice in the first place, since
``last'' implies accessing data received in the past. As a result, both
functions are now deprecated. Their replacements are named
\lstinline^current_message^ and \lstinline^current_sender^ \see{interface}.
\subsection{0.13 to 0.14}
The function \lstinline^timed_sync_send^ has been removed. It offered an
alternative way of defining message handlers, which is inconsistent with the
rest of the API.
The policy classes \lstinline^broadcast^, \lstinline^random^, and
\lstinline^round_robin^ in \lstinline^actor_pool^ were removed and replaced by
factory functions using the same name.
\clearpage
\subsection{0.14 to 0.15}
Version 0.15 replaces the singleton-based architecture with
\lstinline^actor_system^. Most of the free functions in namespace
\lstinline^caf^ are now member functions of \lstinline^actor_system^
\see{actor-system}. Likewise, most functions in namespace \lstinline^caf::io^
are now member functions of \lstinline^middleman^ \see{middleman}. The
structure of CAF applications has changed fundamentally with a focus on
configurability. Setting and fine-tuning the scheduler, changing parameters of
the middleman, etc. is now bundled in the class
\lstinline^actor_system_config^. The new configuration mechanism is also easily
extensible.
Patterns are now limited to the simple notation, because the advanced features
(1) are not implementable for statically typed actors, (2) are not portable to
Windows/MSVC, and (3) drastically impact compile times. Dropping this
functionality also simplifies the implementation and improves performance.
The \lstinline^blocking_api^ flag has been removed. All variants of
\emph{spawn} now auto-detect blocking actors.
This diff is collapsed.
\section{OpenCL-based Actors}
CAF supports transparent integration of OpenCL functions.
\begin{lstlisting}
// opencl kernel for matrix multiplication;
// last parameter is, by convention, the output parameter
constexpr const char* kernel_source = R"__(
__kernel void matrix_mult(__global float* matrix1,
__global float* matrix2,
__global float* output) {
// we only use square matrices, hence: width == height
size_t size = get_global_size(0); // == get_global_size_(1);
size_t x = get_global_id(0);
size_t y = get_global_id(1);
float result = 0;
for (size_t idx = 0; idx < size; ++idx)
result += matrix1[idx + y * size] * matrix2[x + idx * size];
output[x + y * size] = result;
}
)__";
\end{lstlisting}
This diff is collapsed.
This diff is collapsed.
\section{Remote Spawning of Actors \experimental}
\label{remote-spawn}
Remote spawning is an extension of the dynamic spawn using run-time type names
\see{add-custom-actor-type}. The following example assumes a typed actor handle
named \lstinline^calculator^ with an actor implementing this messaging
interface named "calculator".
\cppexample[125-143]{remoting/remote_spawn}
We first connect to a CAF node with \lstinline^middleman().connect(...)^. On
success, \lstinline^connect^ returns the node ID we need for
\lstinline^remote_spawn^. This requires the server to open a port with
\lstinline^middleman().open(...)^ or \lstinline^middleman().publish(...)^.
Alternatively, we can obtain the node ID from an already existing remote actor
handle---returned from \lstinline^remote_actor^ for example---via
\lstinline^hdl->node()^. After connecting to the server, we can use
\lstinline^middleman().remote_spawn<...>(...)^ to create actors remotely.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
\section{Using \texttt{aout} -- A Concurrency-safe Wrapper for \texttt{cout}}
When using \lstinline^cout^ from multiple actors, output often appears
interleaved. Moreover, using \lstinline^cout^ from multiple actors -- and thus
from multiple threads -- in parallel should be avoided regardless, since the
standard does not guarantee a thread-safe implementation.
By replacing \texttt{std::cout} with \texttt{caf::aout}, actors can achieve a
concurrency-safe text output. The header \lstinline^caf/all.hpp^ also defines
overloads for \texttt{std::endl} and \texttt{std::flush} for \lstinline^aout^,
but does not support the full range of ostream operations (yet). Each write
operation to \texttt{aout} sends a message to a `hidden' actor. This actor only
prints lines, unless output is forced using \lstinline^flush^. The example
below illustrates printing of lines of text from multiple actors (in random
order).
\cppexample{aout}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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