Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
dd49a57d
Commit
dd49a57d
authored
Aug 17, 2015
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/remoting-overhaul' into develop
parents
456e7d04
3cb67a3c
Changes
60
Show whitespace changes
Inline
Side-by-side
Showing
60 changed files
with
2933 additions
and
441 deletions
+2933
-441
doc/basp_header.graffle
doc/basp_header.graffle
+0
-0
doc/basp_header.png
doc/basp_header.png
+0
-0
examples/remote_actors/distributed_calculator.cpp
examples/remote_actors/distributed_calculator.cpp
+14
-7
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+3
-1
libcaf_core/caf/abstract_actor.hpp
libcaf_core/caf/abstract_actor.hpp
+20
-0
libcaf_core/caf/actor.hpp
libcaf_core/caf/actor.hpp
+5
-0
libcaf_core/caf/actor_namespace.hpp
libcaf_core/caf/actor_namespace.hpp
+3
-3
libcaf_core/caf/atom.hpp
libcaf_core/caf/atom.hpp
+43
-0
libcaf_core/caf/blocking_actor.hpp
libcaf_core/caf/blocking_actor.hpp
+2
-0
libcaf_core/caf/deserializer.hpp
libcaf_core/caf/deserializer.hpp
+5
-0
libcaf_core/caf/detail/actor_registry.hpp
libcaf_core/caf/detail/actor_registry.hpp
+23
-6
libcaf_core/caf/detail/type_traits.hpp
libcaf_core/caf/detail/type_traits.hpp
+20
-0
libcaf_core/caf/experimental/announce_actor_type.hpp
libcaf_core/caf/experimental/announce_actor_type.hpp
+126
-0
libcaf_core/caf/experimental/stateful_actor.hpp
libcaf_core/caf/experimental/stateful_actor.hpp
+22
-1
libcaf_core/caf/experimental/whereis.hpp
libcaf_core/caf/experimental/whereis.hpp
+34
-0
libcaf_core/caf/fwd.hpp
libcaf_core/caf/fwd.hpp
+12
-2
libcaf_core/caf/infer_handle.hpp
libcaf_core/caf/infer_handle.hpp
+126
-0
libcaf_core/caf/local_actor.hpp
libcaf_core/caf/local_actor.hpp
+14
-2
libcaf_core/caf/message.hpp
libcaf_core/caf/message.hpp
+6
-0
libcaf_core/caf/response_promise.hpp
libcaf_core/caf/response_promise.hpp
+5
-1
libcaf_core/caf/serializer.hpp
libcaf_core/caf/serializer.hpp
+5
-0
libcaf_core/caf/spawn.hpp
libcaf_core/caf/spawn.hpp
+1
-1
libcaf_core/caf/typed_behavior.hpp
libcaf_core/caf/typed_behavior.hpp
+6
-0
libcaf_core/src/abstract_actor.cpp
libcaf_core/src/abstract_actor.cpp
+8
-1
libcaf_core/src/actor_namespace.cpp
libcaf_core/src/actor_namespace.cpp
+1
-1
libcaf_core/src/actor_registry.cpp
libcaf_core/src/actor_registry.cpp
+123
-5
libcaf_core/src/announce_actor_type.cpp
libcaf_core/src/announce_actor_type.cpp
+81
-0
libcaf_core/src/event_based_actor.cpp
libcaf_core/src/event_based_actor.cpp
+1
-1
libcaf_core/src/local_actor.cpp
libcaf_core/src/local_actor.cpp
+120
-14
libcaf_core/src/message.cpp
libcaf_core/src/message.cpp
+3
-2
libcaf_core/src/response_promise.cpp
libcaf_core/src/response_promise.cpp
+2
-3
libcaf_core/src/run_program.cpp
libcaf_core/src/run_program.cpp
+6
-0
libcaf_core/src/singletons.cpp
libcaf_core/src/singletons.cpp
+2
-2
libcaf_core/src/uniform_type_info_map.cpp
libcaf_core/src/uniform_type_info_map.cpp
+0
-12
libcaf_core/src/whereis.cpp
libcaf_core/src/whereis.cpp
+35
-0
libcaf_core/test/announce_actor_type.cpp
libcaf_core/test/announce_actor_type.cpp
+146
-0
libcaf_core/test/announce_actor_type.cpp.autosave
libcaf_core/test/announce_actor_type.cpp.autosave
+255
-0
libcaf_core/test/aout.cpp
libcaf_core/test/aout.cpp
+189
-0
libcaf_core/test/local_migration.cpp
libcaf_core/test/local_migration.cpp
+111
-0
libcaf_core/test/message.cpp
libcaf_core/test/message.cpp
+12
-0
libcaf_io/caf/io/all.hpp
libcaf_io/caf/io/all.hpp
+1
-0
libcaf_io/caf/io/basp.hpp
libcaf_io/caf/io/basp.hpp
+45
-7
libcaf_io/caf/io/basp_broker.hpp
libcaf_io/caf/io/basp_broker.hpp
+22
-2
libcaf_io/caf/io/hook.hpp
libcaf_io/caf/io/hook.hpp
+19
-4
libcaf_io/caf/io/middleman.hpp
libcaf_io/caf/io/middleman.hpp
+4
-0
libcaf_io/caf/io/middleman_actor.hpp
libcaf_io/caf/io/middleman_actor.hpp
+68
-75
libcaf_io/caf/io/network/interfaces.hpp
libcaf_io/caf/io/network/interfaces.hpp
+5
-5
libcaf_io/caf/io/remote_actor.hpp
libcaf_io/caf/io/remote_actor.hpp
+8
-8
libcaf_io/src/basp.cpp
libcaf_io/src/basp.cpp
+97
-31
libcaf_io/src/basp_broker.cpp
libcaf_io/src/basp_broker.cpp
+234
-29
libcaf_io/src/hook.cpp
libcaf_io/src/hook.cpp
+19
-9
libcaf_io/src/middleman.cpp
libcaf_io/src/middleman.cpp
+78
-138
libcaf_io/src/publish.cpp
libcaf_io/src/publish.cpp
+1
-1
libcaf_io/src/remote_actor.cpp
libcaf_io/src/remote_actor.cpp
+12
-7
libcaf_io/src/scribe.cpp
libcaf_io/src/scribe.cpp
+5
-3
libcaf_io/src/unpublish.cpp
libcaf_io/src/unpublish.cpp
+2
-2
libcaf_io/test/automatic_connection.cpp
libcaf_io/test/automatic_connection.cpp
+281
-0
libcaf_io/test/basp.cpp
libcaf_io/test/basp.cpp
+292
-54
libcaf_io/test/remote_spawn.cpp
libcaf_io/test/remote_spawn.cpp
+148
-0
libcaf_io/test/uniform_type.cpp
libcaf_io/test/uniform_type.cpp
+2
-1
No files found.
doc/basp_header.graffle
View file @
dd49a57d
No preview for this file type
doc/basp_header.png
View replaced file @
456e7d04
View file @
dd49a57d
39.3 KB
|
W:
|
H:
76.8 KB
|
W:
|
H:
2-up
Swipe
Onion skin
examples/remote_actors/distributed_calculator.cpp
View file @
dd49a57d
...
...
@@ -35,7 +35,6 @@ using plus_atom = atom_constant<atom("plus")>;
using
minus_atom
=
atom_constant
<
atom
(
"minus"
)
>
;
using
result_atom
=
atom_constant
<
atom
(
"result"
)
>
;
using
rebind_atom
=
atom_constant
<
atom
(
"rebind"
)
>
;
using
connect_atom
=
atom_constant
<
atom
(
"connect"
)
>
;
using
reconnect_atom
=
atom_constant
<
atom
(
"reconnect"
)
>
;
// our "service"
...
...
@@ -105,9 +104,13 @@ private:
behavior
reconnecting
(
std
::
function
<
void
()
>
continuation
=
nullptr
)
{
using
std
::
chrono
::
seconds
;
auto
mm
=
io
::
get_middleman_actor
();
send
(
mm
,
ge
t_atom
::
value
,
host_
,
port_
);
send
(
mm
,
connec
t_atom
::
value
,
host_
,
port_
);
return
{
[
=
](
ok_atom
,
const
actor_addr
&
new_server
)
{
[
=
](
ok_atom
,
node_id
&
,
actor_addr
&
new_server
,
std
::
set
<
std
::
string
>&
)
{
if
(
new_server
==
invalid_actor_addr
)
{
aout
(
this
)
<<
"*** received invalid remote actor"
<<
endl
;
return
;
}
aout
(
this
)
<<
"*** connection succeeded, awaiting tasks"
<<
endl
;
server_
=
actor_cast
<
actor
>
(
new_server
);
// return to previous behavior
...
...
@@ -122,7 +125,7 @@ private:
<<
": "
<<
errstr
<<
" [try again in 3s]"
<<
endl
;
delayed_send
(
mm
,
seconds
(
3
),
ge
t_atom
::
value
,
host_
,
port_
);
delayed_send
(
mm
,
seconds
(
3
),
connec
t_atom
::
value
,
host_
,
port_
);
},
[
=
](
rebind_atom
,
string
&
nhost
,
uint16_t
nport
)
{
aout
(
this
)
<<
"*** rebind to "
<<
nhost
<<
":"
<<
nport
<<
endl
;
...
...
@@ -131,13 +134,17 @@ private:
swap
(
port_
,
nport
);
auto
send_mm
=
[
=
]
{
unbecome
();
send
(
mm
,
ge
t_atom
::
value
,
host_
,
port_
);
send
(
mm
,
connec
t_atom
::
value
,
host_
,
port_
);
};
// await pending ok/error message first, then send new request to MM
become
(
keep_behavior
,
[
=
](
ok_atom
&
,
actor_addr
&
)
{
send_mm
();
},
[
=
](
error_atom
&
,
string
&
)
{
send_mm
();
}
[
=
](
ok_atom
&
,
actor_addr
&
)
{
send_mm
();
},
[
=
](
error_atom
&
,
string
&
)
{
send_mm
();
}
);
},
// simply ignore all requests until we have a connection
...
...
libcaf_core/CMakeLists.txt
View file @
dd49a57d
...
...
@@ -24,6 +24,7 @@ set (LIBCAF_CORE_SRCS
src/actor_proxy.cpp
src/actor_registry.cpp
src/attachable.cpp
src/announce_actor_type.cpp
src/behavior.cpp
src/behavior_stack.cpp
src/behavior_impl.cpp
...
...
@@ -77,7 +78,8 @@ set (LIBCAF_CORE_SRCS
src/sync_request_bouncer.cpp
src/try_match.cpp
src/uniform_type_info.cpp
src/uniform_type_info_map.cpp
)
src/uniform_type_info_map.cpp
src/whereis.cpp
)
add_custom_target
(
libcaf_core
)
...
...
libcaf_core/caf/abstract_actor.hpp
View file @
dd49a57d
...
...
@@ -156,6 +156,8 @@ protected:
public:
/// @cond PRIVATE
static
actor_id
latest_actor_id
();
enum
linking_operation
{
establish_link_op
,
establish_backlink_op
,
...
...
@@ -171,6 +173,8 @@ public:
static
constexpr
int
is_blocking_flag
=
0x10
;
// blocking_actor
static
constexpr
int
is_detached_flag
=
0x20
;
// local_actor
static
constexpr
int
is_priority_aware_flag
=
0x40
;
// local_actor
static
constexpr
int
is_serializable_flag
=
0x40
;
// local_actor
static
constexpr
int
is_migrated_from_flag
=
0x80
;
// local_actor
inline
void
set_flag
(
bool
enable_flag
,
int
mask
)
{
auto
x
=
flags
();
...
...
@@ -227,6 +231,22 @@ public:
set_flag
(
value
,
is_priority_aware_flag
);
}
inline
bool
is_serializable
()
const
{
return
get_flag
(
is_serializable_flag
);
}
inline
void
is_serializable
(
bool
value
)
{
set_flag
(
value
,
is_serializable_flag
);
}
inline
bool
is_migrated_from
()
const
{
return
get_flag
(
is_migrated_from_flag
);
}
inline
void
is_migrated_from
(
bool
value
)
{
set_flag
(
value
,
is_migrated_from_flag
);
}
// Tries to run a custom exception handler for `eptr`.
optional
<
uint32_t
>
handle
(
const
std
::
exception_ptr
&
eptr
);
...
...
libcaf_core/caf/actor.hpp
View file @
dd49a57d
...
...
@@ -135,6 +135,11 @@ public:
/// 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
>
{};
}
/// @cond PRIVATE
inline
abstract_actor
*
operator
->
()
const
noexcept
{
...
...
libcaf_core/caf/actor_namespace.hpp
View file @
dd49a57d
...
...
@@ -20,9 +20,9 @@
#ifndef CAF_ACTOR_NAMESPACE_HPP
#define CAF_ACTOR_NAMESPACE_HPP
#include <map>
#include <utility>
#include <functional>
#include <unordered_map>
#include "caf/node_id.hpp"
#include "caf/actor_cast.hpp"
...
...
@@ -56,7 +56,7 @@ public:
/// Writes an actor address to `sink` and adds the actor
/// to the list of known actors for a later deserialization.
void
write
(
serializer
*
sink
,
const
actor_addr
&
ptr
);
void
write
(
serializer
*
sink
,
const
actor_addr
&
ptr
)
const
;
/// Reads an actor address from `source,` creating
/// addresses for remote actors on the fly if needed.
...
...
@@ -120,7 +120,7 @@ public:
private:
backend
&
backend_
;
std
::
map
<
key_type
,
proxy_map
>
proxies_
;
std
::
unordered_
map
<
key_type
,
proxy_map
>
proxies_
;
};
}
// namespace caf
...
...
libcaf_core/caf/atom.hpp
View file @
dd49a57d
...
...
@@ -21,6 +21,7 @@
#define CAF_ATOM_HPP
#include <string>
#include <functional>
#include <type_traits>
#include "caf/detail/atom_val.hpp"
...
...
@@ -68,6 +69,9 @@ using get_atom = atom_constant<atom("GET")>;
/// Generic 'PUT' atom for request operations.
using
put_atom
=
atom_constant
<
atom
(
"PUT"
)
>
;
/// Generic 'UPDATE' atom, e.g., or signalizing updates in a key-value store.
using
update_atom
=
atom_constant
<
atom
(
"UPDATE"
)
>
;
/// Generic 'DELETE' atom for request operations.
using
delete_atom
=
atom_constant
<
atom
(
"DELETE"
)
>
;
...
...
@@ -104,6 +108,45 @@ using link_atom = atom_constant<atom("LINK")>;
/// Generic 'UNLINK' atom for removing networked links.
using
unlink_atom
=
atom_constant
<
atom
(
"UNLINK"
)
>
;
/// Generic 'PUBLISH' atom, e.g., for publishing actors at a given port.
using
publish_atom
=
atom_constant
<
atom
(
"PUBLISH"
)
>
;
/// Generic 'UNPUBLISH' atom, e.g., for removing an actor/port mapping.
using
unpublish_atom
=
atom_constant
<
atom
(
"UNPUBLISH"
)
>
;
/// Generic 'PUBLISH' atom, e.g., for publishing actors at a given port.
using
subscribe_atom
=
atom_constant
<
atom
(
"SUBSCRIBE"
)
>
;
/// Generic 'UNPUBLISH' atom, e.g., for removing an actor/port mapping.
using
unsubscribe_atom
=
atom_constant
<
atom
(
"UNSUBSCRIB"
)
>
;
/// Generic 'CONNECT' atom, e.g., for connecting to remote CAF instances.
using
connect_atom
=
atom_constant
<
atom
(
"CONNECT"
)
>
;
/// Generic 'OPEN' atom, e.g., for opening a port or file.
using
open_atom
=
atom_constant
<
atom
(
"OPEN"
)
>
;
/// Generic 'CLOSE' atom, e.g., for closing a port or file.
using
close_atom
=
atom_constant
<
atom
(
"CLOSE"
)
>
;
/// Generic 'SPAWN' atom, e.g., for spawning remote actors.
using
spawn_atom
=
atom_constant
<
atom
(
"SPAWN"
)
>
;
/// Atom to signalize an actor to migrate its state to another actor.
using
migrate_atom
=
atom_constant
<
atom
(
"MIGRATE"
)
>
;
}
// namespace caf
namespace
std
{
template
<
>
struct
hash
<
caf
::
atom_value
>
{
size_t
operator
()(
caf
::
atom_value
x
)
const
{
hash
<
uint64_t
>
f
;
return
f
(
static_cast
<
uint64_t
>
(
x
));
}
};
}
// namespace std
#endif // CAF_ATOM_HPP
libcaf_core/caf/blocking_actor.hpp
View file @
dd49a57d
...
...
@@ -46,6 +46,8 @@ class blocking_actor
:
public
extend
<
local_actor
,
blocking_actor
>::
with
<
mixin
::
sync_sender
<
blocking_response_handle_tag
>::
impl
>
{
public:
using
behavior_type
=
behavior
;
blocking_actor
();
~
blocking_actor
();
...
...
libcaf_core/caf/deserializer.hpp
View file @
dd49a57d
...
...
@@ -127,6 +127,11 @@ operator>>(deserializer& source, T& value) {
return
source
.
read
(
value
,
uniform_typeid
<
T
>
());
}
template
<
class
T
>
void
operator
&
(
deserializer
&
source
,
T
&
value
)
{
source
>>
value
;
}
}
// namespace caf
#endif // CAF_DESERIALIZER_HPP
libcaf_core/caf/detail/actor_registry.hpp
View file @
dd49a57d
...
...
@@ -20,13 +20,14 @@
#ifndef CAF_DETAIL_ACTOR_REGISTRY_HPP
#define CAF_DETAIL_ACTOR_REGISTRY_HPP
#include <map>
#include <mutex>
#include <thread>
#include <atomic>
#include <cstdint>
#include <unordered_map>
#include <condition_variable>
#include "caf/actor.hpp"
#include "caf/actor_addr.hpp"
#include "caf/abstract_actor.hpp"
...
...
@@ -38,10 +39,8 @@ namespace detail {
class
singletons
;
class
actor_registry
:
public
singleton_mixin
<
actor_registry
>
{
class
actor_registry
{
public:
friend
class
singleton_mixin
<
actor_registry
>
;
~
actor_registry
();
/// A registry entry consists of a pointer to the actor and an
...
...
@@ -82,19 +81,37 @@ public:
// blocks the caller until running-actors-count becomes `expected`
void
await_running_count_equal
(
size_t
expected
);
actor
get_named
(
atom_value
key
)
const
;
void
put_named
(
atom_value
key
,
actor
value
);
using
named_entries
=
std
::
unordered_map
<
atom_value
,
actor
>
;
named_entries
named_actors
()
const
;
static
actor_registry
*
create_singleton
();
void
dispose
();
void
stop
();
void
initialize
();
private:
using
entries
=
std
::
map
<
actor_id
,
value_type
>
;
using
entries
=
std
::
unordered_
map
<
actor_id
,
value_type
>
;
actor_registry
();
std
::
atomic
<
size_t
>
running_
;
std
::
atomic
<
actor_id
>
ids_
;
std
::
mutex
running_mtx_
;
std
::
condition_variable
running_cv_
;
mutable
detail
::
shared_spinlock
instances_mtx_
;
entries
entries_
;
named_entries
named_entries_
;
mutable
detail
::
shared_spinlock
named_entries_mtx_
;
};
}
// namespace detail
...
...
libcaf_core/caf/detail/type_traits.hpp
View file @
dd49a57d
...
...
@@ -493,6 +493,26 @@ public:
static
constexpr
bool
value
=
false
;
};
// checks whether T is serializable using a free function `serialize` taking
// either a `caf::serializer` or `caf::deserializer` as first argument
template
<
class
T
>
struct
is_serializable
{
private:
template
<
class
U
>
static
int8_t
fun
(
U
*
,
decltype
(
serialize
(
std
::
declval
<
serializer
&>
(),
std
::
declval
<
U
&>
(),
0
))
*
=
nullptr
,
decltype
(
serialize
(
std
::
declval
<
deserializer
&>
(),
std
::
declval
<
U
&>
(),
0
))
*
=
nullptr
);
static
int16_t
fun
(...);
public:
static
constexpr
bool
value
=
sizeof
(
fun
(
static_cast
<
T
*>
(
nullptr
)))
==
1
;
};
template
<
class
T
>
constexpr
bool
is_serializable
<
T
>::
value
;
}
// namespace detail
}
// namespace caf
...
...
libcaf_core/caf/experimental/announce_actor_type.hpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_EXPERIMENTAL_ANNOUNCE_ACTOR_HPP
#define CAF_EXPERIMENTAL_ANNOUNCE_ACTOR_HPP
#include <type_traits>
#include "caf/actor.hpp"
#include "caf/actor_addr.hpp"
#include "caf/infer_handle.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/actor_registry.hpp"
namespace
caf
{
namespace
experimental
{
using
spawn_result
=
std
::
pair
<
actor_addr
,
std
::
set
<
std
::
string
>>
;
using
spawn_fun
=
std
::
function
<
spawn_result
(
message
)
>
;
template
<
class
Trait
,
class
F
>
spawn_result
dyn_spawn_impl
(
F
ibf
)
{
using
impl
=
typename
Trait
::
impl
;
using
behavior_t
=
typename
Trait
::
behavior_type
;
auto
ptr
=
make_counted
<
impl
>
();
ptr
->
initial_behavior_fac
([
=
](
local_actor
*
self
)
->
behavior
{
auto
res
=
ibf
(
self
);
if
(
res
&&
res
->
size
()
>
0
&&
res
->
template
match_element
<
behavior_t
>(
0
))
{
return
std
::
move
(
res
->
template
get_as_mutable
<
behavior_t
>(
0
).
unbox
());
}
return
{};
});
ptr
->
launch
(
nullptr
,
false
,
false
);
return
{
ptr
->
address
(),
Trait
::
type
::
message_types
()};
}
template
<
class
Trait
,
class
F
>
spawn_result
dyn_spawn
(
F
fun
,
message
&
msg
,
spawn_mode_token
<
spawn_mode
::
function
>
)
{
return
dyn_spawn_impl
<
Trait
>
([
=
](
local_actor
*
)
->
optional
<
message
>
{
return
const_cast
<
message
&>
(
msg
).
apply
(
fun
);
});
}
template
<
class
Trait
,
class
F
>
spawn_result
dyn_spawn
(
F
fun
,
message
&
msg
,
spawn_mode_token
<
spawn_mode
::
function_with_selfptr
>
)
{
return
dyn_spawn_impl
<
Trait
>
([
=
](
local_actor
*
self
)
->
optional
<
message
>
{
// we can't use make_message here because of the implicit conversions
using
storage
=
detail
::
tuple_vals
<
typename
Trait
::
impl
*>
;
auto
ptr
=
make_counted
<
storage
>
(
static_cast
<
typename
Trait
::
impl
*>
(
self
));
auto
m
=
message
{
detail
::
message_data
::
cow_ptr
{
std
::
move
(
ptr
)}}
+
msg
;
return
m
.
apply
(
fun
);
});
}
template
<
class
F
>
spawn_fun
make_spawn_fun
(
F
fun
)
{
return
[
fun
](
message
msg
)
->
spawn_result
{
using
trait
=
infer_handle_from_fun
<
F
>
;
spawn_mode_token
<
trait
::
mode
>
tk
;
return
dyn_spawn
<
trait
>
(
fun
,
msg
,
tk
);
};
}
template
<
class
T
,
class
...
Ts
>
spawn_fun
make_spawn_fun
()
{
static_assert
(
std
::
is_same
<
T
*
,
decltype
(
new
T
(
std
::
declval
<
Ts
>
()...))
>::
value
,
"no constructor for T(Ts...) exists"
);
static_assert
(
detail
::
conjunction
<
std
::
is_lvalue_reference
<
Ts
>::
value
...
>::
value
,
"all Ts must be lvalue references"
);
static_assert
(
std
::
is_base_of
<
local_actor
,
T
>::
value
,
"T is not derived from local_actor"
);
using
handle
=
typename
infer_handle_from_class
<
T
>::
type
;
using
pointer
=
intrusive_ptr
<
T
>
;
auto
factory
=
&
make_counted
<
T
,
Ts
...
>
;
return
[
=
](
message
msg
)
->
spawn_result
{
pointer
ptr
;
auto
res
=
msg
.
apply
(
factory
);
if
(
!
res
||
res
->
empty
()
||
!
res
->
template
match_element
<
pointer
>(
0
))
return
{};
ptr
=
std
::
move
(
res
->
template
get_as_mutable
<
pointer
>(
0
));
ptr
->
launch
(
nullptr
,
false
,
false
);
return
{
ptr
->
address
(),
handle
::
message_types
()};
};
}
actor
spawn_announce_actor_type_server
();
void
announce_actor_type_impl
(
std
::
string
&&
name
,
spawn_fun
f
);
template
<
class
F
>
void
announce_actor_type
(
std
::
string
name
,
F
fun
)
{
announce_actor_type_impl
(
std
::
move
(
name
),
make_spawn_fun
(
fun
));
}
template
<
class
T
,
class
...
Ts
>
void
announce_actor_type
(
std
::
string
name
)
{
announce_actor_type_impl
(
std
::
move
(
name
),
make_spawn_fun
<
T
,
Ts
...
>
());
}
}
// namespace experimental
}
// namespace caf
#endif // CAF_EXPERIMENTAL_ANNOUNCE_ACTOR_HPP
libcaf_core/caf/experimental/stateful_actor.hpp
View file @
dd49a57d
...
...
@@ -29,6 +29,18 @@
namespace
caf
{
namespace
experimental
{
template
<
class
Archive
,
class
U
>
typename
std
::
enable_if
<
detail
::
is_serializable
<
U
>::
value
>::
type
serialize_state
(
Archive
&
ar
,
U
&
st
,
const
unsigned
int
version
)
{
serialize
(
ar
,
st
,
version
);
}
template
<
class
Archive
,
class
U
>
typename
std
::
enable_if
<!
detail
::
is_serializable
<
U
>::
value
>::
type
serialize_state
(
Archive
&
,
U
&
,
const
unsigned
int
)
{
throw
std
::
logic_error
(
"serialize_state with unserializable type called"
);
}
/// An event-based actor with managed state. The state is constructed
/// before `make_behavior` will get called and destroyed after the
/// actor called `quit`. This state management brakes cycles and
...
...
@@ -39,7 +51,8 @@ class stateful_actor : public Base {
public:
template
<
class
...
Ts
>
stateful_actor
(
Ts
&&
...
xs
)
:
Base
(
std
::
forward
<
Ts
>
(
xs
)...),
state
(
state_
)
{
// nop
if
(
detail
::
is_serializable
<
State
>::
value
)
this
->
is_serializable
(
true
);
}
~
stateful_actor
()
{
...
...
@@ -55,6 +68,14 @@ public:
return
get_name
(
state_
);
}
void
save
(
serializer
&
sink
,
const
unsigned
int
version
)
override
{
serialize_state
(
sink
,
state
,
version
);
}
void
load
(
deserializer
&
source
,
const
unsigned
int
version
)
override
{
serialize_state
(
source
,
state
,
version
);
}
/// A reference to the actor's state.
State
&
state
;
...
...
libcaf_core/caf/experimental/whereis.hpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_EXPERIMENTAL_WHEREIS_HPP
#define CAF_EXPERIMENTAL_WHEREIS_HPP
#include "caf/fwd.hpp"
#include "caf/atom.hpp"
namespace
caf
{
namespace
experimental
{
actor
whereis
(
atom_value
registered_name
);
}
// namespace experimental
}
// namespace caf
#endif // CAF_EXPERIMENTAL_WHEREIS_HPP
libcaf_core/caf/fwd.hpp
View file @
dd49a57d
...
...
@@ -56,6 +56,8 @@ class mailbox_element;
class
message_handler
;
class
uniform_type_info
;
class
event_based_actor
;
class
binary_serializer
;
class
binary_deserializer
;
class
forwarding_actor_proxy
;
// structs
...
...
@@ -87,8 +89,16 @@ template <class T, typename U>
T
actor_cast
(
const
U
&
);
namespace
io
{
class
broker
;
class
middleman
;
class
broker
;
class
middleman
;
namespace
basp
{
struct
header
;
}
// namespace basp
}
// namespace io
namespace
scheduler
{
...
...
libcaf_core/caf/infer_handle.hpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_INFER_HANDLE_HPP
#define CAF_INFER_HANDLE_HPP
#include "caf/actor.hpp"
#include "caf/actor_addr.hpp"
#include "caf/typed_behavior.hpp"
#include "caf/typed_event_based_actor.hpp"
namespace
caf
{
enum
class
spawn_mode
{
function
,
function_with_selfptr
,
clazz
};
template
<
spawn_mode
X
>
using
spawn_mode_token
=
std
::
integral_constant
<
spawn_mode
,
X
>
;
// default: dynamically typed actor without self pointer
template
<
class
Result
,
class
FirstArg
,
bool
FirstArgValid
=
std
::
is_base_of
<
local_actor
,
typename
std
::
remove_pointer
<
FirstArg
>
::
type
>::
value
>
struct
infer_handle_from_fun_impl
{
using
type
=
actor
;
using
impl
=
event_based_actor
;
using
behavior_type
=
behavior
;
static
constexpr
spawn_mode
mode
=
spawn_mode
::
function
;
};
// dynamically typed actor returning a behavior
template
<
class
Impl
>
struct
infer_handle_from_fun_impl
<
void
,
Impl
*
,
true
>
{
using
type
=
actor
;
using
impl
=
Impl
;
using
behavior_type
=
behavior
;
static
constexpr
spawn_mode
mode
=
spawn_mode
::
function_with_selfptr
;
};
// dynamically typed actor with self pointer
template
<
class
Impl
>
struct
infer_handle_from_fun_impl
<
behavior
,
Impl
*
,
true
>
{
using
type
=
actor
;
using
impl
=
Impl
;
using
behavior_type
=
behavior
;
static
constexpr
spawn_mode
mode
=
spawn_mode
::
function_with_selfptr
;
};
// statically typed actor returning a behavior
template
<
class
...
Sigs
,
class
FirstArg
>
struct
infer_handle_from_fun_impl
<
typed_behavior
<
Sigs
...
>
,
FirstArg
,
false
>
{
using
type
=
typed_actor
<
Sigs
...
>
;
using
impl
=
typed_event_based_actor
<
Sigs
...
>
;
using
behavior_type
=
typed_behavior
<
Sigs
...
>
;
static
constexpr
spawn_mode
mode
=
spawn_mode
::
function
;
};
// statically typed actor with self pointer
template
<
class
Result
,
class
...
Sigs
>
struct
infer_handle_from_fun_impl
<
Result
,
typed_event_based_actor
<
Sigs
...
>*
,
true
>
{
using
type
=
typed_actor
<
Sigs
...
>
;
using
impl
=
typed_event_based_actor
<
Sigs
...
>
;
using
behavior_type
=
typed_behavior
<
Sigs
...
>
;
static
constexpr
spawn_mode
mode
=
spawn_mode
::
function_with_selfptr
;
};
template
<
class
F
,
class
Trait
=
typename
detail
::
get_callable_trait
<
F
>
::
type
>
struct
infer_handle_from_fun
{
using
result_type
=
typename
Trait
::
result_type
;
using
arg_types
=
typename
Trait
::
arg_types
;
using
first_arg
=
typename
detail
::
tl_head
<
arg_types
>::
type
;
using
delegate
=
infer_handle_from_fun_impl
<
result_type
,
first_arg
>
;
using
type
=
typename
delegate
::
type
;
using
impl
=
typename
delegate
::
impl
;
using
behavior_type
=
typename
delegate
::
behavior_type
;
using
fun_type
=
typename
Trait
::
fun_type
;
static
constexpr
spawn_mode
mode
=
delegate
::
mode
;
};
template
<
class
T
>
struct
infer_handle_from_behavior
{
using
type
=
actor
;
};
template
<
class
...
Sigs
>
struct
infer_handle_from_behavior
<
typed_behavior
<
Sigs
...
>>
{
using
type
=
typed_actor
<
Sigs
...
>
;
};
template
<
class
T
>
struct
infer_handle_from_class
{
using
type
=
typename
infer_handle_from_behavior
<
typename
T
::
behavior_type
>::
type
;
static
constexpr
spawn_mode
mode
=
spawn_mode
::
clazz
;
};
}
// namespace caf
#endif // CAF_INFER_HANDLE_HPP
libcaf_core/caf/local_actor.hpp
View file @
dd49a57d
...
...
@@ -392,6 +392,16 @@ public:
/// implementation simply returns "actor".
virtual
const
char
*
name
()
const
;
/// Serializes the state of this actor to `sink`. This function is
/// only called if this actor has set the `is_serializable` flag.
/// The default implementation throws a `std::logic_error`.
virtual
void
save
(
serializer
&
sink
,
const
unsigned
int
version
);
/// Deserializes the state of this actor from `source`. This function is
/// only called if this actor has set the `is_serializable` flag.
/// The default implementation throws a `std::logic_error`.
virtual
void
load
(
deserializer
&
source
,
const
unsigned
int
version
);
/****************************************************************************
* deprecated member functions *
****************************************************************************/
...
...
@@ -463,7 +473,9 @@ public:
return
(
mid
.
is_request
())
?
mid
.
response_id
()
:
message_id
();
}
void
forward_message
(
const
actor
&
dest
,
message_priority
mp
);
void
forward_current_message
(
const
actor
&
dest
);
void
forward_current_message
(
const
actor
&
dest
,
message_priority
mp
);
template
<
class
...
Ts
>
void
delegate
(
message_priority
mp
,
const
actor
&
dest
,
Ts
&&
...
xs
)
{
...
...
@@ -611,9 +623,9 @@ public:
initial_behavior_fac_
=
std
::
move
(
fun
);
}
protected:
void
do_become
(
behavior
bhvr
,
bool
discard_old
);
protected:
// used only in thread-mapped actors
void
await_data
();
...
...
libcaf_core/caf/message.hpp
View file @
dd49a57d
...
...
@@ -390,6 +390,12 @@ inline message make_message(message other) {
return
std
::
move
(
other
);
}
/// Returns an empty `message`.
/// @relates message
inline
message
make_message
()
{
return
message
{};
}
/******************************************************************************
* template member function implementations *
******************************************************************************/
...
...
libcaf_core/caf/response_promise.hpp
View file @
dd49a57d
...
...
@@ -49,9 +49,13 @@ public:
}
/// Sends `response_message` and invalidates this handle afterwards.
void
deliver
(
message
response_message
)
const
;
template
<
class
...
Ts
>
void
deliver
(
Ts
&&
...
xs
)
const
{
deliver_impl
(
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
}
private:
void
deliver_impl
(
message
response_message
)
const
;
actor_addr
from_
;
actor_addr
to_
;
message_id
id_
;
...
...
libcaf_core/caf/serializer.hpp
View file @
dd49a57d
...
...
@@ -107,6 +107,11 @@ operator<<(serializer& sink, const T& value) {
return
sink
.
write
(
value
,
uniform_typeid
<
T
>
());
}
template
<
class
T
>
void
operator
&
(
serializer
&
sink
,
const
T
&
value
)
{
sink
<<
value
;
}
}
// namespace caf
#endif // CAF_SERIALIZER_HPP
libcaf_core/caf/spawn.hpp
View file @
dd49a57d
...
...
@@ -222,7 +222,7 @@ struct infer_typed_actor_base<Result, T*, true> {
/// constructor arguments.
template
<
class
C
,
spawn_options
Os
=
no_spawn_options
,
class
...
Ts
>
typename
actor_handle_from_signature_list
<
typename
C
::
signatures
>::
type
spawn_typed
(
Ts
&&
...
xs
)
{
CAF_DEPRECATED
spawn_typed
(
Ts
&&
...
xs
)
{
return
spawn_class
<
C
,
Os
>
(
nullptr
,
empty_before_launch_callback
{},
std
::
forward
<
Ts
>
(
xs
)...);
}
...
...
libcaf_core/caf/typed_behavior.hpp
View file @
dd49a57d
...
...
@@ -228,6 +228,12 @@ private:
behavior
bhvr_
;
};
template
<
class
T
>
struct
is_typed_behavior
:
std
::
false_type
{
};
template
<
class
...
Sigs
>
struct
is_typed_behavior
<
typed_behavior
<
Sigs
...
>>
:
std
::
true_type
{
};
}
// namespace caf
#endif // CAF_TYPED_BEHAVIOR_HPP
libcaf_core/src/abstract_actor.cpp
View file @
dd49a57d
...
...
@@ -41,9 +41,16 @@
namespace
caf
{
namespace
{
using
guard_type
=
std
::
unique_lock
<
std
::
mutex
>
;
std
::
atomic
<
actor_id
>
ids_
;
}
// namespace <anonymous>
actor_id
abstract_actor
::
latest_actor_id
()
{
return
ids_
.
load
();
}
// exit_reason_ is guaranteed to be set to 0, i.e., exit_reason::not_exited,
// by std::atomic<> constructor
...
...
@@ -59,7 +66,7 @@ abstract_actor::abstract_actor(actor_id aid, node_id nid)
abstract_actor
::
abstract_actor
()
:
abstract_channel
(
abstract_channel
::
is_abstract_actor_flag
,
detail
::
singletons
::
get_node_id
()),
id_
(
detail
::
singletons
::
get_actor_registry
()
->
next_id
()
),
id_
(
++
ids_
),
exit_reason_
(
exit_reason
::
not_exited
),
host_
(
nullptr
)
{
// nop
...
...
libcaf_core/src/actor_namespace.cpp
View file @
dd49a57d
...
...
@@ -62,7 +62,7 @@ actor_namespace::actor_namespace(backend& be) : backend_(be) {
// nop
}
void
actor_namespace
::
write
(
serializer
*
sink
,
const
actor_addr
&
addr
)
{
void
actor_namespace
::
write
(
serializer
*
sink
,
const
actor_addr
&
addr
)
const
{
CAF_ASSERT
(
sink
!=
nullptr
);
if
(
!
addr
)
{
node_id
::
host_id_type
zero
;
...
...
libcaf_core/src/actor_registry.cpp
View file @
dd49a57d
...
...
@@ -22,11 +22,19 @@
#include <mutex>
#include <limits>
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include "caf/spawn.hpp"
#include "caf/locks.hpp"
#include "caf/actor_cast.hpp"
#include "caf/attachable.hpp"
#include "caf/exit_reason.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/experimental/stateful_actor.hpp"
#include "caf/experimental/announce_actor_type.hpp"
#include "caf/scheduler/detached_threads.hpp"
...
...
@@ -47,7 +55,7 @@ actor_registry::~actor_registry() {
// nop
}
actor_registry
::
actor_registry
()
:
running_
(
0
)
,
ids_
(
1
)
{
actor_registry
::
actor_registry
()
:
running_
(
0
)
{
// nop
}
...
...
@@ -95,10 +103,6 @@ void actor_registry::erase(actor_id key, uint32_t reason) {
}
}
uint32_t
actor_registry
::
next_id
()
{
return
++
ids_
;
}
void
actor_registry
::
inc_running
()
{
# if defined(CAF_LOG_LEVEL) && CAF_LOG_LEVEL >= CAF_DEBUG
CAF_LOG_DEBUG
(
"new value = "
<<
++
running_
);
...
...
@@ -130,5 +134,119 @@ void actor_registry::await_running_count_equal(size_t expected) {
}
}
actor
actor_registry
::
get_named
(
atom_value
key
)
const
{
shared_guard
guard
{
named_entries_mtx_
};
auto
i
=
named_entries_
.
find
(
key
);
if
(
i
==
named_entries_
.
end
())
return
invalid_actor
;
return
i
->
second
;
}
void
actor_registry
::
put_named
(
atom_value
key
,
actor
value
)
{
if
(
value
)
value
->
attach_functor
([
=
](
uint32_t
)
{
detail
::
singletons
::
get_actor_registry
()
->
put_named
(
key
,
invalid_actor
);
});
exclusive_guard
guard
{
named_entries_mtx_
};
named_entries_
.
emplace
(
key
,
std
::
move
(
value
));
}
auto
actor_registry
::
named_actors
()
const
->
named_entries
{
shared_guard
guard
{
named_entries_mtx_
};
return
named_entries_
;
}
actor_registry
*
actor_registry
::
create_singleton
()
{
return
new
actor_registry
;
}
void
actor_registry
::
dispose
()
{
delete
this
;
}
void
actor_registry
::
stop
()
{
scoped_actor
self
{
true
};
for
(
auto
&
kvp
:
named_entries_
)
{
self
->
monitor
(
kvp
.
second
);
self
->
send_exit
(
kvp
.
second
,
exit_reason
::
kill
);
self
->
receive
(
[](
const
down_msg
&
)
{
// nop
}
);
}
named_entries_
.
clear
();
}
void
actor_registry
::
initialize
()
{
using
namespace
experimental
;
struct
kvstate
{
using
key_type
=
std
::
string
;
using
mapped_type
=
message
;
using
subscriber_set
=
std
::
unordered_set
<
actor
>
;
using
topic_set
=
std
::
unordered_set
<
std
::
string
>
;
std
::
unordered_map
<
key_type
,
std
::
pair
<
mapped_type
,
subscriber_set
>>
data
;
std
::
unordered_map
<
actor
,
topic_set
>
subscribers
;
const
char
*
name
=
"caf.config_server"
;
};
auto
kvstore
=
[](
stateful_actor
<
kvstate
>*
self
)
->
behavior
{
return
{
[
=
](
put_atom
,
const
std
::
string
&
key
,
message
&
msg
)
{
auto
&
vp
=
self
->
state
.
data
[
key
];
if
(
vp
.
first
==
msg
)
return
;
vp
.
first
=
std
::
move
(
msg
);
for
(
auto
&
subscriber
:
vp
.
second
)
if
(
subscriber
!=
self
->
current_sender
())
self
->
send
(
subscriber
,
update_atom
::
value
,
key
,
vp
.
second
);
},
[
=
](
get_atom
,
std
::
string
&
key
)
->
message
{
auto
i
=
self
->
state
.
data
.
find
(
key
);
return
make_message
(
ok_atom
::
value
,
std
::
move
(
key
),
i
!=
self
->
state
.
data
.
end
()
?
i
->
second
.
first
:
make_message
());
},
[
=
](
subscribe_atom
,
const
std
::
string
&
key
)
{
auto
subscriber
=
actor_cast
<
actor
>
(
self
->
current_sender
());
if
(
!
subscriber
)
return
;
self
->
state
.
data
[
key
].
second
.
insert
(
subscriber
);
auto
&
subscribers
=
self
->
state
.
subscribers
;
auto
i
=
subscribers
.
find
(
subscriber
);
if
(
i
!=
subscribers
.
end
())
{
i
->
second
.
insert
(
key
);
}
else
{
self
->
monitor
(
subscriber
);
subscribers
.
emplace
(
subscriber
,
kvstate
::
topic_set
{
key
});
}
},
[
=
](
unsubscribe_atom
,
const
std
::
string
&
key
)
{
auto
subscriber
=
actor_cast
<
actor
>
(
self
->
current_sender
());
if
(
!
subscriber
)
return
;
self
->
state
.
subscribers
[
subscriber
].
erase
(
key
);
self
->
state
.
data
[
key
].
second
.
erase
(
subscriber
);
},
[
=
](
const
down_msg
&
dm
)
{
auto
subscriber
=
actor_cast
<
actor
>
(
dm
.
source
);
if
(
!
subscriber
)
return
;
auto
&
subscribers
=
self
->
state
.
subscribers
;
auto
i
=
subscribers
.
find
(
subscriber
);
if
(
i
==
subscribers
.
end
())
return
;
for
(
auto
&
key
:
i
->
second
)
self
->
state
.
data
[
key
].
second
.
erase
(
subscriber
);
subscribers
.
erase
(
i
);
},
others
>>
[]
{
return
make_message
(
error_atom
::
value
,
"unsupported operation"
);
}
};
};
named_entries_
.
emplace
(
atom
(
"SpawnServ"
),
spawn_announce_actor_type_server
());
named_entries_
.
emplace
(
atom
(
"ConfigServ"
),
spawn
<
hidden
>
(
kvstore
));
}
}
// namespace detail
}
// namespace caf
libcaf_core/src/announce_actor_type.cpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/experimental/announce_actor_type.hpp"
#include "caf/atom.hpp"
#include "caf/send.hpp"
#include "caf/spawn.hpp"
#include "caf/to_string.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/experimental/stateful_actor.hpp"
#include "caf/detail/logging.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/actor_registry.hpp"
namespace
caf
{
namespace
experimental
{
namespace
{
struct
spawner_state
{
std
::
unordered_map
<
std
::
string
,
spawn_fun
>
funs_
;
};
behavior
announce_actor_type_server
(
stateful_actor
<
spawner_state
>*
self
)
{
return
{
[
=
](
add_atom
,
std
::
string
&
name
,
spawn_fun
&
f
)
{
self
->
state
.
funs_
.
emplace
(
std
::
move
(
name
),
std
::
move
(
f
));
},
[
=
](
get_atom
,
const
std
::
string
&
name
,
message
&
args
)
->
either
<
ok_atom
,
actor_addr
,
std
::
set
<
std
::
string
>>
::
or_else
<
error_atom
,
std
::
string
>
{
auto
i
=
self
->
state
.
funs_
.
find
(
name
);
if
(
i
==
self
->
state
.
funs_
.
end
())
return
{
error_atom
::
value
,
"no actor type found named "
+
name
};
auto
f
=
i
->
second
;
auto
res
=
f
(
args
);
if
(
res
.
first
==
invalid_actor_addr
)
return
{
error_atom
::
value
,
"cannot initialize an actor type "
+
name
+
" using the provided arguments"
};
return
{
ok_atom
::
value
,
res
.
first
,
res
.
second
};
},
others
>>
[
=
]
{
CAF_LOGF_WARNING
(
"Unexpected message: "
<<
to_string
(
self
->
current_message
()));
}
};
}
}
// namespace <anonymous>
actor
spawn_announce_actor_type_server
()
{
return
spawn
<
hidden
+
lazy_init
>
(
announce_actor_type_server
);
}
void
announce_actor_type_impl
(
std
::
string
&&
name
,
spawn_fun
f
)
{
auto
registry
=
detail
::
singletons
::
get_actor_registry
();
auto
server
=
registry
->
get_named
(
atom
(
"SpawnServ"
));
anon_send
(
server
,
add_atom
::
value
,
std
::
move
(
name
),
std
::
move
(
f
));
}
}
// namespace experimental
}
// namespace caf
libcaf_core/src/event_based_actor.cpp
View file @
dd49a57d
...
...
@@ -30,7 +30,7 @@ event_based_actor::~event_based_actor() {
void
event_based_actor
::
forward_to
(
const
actor
&
whom
,
message_priority
prio
)
{
forward_message
(
whom
,
prio
);
forward_
current_
message
(
whom
,
prio
);
}
void
event_based_actor
::
initialize
()
{
...
...
libcaf_core/src/local_actor.cpp
View file @
dd49a57d
...
...
@@ -108,10 +108,16 @@ std::vector<group> local_actor::joined_groups() const {
return
result
;
}
void
local_actor
::
forward_message
(
const
actor
&
dest
,
message_priority
prio
)
{
if
(
!
dest
)
{
void
local_actor
::
forward_current_message
(
const
actor
&
dest
)
{
if
(
!
dest
)
return
;
dest
->
enqueue
(
std
::
move
(
current_element_
),
host
());
}
void
local_actor
::
forward_current_message
(
const
actor
&
dest
,
message_priority
prio
)
{
if
(
!
dest
)
return
;
}
auto
mid
=
current_element_
->
mid
;
current_element_
->
mid
=
prio
==
message_priority
::
high
?
mid
.
with_high_priority
()
...
...
@@ -180,27 +186,117 @@ enum class msg_type {
expired_sync_response
,
// a sync response that already timed out
timeout
,
// triggers currently active timeout
ordinary
,
// an asynchronous message or sync. request
sync_response
// a synchronous response
sync_response
,
// a synchronous response
sys_message
// a system message, e.g., signalizing migration
};
msg_type
filter_msg
(
local_actor
*
self
,
mailbox_element
&
node
)
{
const
message
&
msg
=
node
.
msg
;
message
&
msg
=
node
.
msg
;
auto
mid
=
node
.
mid
;
if
(
mid
.
is_response
())
{
if
(
mid
.
is_response
())
return
self
->
awaits
(
mid
)
?
msg_type
::
sync_response
:
msg_type
::
expired_sync_response
;
// intercept system messages, e.g., signalizing migration
if
(
msg
.
size
()
>
1
&&
msg
.
match_element
<
sys_atom
>
(
0
)
&&
node
.
sender
)
{
bool
mismatch
=
false
;
msg
.
apply
({
[
&
](
sys_atom
,
migrate_atom
,
const
actor
&
mm
)
{
// migrate this actor to `target`
if
(
!
self
->
is_serializable
())
{
node
.
sender
->
enqueue
(
mailbox_element
::
make_joint
(
self
->
address
(),
node
.
mid
.
response_id
(),
error_atom
::
value
,
"not serializable"
),
self
->
host
());
return
;
}
if
(
msg
.
size
()
!=
1
)
{
return
msg_type
::
ordinary
;
std
::
vector
<
char
>
buf
;
binary_serializer
bs
{
std
::
back_inserter
(
buf
)};
self
->
save
(
bs
,
0
);
auto
sender
=
node
.
sender
;
auto
mid
=
node
.
mid
;
// sync_send(...)
auto
req
=
self
->
sync_send_impl
(
message_priority
::
normal
,
mm
,
migrate_atom
::
value
,
self
->
name
(),
std
::
move
(
buf
));
self
->
set_response_handler
(
req
,
behavior
{
[
=
](
ok_atom
,
const
actor_addr
&
dest
)
{
// respond to original message with {'OK', dest}
sender
->
enqueue
(
mailbox_element
::
make_joint
(
self
->
address
(),
mid
.
response_id
(),
ok_atom
::
value
,
dest
),
self
->
host
());
// "decay" into a proxy for `dest`
auto
dest_hdl
=
actor_cast
<
actor
>
(
dest
);
self
->
do_become
(
behavior
{
others
>>
[
=
]
{
self
->
forward_current_message
(
dest_hdl
);
}
},
false
);
self
->
is_migrated_from
(
true
);
},
[
=
](
error_atom
,
std
::
string
&
errmsg
)
{
// respond to original message with {'ERROR', errmsg}
sender
->
enqueue
(
mailbox_element
::
make_joint
(
self
->
address
(),
mid
.
response_id
(),
error_atom
::
value
,
std
::
move
(
errmsg
)),
self
->
host
());
}
});
},
[
&
](
sys_atom
,
migrate_atom
,
std
::
vector
<
char
>&
buf
)
{
// "replace" this actor with the content of `buf`
if
(
!
self
->
is_serializable
())
{
node
.
sender
->
enqueue
(
mailbox_element
::
make_joint
(
self
->
address
(),
node
.
mid
.
response_id
(),
error_atom
::
value
,
"not serializable"
),
self
->
host
());
return
;
}
if
(
self
->
is_migrated_from
())
{
// undo the `do_become` we did when migrating away from this object
self
->
bhvr_stack
().
pop_back
();
self
->
is_migrated_from
(
false
);
}
binary_deserializer
bd
{
buf
.
data
(),
buf
.
size
()};
self
->
load
(
bd
,
0
);
node
.
sender
->
enqueue
(
mailbox_element
::
make_joint
(
self
->
address
(),
node
.
mid
.
response_id
(),
ok_atom
::
value
,
self
->
address
()),
self
->
host
());
},
[
&
](
sys_atom
,
get_atom
,
std
::
string
&
what
)
{
CAF_LOGF_TRACE
(
CAF_ARG
(
what
));
if
(
what
==
"info"
)
{
CAF_LOGF_DEBUG
(
"reply to 'info' message"
);
node
.
sender
->
enqueue
(
mailbox_element
::
make_joint
(
self
->
address
(),
node
.
mid
.
response_id
(),
ok_atom
::
value
,
std
::
move
(
what
),
self
->
address
(),
self
->
name
()),
self
->
host
());
return
;
}
node
.
sender
->
enqueue
(
mailbox_element
::
make_joint
(
self
->
address
(),
node
.
mid
.
response_id
(),
error_atom
::
value
,
"unknown key: "
+
std
::
move
(
what
)),
self
->
host
());
},
others
>>
[
&
]
{
mismatch
=
true
;
}
});
return
mismatch
?
msg_type
::
ordinary
:
msg_type
::
sys_message
;
}
// all other system messages always consist of one element
if
(
msg
.
size
()
!=
1
)
return
msg_type
::
ordinary
;
if
(
msg
.
match_element
<
timeout_msg
>
(
0
))
{
auto
&
tm
=
msg
.
get_as
<
timeout_msg
>
(
0
);
auto
tid
=
tm
.
timeout_id
;
CAF_ASSERT
(
!
mid
.
valid
());
if
(
self
->
is_active_timeout
(
tid
))
{
return
msg_type
::
timeout
;
}
return
msg_type
::
expired_timeout
;
return
self
->
is_active_timeout
(
tid
)
?
msg_type
::
timeout
:
msg_type
::
expired_timeout
;
}
if
(
msg
.
match_element
<
exit_msg
>
(
0
))
{
auto
&
em
=
msg
.
get_as
<
exit_msg
>
(
0
);
...
...
@@ -316,6 +412,9 @@ invoke_message_result local_actor::invoke_message(mailbox_element_ptr& ptr,
case
msg_type
:
:
expired_timeout
:
CAF_LOG_DEBUG
(
"dropped expired timeout message"
);
return
im_dropped
;
case
msg_type
:
:
sys_message
:
CAF_LOG_DEBUG
(
"handled system message"
);
return
im_dropped
;
case
msg_type
:
:
non_normal_exit
:
CAF_LOG_DEBUG
(
"handled non-normal exit signal"
);
// this message was handled
...
...
@@ -348,7 +447,6 @@ invoke_message_result local_actor::invoke_message(mailbox_element_ptr& ptr,
}
else
{
auto
res
=
post_process_invoke_res
(
this
,
mid
,
fun
(
current_mailbox_element
()
->
msg
));
ptr
.
swap
(
current_mailbox_element
());
if
(
!
res
)
{
CAF_LOG_WARNING
(
"sync failure occured in actor "
<<
"with ID "
<<
id
());
...
...
@@ -824,6 +922,14 @@ const char* local_actor::name() const {
return
"actor"
;
}
void
local_actor
::
save
(
serializer
&
,
const
unsigned
int
)
{
throw
std
::
logic_error
(
"local_actor::serialize called"
);
}
void
local_actor
::
load
(
deserializer
&
,
const
unsigned
int
)
{
throw
std
::
logic_error
(
"local_actor::deserialize called"
);
}
behavior
&
local_actor
::
get_behavior
()
{
return
pending_responses_
.
empty
()
?
bhvr_stack_
.
back
()
:
pending_responses_
.
front
().
second
;
...
...
libcaf_core/src/message.cpp
View file @
dd49a57d
...
...
@@ -101,8 +101,9 @@ const char* message::uniform_name_at(size_t pos) const {
}
bool
message
::
equals
(
const
message
&
other
)
const
{
CAF_ASSERT
(
vals_
);
return
vals_
->
equals
(
*
other
.
vals
());
if
(
empty
())
return
other
.
empty
();
return
other
.
empty
()
?
false
:
vals_
->
equals
(
*
other
.
vals
());
}
message
message
::
drop
(
size_t
n
)
const
{
...
...
libcaf_core/src/response_promise.cpp
View file @
dd49a57d
...
...
@@ -30,10 +30,9 @@ response_promise::response_promise(const actor_addr& from, const actor_addr& to,
CAF_ASSERT
(
id
.
is_response
()
||
!
id
.
valid
());
}
void
response_promise
::
deliver
(
message
msg
)
const
{
if
(
!
to_
)
{
void
response_promise
::
deliver
_impl
(
message
msg
)
const
{
if
(
!
to_
)
return
;
}
auto
to
=
actor_cast
<
abstract_actor_ptr
>
(
to_
);
auto
from
=
actor_cast
<
abstract_actor_ptr
>
(
from_
);
to
->
enqueue
(
from_
,
id_
,
std
::
move
(
msg
),
from
->
host
());
...
...
libcaf_core/src/run_program.cpp
View file @
dd49a57d
...
...
@@ -44,6 +44,11 @@ std::thread run_program_impl(actor rc, const char* cpath,
oss
<<
" 2>&1"
;
string
cmdstr
=
oss
.
str
();
return
std
::
thread
{
[
cmdstr
,
rc
]
{
// on FreeBSD, popen() hangs indefinitely in some cases
# ifdef CAF_BSD
system
(
cmdstr
.
c_str
());
anon_send
(
rc
,
""
);
# else
string
output
;
auto
fp
=
popen
(
cmdstr
.
c_str
(),
"r"
);
if
(
!
fp
)
{
...
...
@@ -57,6 +62,7 @@ std::thread run_program_impl(actor rc, const char* cpath,
}
pclose
(
fp
);
anon_send
(
rc
,
output
);
# endif
}};
}
#else
...
...
libcaf_core/src/singletons.cpp
View file @
dd49a57d
...
...
@@ -76,12 +76,12 @@ void singletons::stop_singletons() {
}
CAF_LOGF_DEBUG
(
"stop group manager"
);
stop
(
s_group_manager
);
CAF_LOGF_DEBUG
(
"stop actor registry"
);
stop
(
s_actor_registry
);
CAF_LOGF_DEBUG
(
"stop scheduler"
);
stop
(
s_scheduling_coordinator
);
CAF_LOGF_DEBUG
(
"wait for all detached threads"
);
scheduler
::
await_detached_threads
();
CAF_LOGF_DEBUG
(
"stop actor registry"
);
stop
(
s_actor_registry
);
// dispose singletons, i.e., release memory
CAF_LOGF_DEBUG
(
"dispose plugins"
);
for
(
auto
&
plugin
:
s_plugins
)
{
...
...
libcaf_core/src/uniform_type_info_map.cpp
View file @
dd49a57d
...
...
@@ -95,18 +95,6 @@ const char* numbered_type_names[] = {
namespace
{
// might become part of serializer's public API at some point
template
<
class
T
>
void
operator
&
(
serializer
&
sink
,
const
T
&
value
)
{
sink
<<
value
;
}
// might become part of deserializer's public API at some point
template
<
class
T
>
void
operator
&
(
deserializer
&
source
,
T
&
value
)
{
source
>>
value
;
}
// primitive types are handled by serializer/deserializer directly
template
<
class
T
,
class
U
>
typename
std
::
enable_if
<
...
...
libcaf_core/src/whereis.cpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/experimental/whereis.hpp"
#include "caf/actor.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/actor_registry.hpp"
namespace
caf
{
namespace
experimental
{
actor
whereis
(
atom_value
registered_name
)
{
return
detail
::
singletons
::
get_actor_registry
()
->
get_named
(
registered_name
);
}
}
// namespace experimental
}
// namespace caf
libcaf_core/test/announce_actor_type.cpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE announce_actor_type
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
#include "caf/experimental/announce_actor_type.hpp"
#include "caf/detail/actor_registry.hpp"
using
namespace
caf
;
using
namespace
caf
::
experimental
;
using
std
::
endl
;
namespace
{
struct
fixture
{
actor
aut
;
actor
spawner
;
fixture
()
{
auto
registry
=
detail
::
singletons
::
get_actor_registry
();
spawner
=
registry
->
get_named
(
atom
(
"SpawnServ"
));
}
void
set_aut
(
message
args
,
bool
expect_fail
=
false
)
{
CAF_MESSAGE
(
"set aut"
);
scoped_actor
self
;
self
->
on_sync_failure
([
&
]
{
CAF_TEST_ERROR
(
"received unexpeced sync. response: "
<<
to_string
(
self
->
current_message
()));
});
if
(
expect_fail
)
{
self
->
sync_send
(
spawner
,
get_atom
::
value
,
"test_actor"
,
std
::
move
(
args
)).
await
(
[
&
](
error_atom
,
const
std
::
string
&
)
{
CAF_TEST_VERBOSE
(
"received error_atom (expected)"
);
}
);
}
else
{
self
->
sync_send
(
spawner
,
get_atom
::
value
,
"test_actor"
,
std
::
move
(
args
)).
await
(
[
&
](
ok_atom
,
actor_addr
res
,
const
std
::
set
<
std
::
string
>&
ifs
)
{
CAF_REQUIRE
(
res
!=
invalid_actor_addr
);
aut
=
actor_cast
<
actor
>
(
res
);
CAF_CHECK
(
ifs
.
empty
());
}
);
}
}
~
fixture
()
{
if
(
aut
!=
invalid_actor
)
{
scoped_actor
self
;
self
->
monitor
(
aut
);
self
->
receive
(
[](
const
down_msg
&
dm
)
{
CAF_CHECK
(
dm
.
reason
==
exit_reason
::
normal
);
}
);
}
await_all_actors_done
();
shutdown
();
}
};
}
// namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE
(
announce_actor_type_tests
,
fixture
)
CAF_TEST
(
fun_no_args
)
{
auto
test_actor
=
[]
{
CAF_MESSAGE
(
"inside test_actor"
);
};
announce_actor_type
(
"test_actor"
,
test_actor
);
set_aut
(
make_message
());
}
CAF_TEST
(
fun_no_args_selfptr
)
{
auto
test_actor
=
[](
event_based_actor
*
)
{
CAF_MESSAGE
(
"inside test_actor"
);
};
announce_actor_type
(
"test_actor"
,
test_actor
);
set_aut
(
make_message
());
}
CAF_TEST
(
fun_one_arg
)
{
auto
test_actor
=
[](
int
i
)
{
CAF_CHECK_EQUAL
(
i
,
42
);
};
announce_actor_type
(
"test_actor"
,
test_actor
);
set_aut
(
make_message
(
42
));
}
CAF_TEST
(
fun_one_arg_selfptr
)
{
auto
test_actor
=
[](
event_based_actor
*
,
int
i
)
{
CAF_CHECK_EQUAL
(
i
,
42
);
};
announce_actor_type
(
"test_actor"
,
test_actor
);
set_aut
(
make_message
(
42
));
}
CAF_TEST
(
class_no_arg
)
{
struct
test_actor
:
event_based_actor
{
behavior
make_behavior
()
override
{
return
{};
}
};
announce_actor_type
<
test_actor
>
(
"test_actor"
);
set_aut
(
make_message
(
42
),
true
);
set_aut
(
make_message
());
}
CAF_TEST
(
class_one_arg
)
{
struct
test_actor
:
event_based_actor
{
test_actor
(
int
value
)
{
CAF_CHECK_EQUAL
(
value
,
42
);
}
behavior
make_behavior
()
override
{
return
{};
}
};
announce_actor_type
<
test_actor
,
const
int
&>
(
"test_actor"
);
set_aut
(
make_message
(),
true
);
set_aut
(
make_message
(
42
));
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/announce_actor_type.cpp.autosave
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE announce_actor_type
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
#include "caf/experimental/announce_actor_type.hpp"
#include "caf/detail/actor_registry.hpp"
using namespace caf;
using namespace caf::experimental;
using std::endl;
namespace {
struct fixture {
actor aut;
actor spawner;
fixture() {
auto registry = detail::singletons::get_actor_registry();
spawner = registry->get_named(atom("spawner"));
}
void set_aut(message args, bool expect_fail = false) {
CAF_MESSAGE("set aut");
scoped_actor self;
self->on_sync_failure([&] {
CAF_TEST_ERROR("received unexpeced sync. response: "
<< to_string(self->current_message()));
});
if (expect_fail) {
self->sync_send(spawner, get_atom::value, "test_actor", std::move(args)).await(
[&](error_atom, const std::string&) {
CAF_TEST_VERBOSE("received error_atom (expected)");
}
);
} else {
self->sync_send(spawner, get_atom::value, "test_actor", std::move(args)).await(
[&](ok_atom, actor_addr res, const std::set<std::string>& ifs) {
CAF_REQUIRE(res != invalid_actor_addr);
aut = actor_cast<actor>(res);
CAF_CHECK(ifs.empty());
}
);
}
}
~fixture() {
if (aut != invalid_actor) {
scoped_actor self;
self->monitor(aut);
self->receive(
[](const down_msg& dm) {
CAF_CHECK(dm.reason == exit_reason::normal);
}
);
}
await_all_actors_done();
shutdown();
}
};
using detail::is_serializable;
// not serializable
class testee1 {
};
// serializable via member function
class testee2 {
public:
template <class Archive>
void serialize(Archive&, const unsigned int) {
// nop
}
};
// serializable via free function
class testee3 {
// nop
};
template<class Archive>
void serialize(Archive&, testee3&, const unsigned int) {
// nop
}
template <class Archive, class T>
void serialize(Archive& ar, T& x, const unsigned int version,
decltype(x.serialize(ar, version))* = nullptr) {
x.serialize(ar, version);
}
struct migratable_state {
int value = 0;
};
template <class Archive>
void serialize(Archive& ar, migratable_state& x, const unsigned int) {
ar & x.value;
}
struct migratable_actor : stateful_actor<migratable_state> {
behavior make_behavior() override {
return {
[=](get_atom) {
return state.value;
},
[=](put_atom, int value) {
state.value = value;
},
[=](sys_atom, migrate_atom, actor_addr destination) {
std::vector<char> buf;
binary_serializer bs{std::back_inserter(buf)};
save(bs, 0);
actor dest = actor_cast<actor>(destination);
link_to(dest);
sync_send(dest, sys_atom::value, migrate_atom::value, std::move(buf)).then(
[=](ok_atom) {
// "decay" into a proxy for `dest`
become(
[=](sys_atom, migrate_atom, std::vector<char> buf) {
unlink_from(dest);
become(make_behavior());
binary_deserializer bd{buf.data(), buf.size()};
deserialize(bd, 0);
return make_message(ok_atom::value);
},
others >> [=] {
forward_to(dest);
}
);
},
others >> [=] {
// do nothing, i.e., process further messages as usual
}
);
},
[=](sys_atom, migrate_atom, std::vector<char> buf) {
binary_deserializer bd{buf.data(), buf.size()};
deserialize(bd, 0);
return make_message(ok_atom::value);
}
};
}
};
} // namespace <anonymous>
CAF_TEST(serializable_stuff) {
CAF_CHECK(is_serializable<testee1>::value == false);
CAF_CHECK(is_serializable<testee2>::value == true);
CAF_CHECK(is_serializable<testee3>::value == true);
}
CAF_TEST(migratable_stuff) {
auto a = spawn<migratable_actor>();
auto b = spawn<migratable_actor>();
scoped_actor self;
self->send(a, put_atom::value, 42);
self->send(a, sys_atom::value, migrate_atom::value, b.address());
self->sync_send(a, get_atom::value).await(
[&](int result) {
CAF_CHECK(result == 42);
CAF_CHECK(self->current_sender() == b.address());
}
);
self->send_exit(b, exit_reason::kill);
self->await_all_other_actors_done();
}
CAF_TEST_FIXTURE_SCOPE(announce_actor_type_tests, fixture)
CAF_TEST(fun_no_args) {
auto test_actor = [] {
CAF_MESSAGE("inside test_actor");
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message());
}
CAF_TEST(fun_no_args_selfptr) {
auto test_actor = [](event_based_actor*) {
CAF_MESSAGE("inside test_actor");
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message());
}
CAF_TEST(fun_one_arg) {
auto test_actor = [](int i) {
CAF_CHECK_EQUAL(i, 42);
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message(42));
}
CAF_TEST(fun_one_arg_selfptr) {
auto test_actor = [](event_based_actor*, int i) {
CAF_CHECK_EQUAL(i, 42);
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message(42));
}
CAF_TEST(class_no_arg) {
struct test_actor : event_based_actor {
behavior make_behavior() override {
return {};
}
};
announce_actor_type<test_actor>("test_actor");
set_aut(make_message(42), true);
set_aut(make_message());
}
CAF_TEST(class_one_arg) {
struct test_actor : event_based_actor {
test_actor(int value) {
CAF_CHECK_EQUAL(value, 42);
}
behavior make_behavior() override {
return {};
}
};
announce_actor_type<test_actor, const int&>("test_actor");
set_aut(make_message(), true);
set_aut(make_message(42));
}
CAF_TEST_FIXTURE_SCOPE_END()
libcaf_core/test/aout.cpp
View file @
dd49a57d
...
...
@@ -30,6 +30,67 @@ using std::endl;
namespace
{
struct
meta_info
{
constexpr
meta_info
(
const
char
*
x
,
size_t
y
,
uint32_t
z
)
:
name
(
x
),
hash
(
y
),
version
(
z
)
{
// nop
}
constexpr
meta_info
(
const
meta_info
&
other
)
:
name
(
other
.
name
),
hash
(
other
.
hash
),
version
(
other
.
version
)
{
// nop
}
const
char
*
name
;
size_t
hash
;
uint32_t
version
;
};
constexpr
size_t
str_hash
(
const
char
*
cstr
,
size_t
interim
=
0
)
{
return
(
*
cstr
==
'\0'
)
?
interim
:
str_hash
(
cstr
+
1
,
interim
*
101
+
*
cstr
);
}
constexpr
meta_info
make_meta_info
(
const
char
*
name
,
uint32_t
version
)
{
return
meta_info
{
name
,
str_hash
(
name
),
version
};
}
template
<
class
T
>
struct
meta_information
;
#define CAF_META_INFORMATION(class_name, version) \
template <> \
struct meta_information<class_name> { \
static constexpr meta_info value = make_meta_info(#class_name, version); \
}; \
constexpr meta_info meta_information<class_name>::value;
#define DUMMY(name) \
class name {}; \
CAF_META_INFORMATION(name, 0)
DUMMY
(
foo1
)
DUMMY
(
foo2
)
DUMMY
(
foo3
)
DUMMY
(
foo4
)
DUMMY
(
foo5
)
DUMMY
(
foo6
)
DUMMY
(
foo7
)
DUMMY
(
foo8
)
DUMMY
(
foo9
)
DUMMY
(
foo10
)
DUMMY
(
foo11
)
DUMMY
(
foo12
)
DUMMY
(
foo13
)
DUMMY
(
foo14
)
DUMMY
(
foo15
)
DUMMY
(
foo16
)
DUMMY
(
foo17
)
DUMMY
(
foo18
)
DUMMY
(
foo19
)
DUMMY
(
foo20
)
struct
fixture
{
~
fixture
()
{
await_all_actors_done
();
...
...
@@ -37,6 +98,7 @@ struct fixture {
}
};
constexpr
const
char
*
global_redirect
=
":test"
;
constexpr
const
char
*
local_redirect
=
":test2"
;
...
...
@@ -57,6 +119,133 @@ void chattier_actor(event_based_actor* self, const std::string& fn) {
CAF_TEST_FIXTURE_SCOPE
(
aout_tests
,
fixture
)
const
meta_info
*
lookup
(
const
std
::
vector
<
std
::
pair
<
size_t
,
const
meta_info
*>>&
haystack
,
const
meta_info
*
needle
)
{
/*
auto h = needle->hash;
auto e = haystack.end();
for (auto i = haystack.begin(); i != e; ++i) {
if (i->first == h) {
auto n = i + 1;
if (n == e || n->second->hash != h)
return i->second;
for (auto j = i; j != e; ++j)
if (strcmp(j->second->name, needle->name) == 0)
return j->second;
}
}
*/
auto
h
=
needle
->
hash
;
auto
e
=
haystack
.
end
();
//auto i = std::find_if(haystack.begin(), e,
// [h](const std::pair<size_t, const meta_info*>& x ) { return x.first == h; });
auto
i
=
std
::
lower_bound
(
haystack
.
begin
(),
e
,
h
,
[](
const
std
::
pair
<
size_t
,
const
meta_info
*>&
x
,
size_t
y
)
{
return
x
.
first
<
y
;
});
if
(
i
==
e
||
i
->
first
!=
h
)
return
nullptr
;
// check for collision
auto
n
=
i
+
1
;
if
(
n
==
e
||
n
->
first
!=
h
)
return
i
->
second
;
i
=
std
::
find_if
(
i
,
e
,
[
needle
](
const
std
::
pair
<
size_t
,
const
meta_info
*>&
x
)
{
return
strcmp
(
x
.
second
->
name
,
needle
->
name
)
==
0
;
});
if
(
i
!=
e
)
return
i
->
second
;
return
nullptr
;
}
const
meta_info
*
lookup
(
std
::
unordered_multimap
<
size_t
,
const
meta_info
*>&
haystack
,
const
meta_info
*
needle
)
{
auto
i
=
haystack
.
find
(
needle
->
hash
);
auto
e
=
haystack
.
end
();
if
(
i
==
e
)
return
nullptr
;
auto
n
=
std
::
next
(
i
);
if
(
n
==
e
||
n
->
second
->
hash
!=
i
->
second
->
hash
)
return
i
->
second
;
for
(;
i
!=
e
;
++
i
)
if
(
strcmp
(
i
->
second
->
name
,
needle
->
name
)
==
0
)
return
i
->
second
;
return
nullptr
;
}
const
meta_info
*
lookup
(
std
::
multimap
<
size_t
,
const
meta_info
*>&
haystack
,
const
meta_info
*
needle
)
{
auto
i
=
haystack
.
find
(
needle
->
hash
);
auto
e
=
haystack
.
end
();
if
(
i
==
e
)
return
nullptr
;
auto
n
=
std
::
next
(
i
);
if
(
n
==
e
||
n
->
second
->
hash
!=
i
->
second
->
hash
)
return
i
->
second
;
for
(;
i
!=
e
;
++
i
)
if
(
strcmp
(
i
->
second
->
name
,
needle
->
name
)
==
0
)
return
i
->
second
;
return
nullptr
;
}
CAF_TEST
(
foobar
)
{
using
std
::
make_pair
;
std
::
vector
<
std
::
pair
<
size_t
,
const
meta_info
*>>
map1
;
std
::
unordered_multimap
<
size_t
,
const
meta_info
*>
map2
;
std
::
multimap
<
size_t
,
const
meta_info
*>
map3
;
const
meta_info
*
arr
[]
=
{
&
meta_information
<
foo1
>::
value
,
&
meta_information
<
foo2
>::
value
,
&
meta_information
<
foo3
>::
value
,
&
meta_information
<
foo4
>::
value
,
&
meta_information
<
foo5
>::
value
,
&
meta_information
<
foo6
>::
value
,
&
meta_information
<
foo7
>::
value
,
&
meta_information
<
foo8
>::
value
,
&
meta_information
<
foo9
>::
value
,
&
meta_information
<
foo10
>::
value
,
&
meta_information
<
foo11
>::
value
,
&
meta_information
<
foo12
>::
value
,
&
meta_information
<
foo13
>::
value
,
&
meta_information
<
foo14
>::
value
,
&
meta_information
<
foo15
>::
value
,
&
meta_information
<
foo16
>::
value
,
&
meta_information
<
foo17
>::
value
,
&
meta_information
<
foo18
>::
value
,
&
meta_information
<
foo19
>::
value
,
&
meta_information
<
foo20
>::
value
};
for
(
auto
i
=
std
::
begin
(
arr
);
i
!=
std
::
end
(
arr
);
++
i
)
{
map1
.
emplace_back
((
*
i
)
->
hash
,
*
i
);
map2
.
emplace
((
*
i
)
->
hash
,
*
i
);
map3
.
emplace
((
*
i
)
->
hash
,
*
i
);
}
std
::
sort
(
map1
.
begin
(),
map1
.
end
());
std
::
array
<
const
meta_info
*
,
20
>
dummy
;
{
auto
t0
=
std
::
chrono
::
high_resolution_clock
::
now
();
for
(
auto
i
=
0
;
i
<
10000
;
++
i
)
dummy
[
i
%
20
]
=
lookup
(
map1
,
arr
[
i
%
20
]);
auto
t1
=
std
::
chrono
::
high_resolution_clock
::
now
();
std
::
cout
<<
"vector: "
<<
std
::
chrono
::
duration_cast
<
std
::
chrono
::
microseconds
>
(
t1
-
t0
).
count
()
<<
std
::
endl
;
std
::
cout
<<
"check: "
<<
std
::
equal
(
dummy
.
begin
(),
dummy
.
end
(),
std
::
begin
(
arr
))
<<
std
::
endl
;
}
{
auto
t0
=
std
::
chrono
::
high_resolution_clock
::
now
();
for
(
auto
i
=
0
;
i
<
10000
;
++
i
)
dummy
[
i
%
20
]
=
lookup
(
map2
,
arr
[
i
%
20
]);
auto
t1
=
std
::
chrono
::
high_resolution_clock
::
now
();
std
::
cout
<<
"hash map: "
<<
std
::
chrono
::
duration_cast
<
std
::
chrono
::
microseconds
>
(
t1
-
t0
).
count
()
<<
std
::
endl
;
std
::
cout
<<
"check: "
<<
std
::
equal
(
dummy
.
begin
(),
dummy
.
end
(),
std
::
begin
(
arr
))
<<
std
::
endl
;
}
{
auto
t0
=
std
::
chrono
::
high_resolution_clock
::
now
();
for
(
auto
i
=
0
;
i
<
10000
;
++
i
)
dummy
[
i
%
20
]
=
lookup
(
map3
,
arr
[
i
%
20
]);
auto
t1
=
std
::
chrono
::
high_resolution_clock
::
now
();
std
::
cout
<<
"map: "
<<
std
::
chrono
::
duration_cast
<
std
::
chrono
::
microseconds
>
(
t1
-
t0
).
count
()
<<
std
::
endl
;
std
::
cout
<<
"check: "
<<
std
::
equal
(
dummy
.
begin
(),
dummy
.
end
(),
std
::
begin
(
arr
))
<<
std
::
endl
;
}
CAF_CHECK
(
meta_information
<
foo1
>::
value
.
name
==
"foo1"
);
CAF_CHECK
(
meta_information
<
foo1
>::
value
.
hash
==
str_hash
(
"foo1"
));
CAF_CHECK
(
meta_information
<
foo1
>::
value
.
version
==
0
);
}
CAF_TEST
(
global_redirect
)
{
scoped_actor
self
;
self
->
join
(
group
::
get
(
"local"
,
global_redirect
));
...
...
libcaf_core/test/local_migration.cpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE local_migration
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
#include "caf/detail/actor_registry.hpp"
using
namespace
caf
;
using
namespace
caf
::
experimental
;
using
std
::
endl
;
namespace
{
struct
migratable_state
{
int
value
=
0
;
static
const
char
*
name
;
};
const
char
*
migratable_state
::
name
=
"migratable_actor"
;
template
<
class
Archive
>
void
serialize
(
Archive
&
ar
,
migratable_state
&
x
,
const
unsigned
int
)
{
ar
&
x
.
value
;
}
struct
migratable_actor
:
stateful_actor
<
migratable_state
>
{
behavior
make_behavior
()
override
{
return
{
[
=
](
get_atom
)
{
return
state
.
value
;
},
[
=
](
put_atom
,
int
value
)
{
state
.
value
=
value
;
}
};
}
};
// always migrates to `dest`
behavior
pseudo_mm
(
event_based_actor
*
self
,
const
actor
&
dest
)
{
return
{
[
=
](
migrate_atom
,
const
std
::
string
&
name
,
std
::
vector
<
char
>&
buf
)
{
CAF_CHECK
(
name
==
"migratable_actor"
);
self
->
delegate
(
dest
,
sys_atom
::
value
,
migrate_atom
::
value
,
std
::
move
(
buf
));
}
};
}
}
// namespace <anonymous>
CAF_TEST
(
migrate_locally
)
{
auto
a
=
spawn
<
migratable_actor
>
();
auto
b
=
spawn
<
migratable_actor
>
();
auto
mm1
=
spawn
(
pseudo_mm
,
b
);
scoped_actor
self
;
self
->
send
(
a
,
put_atom
::
value
,
42
);
// migrate from a to b
self
->
sync_send
(
a
,
sys_atom
::
value
,
migrate_atom
::
value
,
mm1
).
await
(
[
&
](
ok_atom
,
const
actor_addr
&
dest
)
{
CAF_CHECK
(
dest
==
b
);
}
);
self
->
sync_send
(
a
,
get_atom
::
value
).
await
(
[
&
](
int
result
)
{
CAF_CHECK
(
result
==
42
);
CAF_CHECK
(
self
->
current_sender
()
==
b
.
address
());
}
);
auto
mm2
=
spawn
(
pseudo_mm
,
a
);
self
->
send
(
b
,
put_atom
::
value
,
23
);
// migrate back from b to a
self
->
sync_send
(
b
,
sys_atom
::
value
,
migrate_atom
::
value
,
mm2
).
await
(
[
&
](
ok_atom
,
const
actor_addr
&
dest
)
{
CAF_CHECK
(
dest
==
a
);
}
);
self
->
sync_send
(
b
,
get_atom
::
value
).
await
(
[
&
](
int
result
)
{
CAF_CHECK
(
result
==
23
);
CAF_CHECK
(
self
->
current_sender
()
==
a
.
address
());
}
);
self
->
send_exit
(
a
,
exit_reason
::
kill
);
self
->
send_exit
(
b
,
exit_reason
::
kill
);
self
->
send_exit
(
mm1
,
exit_reason
::
kill
);
self
->
send_exit
(
mm2
,
exit_reason
::
kill
);
self
->
await_all_other_actors_done
();
}
libcaf_core/test/message.cpp
View file @
dd49a57d
...
...
@@ -27,6 +27,18 @@
using
namespace
caf
;
CAF_TEST
(
apply
)
{
auto
f1
=
[]
{
CAF_TEST_ERROR
(
"f1 invoked!"
);
};
auto
f2
=
[](
int
i
)
{
CAF_CHECK_EQUAL
(
i
,
42
);
};
auto
m
=
make_message
(
42
);
m
.
apply
(
f1
);
m
.
apply
(
f2
);
}
CAF_TEST
(
drop
)
{
auto
m1
=
make_message
(
1
,
2
,
3
,
4
,
5
);
std
::
vector
<
message
>
messages
{
...
...
libcaf_io/caf/io/all.hpp
View file @
dd49a57d
...
...
@@ -32,6 +32,7 @@
#include "caf/io/remote_group.hpp"
#include "caf/io/set_middleman.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/io/middleman_actor.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/io/publish_local_groups.hpp"
...
...
libcaf_io/caf/io/basp.hpp
View file @
dd49a57d
...
...
@@ -27,14 +27,16 @@
#include <unordered_map>
#include <unordered_set>
#include "caf/fwd.hpp"
#include "caf/node_id.hpp"
#include "caf/callback.hpp"
#include "caf/actor_addr.hpp"
#include "caf/serializer.hpp"
#include "caf/deserializer.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/callback.hpp"
#include "caf/actor_namespace.hpp"
#include "caf/io/middleman.hpp"
#include "caf/io/receive_policy.hpp"
#include "caf/io/abstract_broker.hpp"
#include "caf/io/system_messages.hpp"
...
...
@@ -307,6 +309,11 @@ public:
/// `invalid_connection_handle` if no direct connection to `nid` exists.
connection_handle
lookup_direct
(
const
node_id
&
nid
)
const
;
/// Returns the next hop that would be chosen for `nid`
/// or `invalid_node_id` if there's no indirect route to `nid`.
node_id
lookup_indirect
(
const
node_id
&
nid
)
const
;
/// Flush output buffer for `r`.
void
flush
(
const
route
&
r
);
...
...
@@ -315,7 +322,7 @@ public:
void
add_direct
(
const
connection_handle
&
hdl
,
const
node_id
&
dest
);
/// Adds a new indirect route to the table.
void
add_indirect
(
const
node_id
&
hop
,
const
node_id
&
dest
);
bool
add_indirect
(
const
node_id
&
hop
,
const
node_id
&
dest
);
/// Blacklist the route to `dest` via `hop`.
void
blacklist
(
const
node_id
&
hop
,
const
node_id
&
dest
);
...
...
@@ -325,6 +332,10 @@ public:
/// including the node that is assigned as direct path for `hdl`.
void
erase_direct
(
const
connection_handle
&
hdl
,
erase_callback
&
cb
);
/// Removes any entry for indirect connection to `dest` and returns
/// `true` if `dest` had an indirect route, otherwise `false`.
bool
erase_indirect
(
const
node_id
&
dest
);
/// Queries whether `dest` is reachable.
bool
reachable
(
const
node_id
&
dest
);
...
...
@@ -362,14 +373,14 @@ public:
/// Provides a callback-based interface for certain BASP events.
class
callee
{
public:
explicit
callee
(
actor_namespace
::
backend
&
mgm
);
explicit
callee
(
actor_namespace
::
backend
&
mgm
,
middleman
&
mm
);
virtual
~
callee
();
/// Called if a server handshake was received and
/// the connection to `nid` is established.
virtual
void
finalize_handshake
(
const
node_id
&
nid
,
actor_id
aid
,
const
std
::
set
<
std
::
string
>&
sigs
)
=
0
;
std
::
set
<
std
::
string
>&
sigs
)
=
0
;
/// Called whenever a direct connection was closed or a
/// node became unrechable for other reasons *before*
...
...
@@ -378,8 +389,12 @@ public:
/// routing table from this callback.
virtual
void
purge_state
(
const
node_id
&
nid
)
=
0
;
/// Called whenever a remote node created a proxy
/// for one of our local actors.
virtual
void
proxy_announced
(
const
node_id
&
nid
,
actor_id
aid
)
=
0
;
/// Called whenever a remote actor died to destroy
/// the proxy instance on our end.
virtual
void
kill_proxy
(
const
node_id
&
nid
,
actor_id
aid
,
uint32_t
rsn
)
=
0
;
/// Called whenever a `dispatch_message` arrived for a local actor.
...
...
@@ -387,13 +402,27 @@ public:
const
node_id
&
dest_node
,
actor_id
dest_actor
,
message
&
msg
,
message_id
mid
)
=
0
;
/// Called whenever BASP learns the ID of a remote node
/// to which it does not have a direct connection.
virtual
void
learned_new_node_directly
(
const
node_id
&
nid
,
bool
was_known_indirectly
)
=
0
;
/// Called whenever BASP learns the ID of a remote node
/// to which it does not have a direct connection.
virtual
void
learned_new_node_indirectly
(
const
node_id
&
nid
)
=
0
;
/// Returns the actor namespace associated to this BASP protocol instance.
actor_namespace
&
get_namespace
()
{
inline
actor_namespace
&
get_namespace
()
{
return
namespace_
;
}
public:
inline
middleman
&
get_middleman
()
{
return
middleman_
;
}
protected:
actor_namespace
namespace_
;
middleman
&
middleman_
;
};
/// Describes a function object responsible for writing
...
...
@@ -490,6 +519,9 @@ public:
/// written (e.g. used when establishing direct connections on-the-fly).
void
write_server_handshake
(
buffer_type
&
buf
,
optional
<
uint16_t
>
port
);
/// Writes the client handshake to `buf`.
void
write_client_handshake
(
buffer_type
&
buf
,
const
node_id
&
remote_side
);
/// Writes a `dispatch_error` to `buf`.
void
write_dispatch_error
(
buffer_type
&
buf
,
const
node_id
&
source_node
,
...
...
@@ -504,10 +536,16 @@ public:
actor_id
aid
,
uint32_t
rsn
);
const
node_id
&
this_node
()
const
{
inline
const
node_id
&
this_node
()
const
{
return
this_node_
;
}
/// Invokes the callback(s) associated with given event.
template
<
hook
::
event_type
Event
,
typename
...
Ts
>
void
notify
(
Ts
&&
...
xs
)
{
callee_
.
get_middleman
().
template
notify
<
Event
>(
std
::
forward
<
Ts
>
(
xs
)...);
}
private:
routing_table
tbl_
;
published_actor_map
published_actors_
;
...
...
libcaf_io/caf/io/basp_broker.hpp
View file @
dd49a57d
...
...
@@ -49,7 +49,7 @@ struct basp_broker_state : actor_namespace::backend, basp::instance::callee {
// inherited from basp::instance::listener
void
finalize_handshake
(
const
node_id
&
nid
,
actor_id
aid
,
const
std
::
set
<
std
::
string
>&
sigs
)
override
;
std
::
set
<
std
::
string
>&
sigs
)
override
;
// inherited from basp::instance::listener
void
purge_state
(
const
node_id
&
id
)
override
;
...
...
@@ -60,10 +60,21 @@ struct basp_broker_state : actor_namespace::backend, basp::instance::callee {
// inherited from basp::instance::listener
void
kill_proxy
(
const
node_id
&
nid
,
actor_id
aid
,
uint32_t
rsn
)
override
;
// inherited from basp::instance::listener
void
deliver
(
const
node_id
&
source_node
,
actor_id
source_actor
,
const
node_id
&
dest_node
,
actor_id
dest_actor
,
message
&
msg
,
message_id
mid
)
override
;
// performs bookkeeping such as managing `spawn_servers`
void
learned_new_node
(
const
node_id
&
nid
);
// inherited from basp::instance::listener
void
learned_new_node_directly
(
const
node_id
&
nid
,
bool
was_known_indirectly_before
)
override
;
// inherited from basp::instance::listener
void
learned_new_node_indirectly
(
const
node_id
&
nid
)
override
;
struct
connection_context
{
basp
::
connection_state
cstate
;
basp
::
header
hdr
;
...
...
@@ -71,7 +82,6 @@ struct basp_broker_state : actor_namespace::backend, basp::instance::callee {
node_id
id
;
uint16_t
remote_port
;
optional
<
response_promise
>
callback
;
std
::
set
<
std
::
string
>
expected_sigs
;
};
void
set_context
(
connection_handle
hdl
);
...
...
@@ -94,6 +104,16 @@ struct basp_broker_state : actor_namespace::backend, basp::instance::callee {
// keeps the associated proxies alive to work around subtle bugs
std
::
unordered_map
<
node_id
,
std
::
pair
<
uint16_t
,
actor_addr
>>
known_remotes
;
// stores handles to spawn servers for other nodes; these servers
// are spawned whenever the broker learns a new node ID and try to
// get a 'SpawnServ' instance on the remote side
std
::
unordered_map
<
node_id
,
actor
>
spawn_servers
;
// can be enabled by the user to let CAF automatically try
// to establish new connections at runtime to optimize
// routing paths by forming a mesh between all nodes
bool
enable_automatic_connections
=
false
;
const
node_id
&
this_node
()
const
{
return
instance
.
this_node
();
}
...
...
libcaf_io/caf/io/hook.hpp
View file @
dd49a57d
...
...
@@ -20,6 +20,7 @@
#ifndef CAF_IO_HOOK_HPP
#define CAF_IO_HOOK_HPP
#include <set>
#include <memory>
#include <vector>
...
...
@@ -71,16 +72,17 @@ public:
const
message
&
payload
);
/// Called whenever a message is forwarded to a different node.
virtual
void
message_forwarded_cb
(
const
node_id
&
from
,
const
node_id
&
dest
,
virtual
void
message_forwarded_cb
(
const
basp
::
header
&
hdr
,
const
std
::
vector
<
char
>*
payload
);
/// Called whenever no route for a forwarding request exists.
virtual
void
message_forwarding_failed_cb
(
const
node_id
&
from
,
const
node_id
&
to
,
virtual
void
message_forwarding_failed_cb
(
const
basp
::
header
&
hdr
,
const
std
::
vector
<
char
>*
payload
);
/// Called whenever an actor has been published.
virtual
void
actor_published_cb
(
const
actor_addr
&
addr
,
uint16_t
port
);
virtual
void
actor_published_cb
(
const
actor_addr
&
addr
,
const
std
::
set
<
std
::
string
>&
ifs
,
uint16_t
port
);
/// Called whenever a new remote actor appeared.
virtual
void
new_remote_actor_cb
(
const
actor_addr
&
addr
);
...
...
@@ -93,6 +95,15 @@ public:
/// @param node The newly added entry to the routing table.
virtual
void
new_route_added_cb
(
const
node_id
&
via
,
const
node_id
&
node
);
/// Called whenever a direct connection was lost.
virtual
void
connection_lost_cb
(
const
node_id
&
dest
);
/// Called whenever a route became unavailable.
/// @param hop The node that was either disconnected
/// or lost a connection itself.
/// @param dest The node that is no longer reachable via `hop`.
virtual
void
route_lost_cb
(
const
node_id
&
hop
,
const
node_id
&
dest
);
/// Called whenever a message was discarded because a remote node
/// tried to send a message to an actor ID that could not be found
/// in the registry.
...
...
@@ -115,6 +126,8 @@ public:
new_remote_actor
,
new_connection_established
,
new_route_added
,
connection_lost
,
route_lost
,
invalid_message_received
,
before_shutdown
};
...
...
@@ -150,6 +163,8 @@ private:
CAF_IO_HOOK_DISPATCH
(
new_remote_actor
)
CAF_IO_HOOK_DISPATCH
(
new_connection_established
)
CAF_IO_HOOK_DISPATCH
(
new_route_added
)
CAF_IO_HOOK_DISPATCH
(
connection_lost
)
CAF_IO_HOOK_DISPATCH
(
route_lost
)
CAF_IO_HOOK_DISPATCH
(
invalid_message_received
)
CAF_IO_HOOK_DISPATCH
(
before_shutdown
)
};
...
...
libcaf_io/caf/io/middleman.hpp
View file @
dd49a57d
...
...
@@ -99,6 +99,10 @@ public:
});
}
inline
bool
has_hook
()
const
{
return
hooks_
!=
nullptr
;
}
template
<
class
F
>
void
add_shutdown_cb
(
F
fun
)
{
struct
impl
:
hook
{
...
...
libcaf_io/caf/io/middleman_actor.hpp
View file @
dd49a57d
...
...
@@ -30,100 +30,94 @@ namespace io {
///
/// The interface implements the following pseudo code.
/// ~~~
/// using put_result =
/// either (ok_atom, uint16_t port)
/// or (error_atom, string error_string);
///
/// using get_result =
/// either (ok_atom, actor_addr remote_address)
/// or (error_atom, string error_string);
///
/// using delete_result =
/// either (ok_atom)
/// or (error_atom, string error_string);
///
/// interface middleman_actor {
/// (put_atom, actor_addr whom, uint16_t port, string addr, bool reuse_addr)
/// -> put_result;
///
/// (put_atom, actor_addr whom, uint16_t port, string addr)
/// -> put_result;
/// // Establishes a new `port <-> actor` mapping and returns the actual
/// // port in use on success. Passing 0 as port instructs the OS to choose
/// // the next high-level port available for binding.
/// // @param port: Unused TCP port or 0 for any.
/// // @param whom: Actor that should be published at given port.
/// // @param ifs: Interface of given actor.
/// // @param addr: IP address to listen to or empty for any.
/// // @param reuse_addr: Enables or disables SO_REUSEPORT option.
/// (publish_atom, uint16_t port, actor_addr whom,
/// set<string> ifs, string addr, bool reuse_addr)
/// -> either (ok_atom, uint16_t port)
/// or (error_atom, string error_string)
///
/// (put_atom, actor_addr whom, uint16_t port, bool reuse_addr)
/// -> put_result;
/// // Opens a new port other CAF instances can connect to. The
/// // difference between `PUBLISH` and `OPEN` is that no actor is mapped to
/// // this port, meaning that connecting nodes only get a valid `node_id`
/// // handle when connecting.
/// // @param port: Unused TCP port or 0 for any.
/// // @param addr: IP address to listen to or empty for any.
/// // @param reuse_addr: Enables or disables SO_REUSEPORT option.
/// (open_atom, uint16_t port, string addr, bool reuse_addr)
/// -> either (ok_atom, uint16_t port)
/// or (error_atom, string error_string)
///
/// (put_atom, actor_addr whom, uint16_t port)
/// -> put_result;
/// // Queries a remote node and returns an ID to this node as well as
/// // an `actor_addr` to a remote actor if an actor was published at this
/// // port. The actor address must be cast to either `actor` or
/// // `typed_actor` using `actor_cast` after validating `ifs`.
/// // @param hostname IP address or DNS hostname.
/// // @param port TCP port.
/// (connect_atom, string hostname, uint16_t port)
/// -> either (ok_atom, node_id nid, actor_addr remote_actor, set<string> ifs)
/// or (error_atom, string error_string)
///
/// (get_atom, string hostname, uint16_t port)
/// -> get_result;
/// // Closes `port` if it is mapped to `whom`.
/// // @param whom A published actor.
/// // @param port Used TCP port.
/// (unpublish_atom, actor_addr whom, uint16_t port)
/// -> either (ok_atom)
/// or (error_atom, string error_string)
///
/// (get_atom, string hostname, uint16_t port, set<string> expected_ifs)
/// -> get_result;
/// // Unconditionally closes `port`, removing any actor
/// // published at this port.
/// // @param port Used TCP port.
/// (close_atom, uint16_t port)
/// -> either (ok_atom)
/// or (error_atom, string error_string)
///
/// (delete_atom, actor_addr whom)
/// -> delete_result;
/// // Spawns an actor on a remote node, initializing it
/// // using the arguments stored in `msg`.
/// // @param nid ID of the remote node that should spawn the actor.
/// // @param name Announced type name of the actor.
/// // @param args Initialization arguments for the actor.
/// // @returns The address of the spawned actor and its interface
/// // description on success; an error string otherwise.
/// (spawn_atom, node_id nid, string name, message args)
/// -> either (ok_atom, actor_addr, set<string>
/// or (error_atom, string error_string)
///
/// (delete_atom, actor_addr whom, uint16_t port)
/// -> delete_result;
/// }
/// ~~~
///
/// The `middleman_actor` actor offers the following operations:
/// - `PUT` establishes a new `port <-> actor`
/// mapping and returns the actual port in use on success.
/// Passing 0 as port instructs the OS to choose the next high-level port
/// available for binding.
/// Type | Name | Parameter Description
/// -----------|------------|--------------------------------------------------
/// put_atom | | Identifies `PUT` operations.
/// actor_addr | whom | Actor that should be published at given port.
/// uint16_t | port | Unused TCP port or 0 for any.
/// string | addr | Optional; IP address to listen to or `INADDR_ANY`
/// bool | reuse_addr | Optional; enable SO_REUSEPORT option
///
/// - `GET` queries a remote node and returns an `actor_addr` to the remote actor
/// on success. This handle must be cast to either `actor` or `typed_actor`
/// using `actor_cast`.
/// Type | Name | Parameter Description
/// ------------|-------------|------------------------------------------------
/// get_atom | | Identifies `GET` operations.
/// string | hostname | Valid hostname or IP address.
/// uint16_t | port | TCP port.
/// set<string> | expected_ifs | Optional; Interface of typed remote actor.
///
/// - `DELETE` removes either all `port <-> actor` mappings for an actor or only
/// a single one if the optional `port` parameter is set.
/// Type | Name | Parameter Description
/// ------------|-------------|------------------------------------------------
/// delete_atom | | Identifies `DELETE` operations.
/// actor_addr | whom | Published actor.
/// uint16_t | port | Optional; remove only a single mapping.
using
middleman_actor
=
typed_actor
<
replies_to
<
put_atom
,
uint16_t
,
actor_addr
,
std
::
set
<
std
::
string
>
,
std
::
string
,
bool
>
::
with_either
<
ok_atom
,
uint16_t
>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
put_atom
,
uint16_t
,
actor_addr
,
std
::
set
<
std
::
string
>
,
std
::
string
>
::
with_either
<
ok_atom
,
uint16_t
>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
put_atom
,
uint16_t
,
actor_addr
,
std
::
set
<
std
::
string
>
,
bool
>
replies_to
<
publish_atom
,
uint16_t
,
actor_addr
,
std
::
set
<
std
::
string
>
,
std
::
string
,
bool
>
::
with_either
<
ok_atom
,
uint16_t
>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
put_atom
,
uint16_t
,
actor_addr
,
std
::
set
<
std
::
string
>>
replies_to
<
open_atom
,
uint16_t
,
std
::
string
,
bool
>
::
with_either
<
ok_atom
,
uint16_t
>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
get_atom
,
std
::
string
,
uint16_t
>
::
with_either
<
ok_atom
,
actor_addr
>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
get_atom
,
std
::
string
,
uint16_t
,
std
::
set
<
std
::
string
>>
::
with_either
<
ok_atom
,
actor_addr
>
replies_to
<
connect_atom
,
std
::
string
,
uint16_t
>
::
with_either
<
ok_atom
,
node_id
,
actor_addr
,
std
::
set
<
std
::
string
>>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
delete_atom
,
actor_addr
>
replies_to
<
unpublish_atom
,
actor_addr
,
uint16_t
>
::
with_either
<
ok_atom
>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
delete_atom
,
actor_addr
,
uint16_t
>
replies_to
<
close_atom
,
uint16_t
>
::
with_either
<
ok_atom
>
::
or_else
<
error_atom
,
std
::
string
>
,
replies_to
<
spawn_atom
,
node_id
,
std
::
string
,
message
>
::
with_either
<
ok_atom
,
actor_addr
,
std
::
set
<
std
::
string
>>
::
or_else
<
error_atom
,
std
::
string
>>
;
/// Returns a handle for asynchronous networking operations.
...
...
@@ -133,4 +127,3 @@ middleman_actor get_middleman_actor(); // implemented in middleman.cpp
}
// namespace caf
#endif // CAF_IO_MIDDLEMAN_ACTOR_HPP
libcaf_io/caf/io/network/interfaces.hpp
View file @
dd49a57d
...
...
@@ -33,10 +33,11 @@ namespace caf {
namespace
io
{
namespace
network
{
// {protocol => address}
using
address_listing
=
std
::
map
<
protocol
,
std
::
vector
<
std
::
string
>>
;
// {interface_name => {protocol => address}}
using
interfaces_map
=
std
::
map
<
std
::
string
,
std
::
map
<
protocol
,
std
::
vector
<
std
::
string
>>>
;
using
interfaces_map
=
std
::
map
<
std
::
string
,
address_listing
>
;
/// Utility class bundling access to network interface names and addresses.
class
interfaces
{
...
...
@@ -45,8 +46,7 @@ public:
static
interfaces_map
list_all
(
bool
include_localhost
=
true
);
/// Returns all addresses for all devices for all protocols.
static
std
::
map
<
protocol
,
std
::
vector
<
std
::
string
>>
list_addresses
(
bool
include_localhost
=
true
);
static
address_listing
list_addresses
(
bool
include_localhost
=
true
);
/// Returns all addresses for all devices for given protocol.
static
std
::
vector
<
std
::
string
>
list_addresses
(
protocol
proc
,
...
...
libcaf_io/caf/io/remote_actor.hpp
View file @
dd49a57d
...
...
@@ -31,8 +31,8 @@
namespace
caf
{
namespace
io
{
a
bstract_actor_pt
r
remote_actor_impl
(
std
::
set
<
std
::
string
>
ifs
,
const
std
::
string
&
host
,
uint16_t
port
);
a
ctor_add
r
remote_actor_impl
(
std
::
set
<
std
::
string
>
ifs
,
std
::
string
host
,
uint16_t
port
);
/// Establish a new connection to the actor at `host` on given `port`.
/// @param host Valid hostname or IP address.
...
...
@@ -41,8 +41,8 @@ abstract_actor_ptr remote_actor_impl(std::set<std::string> ifs,
/// representing a remote actor.
/// @throws network_error Thrown on connection error or
/// when connecting to a typed actor.
inline
actor
remote_actor
(
const
std
::
string
&
host
,
uint16_t
port
)
{
auto
res
=
remote_actor_impl
(
std
::
set
<
std
::
string
>
{},
host
,
port
);
inline
actor
remote_actor
(
std
::
string
host
,
uint16_t
port
)
{
auto
res
=
remote_actor_impl
(
std
::
set
<
std
::
string
>
{},
std
::
move
(
host
)
,
port
);
return
actor_cast
<
actor
>
(
res
);
}
...
...
@@ -54,10 +54,10 @@ inline actor remote_actor(const std::string& host, uint16_t port) {
/// @throws network_error Thrown on connection error or when connecting
/// to an untyped otherwise unexpected actor.
template
<
class
ActorHandle
>
ActorHandle
typed_remote_actor
(
const
std
::
string
&
host
,
uint16_t
port
)
{
auto
iface
=
ActorHandle
::
message_types
();
return
actor_cast
<
ActorHandle
>
(
remote_actor_impl
(
std
::
move
(
iface
),
host
,
port
)
);
ActorHandle
typed_remote_actor
(
std
::
string
host
,
uint16_t
port
)
{
auto
res
=
remote_actor_impl
(
ActorHandle
::
message_types
(),
std
::
move
(
host
),
port
);
return
actor_cast
<
ActorHandle
>
(
res
);
}
}
// namespace io
...
...
libcaf_io/src/basp.cpp
View file @
dd49a57d
...
...
@@ -39,15 +39,15 @@ namespace basp {
std
::
string
to_string
(
message_type
x
)
{
switch
(
x
)
{
case
basp
:
:
message_type
::
server_handshake
:
case
message_type
:
:
server_handshake
:
return
"server_handshake"
;
case
basp
:
:
message_type
::
client_handshake
:
case
message_type
:
:
client_handshake
:
return
"client_handshake"
;
case
basp
:
:
message_type
::
dispatch_message
:
case
message_type
:
:
dispatch_message
:
return
"dispatch_message"
;
case
basp
:
:
message_type
::
announce_proxy_instance
:
case
message_type
:
:
announce_proxy_instance
:
return
"announce_proxy_instance"
;
case
basp
:
:
message_type
::
kill_proxy_instance
:
case
message_type
:
:
kill_proxy_instance
:
return
"kill_proxy_instance"
;
default:
return
"???"
;
...
...
@@ -146,7 +146,6 @@ bool announce_proxy_instance_valid(const header& hdr) {
&&
zero
(
hdr
.
operation_data
);
}
bool
kill_proxy_instance_valid
(
const
header
&
hdr
)
{
return
valid
(
hdr
.
source_node
)
&&
valid
(
hdr
.
dest_node
)
...
...
@@ -192,6 +191,19 @@ optional<routing_table::route> routing_table::lookup(const node_id& target) {
auto
hdl
=
lookup_direct
(
target
);
if
(
hdl
!=
invalid_connection_handle
)
return
route
{
parent_
->
wr_buf
(
hdl
),
target
,
hdl
};
// pick first available indirect route
auto
i
=
indirect_
.
find
(
target
);
if
(
i
!=
indirect_
.
end
())
{
auto
&
hops
=
i
->
second
;
while
(
!
hops
.
empty
())
{
auto
&
hop
=
*
hops
.
begin
();
auto
hdl
=
lookup_direct
(
hop
);
if
(
hdl
!=
invalid_connection_handle
)
return
route
{
parent_
->
wr_buf
(
hdl
),
hop
,
hdl
};
else
hops
.
erase
(
hops
.
begin
());
}
}
return
none
;
}
...
...
@@ -209,6 +221,15 @@ routing_table::lookup_direct(const node_id& nid) const {
return
get_opt
(
direct_by_nid_
,
nid
,
invalid_connection_handle
);
}
node_id
routing_table
::
lookup_indirect
(
const
node_id
&
nid
)
const
{
auto
i
=
indirect_
.
find
(
nid
);
if
(
i
==
indirect_
.
end
())
return
invalid_node_id
;
if
(
i
->
second
.
empty
())
return
invalid_node_id
;
return
*
i
->
second
.
begin
();
}
void
routing_table
::
blacklist
(
const
node_id
&
hop
,
const
node_id
&
dest
)
{
blacklist_
[
dest
].
emplace
(
hop
);
auto
i
=
indirect_
.
find
(
dest
);
...
...
@@ -225,22 +246,41 @@ void routing_table::erase_direct(const connection_handle& hdl,
if
(
i
==
direct_by_hdl_
.
end
())
return
;
cb
(
i
->
second
);
parent_
->
parent
().
notify
<
hook
::
connection_lost
>
(
i
->
second
);
direct_by_nid_
.
erase
(
i
->
second
);
direct_by_hdl_
.
erase
(
i
);
}
bool
routing_table
::
erase_indirect
(
const
node_id
&
dest
)
{
auto
i
=
indirect_
.
find
(
dest
);
if
(
i
==
indirect_
.
end
())
return
false
;
if
(
parent_
->
parent
().
has_hook
())
for
(
auto
&
nid
:
i
->
second
)
parent_
->
parent
().
notify
<
hook
::
route_lost
>
(
nid
,
dest
);
indirect_
.
erase
(
i
);
return
true
;
}
void
routing_table
::
add_direct
(
const
connection_handle
&
hdl
,
const
node_id
&
nid
)
{
CAF_ASSERT
(
direct_by_hdl_
.
count
(
hdl
)
==
0
);
CAF_ASSERT
(
direct_by_nid_
.
count
(
nid
)
==
0
);
direct_by_hdl_
.
emplace
(
hdl
,
nid
);
direct_by_nid_
.
emplace
(
nid
,
hdl
);
parent_
->
parent
().
notify
<
hook
::
new_connection_established
>
(
nid
);
}
void
routing_table
::
add_indirect
(
const
node_id
&
hop
,
const
node_id
&
dest
)
{
bool
routing_table
::
add_indirect
(
const
node_id
&
hop
,
const
node_id
&
dest
)
{
auto
i
=
blacklist_
.
find
(
dest
);
if
(
i
==
blacklist_
.
end
()
||
i
->
second
.
count
(
hop
)
==
0
)
indirect_
[
dest
].
emplace
(
hop
);
if
(
i
==
blacklist_
.
end
()
||
i
->
second
.
count
(
hop
)
==
0
)
{
auto
&
hops
=
indirect_
[
dest
];
auto
added_first
=
hops
.
empty
();
hops
.
emplace
(
hop
);
parent_
->
parent
().
notify
<
hook
::
new_route_added
>
(
hop
,
dest
);
return
added_first
;
}
return
false
;
// blacklisted
}
bool
routing_table
::
reachable
(
const
node_id
&
dest
)
{
...
...
@@ -253,14 +293,17 @@ size_t routing_table::erase(const node_id& dest, erase_callback& cb) {
auto
i
=
indirect_
.
find
(
dest
);
if
(
i
!=
indirect_
.
end
())
{
res
=
i
->
second
.
size
();
for
(
auto
&
nid
:
i
->
second
)
for
(
auto
&
nid
:
i
->
second
)
{
cb
(
nid
);
parent_
->
parent
().
notify
<
hook
::
route_lost
>
(
nid
,
dest
);
}
indirect_
.
erase
(
i
);
}
auto
hdl
=
lookup_direct
(
dest
);
if
(
hdl
!=
invalid_connection_handle
)
{
direct_by_hdl_
.
erase
(
hdl
);
direct_by_nid_
.
erase
(
dest
);
parent_
->
parent
().
notify
<
hook
::
connection_lost
>
(
dest
);
++
res
;
}
return
res
;
...
...
@@ -270,7 +313,9 @@ size_t routing_table::erase(const node_id& dest, erase_callback& cb) {
* callee *
******************************************************************************/
instance
::
callee
::
callee
(
actor_namespace
::
backend
&
mgm
)
:
namespace_
(
mgm
)
{
instance
::
callee
::
callee
(
actor_namespace
::
backend
&
mgm
,
middleman
&
mm
)
:
namespace_
(
mgm
),
middleman_
(
mm
)
{
// nop
}
...
...
@@ -328,6 +373,7 @@ connection_state instance::handle(const new_data_msg& dm, header& hdr,
if
(
payload
)
bs
.
write_raw
(
payload
->
size
(),
payload
->
data
());
tbl_
.
flush
(
*
path
);
notify
<
hook
::
message_forwarded
>
(
hdr
,
payload
);
}
else
{
CAF_LOG_INFO
(
"cannot forward message, no route to destination"
);
if
(
hdr
.
source_node
!=
this_node_
)
{
...
...
@@ -345,6 +391,7 @@ connection_state instance::handle(const new_data_msg& dm, header& hdr,
}
else
{
CAF_LOG_WARNING
(
"lost packet with probably spoofed source"
);
}
notify
<
hook
::
message_forwarding_failed
>
(
hdr
,
payload
);
}
return
await_header
;
}
...
...
@@ -375,36 +422,47 @@ connection_state instance::handle(const new_data_msg& dm, header& hdr,
callee_
.
finalize_handshake
(
hdr
.
source_node
,
aid
,
sigs
);
return
err
();
}
// add
entry to routing table and call client
// add
direct route to this node and remove any indirect entry
CAF_LOG_INFO
(
"new direct connection: "
<<
to_string
(
hdr
.
source_node
));
tbl_
.
add_direct
(
dm
.
handle
,
hdr
.
source_node
);
auto
was_indirect
=
tbl_
.
erase_indirect
(
hdr
.
source_node
);
// write handshake as client in response
auto
path
=
tbl_
.
lookup
(
hdr
.
source_node
);
if
(
!
path
)
{
CAF_LOG_ERROR
(
"no route to host after server handshake"
);
return
err
();
}
write
(
path
->
wr_buf
,
message_type
::
client_handshake
,
nullptr
,
0
,
this_node_
,
hdr
.
source_node
,
invalid_actor_id
,
invalid_actor_id
);
// tell client to create proxy etc.
write_client_handshake
(
path
->
wr_buf
,
hdr
.
source_node
);
callee_
.
learned_new_node_directly
(
hdr
.
source_node
,
was_indirect
);
callee_
.
finalize_handshake
(
hdr
.
source_node
,
aid
,
sigs
);
flush
(
*
path
);
break
;
}
case
message_type
:
:
client_handshake
:
case
message_type
:
:
client_handshake
:
{
if
(
tbl_
.
lookup_direct
(
hdr
.
source_node
)
!=
invalid_connection_handle
)
{
CAF_LOG_INFO
(
"received second client handshake for "
<<
to_string
(
hdr
.
source_node
)
<<
"
, close connection
"
);
return
err
()
;
<<
to_string
(
hdr
.
source_node
)
<<
"
(ignored)
"
);
break
;
}
// add direct route to this node
// add direct route to this node and remove any indirect entry
CAF_LOG_INFO
(
"new direct connection: "
<<
to_string
(
hdr
.
source_node
));
tbl_
.
add_direct
(
dm
.
handle
,
hdr
.
source_node
);
auto
was_indirect
=
tbl_
.
erase_indirect
(
hdr
.
source_node
);
callee_
.
learned_new_node_directly
(
hdr
.
source_node
,
was_indirect
);
break
;
}
case
message_type
:
:
dispatch_message
:
{
if
(
!
payload_valid
())
return
err
();
// in case the sender of this message was received via a third node,
// we assume that that node to offers a route to the original source
auto
last_hop
=
tbl_
.
lookup_direct
(
dm
.
handle
);
if
(
hdr
.
source_node
!=
invalid_node_id
&&
hdr
.
source_node
!=
this_node_
&&
last_hop
!=
hdr
.
source_node
&&
tbl_
.
lookup_direct
(
hdr
.
source_node
)
==
invalid_connection_handle
&&
tbl_
.
add_indirect
(
last_hop
,
hdr
.
source_node
))
callee_
.
learned_new_node_indirectly
(
hdr
.
source_node
);
binary_deserializer
bd
{
payload
->
data
(),
payload
->
size
(),
&
get_namespace
()};
message
msg
;
...
...
@@ -468,6 +526,7 @@ void instance::add_published_actor(uint16_t port,
auto
&
entry
=
published_actors_
[
port
];
swap
(
entry
.
first
,
published_actor
);
swap
(
entry
.
second
,
published_interface
);
notify
<
hook
::
actor_published
>
(
entry
.
first
,
entry
.
second
,
port
);
}
size_t
instance
::
remove_published_actor
(
uint16_t
port
,
...
...
@@ -511,11 +570,12 @@ size_t instance::remove_published_actor(const actor_addr& whom, uint16_t port,
bool
instance
::
dispatch
(
const
actor_addr
&
sender
,
const
actor_addr
&
receiver
,
message_id
mid
,
const
message
&
msg
)
{
CAF_LOG_TRACE
(
""
);
if
(
!
receiver
.
is_remote
())
return
false
;
CAF_ASSERT
(
receiver
.
is_remote
());
auto
path
=
lookup
(
receiver
->
node
());
if
(
!
path
)
if
(
!
path
)
{
notify
<
hook
::
message_sending_failed
>
(
sender
,
receiver
,
mid
,
msg
);
return
false
;
}
auto
writer
=
make_callback
([
&
](
serializer
&
sink
)
{
msg
.
serialize
(
sink
);
});
...
...
@@ -524,6 +584,7 @@ bool instance::dispatch(const actor_addr& sender, const actor_addr& receiver,
sender
?
sender
->
id
()
:
invalid_actor_id
,
receiver
->
id
()};
write
(
path
->
wr_buf
,
hdr
,
&
writer
);
flush
(
*
path
);
notify
<
hook
::
message_sent
>
(
sender
,
path
->
next_hop
,
receiver
,
mid
,
msg
);
return
true
;
}
...
...
@@ -577,6 +638,7 @@ void instance::write(buffer_type& buf, header& hdr, payload_writer* pw) {
void
instance
::
write_server_handshake
(
buffer_type
&
out_buf
,
optional
<
uint16_t
>
port
)
{
using
namespace
detail
;
published_actor
*
pa
=
nullptr
;
if
(
port
)
{
auto
i
=
published_actors_
.
find
(
*
port
);
...
...
@@ -584,12 +646,8 @@ void instance::write_server_handshake(buffer_type& out_buf,
pa
=
&
i
->
second
;
}
auto
writer
=
make_callback
([
&
](
serializer
&
sink
)
{
if
(
!
pa
)
{
return
;
}
sink
<<
pa
->
first
.
id
()
<<
static_cast
<
uint32_t
>
(
pa
->
second
.
size
());
for
(
auto
&
sig
:
pa
->
second
)
sink
<<
sig
;
if
(
pa
)
sink
<<
pa
->
first
.
id
()
<<
pa
->
second
;
});
header
hdr
{
message_type
::
server_handshake
,
0
,
version
,
this_node_
,
invalid_node_id
,
...
...
@@ -597,6 +655,14 @@ void instance::write_server_handshake(buffer_type& out_buf,
write
(
out_buf
,
hdr
,
&
writer
);
}
void
instance
::
write_client_handshake
(
buffer_type
&
buf
,
const
node_id
&
remote_side
)
{
write
(
buf
,
message_type
::
client_handshake
,
nullptr
,
0
,
this_node_
,
remote_side
,
invalid_actor_id
,
invalid_actor_id
);
}
void
instance
::
write_dispatch_error
(
buffer_type
&
buf
,
const
node_id
&
source_node
,
const
node_id
&
dest_node
,
...
...
libcaf_io/src/basp_broker.cpp
View file @
dd49a57d
...
...
@@ -19,10 +19,15 @@
#include "caf/io/basp_broker.hpp"
#include <limits>
#include "caf/exception.hpp"
#include "caf/make_counted.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/forwarding_actor_proxy.hpp"
#include "caf/experimental/whereis.hpp"
#include "caf/detail/singletons.hpp"
#include "caf/detail/actor_registry.hpp"
#include "caf/detail/sync_request_bouncer.hpp"
...
...
@@ -31,7 +36,9 @@
#include "caf/io/middleman.hpp"
#include "caf/io/unpublish.hpp"
using
std
::
string
;
#include "caf/io/network/interfaces.hpp"
using
namespace
caf
::
experimental
;
namespace
caf
{
namespace
io
{
...
...
@@ -41,7 +48,8 @@ namespace io {
******************************************************************************/
basp_broker_state
::
basp_broker_state
(
broker
*
selfptr
)
:
basp
::
instance
::
callee
(
static_cast
<
actor_namespace
::
backend
&>
(
*
this
)),
:
basp
::
instance
::
callee
(
static_cast
<
actor_namespace
::
backend
&>
(
*
this
),
selfptr
->
parent
()),
self
(
selfptr
),
instance
(
selfptr
,
*
this
)
{
CAF_ASSERT
(
this_node
()
!=
invalid_node_id
);
...
...
@@ -56,8 +64,10 @@ actor_proxy_ptr basp_broker_state::make_proxy(const node_id& nid,
// this member function is being called whenever we deserialize a
// payload received from a remote node; if a remote node A sends
// us a handle to a third node B, then we assume that A offers a route to B
if
(
nid
!=
this_context
->
id
)
instance
.
tbl
().
add_indirect
(
this_context
->
id
,
nid
);
if
(
nid
!=
this_context
->
id
&&
instance
.
tbl
().
lookup_direct
(
nid
)
==
invalid_connection_handle
&&
instance
.
tbl
().
add_indirect
(
this_context
->
id
,
nid
))
learned_new_node_indirectly
(
nid
);
// we need to tell remote side we are watching this actor now;
// use a direct route if possible, i.e., when talking to a third node
auto
path
=
instance
.
tbl
().
lookup
(
nid
);
...
...
@@ -88,12 +98,12 @@ actor_proxy_ptr basp_broker_state::make_proxy(const node_id& nid,
this_node
(),
nid
,
invalid_actor_id
,
aid
);
instance
.
tbl
().
flush
(
*
path
);
self
->
parent
()
.
notify
<
hook
::
new_remote_actor
>
(
res
->
address
());
middleman_
.
notify
<
hook
::
new_remote_actor
>
(
res
->
address
());
return
res
;
}
void
basp_broker_state
::
finalize_handshake
(
const
node_id
&
nid
,
actor_id
aid
,
const
std
::
set
<
std
::
string
>&
sigs
)
{
std
::
set
<
std
::
string
>&
sigs
)
{
CAF_LOG_TRACE
(
CAF_TSARG
(
nid
)
<<
", "
<<
CAF_ARG
(
aid
)
<<
", "
<<
CAF_TSARG
(
make_message
(
sigs
)));
...
...
@@ -102,20 +112,15 @@ void basp_broker_state::finalize_handshake(const node_id& nid, actor_id aid,
auto
&
cb
=
this_context
->
callback
;
if
(
!
cb
)
return
;
auto
&
exp
=
this_context
->
expected_sigs
;
auto
cleanup
=
detail
::
make_scope_guard
([
&
]
{
cb
=
none
;
exp
.
clear
();
});
if
(
!
std
::
includes
(
sigs
.
begin
(),
sigs
.
end
(),
exp
.
begin
(),
exp
.
end
()))
{
cb
->
deliver
(
make_message
(
error_atom
::
value
,
"expected signature does not "
"comply to found signature"
));
return
;
}
if
(
aid
==
invalid_actor_id
)
{
// can occur when connecting to the default port of a node
cb
->
deliver
(
make_message
(
ok_atom
::
value
,
actor_addr
{
invalid_actor_addr
}));
cb
->
deliver
(
make_message
(
ok_atom
::
value
,
nid
,
actor_addr
{
invalid_actor_addr
},
std
::
move
(
sigs
)));
return
;
}
abstract_actor_ptr
ptr
;
...
...
@@ -132,9 +137,8 @@ void basp_broker_state::finalize_handshake(const node_id& nid, actor_id aid,
actor_addr
addr
=
ptr
?
ptr
->
address
()
:
invalid_actor_addr
;
if
(
addr
.
is_remote
())
known_remotes
.
emplace
(
nid
,
std
::
make_pair
(
this_context
->
remote_port
,
addr
));
cb
->
deliver
(
make_message
(
ok_atom
::
value
,
addr
));
cb
->
deliver
(
make_message
(
ok_atom
::
value
,
nid
,
addr
,
std
::
move
(
sigs
)
));
this_context
->
callback
=
none
;
this_context
->
expected_sigs
.
clear
();
}
void
basp_broker_state
::
purge_state
(
const
node_id
&
nid
)
{
...
...
@@ -207,21 +211,179 @@ void basp_broker_state::deliver(const node_id& source_node,
}
abstract_actor_ptr
dest
;
uint32_t
rsn
=
exit_reason
::
remote_link_unreachable
;
if
(
dest_node
==
this_node
())
std
::
tie
(
dest
,
rsn
)
=
registry
->
get_entry
(
dest_actor
);
else
if
(
dest_node
!=
this_node
())
dest
=
get_namespace
().
get_or_put
(
dest_node
,
dest_actor
);
else
if
(
dest_actor
==
std
::
numeric_limits
<
actor_id
>::
max
())
{
// this hack allows CAF to talk to older CAF versions; CAF <= 0.14 will
// discard this message silently as the receiver is not found, while
// CAF >= 0.14.1 will use the operation data to identify named actors
auto
dest_name
=
static_cast
<
atom_value
>
(
mid
.
integer_value
());
CAF_LOG_DEBUG
(
"dest_name = "
<<
to_string
(
dest_name
));
mid
=
message_id
::
make
();
// override this since the message is async
dest
=
actor_cast
<
abstract_actor_ptr
>
(
registry
->
get_named
(
dest_name
));
}
else
{
std
::
tie
(
dest
,
rsn
)
=
registry
->
get_entry
(
dest_actor
);
}
if
(
!
dest
)
{
CAF_LOG_INFO
(
"cannot deliver message, destination not found"
);
self
->
parent
().
notify
<
hook
::
invalid_message_received
>
(
source_node
,
src
,
dest_actor
,
mid
,
msg
);
if
(
mid
.
valid
()
&&
src
!=
invalid_actor_addr
)
{
detail
::
sync_request_bouncer
srb
{
rsn
};
srb
(
src
,
mid
);
}
return
;
}
self
->
parent
().
notify
<
hook
::
message_received
>
(
source_node
,
src
,
dest
->
address
(),
mid
,
msg
);
dest
->
enqueue
(
src
,
mid
,
std
::
move
(
msg
),
nullptr
);
}
void
basp_broker_state
::
learned_new_node
(
const
node_id
&
nid
)
{
CAF_LOG_TRACE
(
CAF_TSARG
(
nid
));
auto
&
tmp
=
spawn_servers
[
nid
];
tmp
=
spawn
<
hidden
>
([
=
](
event_based_actor
*
this_actor
)
->
behavior
{
return
{
[
=
](
ok_atom
,
const
std
::
string
&
/* key == "info" */
,
const
actor_addr
&
config_serv_addr
,
const
std
::
string
&
/* name */
)
{
auto
config_serv
=
actor_cast
<
actor
>
(
config_serv_addr
);
this_actor
->
monitor
(
config_serv
);
this_actor
->
become
(
[
=
](
spawn_atom
,
std
::
string
&
type
,
message
&
args
)
->
delegated
<
either
<
ok_atom
,
actor_addr
,
std
::
set
<
std
::
string
>>
::
or_else
<
error_atom
,
std
::
string
>>
{
this_actor
->
delegate
(
config_serv
,
get_atom
::
value
,
std
::
move
(
type
),
std
::
move
(
args
));
return
{};
},
[
=
](
const
down_msg
&
dm
)
{
this_actor
->
quit
(
dm
.
reason
);
},
others
>>
[
=
]
{
CAF_LOGF_ERROR
(
"spawn server has received unexpected message: "
<<
to_string
(
this_actor
->
current_message
()));
}
);
},
after
(
std
::
chrono
::
minutes
(
5
))
>>
[
=
]
{
CAF_LOGF_INFO
(
"no spawn server on node "
<<
to_string
(
nid
));
this_actor
->
quit
();
}
};
});
using
namespace
detail
;
singletons
::
get_actor_registry
()
->
put
(
tmp
.
id
(),
actor_cast
<
abstract_actor_ptr
>
(
tmp
));
auto
writer
=
make_callback
([](
serializer
&
sink
)
{
auto
msg
=
make_message
(
sys_atom
::
value
,
get_atom
::
value
,
"info"
);
msg
.
serialize
(
sink
);
});
auto
path
=
instance
.
tbl
().
lookup
(
nid
);
if
(
!
path
)
{
CAF_LOG_ERROR
(
"learned_new_node called, but no route to nid"
);
return
;
}
// writing std::numeric_limits<actor_id>::max() is a hack to get
// this send-to-named-actor feature working with older CAF releases
instance
.
write
(
path
->
wr_buf
,
basp
::
message_type
::
dispatch_message
,
nullptr
,
static_cast
<
uint64_t
>
(
atom
(
"SpawnServ"
)),
this_node
(),
nid
,
tmp
.
id
(),
std
::
numeric_limits
<
actor_id
>::
max
(),
&
writer
);
instance
.
flush
(
*
path
);
}
void
basp_broker_state
::
learned_new_node_directly
(
const
node_id
&
nid
,
bool
was_indirectly_before
)
{
CAF_ASSERT
(
this_context
!=
nullptr
);
CAF_LOG_TRACE
(
CAF_TSARG
(
nid
));
if
(
!
was_indirectly_before
)
learned_new_node
(
nid
);
}
void
basp_broker_state
::
learned_new_node_indirectly
(
const
node_id
&
nid
)
{
CAF_ASSERT
(
this_context
!=
nullptr
);
CAF_LOG_TRACE
(
CAF_TSARG
(
nid
));
learned_new_node
(
nid
);
if
(
!
enable_automatic_connections
)
return
;
actor
bb
=
self
;
// a handle for our helper back to this BASP broker
// this member function gets only called once, after adding a new
// indirect connection to the routing table; hence, spawning
// our helper here exactly once and there is no need to track
// in-flight connection requests
auto
connection_helper
=
[
=
](
event_based_actor
*
helper
,
actor
s
)
->
behavior
{
helper
->
monitor
(
s
);
return
{
// this config is send from the remote `ConfigServ`
[
=
](
ok_atom
,
const
std
::
string
&
,
message
&
msg
)
{
CAF_LOGF_DEBUG
(
"received requested config: "
<<
to_string
(
msg
));
// whatever happens, we are done afterwards
helper
->
quit
();
msg
.
apply
({
[
&
](
uint16_t
port
,
network
::
address_listing
&
addresses
)
{
auto
&
mx
=
middleman
::
instance
()
->
backend
();
for
(
auto
&
kvp
:
addresses
)
if
(
kvp
.
first
!=
network
::
protocol
::
ethernet
)
for
(
auto
&
addr
:
kvp
.
second
)
{
try
{
auto
hdl
=
mx
.
new_tcp_scribe
(
addr
,
port
);
// gotcha! send scribe to our BASP broker
// to initiate handshake etc.
CAF_LOGF_INFO
(
"connected directly via "
<<
addr
);
helper
->
send
(
bb
,
connect_atom
::
value
,
hdl
,
port
);
return
;
}
catch
(...)
{
// simply try next address
}
}
CAF_LOGF_INFO
(
"could not connect to node directly, nid = "
<<
to_string
(
nid
));
}
});
},
[
=
](
const
down_msg
&
dm
)
{
helper
->
quit
(
dm
.
reason
);
},
after
(
std
::
chrono
::
minutes
(
10
))
>>
[
=
]
{
// nothing heard in about 10 minutes... just a call it a day, then
CAF_LOGF_INFO
(
"aborted direct connection attempt after 10min, nid = "
<<
to_string
(
nid
));
helper
->
quit
(
exit_reason
::
user_shutdown
);
}
};
};
auto
path
=
instance
.
tbl
().
lookup
(
nid
);
if
(
!
path
)
{
CAF_LOG_ERROR
(
"learned_new_node_indirectly called, but no route to nid"
);
return
;
}
if
(
path
->
next_hop
==
nid
)
{
CAF_LOG_ERROR
(
"learned_new_node_indirectly called with direct connection"
);
return
;
}
using
namespace
detail
;
auto
tmp
=
spawn
<
detached
+
hidden
>
(
connection_helper
,
self
);
singletons
::
get_actor_registry
()
->
put
(
tmp
.
id
(),
actor_cast
<
abstract_actor_ptr
>
(
tmp
));
auto
writer
=
make_callback
([](
serializer
&
sink
)
{
auto
msg
=
make_message
(
get_atom
::
value
,
"basp.default-connectivity"
);
msg
.
serialize
(
sink
);
});
// writing std::numeric_limits<actor_id>::max() is a hack to get
// this send-to-named-actor feature working with older CAF releases
instance
.
write
(
path
->
wr_buf
,
basp
::
message_type
::
dispatch_message
,
nullptr
,
static_cast
<
uint64_t
>
(
atom
(
"ConfigServ"
)),
this_node
(),
nid
,
tmp
.
id
(),
std
::
numeric_limits
<
actor_id
>::
max
(),
&
writer
);
instance
.
flush
(
*
path
);
}
void
basp_broker_state
::
set_context
(
connection_handle
hdl
)
{
CAF_LOG_TRACE
(
CAF_MARG
(
hdl
,
id
));
auto
i
=
ctx
.
find
(
hdl
);
...
...
@@ -236,8 +398,7 @@ void basp_broker_state::set_context(connection_handle hdl) {
hdl
,
invalid_node_id
,
0
,
none
,
std
::
set
<
std
::
string
>
{}}).
first
;
none
}).
first
;
}
this_context
=
&
i
->
second
;
}
...
...
@@ -262,11 +423,14 @@ bool basp_broker_state::erase_context(connection_handle hdl) {
******************************************************************************/
basp_broker
::
basp_broker
(
middleman
&
mm
)
:
caf
::
experimental
::
stateful_actor
<
basp_broker_state
,
broker
>
(
mm
)
{
:
stateful_actor
<
basp_broker_state
,
broker
>
(
mm
)
{
// nop
}
behavior
basp_broker
::
make_behavior
()
{
// ask the configuration server whether we should open a default port
auto
config_server
=
whereis
(
atom
(
"ConfigServ"
));
send
(
config_server
,
get_atom
::
value
,
"global.enable-automatic-connections"
);
return
{
// received from underlying broker implementation
[
=
](
new_data_msg
&
msg
)
{
...
...
@@ -339,7 +503,7 @@ behavior basp_broker::make_behavior() {
state
.
instance
.
remove_published_actor
(
port
);
},
// received from middleman actor
[
=
](
pu
t
_atom
,
accept_handle
hdl
,
uint16_t
port
,
const
actor_addr
&
whom
,
[
=
](
pu
blish
_atom
,
accept_handle
hdl
,
uint16_t
port
,
const
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
hdl
.
id
())
<<
", "
<<
CAF_TSARG
(
whom
)
<<
", "
<<
CAF_ARG
(
port
));
...
...
@@ -354,11 +518,9 @@ behavior basp_broker::make_behavior() {
}
detail
::
singletons
::
get_actor_registry
()
->
put
(
whom
->
id
(),
whom
);
state
.
instance
.
add_published_actor
(
port
,
whom
,
std
::
move
(
sigs
));
parent
().
notify
<
hook
::
actor_published
>
(
whom
,
port
);
},
// received from middleman actor (delegated)
[
=
](
get_atom
,
connection_handle
hdl
,
uint16_t
port
,
std
::
set
<
std
::
string
>&
expected_ifs
)
{
[
=
](
connect_atom
,
connection_handle
hdl
,
uint16_t
port
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
hdl
.
id
()));
auto
rp
=
make_response_promise
();
try
{
...
...
@@ -376,7 +538,6 @@ behavior basp_broker::make_behavior() {
ctx
.
remote_port
=
port
;
ctx
.
cstate
=
basp
::
await_header
;
ctx
.
callback
=
rp
;
ctx
.
expected_sigs
=
std
::
move
(
expected_ifs
);
// await server handshake
configure_read
(
hdl
,
receive_policy
::
exactly
(
basp
::
header_size
));
},
...
...
@@ -384,7 +545,7 @@ behavior basp_broker::make_behavior() {
CAF_LOG_TRACE
(
CAF_TSARG
(
nid
)
<<
", "
<<
CAF_ARG
(
aid
));
state
.
get_namespace
().
erase
(
nid
,
aid
);
},
[
=
](
delete
_atom
,
const
actor_addr
&
whom
,
uint16_t
port
)
->
message
{
[
=
](
unpublish
_atom
,
const
actor_addr
&
whom
,
uint16_t
port
)
->
message
{
CAF_LOG_TRACE
(
CAF_TSARG
(
whom
)
<<
", "
<<
CAF_ARG
(
port
));
if
(
whom
==
invalid_actor_addr
)
return
make_message
(
error_atom
::
value
,
"whom == invalid_actor_addr"
);
...
...
@@ -397,6 +558,50 @@ behavior basp_broker::make_behavior() {
?
make_message
(
error_atom
::
value
,
"no mapping found"
)
:
make_message
(
ok_atom
::
value
);
},
[
=
](
close_atom
,
uint16_t
port
)
->
message
{
if
(
port
==
0
)
return
make_message
(
error_atom
::
value
,
"port == 0"
);
// it is well-defined behavior to not have an actor published here,
// hence the result can be ignored safely
state
.
instance
.
remove_published_actor
(
port
,
nullptr
);
auto
acceptor
=
hdl_by_port
(
port
);
if
(
acceptor
)
{
close
(
*
acceptor
);
return
make_message
(
ok_atom
::
value
);
}
return
make_message
(
error_atom
::
value
,
"no doorman for given port found"
);
},
[
=
](
spawn_atom
,
const
node_id
&
nid
,
std
::
string
&
type
,
message
&
args
)
->
delegated
<
either
<
ok_atom
,
actor_addr
,
std
::
set
<
std
::
string
>>
::
or_else
<
error_atom
,
std
::
string
>>
{
auto
err
=
[
=
](
std
::
string
errmsg
)
{
auto
rp
=
make_response_promise
();
rp
.
deliver
(
error_atom
::
value
,
std
::
move
(
errmsg
));
};
auto
i
=
state
.
spawn_servers
.
find
(
nid
);
if
(
i
==
state
.
spawn_servers
.
end
())
{
err
(
"no connection to requested node"
);
return
{};
}
delegate
(
i
->
second
,
spawn_atom
::
value
,
std
::
move
(
type
),
std
::
move
(
args
));
return
{};
},
[
=
](
ok_atom
,
const
std
::
string
&
key
,
message
&
value
)
{
if
(
key
==
"global.enable-automatic-connections"
)
{
value
.
apply
([
&
](
bool
enabled
)
{
if
(
!
enabled
)
return
;
CAF_LOG_INFO
(
"enable automatic connection"
);
// open a random port and store a record for others
// how to connect to this port in the configuration server
auto
port
=
add_tcp_doorman
(
uint16_t
{
0
}).
second
;
auto
addrs
=
network
::
interfaces
::
list_addresses
(
false
);
send
(
config_server
,
put_atom
::
value
,
"basp.default-connectivity"
,
make_message
(
port
,
std
::
move
(
addrs
)));
state
.
enable_automatic_connections
=
true
;
});
}
},
// catch-all error handler
others
>>
[
=
]
{
...
...
libcaf_io/src/hook.cpp
View file @
dd49a57d
...
...
@@ -40,9 +40,14 @@ void hook::message_sent_cb(const actor_addr& from, const node_id& dest_node,
call_next
<
message_sent
>
(
from
,
dest_node
,
dest
,
mid
,
payload
);
}
void
hook
::
message_forwarded_cb
(
const
node_id
&
from
,
const
node_id
&
dest
,
void
hook
::
message_forwarded_cb
(
const
basp
::
header
&
hdr
,
const
std
::
vector
<
char
>*
payload
)
{
call_next
<
message_forwarded
>
(
from
,
dest
,
payload
);
call_next
<
message_forwarded
>
(
hdr
,
payload
);
}
void
hook
::
message_forwarding_failed_cb
(
const
basp
::
header
&
hdr
,
const
std
::
vector
<
char
>*
payload
)
{
call_next
<
message_forwarding_failed
>
(
hdr
,
payload
);
}
void
hook
::
message_sending_failed_cb
(
const
actor_addr
&
from
,
...
...
@@ -52,13 +57,10 @@ void hook::message_sending_failed_cb(const actor_addr& from,
call_next
<
message_sending_failed
>
(
from
,
dest
,
mid
,
payload
);
}
void
hook
::
message_forwarding_failed_cb
(
const
node_id
&
from
,
const
node_id
&
to
,
const
std
::
vector
<
char
>*
payload
)
{
call_next
<
message_forwarding_failed
>
(
from
,
to
,
payload
);
}
void
hook
::
actor_published_cb
(
const
actor_addr
&
addr
,
uint16_t
port
)
{
call_next
<
actor_published
>
(
addr
,
port
);
void
hook
::
actor_published_cb
(
const
actor_addr
&
addr
,
const
std
::
set
<
std
::
string
>&
ifs
,
uint16_t
port
)
{
call_next
<
actor_published
>
(
addr
,
ifs
,
port
);
}
void
hook
::
new_remote_actor_cb
(
const
actor_addr
&
addr
)
{
...
...
@@ -73,6 +75,14 @@ void hook::new_route_added_cb(const node_id& via, const node_id& node) {
call_next
<
new_route_added
>
(
via
,
node
);
}
void
hook
::
connection_lost_cb
(
const
node_id
&
dest
)
{
call_next
<
connection_lost
>
(
dest
);
}
void
hook
::
route_lost_cb
(
const
node_id
&
hop
,
const
node_id
&
dest
)
{
call_next
<
route_lost
>
(
hop
,
dest
);
}
void
hook
::
invalid_message_received_cb
(
const
node_id
&
source
,
const
actor_addr
&
sender
,
actor_id
invalid_dest
,
...
...
libcaf_io/src/middleman.cpp
View file @
dd49a57d
...
...
@@ -40,6 +40,8 @@
#include "caf/io/basp_broker.hpp"
#include "caf/io/system_messages.hpp"
#include "caf/io/network/interfaces.hpp"
#include "caf/detail/logging.hpp"
#include "caf/detail/ripemd_160.hpp"
#include "caf/detail/safe_equal.hpp"
...
...
@@ -58,62 +60,48 @@ namespace io {
namespace
{
template
<
class
Subtype
,
class
I
>
inline
void
serialize_impl
(
const
handle
<
Subtype
,
I
>&
hdl
,
serializer
*
sink
)
{
sink
->
write_value
(
hdl
.
id
());
void
serialize
(
serializer
&
sink
,
std
::
vector
<
char
>&
buf
)
{
sink
<<
static_cast
<
uint32_t
>
(
buf
.
size
());
sink
.
write_raw
(
buf
.
size
(),
buf
.
data
());
}
template
<
class
Subtype
,
class
I
>
inline
void
deserialize_impl
(
handle
<
Subtype
,
I
>&
hdl
,
deserializer
*
source
)
{
hdl
.
set_id
(
source
->
read
<
int64_t
>
());
void
serialize
(
deserializer
&
source
,
std
::
vector
<
char
>&
buf
)
{
auto
bs
=
source
.
read
<
uint32_t
>
();
buf
.
resize
(
bs
);
source
.
read_raw
(
buf
.
size
(),
buf
.
data
());
}
inline
void
serialize_impl
(
const
new_connection_msg
&
msg
,
serializer
*
sink
)
{
serialize_impl
(
msg
.
source
,
sink
);
s
erialize_impl
(
msg
.
handle
,
sink
);
template
<
class
Subtype
,
class
I
>
void
serialize
(
serializer
&
sink
,
handle
<
Subtype
,
I
>&
hdl
)
{
s
ink
.
write_value
(
hdl
.
id
()
);
}
inline
void
deserialize_impl
(
new_connection_msg
&
msg
,
deserializer
*
source
)
{
deserialize_impl
(
msg
.
source
,
source
);
deserialize_impl
(
msg
.
handle
,
source
);
template
<
class
Subtype
,
class
I
>
void
serialize
(
deserializer
&
source
,
handle
<
Subtype
,
I
>&
hdl
)
{
hdl
.
set_id
(
source
.
read
<
int64_t
>
()
);
}
inline
void
serialize_impl
(
const
new_data_msg
&
msg
,
serializer
*
sink
)
{
serialize_impl
(
msg
.
handle
,
sink
);
auto
buf_size
=
static_cast
<
uint32_t
>
(
msg
.
buf
.
size
());
if
(
buf_size
!=
msg
.
buf
.
size
())
{
// narrowing error
std
::
ostringstream
oss
;
oss
<<
"attempted to send more than "
<<
std
::
numeric_limits
<
uint32_t
>::
max
()
<<
" bytes"
;
auto
errstr
=
oss
.
str
();
CAF_LOGF_INFO
(
errstr
);
throw
std
::
ios_base
::
failure
(
std
::
move
(
errstr
));
}
sink
->
write_value
(
buf_size
);
sink
->
write_raw
(
msg
.
buf
.
size
(),
msg
.
buf
.
data
());
template
<
class
Archive
>
void
serialize
(
Archive
&
ar
,
new_connection_msg
&
msg
)
{
serialize
(
ar
,
msg
.
source
);
serialize
(
ar
,
msg
.
handle
);
}
inline
void
deserialize_impl
(
new_data_msg
&
msg
,
deserializer
*
source
)
{
deserialize_impl
(
msg
.
handle
,
source
);
auto
buf_size
=
source
->
read
<
uint32_t
>
();
msg
.
buf
.
resize
(
buf_size
);
source
->
read_raw
(
msg
.
buf
.
size
(),
msg
.
buf
.
data
());
template
<
class
Archive
>
void
serialize
(
Archive
&
ar
,
new_data_msg
&
msg
)
{
serialize
(
ar
,
msg
.
handle
);
serialize
(
ar
,
msg
.
buf
);
}
// connection_closed_msg & acceptor_closed_msg have the same fields
template
<
class
T
>
typename
std
::
enable_if
<
std
::
is_same
<
T
,
connection_closed_msg
>::
value
||
std
::
is_same
<
T
,
acceptor_closed_msg
>::
value
>::
type
serialize_impl
(
const
T
&
dm
,
serializer
*
sink
)
{
serialize_impl
(
dm
.
handle
,
sink
);
}
// connection_closed_msg & acceptor_closed_msg have the same fields
template
<
class
T
>
typename
std
::
enable_if
<
std
::
is_same
<
T
,
connection_closed_msg
>::
value
||
std
::
is_same
<
T
,
acceptor_closed_msg
>::
value
>::
type
deserialize_impl
(
T
&
dm
,
deserializer
*
source
)
{
deserialize_impl
(
dm
.
handle
,
source
);
template
<
class
Archive
,
class
T
>
typename
std
::
enable_if
<
std
::
is_same
<
T
,
connection_closed_msg
>::
value
||
std
::
is_same
<
T
,
acceptor_closed_msg
>::
value
>::
type
serialize
(
Archive
&
ar
,
T
&
dm
)
{
serialize
(
ar
,
dm
.
handle
);
}
template
<
class
T
>
...
...
@@ -126,11 +114,11 @@ public:
}
void
serialize
(
const
void
*
instance
,
serializer
*
sink
)
const
{
serialize_impl
(
super
::
deref
(
instance
),
sink
);
io
::
serialize
(
*
sink
,
super
::
deref
(
const_cast
<
void
*>
(
instance
))
);
}
void
deserialize
(
void
*
instance
,
deserializer
*
source
)
const
{
deserialize_impl
(
super
::
deref
(
instance
),
source
);
io
::
serialize
(
*
source
,
super
::
deref
(
instance
)
);
}
};
...
...
@@ -139,8 +127,6 @@ void do_announce(const char* tname) {
announce
(
typeid
(
T
),
uniform_type_info_ptr
{
new
uti_impl
<
T
>
(
tname
)});
}
}
// namespace <anonymous>
class
middleman_actor_impl
:
public
middleman_actor
::
base
{
public:
middleman_actor_impl
(
middleman
&
mref
,
actor
default_broker
)
...
...
@@ -149,65 +135,68 @@ public:
// nop
}
~
middleman_actor_impl
();
void
on_exit
()
{
CAF_LOG_TRACE
(
""
);
broker_
=
invalid_actor
;
}
using
get_op_result
=
either
<
ok_atom
,
actor_addr
>
::
or_else
<
error_atom
,
std
::
string
>
;
using
get_op_promise
=
typed_response_promise
<
get_op_result
>
;
using
put_res
=
either
<
ok_atom
,
uint16_t
>::
or_else
<
error_atom
,
std
::
string
>
;
using
del_op_result
=
either
<
ok_atom
>::
or_else
<
error_atom
,
std
::
string
>
;
using
get_res
=
delegated
<
either
<
ok_atom
,
node_id
,
actor_addr
,
std
::
set
<
std
::
string
>>
::
or_else
<
error_atom
,
std
::
string
>>
;
using
del_op_promise
=
delegated
<
del_op_result
>
;
using
map_type
=
std
::
map
<
int64_t
,
response_promise
>
;
using
del_res
=
delegated
<
either
<
ok_atom
>::
or_else
<
error_atom
,
std
::
string
>>
;
behavior_type
make_behavior
()
override
{
return
{
[
=
](
put_atom
,
uint16_t
port
,
const
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
,
const
std
::
string
&
addr
,
bool
reuse_addr
)
{
return
put
(
port
,
whom
,
sigs
,
addr
.
c_str
(),
reuse_addr
);
},
[
=
](
put_atom
,
uint16_t
port
,
const
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
,
const
std
::
string
&
addr
)
{
return
put
(
port
,
whom
,
sigs
,
addr
.
c_str
());
[
=
](
publish_atom
,
uint16_t
port
,
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
,
std
::
string
&
addr
,
bool
reuse
)
{
return
put
(
port
,
whom
,
sigs
,
addr
.
c_str
(),
reuse
);
},
[
=
](
put_atom
,
uint16_t
port
,
const
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
,
bool
reuse_addr
)
{
return
put
(
port
,
whom
,
sigs
,
nullptr
,
reuse_addr
);
[
=
](
open_atom
,
uint16_t
port
,
std
::
string
&
addr
,
bool
reuse
)
->
put_res
{
actor_addr
whom
=
invalid_actor_addr
;
std
::
set
<
std
::
string
>
sigs
;
return
put
(
port
,
whom
,
sigs
,
addr
.
c_str
(),
reuse
);
},
[
=
](
put_atom
,
uint16_t
port
,
const
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
)
{
return
put
(
port
,
whom
,
sigs
);
},
[
=
](
get_atom
,
const
std
::
string
&
hostname
,
uint16_t
port
,
std
::
set
<
std
::
string
>&
expected_ifs
)
{
return
get
(
hostname
,
port
,
std
::
move
(
expected_ifs
));
[
=
](
connect_atom
,
const
std
::
string
&
hostname
,
uint16_t
port
)
->
get_res
{
CAF_LOG_TRACE
(
CAF_ARG
(
hostname
)
<<
", "
<<
CAF_ARG
(
port
));
try
{
auto
hdl
=
parent_
.
backend
().
new_tcp_scribe
(
hostname
,
port
);
delegate
(
broker_
,
connect_atom
::
value
,
hdl
,
port
);
}
catch
(
network_error
&
err
)
{
// fullfil promise immediately
std
::
string
msg
=
"network_error: "
;
msg
+=
err
.
what
();
auto
rp
=
make_response_promise
();
rp
.
deliver
(
make_message
(
error_atom
::
value
,
std
::
move
(
msg
)));
}
return
{};
},
[
=
](
get_atom
,
const
std
::
string
&
hostname
,
uint16_t
port
)
{
return
get
(
hostname
,
port
,
std
::
set
<
std
::
string
>
());
[
=
](
unpublish_atom
,
const
actor_addr
&
,
uint16_t
)
->
del_res
{
forward_current_message
(
broker_
);
return
{};
},
[
=
](
delete_atom
,
const
actor_addr
&
whom
)
{
return
del
(
whom
);
[
=
](
close_atom
,
uint16_t
)
->
del_res
{
forward_current_message
(
broker_
);
return
{};
},
[
=
](
delete_atom
,
const
actor_addr
&
whom
,
uint16_t
port
)
{
return
del
(
whom
,
port
);
[
=
](
spawn_atom
,
const
node_id
&
,
const
std
::
string
&
,
const
message
&
)
->
delegated
<
either
<
ok_atom
,
actor_addr
,
std
::
set
<
std
::
string
>>
::
or_else
<
error_atom
,
std
::
string
>>
{
forward_current_message
(
broker_
);
return
{};
}
};
}
private:
either
<
ok_atom
,
uint16_t
>::
or_else
<
error_atom
,
std
::
string
>
put
(
uint16_t
port
,
const
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
,
const
char
*
in
=
nullptr
,
bool
reuse_addr
=
false
)
{
CAF_LOG_TRACE
(
CAF_TSARG
(
whom
)
<<
", "
<<
CAF_ARG
(
port
)
<<
", "
<<
CAF_ARG
(
reuse_addr
));
put_res
put
(
uint16_t
port
,
actor_addr
&
whom
,
std
::
set
<
std
::
string
>&
sigs
,
const
char
*
in
=
nullptr
,
bool
reuse_addr
=
false
)
{
CAF_LOG_TRACE
(
CAF_TSARG
(
whom
)
<<
", "
<<
CAF_ARG
(
port
)
<<
", "
<<
CAF_ARG
(
reuse_addr
));
accept_handle
hdl
;
uint16_t
actual_port
;
try
{
...
...
@@ -225,66 +214,16 @@ private:
catch
(
network_error
&
err
)
{
return
{
error_atom
::
value
,
std
::
string
(
"network_error: "
)
+
err
.
what
()};
}
send
(
broker_
,
put_atom
::
value
,
hdl
,
actual_port
,
whom
,
std
::
move
(
sigs
));
send
(
broker_
,
publish_atom
::
value
,
hdl
,
actual_port
,
std
::
move
(
whom
),
std
::
move
(
sigs
));
return
{
ok_atom
::
value
,
actual_port
};
}
get_op_promise
get
(
const
std
::
string
&
hostname
,
uint16_t
port
,
std
::
set
<
std
::
string
>
expected_ifs
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
hostname
)
<<
", "
<<
CAF_ARG
(
port
));
get_op_promise
result
;
try
{
auto
hdl
=
parent_
.
backend
().
new_tcp_scribe
(
hostname
,
port
);
delegate
(
broker_
,
get_atom
::
value
,
hdl
,
port
,
std
::
move
(
expected_ifs
));
}
catch
(
network_error
&
err
)
{
// fullfil promise immediately
std
::
string
msg
=
"network_error: "
;
msg
+=
err
.
what
();
result
=
make_response_promise
();
result
.
deliver
(
get_op_result
{
error_atom
::
value
,
std
::
move
(
msg
)});
}
return
result
;
}
del_op_promise
del
(
const
actor_addr
&
whom
,
uint16_t
port
=
0
)
{
CAF_LOG_TRACE
(
CAF_TSARG
(
whom
)
<<
", "
<<
CAF_ARG
(
port
));
delegate
(
broker_
,
delete_atom
::
value
,
whom
,
port
);
return
{};
}
template
<
class
T
,
class
...
Ts
>
void
handle_ok
(
map_type
&
storage
,
int64_t
request_id
,
Ts
&&
...
xs
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
request_id
));
auto
i
=
storage
.
find
(
request_id
);
if
(
i
==
storage
.
end
())
{
CAF_LOG_ERROR
(
"request id not found: "
<<
request_id
);
return
;
}
i
->
second
.
deliver
(
T
{
ok_atom
::
value
,
std
::
forward
<
Ts
>
(
xs
)...}.
value
);
storage
.
erase
(
i
);
}
template
<
class
F
>
bool
finalize_request
(
map_type
&
storage
,
int64_t
req_id
,
F
fun
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
req_id
));
auto
i
=
storage
.
find
(
req_id
);
if
(
i
==
storage
.
end
())
{
CAF_LOG_INFO
(
"request ID not found in storage"
);
return
false
;
}
fun
(
i
->
second
);
storage
.
erase
(
i
);
return
true
;
}
actor
broker_
;
middleman
&
parent_
;
};
middleman_actor_impl
::~
middleman_actor_impl
()
{
CAF_LOG_TRACE
(
""
);
}
}
// namespace <anonymous>
middleman
*
middleman
::
instance
()
{
CAF_LOGF_TRACE
(
""
);
...
...
@@ -319,6 +258,7 @@ void middleman::initialize() {
}
// announce io-related types
announce
<
network
::
protocol
>
(
"caf::io::network::protocol"
);
announce
<
network
::
address_listing
>
(
"caf::io::network::address_listing"
);
do_announce
<
new_data_msg
>
(
"caf::io::new_data_msg"
);
do_announce
<
new_connection_msg
>
(
"caf::io::new_connection_msg"
);
do_announce
<
acceptor_closed_msg
>
(
"caf::io::acceptor_closed_msg"
);
...
...
libcaf_io/src/publish.cpp
View file @
dd49a57d
...
...
@@ -53,7 +53,7 @@ uint16_t publish_impl(uint16_t port, actor_addr whom,
uint16_t
result
;
std
::
string
error_msg
;
try
{
self
->
sync_send
(
mm
,
pu
t
_atom
::
value
,
port
,
self
->
sync_send
(
mm
,
pu
blish
_atom
::
value
,
port
,
std
::
move
(
whom
),
std
::
move
(
sigs
),
str
,
ru
).
await
(
[
&
](
ok_atom
,
uint16_t
res
)
{
result
=
res
;
...
...
libcaf_io/src/remote_actor.cpp
View file @
dd49a57d
...
...
@@ -42,14 +42,20 @@
namespace
caf
{
namespace
io
{
a
bstract_actor_pt
r
remote_actor_impl
(
std
::
set
<
std
::
string
>
ifs
,
const
std
::
string
&
host
,
uint16_t
port
)
{
a
ctor_add
r
remote_actor_impl
(
std
::
set
<
std
::
string
>
ifs
,
std
::
string
host
,
uint16_t
port
)
{
auto
mm
=
get_middleman_actor
();
actor_addr
result
;
scoped_actor
self
;
abstract_actor_ptr
result
;
self
->
sync_send
(
mm
,
get_atom
{},
std
::
move
(
host
),
port
,
std
::
move
(
ifs
)).
await
(
[
&
](
ok_atom
,
actor_addr
res
)
{
result
=
actor_cast
<
abstract_actor_ptr
>
(
res
);
self
->
sync_send
(
mm
,
connect_atom
::
value
,
std
::
move
(
host
),
port
).
await
(
[
&
](
ok_atom
,
const
node_id
&
,
actor_addr
res
,
std
::
set
<
std
::
string
>&
xs
)
{
if
(
!
res
)
throw
network_error
(
"no actor published at given port"
);
if
(
!
(
xs
.
empty
()
&&
ifs
.
empty
())
&&
!
std
::
includes
(
xs
.
begin
(),
xs
.
end
(),
ifs
.
begin
(),
ifs
.
end
()))
throw
network_error
(
"expected signature does not "
"comply to found signature"
);
result
=
std
::
move
(
res
);
},
[
&
](
error_atom
,
std
::
string
&
msg
)
{
throw
network_error
(
std
::
move
(
msg
));
...
...
@@ -60,4 +66,3 @@ abstract_actor_ptr remote_actor_impl(std::set<std::string> ifs,
}
// namespace io
}
// namespace caf
libcaf_io/src/scribe.cpp
View file @
dd49a57d
...
...
@@ -61,9 +61,11 @@ void scribe::consume(const void*, size_t num_bytes) {
buf
.
resize
(
num_bytes
);
read_msg
().
buf
.
swap
(
buf
);
parent
()
->
invoke_message
(
invalid_actor_addr
,
invalid_message_id
,
read_msg_
);
if
(
!
read_msg_
.
empty
())
{
// swap buffer back to stream and implicitly flush wr_buf()
read_msg
().
buf
.
swap
(
buf
);
flush
();
}
}
void
scribe
::
io_failure
(
network
::
operation
op
)
{
...
...
libcaf_io/src/unpublish.cpp
View file @
dd49a57d
...
...
@@ -34,7 +34,7 @@ void unpublish_impl(const actor_addr& whom, uint16_t port, bool blocking) {
auto
mm
=
get_middleman_actor
();
if
(
blocking
)
{
scoped_actor
self
;
self
->
sync_send
(
mm
,
delete
_atom
::
value
,
whom
,
port
).
await
(
self
->
sync_send
(
mm
,
unpublish
_atom
::
value
,
whom
,
port
).
await
(
[](
ok_atom
)
{
// ok, basp_broker is done
},
...
...
@@ -43,7 +43,7 @@ void unpublish_impl(const actor_addr& whom, uint16_t port, bool blocking) {
}
);
}
else
{
anon_send
(
mm
,
delete
_atom
::
value
,
whom
,
port
);
anon_send
(
mm
,
unpublish
_atom
::
value
,
whom
,
port
);
}
}
...
...
libcaf_io/test/automatic_connection.cpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE io_automatic_connection
#include "caf/test/unit_test.hpp"
#include <set>
#include <thread>
#include <vector>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
#include "caf/experimental/whereis.hpp"
#include "caf/io/network/interfaces.hpp"
#include "caf/io/network/test_multiplexer.hpp"
#include "caf/detail/run_program.hpp"
using
namespace
caf
;
using
namespace
caf
::
io
;
using
namespace
caf
::
experimental
;
using
std
::
string
;
using
ping_atom
=
atom_constant
<
atom
(
"ping"
)
>
;
using
pong_atom
=
atom_constant
<
atom
(
"pong"
)
>
;
/*
This test checks whether automatic connections work as expected
by first connecting three nodes "in line". In step 2, we send a
message across the line, forcing the nodes to build a mesh. In step 3,
we disconnect the node that originally connected the other two and expect
that the other two nodes communicate uninterrupted.
1) Initial setup:
Earth ---- Mars ---- Jupiter
2) After Jupiter has send a message to Earth:
Earth ---- Mars
\ /
\ /
\ /
Jupiter
3) After Earth has received the message and disconnected Mars:
Earth ---- Jupiter
*/
std
::
thread
run_prog
(
const
char
*
arg
,
uint16_t
port
,
bool
use_asio
)
{
return
detail
::
run_program
(
invalid_actor
,
caf
::
test
::
engine
::
path
(),
"-n"
,
"-s"
,
CAF_XSTR
(
CAF_SUITE
),
"--"
,
arg
,
"-p"
,
port
,
(
use_asio
?
"--use-asio"
:
""
));
}
// we run the same code on all three nodes, a simple ping-pong client
struct
testee_state
{
std
::
set
<
actor
>
buddies
;
uint16_t
port
=
0
;
const
char
*
name
=
"testee"
;
};
behavior
testee
(
stateful_actor
<
testee_state
>*
self
)
{
return
{
[
self
](
ping_atom
,
actor
buddy
,
bool
please_broadcast
)
->
message
{
if
(
please_broadcast
)
for
(
auto
&
x
:
self
->
state
.
buddies
)
if
(
x
!=
buddy
)
send_as
(
buddy
,
x
,
ping_atom
::
value
,
buddy
,
false
);
self
->
state
.
buddies
.
emplace
(
std
::
move
(
buddy
));
return
make_message
(
pong_atom
::
value
,
self
);
},
[
self
](
pong_atom
,
actor
buddy
)
{
self
->
state
.
buddies
.
emplace
(
std
::
move
(
buddy
));
},
[
self
](
put_atom
,
uint16_t
new_port
)
{
self
->
state
.
port
=
new_port
;
},
[
self
](
get_atom
)
{
return
self
->
state
.
port
;
}
};
}
void
run_earth
(
bool
use_asio
,
bool
as_server
,
uint16_t
pub_port
)
{
scoped_actor
self
;
struct
captain
:
hook
{
public:
captain
(
actor
parent
)
:
parent_
(
std
::
move
(
parent
))
{
// nop
}
void
new_connection_established_cb
(
const
node_id
&
node
)
override
{
anon_send
(
parent_
,
put_atom
::
value
,
node
);
call_next
<
hook
::
new_connection_established
>
(
node
);
}
void
new_remote_actor_cb
(
const
actor_addr
&
addr
)
override
{
anon_send
(
parent_
,
put_atom
::
value
,
addr
);
call_next
<
hook
::
new_remote_actor
>
(
addr
);
}
void
connection_lost_cb
(
const
node_id
&
dest
)
override
{
anon_send
(
parent_
,
delete_atom
::
value
,
dest
);
}
private:
actor
parent_
;
};
middleman
::
instance
()
->
add_hook
<
captain
>
(
self
);
auto
aut
=
spawn
(
testee
);
auto
port
=
publish
(
aut
,
pub_port
);
CAF_TEST_VERBOSE
(
"published testee at port "
<<
port
);
std
::
thread
mars_process
;
std
::
thread
jupiter_process
;
// launch process for Mars
if
(
!
as_server
)
{
CAF_TEST_VERBOSE
(
"launch process for Mars"
);
mars_process
=
run_prog
(
"--mars"
,
port
,
use_asio
);
}
CAF_TEST_VERBOSE
(
"wait for Mars to connect"
);
node_id
mars
;
self
->
receive
(
[
&
](
put_atom
,
const
node_id
&
nid
)
{
mars
=
nid
;
CAF_TEST_VERBOSE
(
CAF_TSARG
(
mars
));
}
);
actor_addr
mars_addr
;
uint16_t
mars_port
;
self
->
receive_while
([
&
]
{
return
mars_addr
==
invalid_actor_addr
;
})(
[
&
](
put_atom
,
const
actor_addr
&
addr
)
{
auto
hdl
=
actor_cast
<
actor
>
(
addr
);
self
->
sync_send
(
hdl
,
sys_atom
::
value
,
get_atom
::
value
,
"info"
).
await
(
[
&
](
ok_atom
,
const
string
&
,
const
actor_addr
&
,
const
string
&
name
)
{
if
(
name
!=
"testee"
)
return
;
mars_addr
=
addr
;
CAF_TEST_VERBOSE
(
CAF_TSARG
(
mars_addr
));
self
->
sync_send
(
actor_cast
<
actor
>
(
mars_addr
),
get_atom
::
value
).
await
(
[
&
](
uint16_t
mp
)
{
CAF_TEST_VERBOSE
(
"mars published its actor at port "
<<
mp
);
mars_port
=
mp
;
}
);
}
);
}
);
// launch process for Jupiter
if
(
!
as_server
)
{
CAF_TEST_VERBOSE
(
"launch process for Jupiter"
);
jupiter_process
=
run_prog
(
"--jupiter"
,
mars_port
,
use_asio
);
}
CAF_TEST_VERBOSE
(
"wait for Jupiter to connect"
);
self
->
receive
(
[](
put_atom
,
const
node_id
&
jupiter
)
{
CAF_TEST_VERBOSE
(
CAF_TSARG
(
jupiter
));
}
);
actor_addr
jupiter_addr
;
self
->
receive_while
([
&
]
{
return
jupiter_addr
==
invalid_actor_addr
;
})(
[
&
](
put_atom
,
const
actor_addr
&
addr
)
{
auto
hdl
=
actor_cast
<
actor
>
(
addr
);
self
->
sync_send
(
hdl
,
sys_atom
::
value
,
get_atom
::
value
,
"info"
).
await
(
[
&
](
ok_atom
,
const
string
&
,
const
actor_addr
&
,
const
string
&
name
)
{
if
(
name
!=
"testee"
)
return
;
jupiter_addr
=
addr
;
CAF_TEST_VERBOSE
(
CAF_TSARG
(
jupiter_addr
));
}
);
}
);
CAF_TEST_VERBOSE
(
"shutdown Mars"
);
anon_send_exit
(
mars_addr
,
exit_reason
::
kill
);
if
(
mars_process
.
joinable
())
mars_process
.
join
();
self
->
receive
(
[
&
](
delete_atom
,
const
node_id
&
nid
)
{
CAF_CHECK_EQUAL
(
nid
,
mars
);
}
);
CAF_TEST_VERBOSE
(
"check whether we still can talk to Jupiter"
);
self
->
send
(
aut
,
ping_atom
::
value
,
self
,
true
);
std
::
set
<
actor_addr
>
found
;
int
i
=
0
;
self
->
receive_for
(
i
,
2
)(
[
&
](
pong_atom
,
const
actor
&
)
{
found
.
emplace
(
self
->
current_sender
());
}
);
std
::
set
<
actor_addr
>
expected
{
aut
.
address
(),
jupiter_addr
};
CAF_CHECK_EQUAL
(
found
,
expected
);
CAF_TEST_VERBOSE
(
"shutdown Jupiter"
);
anon_send_exit
(
jupiter_addr
,
exit_reason
::
kill
);
if
(
jupiter_process
.
joinable
())
jupiter_process
.
join
();
anon_send_exit
(
aut
,
exit_reason
::
kill
);
}
void
run_mars
(
uint16_t
port_to_earth
,
uint16_t
pub_port
)
{
auto
aut
=
spawn
(
testee
);
auto
port
=
publish
(
aut
,
pub_port
);
anon_send
(
aut
,
put_atom
::
value
,
port
);
CAF_TEST_VERBOSE
(
"published testee at port "
<<
port
);
auto
earth
=
remote_actor
(
"localhost"
,
port_to_earth
);
send_as
(
aut
,
earth
,
ping_atom
::
value
,
aut
,
false
);
}
void
run_jupiter
(
uint16_t
port_to_mars
)
{
auto
aut
=
spawn
(
testee
);
auto
mars
=
remote_actor
(
"localhost"
,
port_to_mars
);
send_as
(
aut
,
mars
,
ping_atom
::
value
,
aut
,
true
);
}
CAF_TEST
(
triangle_setup
)
{
uint16_t
port
=
0
;
uint16_t
publish_port
=
0
;
auto
argv
=
caf
::
test
::
engine
::
argv
();
auto
argc
=
caf
::
test
::
engine
::
argc
();
auto
r
=
message_builder
(
argv
,
argv
+
argc
).
extract_opts
({
{
"port,p"
,
"port of remote side (when running mars or jupiter)"
,
port
},
{
"mars"
,
"run mars"
},
{
"jupiter"
,
"run jupiter"
},
{
"use-asio"
,
"use ASIO network backend (if available)"
},
{
"server,s"
,
"run in server mode (don't run clients)"
,
publish_port
}
});
// check arguments
bool
is_mars
=
r
.
opts
.
count
(
"mars"
)
>
0
;
bool
is_jupiter
=
r
.
opts
.
count
(
"jupiter"
)
>
0
;
bool
has_port
=
r
.
opts
.
count
(
"port"
)
>
0
;
if
(((
is_mars
||
is_jupiter
)
&&
!
has_port
)
||
(
is_mars
&&
is_jupiter
))
{
CAF_TEST_ERROR
(
"need a port when running Mars or Jupiter and cannot "
"both at the same time"
);
return
;
}
auto
use_asio
=
r
.
opts
.
count
(
"use-asio"
)
>
0
;
# ifdef CAF_USE_ASIO
if
(
use_asio
)
{
CAF_MESSAGE
(
"enable ASIO backend"
);
set_middleman
<
network
::
asio_multiplexer
>
();
}
# endif // CAF_USE_ASIO
// enable automatic connections
anon_send
(
whereis
(
atom
(
"ConfigServ"
)),
put_atom
::
value
,
"global.enable-automatic-connections"
,
make_message
(
true
));
auto
as_server
=
r
.
opts
.
count
(
"server"
)
>
0
;
if
(
is_mars
)
run_mars
(
port
,
publish_port
);
else
if
(
is_jupiter
)
run_jupiter
(
port
);
else
run_earth
(
use_asio
,
as_server
,
publish_port
);
await_all_actors_done
();
shutdown
();
}
libcaf_io/test/basp.cpp
View file @
dd49a57d
...
...
@@ -25,17 +25,59 @@
#include <array>
#include <mutex>
#include <memory>
#include <limits>
#include <iostream>
#include <condition_variable>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
#include "caf/experimental/whereis.hpp"
#include "caf/io/network/interfaces.hpp"
#include "caf/io/network/test_multiplexer.hpp"
namespace
std
{
ostream
&
operator
<<
(
ostream
&
out
,
const
caf
::
io
::
basp
::
message_type
&
x
)
{
return
out
<<
to_string
(
x
);
}
template
<
class
T
>
ostream
&
operator
<<
(
ostream
&
out
,
const
caf
::
variant
<
caf
::
anything
,
T
>&
x
)
{
using
std
::
to_string
;
using
caf
::
to_string
;
using
caf
::
io
::
basp
::
to_string
;
if
(
get
<
caf
::
anything
>
(
&
x
)
!=
nullptr
)
return
out
<<
"*"
;
return
out
<<
to_string
(
get
<
T
>
(
x
));
}
}
// namespace std
namespace
caf
{
template
<
class
T
>
bool
operator
==
(
const
variant
<
anything
,
T
>&
x
,
const
T
&
y
)
{
return
get
<
anything
>
(
&
x
)
!=
nullptr
||
get
<
T
>
(
x
)
==
y
;
}
template
<
class
T
>
bool
operator
==
(
const
T
&
x
,
const
variant
<
anything
,
T
>&
y
)
{
return
(
y
==
x
);
}
template
<
class
T
>
std
::
string
to_string
(
const
variant
<
anything
,
T
>&
x
)
{
return
get
<
anything
>
(
&
x
)
==
nullptr
?
std
::
string
{
"*"
}
:
to_string
(
get
<
T
>
(
x
));
}
}
// namespace caf
using
namespace
std
;
using
namespace
caf
;
using
namespace
caf
::
io
;
using
namespace
caf
::
experimental
;
namespace
{
...
...
@@ -71,9 +113,12 @@ public:
auto
mm
=
middleman
::
instance
();
aut_
=
mm
->
get_named_broker
<
basp_broker
>
(
atom
(
"_BASP"
));
this_node_
=
detail
::
singletons
::
get_node_id
();
CAF_TEST_VERBOSE
(
"this node: "
<<
to_string
(
this_node_
));
self_
.
reset
(
new
scoped_actor
);
// run the initialization message of the BASP broker
mpx_
->
exec_runnable
();
// handle the message from the configuration server
mpx
()
->
exec_runnable
();
ahdl_
=
accept_handle
::
from_int
(
1
);
mpx_
->
assign_tcp_doorman
(
aut_
.
get
(),
ahdl_
);
registry_
=
detail
::
singletons
::
get_actor_registry
();
...
...
@@ -179,6 +224,22 @@ public:
using
payload_writer
=
basp
::
instance
::
payload_writer
;
void
to_payload
(
binary_serializer
&
)
{
// end of recursion
}
template
<
class
T
,
class
...
Ts
>
void
to_payload
(
binary_serializer
&
bs
,
const
T
&
x
,
const
Ts
&
...
xs
)
{
bs
<<
x
;
to_payload
(
bs
,
xs
...);
}
template
<
class
...
Ts
>
void
to_payload
(
buffer
&
buf
,
const
Ts
&
...
xs
)
{
binary_serializer
bs
{
std
::
back_inserter
(
buf
),
&
get_namespace
()};
to_payload
(
bs
,
xs
...);
}
void
to_buf
(
buffer
&
buf
,
basp
::
header
&
hdr
,
payload_writer
*
writer
)
{
instance
().
write
(
buf
,
hdr
,
writer
);
}
...
...
@@ -229,16 +290,24 @@ public:
invalid_actor_id
,
invalid_actor_id
});
if
(
published_actor_id
!=
invalid_actor_id
)
m
.
expect
(
hdl
,
{
basp
::
message_type
::
server_handshake
,
0
,
basp
::
version
,
this_node
(),
invalid_node_id
,
published_actor_id
,
invalid_actor_id
}
,
basp
::
message_type
::
server_handshake
,
any_vals
,
basp
::
version
,
this_node
(),
node_id
{
invalid_node_id
}
,
published_actor_id
,
invalid_actor_id
,
published_actor_id
,
published_actor_ifs
);
else
m
.
expect
(
hdl
,
{
basp
::
message_type
::
server_handshake
,
0
,
basp
::
version
,
this_node
(),
invalid_node_id
,
invalid_actor_id
,
invalid_actor_id
});
basp
::
message_type
::
server_handshake
,
any_vals
,
basp
::
version
,
this_node
(),
node_id
{
invalid_node_id
},
invalid_actor_id
,
invalid_actor_id
);
// upon receiving our client handshake, BASP will check
// whether there is a SpawnServ actor on this node
m
.
expect
(
hdl
,
basp
::
message_type
::
dispatch_message
,
any_vals
,
static_cast
<
uint64_t
>
(
atom
(
"SpawnServ"
)),
this_node
(),
remote_node
(
i
),
any_vals
,
std
::
numeric_limits
<
actor_id
>::
max
(),
make_message
(
sys_atom
::
value
,
get_atom
::
value
,
"info"
));
// test whether basp instance correctly updates the
// routing table upon receiving client handshakes
auto
path
=
tbl
().
lookup
(
remote_node
(
i
));
...
...
@@ -289,29 +358,49 @@ public:
}
template
<
class
...
Ts
>
mock_t
&
expect
(
connection_handle
hdl
,
basp
::
header
hdr
,
const
Ts
&
...
xs
)
{
CAF_MESSAGE
(
"expect "
<<
num
++
<<
". sent message to be a "
<<
to_string
(
hdr
.
operation
));
mock_t
&
expect
(
connection_handle
hdl
,
variant
<
anything
,
basp
::
message_type
>
operation
,
variant
<
anything
,
uint32_t
>
payload_len
,
variant
<
anything
,
uint64_t
>
operation_data
,
variant
<
anything
,
node_id
>
source_node
,
variant
<
anything
,
node_id
>
dest_node
,
variant
<
anything
,
actor_id
>
source_actor
,
variant
<
anything
,
actor_id
>
dest_actor
,
const
Ts
&
...
xs
)
{
CAF_MESSAGE
(
"expect "
<<
num
<<
". sent message to be a "
<<
operation
);
buffer
buf
;
this_
->
to_
buf
(
buf
,
hdr
,
nullptr
,
xs
...);
this_
->
to_
payload
(
buf
,
xs
...);
buffer
&
ob
=
this_
->
mpx
()
->
output_buffer
(
hdl
);
CAF_REQUIRE
(
buf
.
size
()
<=
ob
.
size
());
auto
first
=
ob
.
begin
();
auto
last
=
ob
.
begin
()
+
static_cast
<
ptrdiff_t
>
(
buf
.
size
());
buffer
cpy
(
first
,
last
);
ob
.
erase
(
first
,
last
);
basp
::
header
tmp
;
CAF_TEST_VERBOSE
(
"output buffer has "
<<
ob
.
size
()
<<
" bytes"
);
CAF_REQUIRE
(
ob
.
size
()
>=
basp
::
header_size
);
basp
::
header
hdr
;
{
// lifetime scope of source
auto
source
=
this_
->
make_deserializer
(
cpy
);
basp
::
read_hdr
(
source
,
tmp
);
}
CAF_REQUIRE
(
to_string
(
hdr
)
==
to_string
(
tmp
));
CAF_REQUIRE
(
equal
(
buf
.
begin
(),
buf
.
begin
()
+
basp
::
header_size
,
cpy
.
begin
()));
buf
.
erase
(
buf
.
begin
(),
buf
.
begin
()
+
basp
::
header_size
);
cpy
.
erase
(
cpy
.
begin
(),
cpy
.
begin
()
+
basp
::
header_size
);
CAF_REQUIRE
(
hdr
.
payload_len
==
buf
.
size
()
&&
cpy
.
size
()
==
buf
.
size
());
CAF_REQUIRE
(
hexstr
(
buf
)
==
hexstr
(
cpy
));
auto
source
=
this_
->
make_deserializer
(
ob
);
basp
::
read_hdr
(
source
,
hdr
);
}
buffer
payload
;
if
(
hdr
.
payload_len
>
0
)
{
CAF_REQUIRE
(
ob
.
size
()
>=
(
basp
::
header_size
+
hdr
.
payload_len
));
auto
first
=
ob
.
begin
()
+
basp
::
header_size
;
auto
end
=
first
+
hdr
.
payload_len
;
payload
.
assign
(
first
,
end
);
CAF_TEST_VERBOSE
(
"erase "
<<
std
::
distance
(
ob
.
begin
(),
end
)
<<
" bytes from output buffer"
);
ob
.
erase
(
ob
.
begin
(),
end
);
}
else
{
ob
.
erase
(
ob
.
begin
(),
ob
.
begin
()
+
basp
::
header_size
);
}
CAF_CHECK_EQUAL
(
operation
,
hdr
.
operation
);
CAF_CHECK_EQUAL
(
payload_len
,
hdr
.
payload_len
);
CAF_CHECK_EQUAL
(
operation_data
,
hdr
.
operation_data
);
CAF_CHECK_EQUAL
(
source_node
,
hdr
.
source_node
);
CAF_CHECK_EQUAL
(
dest_node
,
hdr
.
dest_node
);
CAF_CHECK_EQUAL
(
source_actor
,
hdr
.
source_actor
);
CAF_CHECK_EQUAL
(
dest_actor
,
hdr
.
dest_actor
);
CAF_REQUIRE
(
payload
.
size
()
==
buf
.
size
());
CAF_REQUIRE
(
hexstr
(
payload
)
==
hexstr
(
buf
));
++
num
;
return
*
this
;
}
...
...
@@ -358,14 +447,13 @@ CAF_TEST(empty_server_handshake) {
buffer
payload
;
std
::
tie
(
hdr
,
payload
)
=
from_buf
(
buf
);
basp
::
header
expected
{
basp
::
message_type
::
server_handshake
,
0
,
static_cast
<
uint32_t
>
(
payload
.
size
())
,
basp
::
version
,
this_node
(),
invalid_node_id
,
invalid_actor_id
,
invalid_actor_id
};
CAF_CHECK
(
basp
::
valid
(
hdr
));
CAF_CHECK
(
basp
::
is_handshake
(
hdr
));
CAF_CHECK
(
hdr
==
expected
);
CAF_CHECK
(
payload
.
empty
());
CAF_CHECK_EQUAL
(
to_string
(
hdr
),
to_string
(
expected
));
}
CAF_TEST
(
non_empty_server_handshake
)
{
...
...
@@ -392,9 +480,9 @@ CAF_TEST(client_handshake_and_dispatch) {
remote_node
(
0
),
this_node
(),
pseudo_remote
(
0
)
->
id
(),
self
()
->
id
()},
make_message
(
1
,
2
,
3
))
.
expect
(
remote_hdl
(
0
),
{
basp
::
message_type
::
announce_proxy_instance
,
0
,
0
,
basp
::
message_type
::
announce_proxy_instance
,
uint32_t
{
0
},
uint64_t
{
0
}
,
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
()}
);
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
()
);
// must've created a proxy for our remote actor
CAF_REQUIRE
(
get_namespace
().
count_proxies
(
remote_node
(
0
))
==
1
);
// must've send remote node a message that this proxy is monitored now
...
...
@@ -408,8 +496,8 @@ CAF_TEST(client_handshake_and_dispatch) {
},
THROW_ON_UNEXPECTED
(
self
())
);
// check for message forwarded by `forwarding_actor_proxy`
mpx
()
->
exec_runnable
();
// exec the message of our forwarding proxy
CAF_TEST_VERBOSE
(
"exec message of forwarding proxy"
);
mpx
()
->
exec_runnable
();
dispatch_out_buf
(
remote_hdl
(
0
));
// deserialize and send message from out buf
pseudo_remote
(
0
)
->
receive
(
[](
int
i
)
{
...
...
@@ -431,9 +519,9 @@ CAF_TEST(message_forwarding) {
invalid_actor_id
,
pseudo_remote
(
1
)
->
id
()},
msg
)
.
expect
(
remote_hdl
(
1
),
{
basp
::
message_type
::
dispatch_message
,
0
,
0
,
basp
::
message_type
::
dispatch_message
,
any_vals
,
uint64_t
{
0
}
,
remote_node
(
0
),
remote_node
(
1
),
invalid_actor_id
,
pseudo_remote
(
1
)
->
id
()}
,
invalid_actor_id
,
pseudo_remote
(
1
)
->
id
()
,
msg
);
}
...
...
@@ -449,13 +537,15 @@ CAF_TEST(remote_actor_and_send) {
CAF_MESSAGE
(
"self: "
<<
to_string
(
self
()
->
address
()));
mpx
()
->
provide_scribe
(
"localhost"
,
4242
,
remote_hdl
(
0
));
CAF_CHECK
(
mpx
()
->
pending_scribes
().
count
(
make_pair
(
"localhost"
,
4242
))
==
1
);
auto
mm
=
get_middleman_actor
();
auto
mm
1
=
get_middleman_actor
();
actor
result
;
auto
f
=
self
()
->
sync_send
(
mm
,
get_atom
::
value
,
"localhost"
,
uint16_t
{
4242
});
auto
f
=
self
()
->
sync_send
(
mm1
,
connect_atom
::
value
,
"localhost"
,
uint16_t
{
4242
});
mpx
()
->
exec_runnable
();
// process message in basp_broker
CAF_CHECK
(
mpx
()
->
pending_scribes
().
count
(
make_pair
(
"localhost"
,
4242
))
==
0
);
// build a fake server handshake containing the id of our first pseudo actor
CAF_TEST_VERBOSE
(
"server handshake => client handshake + proxy announcement"
);
auto
na
=
registry
()
->
named_actors
();
mock
(
remote_hdl
(
0
),
{
basp
::
message_type
::
server_handshake
,
0
,
basp
::
version
,
remote_node
(
0
),
invalid_node_id
,
...
...
@@ -463,22 +553,30 @@ CAF_TEST(remote_actor_and_send) {
pseudo_remote
(
0
)
->
id
(),
uint32_t
{
0
})
.
expect
(
remote_hdl
(
0
),
{
basp
::
message_type
::
client_handshake
,
0
,
0
,
basp
::
message_type
::
client_handshake
,
uint32_t
{
0
},
uint64_t
{
0
}
,
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
invalid_actor_id
}
)
invalid_actor_id
,
invalid_actor_id
)
.
expect
(
remote_hdl
(
0
),
{
basp
::
message_type
::
announce_proxy_instance
,
0
,
0
,
basp
::
message_type
::
dispatch_message
,
any_vals
,
static_cast
<
uint64_t
>
(
atom
(
"SpawnServ"
)),
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
()});
any_vals
,
std
::
numeric_limits
<
actor_id
>::
max
(),
make_message
(
sys_atom
::
value
,
get_atom
::
value
,
"info"
))
.
expect
(
remote_hdl
(
0
),
basp
::
message_type
::
announce_proxy_instance
,
uint32_t
{
0
},
uint64_t
{
0
},
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
());
// basp broker should've send the proxy
f
.
await
(
[
&
](
ok_atom
,
actor_addr
re
s
)
{
[
&
](
ok_atom
,
node_id
nid
,
actor_addr
res
,
std
::
set
<
std
::
string
>
if
s
)
{
auto
aptr
=
actor_cast
<
abstract_actor_ptr
>
(
res
);
CAF_REQUIRE
(
aptr
.
downcast
<
forwarding_actor_proxy
>
()
!=
nullptr
);
CAF_CHECK
(
get_namespace
().
get_all
().
size
()
==
1
);
CAF_CHECK
(
get_namespace
().
count_proxies
(
remote_node
(
0
))
==
1
);
CAF_CHECK
(
nid
==
remote_node
(
0
));
CAF_CHECK
(
res
.
node
()
==
remote_node
(
0
));
CAF_CHECK
(
res
.
id
()
==
pseudo_remote
(
0
)
->
id
());
CAF_CHECK
(
ifs
.
empty
());
auto
proxy
=
get_namespace
().
get
(
remote_node
(
0
),
pseudo_remote
(
0
)
->
id
());
CAF_REQUIRE
(
proxy
!=
nullptr
);
CAF_REQUIRE
(
proxy
->
address
()
==
res
);
...
...
@@ -493,9 +591,9 @@ CAF_TEST(remote_actor_and_send) {
mpx
()
->
exec_runnable
();
// process forwarded message in basp_broker
mock
()
.
expect
(
remote_hdl
(
0
),
{
basp
::
message_type
::
dispatch_message
,
0
,
0
,
basp
::
message_type
::
dispatch_message
,
any_vals
,
uint64_t
{
0
}
,
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
()}
,
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
()
,
make_message
(
42
));
auto
msg
=
make_message
(
"hi there!"
);
CAF_MESSAGE
(
"send message via BASP (from proxy)"
);
...
...
@@ -527,9 +625,9 @@ CAF_TEST(actor_serialize_and_deserialize) {
auto
prx
=
get_namespace
().
get_or_put
(
remote_node
(
0
),
pseudo_remote
(
0
)
->
id
());
mock
()
.
expect
(
remote_hdl
(
0
),
{
basp
::
message_type
::
announce_proxy_instance
,
0
,
0
,
basp
::
message_type
::
announce_proxy_instance
,
uint32_t
{
0
},
uint64_t
{
0
}
,
this_node
(),
prx
->
node
(),
invalid_actor_id
,
prx
->
id
()}
);
invalid_actor_id
,
prx
->
id
()
);
CAF_CHECK
(
prx
->
node
()
==
remote_node
(
0
));
CAF_CHECK
(
prx
->
id
()
==
pseudo_remote
(
0
)
->
id
());
auto
testee
=
spawn
(
testee_impl
);
...
...
@@ -547,10 +645,150 @@ CAF_TEST(actor_serialize_and_deserialize) {
// output buffer must contain the reflected message
mock
()
.
expect
(
remote_hdl
(
0
),
{
basp
::
message_type
::
dispatch_message
,
0
,
0
,
basp
::
message_type
::
dispatch_message
,
any_vals
,
uint64_t
{
0
}
,
this_node
(),
prx
->
node
(),
testee
->
id
(),
prx
->
id
()}
,
testee
->
id
(),
prx
->
id
()
,
msg
);
}
CAF_TEST
(
indirect_connections
)
{
// jupiter [remote hdl 0] -> mars [remote hdl 1] -> earth [this_node]
// (this node receives a message from jupiter via mars and responds via mars)
CAF_MESSAGE
(
"self: "
<<
to_string
(
self
()
->
address
()));
auto
ax
=
accept_handle
::
from_int
(
4242
);
mpx
()
->
provide_acceptor
(
4242
,
ax
);
publish
(
self
(),
4242
);
mpx
()
->
exec_runnable
();
// process publish message in basp_broker
// connect to mars
connect_node
(
1
,
ax
,
self
()
->
id
());
// now, an actor from jupiter sends a message to us via mars
mock
(
remote_hdl
(
1
),
{
basp
::
message_type
::
dispatch_message
,
0
,
0
,
remote_node
(
0
),
this_node
(),
pseudo_remote
(
0
)
->
id
(),
self
()
->
id
()},
make_message
(
"hello from jupiter!"
))
// this asks Jupiter if it has a 'SpawnServ'
.
expect
(
remote_hdl
(
1
),
basp
::
message_type
::
dispatch_message
,
any_vals
,
static_cast
<
uint64_t
>
(
atom
(
"SpawnServ"
)),
this_node
(),
remote_node
(
0
),
any_vals
,
std
::
numeric_limits
<
actor_id
>::
max
(),
make_message
(
sys_atom
::
value
,
get_atom
::
value
,
"info"
))
// this tells Jupiter that Earth learned the address of one its actors
.
expect
(
remote_hdl
(
1
),
basp
::
message_type
::
announce_proxy_instance
,
uint32_t
{
0
},
uint64_t
{
0
},
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
());
CAF_MESSAGE
(
"receive message from jupiter"
);
self
()
->
receive
(
[](
const
std
::
string
&
str
)
->
std
::
string
{
CAF_CHECK
(
str
==
"hello from jupiter!"
);
return
"hello from earth!"
;
},
THROW_ON_UNEXPECTED
(
self
())
);
mpx
()
->
exec_runnable
();
// process forwarded message in basp_broker
mock
()
.
expect
(
remote_hdl
(
1
),
basp
::
message_type
::
dispatch_message
,
any_vals
,
uint64_t
{
0
},
this_node
(),
remote_node
(
0
),
self
()
->
id
(),
pseudo_remote
(
0
)
->
id
(),
make_message
(
"hello from earth!"
));
}
CAF_TEST
(
automatic_connection
)
{
// this tells our BASP broker to enable the automatic connection feature
anon_send
(
aut
(),
ok_atom
::
value
,
"global.enable-automatic-connections"
,
make_message
(
true
));
mpx
()
->
exec_runnable
();
// process publish message in basp_broker
// jupiter [remote hdl 0] -> mars [remote hdl 1] -> earth [this_node]
// (this node receives a message from jupiter via mars and responds via mars,
// but then also establishes a connection to jupiter directly)
mpx
()
->
provide_scribe
(
"jupiter"
,
8080
,
remote_hdl
(
0
));
CAF_CHECK
(
mpx
()
->
pending_scribes
().
count
(
make_pair
(
"jupiter"
,
8080
))
==
1
);
CAF_MESSAGE
(
"self: "
<<
to_string
(
self
()
->
address
()));
auto
ax
=
accept_handle
::
from_int
(
4242
);
mpx
()
->
provide_acceptor
(
4242
,
ax
);
publish
(
self
(),
4242
);
mpx
()
->
exec_runnable
();
// process publish message in basp_broker
// connect to mars
connect_node
(
1
,
ax
,
self
()
->
id
());
CAF_CHECK_EQUAL
(
tbl
().
lookup_direct
(
remote_node
(
1
)).
id
(),
remote_hdl
(
1
).
id
());
// now, an actor from jupiter sends a message to us via mars
mock
(
remote_hdl
(
1
),
{
basp
::
message_type
::
dispatch_message
,
0
,
0
,
remote_node
(
0
),
this_node
(),
pseudo_remote
(
0
)
->
id
(),
self
()
->
id
()},
make_message
(
"hello from jupiter!"
))
.
expect
(
remote_hdl
(
1
),
basp
::
message_type
::
dispatch_message
,
any_vals
,
static_cast
<
uint64_t
>
(
atom
(
"SpawnServ"
)),
this_node
(),
remote_node
(
0
),
any_vals
,
std
::
numeric_limits
<
actor_id
>::
max
(),
make_message
(
sys_atom
::
value
,
get_atom
::
value
,
"info"
))
.
expect
(
remote_hdl
(
1
),
basp
::
message_type
::
dispatch_message
,
any_vals
,
static_cast
<
uint64_t
>
(
atom
(
"ConfigServ"
)),
this_node
(),
remote_node
(
0
),
any_vals
,
// actor ID of an actor spawned by the BASP broker
std
::
numeric_limits
<
actor_id
>::
max
(),
make_message
(
get_atom
::
value
,
"basp.default-connectivity"
))
.
expect
(
remote_hdl
(
1
),
basp
::
message_type
::
announce_proxy_instance
,
uint32_t
{
0
},
uint64_t
{
0
},
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
pseudo_remote
(
0
)
->
id
());
CAF_CHECK_EQUAL
(
mpx
()
->
output_buffer
(
remote_hdl
(
1
)).
size
(),
0
);
CAF_CHECK_EQUAL
(
tbl
().
lookup_indirect
(
remote_node
(
0
)),
remote_node
(
1
));
CAF_CHECK_EQUAL
(
tbl
().
lookup_indirect
(
remote_node
(
1
)),
invalid_node_id
);
auto
connection_helper
=
abstract_actor
::
latest_actor_id
();
CAF_CHECK_EQUAL
(
mpx
()
->
output_buffer
(
remote_hdl
(
1
)).
size
(),
0
);
// create a dummy config server and respond to the name lookup
CAF_MESSAGE
(
"receive ConfigServ of jupiter"
);
network
::
address_listing
res
;
res
[
network
::
protocol
::
ipv4
].
push_back
(
"jupiter"
);
mock
(
remote_hdl
(
1
),
{
basp
::
message_type
::
dispatch_message
,
0
,
0
,
this_node
(),
this_node
(),
invalid_actor_id
,
connection_helper
},
make_message
(
ok_atom
::
value
,
"basp.default-connectivity"
,
make_message
(
uint16_t
{
8080
},
std
::
move
(
res
))));
// our connection helper should now connect to jupiter and
// send the scribe handle over to the BASP broker
mpx
()
->
exec_runnable
();
CAF_CHECK_EQUAL
(
mpx
()
->
output_buffer
(
remote_hdl
(
1
)).
size
(),
0
);
CAF_CHECK
(
mpx
()
->
pending_scribes
().
count
(
make_pair
(
"jupiter"
,
8080
))
==
0
);
// send handshake from jupiter
mock
(
remote_hdl
(
0
),
{
basp
::
message_type
::
server_handshake
,
0
,
basp
::
version
,
remote_node
(
0
),
invalid_node_id
,
pseudo_remote
(
0
)
->
id
(),
invalid_actor_id
},
pseudo_remote
(
0
)
->
id
(),
uint32_t
{
0
})
.
expect
(
remote_hdl
(
0
),
basp
::
message_type
::
client_handshake
,
uint32_t
{
0
},
uint64_t
{
0
},
this_node
(),
remote_node
(
0
),
invalid_actor_id
,
invalid_actor_id
);
CAF_CHECK_EQUAL
(
tbl
().
lookup_indirect
(
remote_node
(
0
)),
invalid_node_id
);
CAF_CHECK_EQUAL
(
tbl
().
lookup_indirect
(
remote_node
(
1
)),
invalid_node_id
);
CAF_CHECK_EQUAL
(
tbl
().
lookup_direct
(
remote_node
(
0
)).
id
(),
remote_hdl
(
0
).
id
());
CAF_CHECK_EQUAL
(
tbl
().
lookup_direct
(
remote_node
(
1
)).
id
(),
remote_hdl
(
1
).
id
());
CAF_MESSAGE
(
"receive message from jupiter"
);
self
()
->
receive
(
[](
const
std
::
string
&
str
)
->
std
::
string
{
CAF_CHECK
(
str
==
"hello from jupiter!"
);
return
"hello from earth!"
;
},
THROW_ON_UNEXPECTED
(
self
())
);
mpx
()
->
exec_runnable
();
// process forwarded message in basp_broker
CAF_MESSAGE
(
"response message must take direct route now"
);
mock
()
.
expect
(
remote_hdl
(
0
),
basp
::
message_type
::
dispatch_message
,
any_vals
,
uint64_t
{
0
},
this_node
(),
remote_node
(
0
),
self
()
->
id
(),
pseudo_remote
(
0
)
->
id
(),
make_message
(
"hello from earth!"
));
CAF_CHECK
(
mpx
()
->
output_buffer
(
remote_hdl
(
1
)).
size
()
==
0
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_io/test/remote_spawn.cpp
0 → 100644
View file @
dd49a57d
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE io_remote_spawn
#include "caf/test/unit_test.hpp"
#include <thread>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <functional>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
#include "caf/detail/run_program.hpp"
#include "caf/experimental/announce_actor_type.hpp"
#ifdef CAF_USE_ASIO
#include "caf/io/network/asio_multiplexer.hpp"
#endif // CAF_USE_ASIO
using
namespace
caf
;
using
namespace
caf
::
experimental
;
namespace
{
behavior
mirror
(
event_based_actor
*
self
)
{
return
{
others
>>
[
=
]
{
return
self
->
current_message
();
}
};
}
behavior
client
(
event_based_actor
*
self
,
actor
serv
)
{
self
->
send
(
serv
,
ok_atom
::
value
);
return
{
others
>>
[
=
]
{
CAF_TEST_ERROR
(
"unexpected message: "
<<
to_string
(
self
->
current_message
()));
}
};
}
struct
server_state
{
actor
client
;
// the spawn we connect to the server in our main
actor
aut
;
// our mirror
};
behavior
server
(
stateful_actor
<
server_state
>*
self
)
{
self
->
on_sync_failure
([
=
]
{
CAF_TEST_ERROR
(
"unexpected sync response: "
<<
to_string
(
self
->
current_message
()));
});
return
{
[
=
](
ok_atom
)
{
auto
s
=
self
->
current_sender
();
CAF_REQUIRE
(
s
!=
invalid_actor_addr
);
CAF_REQUIRE
(
s
.
is_remote
());
self
->
state
.
client
=
actor_cast
<
actor
>
(
s
);
auto
mm
=
io
::
get_middleman_actor
();
self
->
sync_send
(
mm
,
spawn_atom
::
value
,
s
.
node
(),
"mirror"
,
make_message
()).
then
(
[
=
](
ok_atom
,
const
actor_addr
&
addr
,
const
std
::
set
<
std
::
string
>&
ifs
)
{
CAF_REQUIRE
(
addr
!=
invalid_actor_addr
);
CAF_CHECK
(
ifs
.
empty
());
self
->
state
.
aut
=
actor_cast
<
actor
>
(
addr
);
self
->
send
(
self
->
state
.
aut
,
"hello mirror"
);
self
->
become
(
[
=
](
const
std
::
string
&
str
)
{
CAF_CHECK
(
self
->
current_sender
()
==
self
->
state
.
aut
);
CAF_CHECK
(
str
==
"hello mirror"
);
self
->
send_exit
(
self
->
state
.
aut
,
exit_reason
::
kill
);
self
->
send_exit
(
self
->
state
.
client
,
exit_reason
::
kill
);
self
->
quit
();
}
);
},
[
=
](
error_atom
,
const
std
::
string
&
errmsg
)
{
CAF_TEST_ERROR
(
"could not spawn mirror: "
<<
errmsg
);
}
);
}
};
}
}
// namespace <anonymous>
CAF_TEST
(
remote_spawn
)
{
announce_actor_type
(
"mirror"
,
mirror
);
auto
argv
=
caf
::
test
::
engine
::
argv
();
auto
argc
=
caf
::
test
::
engine
::
argc
();
uint16_t
port
=
0
;
auto
r
=
message_builder
(
argv
,
argv
+
argc
).
extract_opts
({
{
"server,s"
,
"run as server (don't run client"
},
{
"client,c"
,
"add client port (two needed)"
,
port
},
{
"port,p"
,
"force a port in server mode"
,
port
},
{
"use-asio"
,
"use ASIO network backend (if available)"
}
});
if
(
!
r
.
error
.
empty
()
||
r
.
opts
.
count
(
"help"
)
>
0
||
!
r
.
remainder
.
empty
())
{
std
::
cout
<<
r
.
error
<<
std
::
endl
<<
std
::
endl
<<
r
.
helptext
<<
std
::
endl
;
return
;
}
auto
use_asio
=
r
.
opts
.
count
(
"use-asio"
)
>
0
;
if
(
use_asio
)
{
# ifdef CAF_USE_ASIO
CAF_MESSAGE
(
"enable ASIO backend"
);
io
::
set_middleman
<
io
::
network
::
asio_multiplexer
>
();
# endif // CAF_USE_ASIO
}
if
(
r
.
opts
.
count
(
"client"
)
>
0
)
{
auto
serv
=
io
::
remote_actor
(
"localhost"
,
port
);
spawn
(
client
,
serv
);
await_all_actors_done
();
return
;
}
auto
serv
=
spawn
(
server
);
port
=
io
::
publish
(
serv
,
port
);
CAF_TEST_INFO
(
"published server at port "
<<
port
);
if
(
r
.
opts
.
count
(
"server"
)
==
0
)
{
auto
child
=
detail
::
run_program
(
invalid_actor
,
argv
[
0
],
"-n"
,
"-s"
,
CAF_XSTR
(
CAF_SUITE
),
"--"
,
"-c"
,
port
,
(
use_asio
?
"--use-asio"
:
""
));
child
.
join
();
}
await_all_actors_done
();
}
libcaf_io/test/uniform_type.cpp
View file @
dd49a57d
...
...
@@ -219,7 +219,8 @@ CAF_TEST(test_uniform_type) {
"caf::io::connection_closed_msg"
,
"caf::io::network::protocol"
,
"caf::io::new_connection_msg"
,
"caf::io::new_data_msg"
));
"caf::io::new_data_msg"
,
"caf::io::network::address_listing"
));
CAF_MESSAGE
(
"io types checked"
);
}
// check whether enums can be announced as members
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment