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
0e5a90e1
Unverified
Commit
0e5a90e1
authored
Mar 01, 2023
by
Noir
Committed by
GitHub
Mar 01, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into issue/1364
parents
61bf48f2
b3164d5b
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
814 additions
and
85 deletions
+814
-85
CHANGELOG.md
CHANGELOG.md
+9
-0
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+2
-0
libcaf_core/caf/async/blocking_consumer.hpp
libcaf_core/caf/async/blocking_consumer.hpp
+1
-1
libcaf_core/caf/detail/meta_object.hpp
libcaf_core/caf/detail/meta_object.hpp
+7
-2
libcaf_core/caf/detail/profiled_send.hpp
libcaf_core/caf/detail/profiled_send.hpp
+9
-7
libcaf_core/caf/flow/observable_decl.hpp
libcaf_core/caf/flow/observable_decl.hpp
+2
-1
libcaf_core/caf/flow/op/buffer.hpp
libcaf_core/caf/flow/op/buffer.hpp
+3
-2
libcaf_core/caf/mixin/sender.hpp
libcaf_core/caf/mixin/sender.hpp
+31
-26
libcaf_core/caf/response_handle.hpp
libcaf_core/caf/response_handle.hpp
+4
-2
libcaf_core/caf/scheduled_actor.hpp
libcaf_core/caf/scheduled_actor.hpp
+1
-1
libcaf_core/caf/typed_actor_view.hpp
libcaf_core/caf/typed_actor_view.hpp
+107
-0
libcaf_core/src/async/batch.cpp
libcaf_core/src/async/batch.cpp
+3
-3
libcaf_core/src/config_value.cpp
libcaf_core/src/config_value.cpp
+8
-9
libcaf_core/src/detail/meta_object.cpp
libcaf_core/src/detail/meta_object.cpp
+20
-2
libcaf_core/src/error.cpp
libcaf_core/src/error.cpp
+5
-7
libcaf_core/src/message.cpp
libcaf_core/src/message.cpp
+5
-7
libcaf_core/src/message_builder.cpp
libcaf_core/src/message_builder.cpp
+5
-5
libcaf_core/src/scheduled_actor.cpp
libcaf_core/src/scheduled_actor.cpp
+25
-0
libcaf_core/src/type_id.cpp
libcaf_core/src/type_id.cpp
+2
-2
libcaf_core/src/type_id_list.cpp
libcaf_core/src/type_id_list.cpp
+4
-6
libcaf_core/test/messaging.cpp
libcaf_core/test/messaging.cpp
+284
-0
libcaf_core/test/response_handle.cpp
libcaf_core/test/response_handle.cpp
+94
-2
libcaf_core/test/typed_actor_view.cpp
libcaf_core/test/typed_actor_view.cpp
+183
-0
No files found.
CHANGELOG.md
View file @
0e5a90e1
...
...
@@ -16,6 +16,14 @@ is based on [Keep a Changelog](https://keepachangelog.com).
unreachable if other actors no longer reference it.
-
Typed actors that use a
`typed_actor_pointer`
can now access the
`run_{delayed,scheduled}`
member functions.
-
Scheduled and delayed sends now return a disposable (#1362).
-
Typed response handles received support for converting them to observable or
single objects.
-
Typed actors that use the type-erased pointer-view type received access to the
new flow API functions (e.g.,
`make_observable`
).
-
Not initializing the meta objects table now prints a diagnosis message before
aborting the program. Previously, the application would usually just crash due
to a
`nullptr`
-access inside some CAF function.
### Fixed
...
...
@@ -32,6 +40,7 @@ is based on [Keep a Changelog](https://keepachangelog.com).
operators that use the
`ucast`
operator internally.
-
The
`mcast`
and
`ucast`
operators now stop calling
`on_next`
immediately when
disposed.
-
Actors no longer terminate despite having open streams (#1377).
-
Actors reading from external sources such as SPSC buffers via a local flow
could end up in a long-running read loop. To avoid potentially starving other
actors or activities, scheduled actors now limit the amount of actions that
...
...
libcaf_core/CMakeLists.txt
View file @
0e5a90e1
...
...
@@ -332,6 +332,7 @@ caf_add_component(
message_builder
message_id
message_lifetime
messaging
metaprogramming
mixin.requester
mixin.sender
...
...
@@ -369,6 +370,7 @@ caf_add_component(
thread_hook
tracing_data
type_id_list
typed_actor_view
typed_behavior
typed_message_view
typed_response_promise
...
...
libcaf_core/caf/async/blocking_consumer.hpp
View file @
0e5a90e1
...
...
@@ -179,7 +179,7 @@ public:
}
error
abort_reason
()
const
{
impl_
->
abort_reason
();
return
impl_
->
abort_reason
();
}
private:
...
...
libcaf_core/caf/detail/meta_object.hpp
View file @
0e5a90e1
...
...
@@ -64,8 +64,13 @@ CAF_CORE_EXPORT global_meta_objects_guard_type global_meta_objects_guard();
/// is the index for accessing the corresponding meta object.
CAF_CORE_EXPORT
span
<
const
meta_object
>
global_meta_objects
();
/// Returns the global meta object for given type ID.
CAF_CORE_EXPORT
const
meta_object
*
global_meta_object
(
type_id_t
id
);
/// Returns the global meta object for given type ID. Aborts the program if no
/// meta object exists for `id`.
CAF_CORE_EXPORT
const
meta_object
&
global_meta_object
(
type_id_t
id
);
/// Returns the global meta object for given type ID or `nullptr` if no meta
/// object exists for `id`.
CAF_CORE_EXPORT
const
meta_object
*
global_meta_object_or_null
(
type_id_t
id
);
/// Clears the array for storing global meta objects.
/// @warning intended for unit testing only!
...
...
libcaf_core/caf/detail/profiled_send.hpp
View file @
0e5a90e1
...
...
@@ -10,6 +10,7 @@
#include "caf/actor_clock.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/actor_profiler.hpp"
#include "caf/disposable.hpp"
#include "caf/fwd.hpp"
#include "caf/mailbox_element.hpp"
#include "caf/message_id.hpp"
...
...
@@ -33,22 +34,23 @@ void profiled_send(Self* self, SelfHandle&& src, const Handle& dst,
}
template
<
class
Self
,
class
SelfHandle
,
class
Handle
,
class
...
Ts
>
void
profiled_send
(
Self
*
self
,
SelfHandle
&&
src
,
const
Handle
&
dst
,
actor_clock
&
clock
,
actor_clock
::
time_point
timeout
,
[[
maybe_unused
]]
message_id
msg_id
,
Ts
&&
...
xs
)
{
disposable
profiled_send
(
Self
*
self
,
SelfHandle
&&
src
,
const
Handle
&
dst
,
actor_clock
&
clock
,
actor_clock
::
time_point
timeout
,
[[
maybe_unused
]]
message_id
msg_id
,
Ts
&&
...
xs
)
{
if
(
dst
)
{
if
constexpr
(
std
::
is_same
<
Handle
,
group
>::
value
)
{
clock
.
schedule_message
(
timeout
,
dst
,
std
::
forward
<
SelfHandle
>
(
src
),
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
return
clock
.
schedule_message
(
timeout
,
dst
,
std
::
forward
<
SelfHandle
>
(
src
),
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
}
else
{
auto
element
=
make_mailbox_element
(
std
::
forward
<
SelfHandle
>
(
src
),
msg_id
,
no_stages
,
std
::
forward
<
Ts
>
(
xs
)...);
CAF_BEFORE_SENDING_SCHEDULED
(
self
,
timeout
,
*
element
);
clock
.
schedule_message
(
timeout
,
actor_cast
<
strong_actor_ptr
>
(
dst
),
std
::
move
(
element
));
return
clock
.
schedule_message
(
timeout
,
actor_cast
<
strong_actor_ptr
>
(
dst
),
std
::
move
(
element
));
}
}
else
{
self
->
home_system
().
base_metrics
().
rejected_messages
->
inc
();
return
{};
}
}
...
...
libcaf_core/caf/flow/observable_decl.hpp
View file @
0e5a90e1
...
...
@@ -249,8 +249,9 @@ public:
async
::
consumer_resource
<
T
>
to_resource
()
{
return
to_resource
(
defaults
::
flow
::
buffer_size
,
defaults
::
flow
::
min_demand
);
}
const
observable
&
as_observable
()
const
&
noexcept
{
return
std
::
move
(
*
this
)
;
return
*
this
;
}
observable
&&
as_observable
()
&&
noexcept
{
...
...
libcaf_core/caf/flow/op/buffer.hpp
View file @
0e5a90e1
...
...
@@ -142,8 +142,9 @@ public:
void
fwd_on_complete
(
buffer_emit_t
)
{
control_sub_
=
nullptr
;
err_
=
make_error
(
sec
::
end_of_stream
,
"buffer: unexpected end of the control stream"
);
if
(
state_
==
state
::
running
)
err_
=
make_error
(
sec
::
end_of_stream
,
"buffer: unexpected end of the control stream"
);
shutdown
();
}
...
...
libcaf_core/caf/mixin/sender.hpp
View file @
0e5a90e1
...
...
@@ -99,7 +99,7 @@ public:
/// passed already).
template
<
message_priority
P
=
message_priority
::
normal
,
class
Dest
=
actor
,
class
...
Ts
>
detail
::
enable_if_t
<!
std
::
is_same
<
Dest
,
group
>::
value
>
detail
::
enable_if_t
<!
std
::
is_same
<
Dest
,
group
>::
value
,
disposable
>
scheduled_send
(
const
Dest
&
dest
,
actor_clock
::
time_point
timeout
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"no message to send"
);
...
...
@@ -109,15 +109,16 @@ public:
detail
::
type_list
<
detail
::
strip_and_convert_t
<
Ts
>
...
>
args_token
;
type_check
(
dest
,
args_token
);
auto
self
=
dptr
();
detail
::
profiled_send
(
self
,
self
->
ctrl
(),
dest
,
self
->
system
().
clock
(),
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
return
detail
::
profiled_send
(
self
,
self
->
ctrl
(),
dest
,
self
->
system
().
clock
(),
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
}
/// Sends a message at given time point (or immediately if `timeout` has
/// passed already).
template
<
class
...
Ts
>
void
scheduled_send
(
const
group
&
dest
,
actor_clock
::
time_point
timeout
,
Ts
&&
...
xs
)
{
disposable
scheduled_send
(
const
group
&
dest
,
actor_clock
::
time_point
timeout
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"no message to send"
);
static_assert
((
detail
::
sendable
<
Ts
>
&&
...),
"at least one type has no ID, "
...
...
@@ -128,17 +129,17 @@ public:
auto
self
=
dptr
();
if
(
dest
)
{
auto
&
clock
=
self
->
system
().
clock
();
clock
.
schedule_message
(
timeout
,
dest
,
self
->
ctrl
(),
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
}
else
{
self
->
home_system
().
base_metrics
().
rejected_messages
->
inc
();
return
clock
.
schedule_message
(
timeout
,
dest
,
self
->
ctrl
(),
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
}
self
->
home_system
().
base_metrics
().
rejected_messages
->
inc
();
return
{};
}
/// Sends a message after a relative timeout.
template
<
message_priority
P
=
message_priority
::
normal
,
class
Rep
=
int
,
class
Period
=
std
::
ratio
<
1
>,
class
Dest
=
actor
,
class
...
Ts
>
detail
::
enable_if_t
<!
std
::
is_same
<
Dest
,
group
>::
value
>
detail
::
enable_if_t
<!
std
::
is_same
<
Dest
,
group
>::
value
,
disposable
>
delayed_send
(
const
Dest
&
dest
,
std
::
chrono
::
duration
<
Rep
,
Period
>
rel_timeout
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"no message to send"
);
...
...
@@ -150,15 +151,16 @@ public:
auto
self
=
dptr
();
auto
&
clock
=
self
->
system
().
clock
();
auto
timeout
=
clock
.
now
()
+
rel_timeout
;
detail
::
profiled_send
(
self
,
self
->
ctrl
(),
dest
,
clock
,
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
return
detail
::
profiled_send
(
self
,
self
->
ctrl
(),
dest
,
clock
,
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
}
/// Sends a message after a relative timeout.
template
<
class
Rep
=
int
,
class
Period
=
std
::
ratio
<
1
>,
class
Dest
=
actor
,
class
...
Ts
>
void
delayed_send
(
const
group
&
dest
,
std
::
chrono
::
duration
<
Rep
,
Period
>
rtime
,
Ts
&&
...
xs
)
{
disposable
delayed_send
(
const
group
&
dest
,
std
::
chrono
::
duration
<
Rep
,
Period
>
rtime
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"no message to send"
);
static_assert
((
detail
::
sendable
<
Ts
>
&&
...),
"at least one type has no ID, "
...
...
@@ -166,19 +168,21 @@ public:
static_assert
(
!
statically_typed
<
Subtype
>
(),
"statically typed actors are not allowed to send to groups"
);
// TODO: consider whether it's feasible to track messages to groups
auto
self
=
dptr
();
if
(
dest
)
{
auto
self
=
dptr
();
auto
&
clock
=
self
->
system
().
clock
();
auto
timeout
=
clock
.
now
()
+
rtime
;
clock
.
schedule_message
(
timeout
,
dest
,
self
->
ctrl
(),
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
return
clock
.
schedule_message
(
timeout
,
dest
,
self
->
ctrl
(),
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
}
self
->
home_system
().
base_metrics
().
rejected_messages
->
inc
();
return
{};
}
template
<
message_priority
P
=
message_priority
::
normal
,
class
Dest
=
actor
,
class
...
Ts
>
void
scheduled_anon_send
(
const
Dest
&
dest
,
actor_clock
::
time_point
timeou
t
,
Ts
&&
...
xs
)
{
disposable
scheduled_anon_send
(
const
Dest
&
des
t
,
actor_clock
::
time_point
timeout
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"no message to send"
);
static_assert
((
detail
::
sendable
<
Ts
>
&&
...),
"at least one type has no ID, "
...
...
@@ -186,15 +190,16 @@ public:
detail
::
type_list
<
detail
::
strip_and_convert_t
<
Ts
>
...
>
args_token
;
type_check
(
dest
,
args_token
);
auto
self
=
dptr
();
detail
::
profiled_send
(
self
,
nullptr
,
dest
,
self
->
system
().
clock
(),
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
return
detail
::
profiled_send
(
self
,
nullptr
,
dest
,
self
->
system
().
clock
(),
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
}
template
<
message_priority
P
=
message_priority
::
normal
,
class
Dest
=
actor
,
class
Rep
=
int
,
class
Period
=
std
::
ratio
<
1
>,
class
...
Ts
>
void
delayed_anon_send
(
const
Dest
&
dest
,
std
::
chrono
::
duration
<
Rep
,
Period
>
rel_timeout
,
Ts
&&
...
xs
)
{
disposable
delayed_anon_send
(
const
Dest
&
dest
,
std
::
chrono
::
duration
<
Rep
,
Period
>
rel_timeout
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"no message to send"
);
static_assert
((
detail
::
sendable
<
Ts
>
&&
...),
"at least one type has no ID, "
...
...
@@ -204,8 +209,8 @@ public:
auto
self
=
dptr
();
auto
&
clock
=
self
->
system
().
clock
();
auto
timeout
=
clock
.
now
()
+
rel_timeout
;
detail
::
profiled_send
(
self
,
nullptr
,
dest
,
clock
,
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
return
detail
::
profiled_send
(
self
,
nullptr
,
dest
,
clock
,
timeout
,
make_message_id
(
P
),
std
::
forward
<
Ts
>
(
xs
)...);
}
private:
...
...
libcaf_core/caf/response_handle.hpp
View file @
0e5a90e1
...
...
@@ -107,13 +107,15 @@ public:
template
<
class
T
>
flow
::
assert_scheduled_actor_hdr_t
<
flow
::
single
<
T
>>
as_single
()
&&
{
static_assert
(
std
::
is_same_v
<
response_type
,
message
>
);
static_assert
(
std
::
is_same_v
<
response_type
,
detail
::
type_list
<
T
>>
||
std
::
is_same_v
<
response_type
,
message
>
);
return
self_
->
template
single_from_response
<
T
>(
policy_
);
}
template
<
class
T
>
flow
::
assert_scheduled_actor_hdr_t
<
flow
::
observable
<
T
>>
as_observable
()
&&
{
static_assert
(
std
::
is_same_v
<
response_type
,
message
>
);
static_assert
(
std
::
is_same_v
<
response_type
,
detail
::
type_list
<
T
>>
||
std
::
is_same_v
<
response_type
,
message
>
);
return
self_
->
template
single_from_response
<
T
>(
policy_
).
as_observable
();
}
...
...
libcaf_core/caf/scheduled_actor.hpp
View file @
0e5a90e1
...
...
@@ -709,7 +709,7 @@ public:
bool
alive
()
const
noexcept
{
return
!
bhvr_stack_
.
empty
()
||
!
awaited_responses_
.
empty
()
||
!
multiplexed_responses_
.
empty
()
||
!
watched_disposables_
.
empty
()
||
!
stream_sources_
.
empty
();
||
!
stream_sources_
.
empty
()
||
!
stream_bridges_
.
empty
()
;
}
/// Runs all pending actions.
...
...
libcaf_core/caf/typed_actor_view.hpp
View file @
0e5a90e1
...
...
@@ -8,12 +8,24 @@
#include "caf/config.hpp"
#include "caf/mixin/requester.hpp"
#include "caf/mixin/sender.hpp"
#include "caf/none.hpp"
#include "caf/scheduled_actor.hpp"
#include "caf/stream.hpp"
#include "caf/timespan.hpp"
#include "caf/typed_actor_view_base.hpp"
#include "caf/typed_stream.hpp"
namespace
caf
{
/// Utility function to force the type of `self` to depend on `T` and to raise a
/// compiler error if the user did not include 'caf/scheduled_actor/flow.hpp'.
/// The function itself does nothing and simply returns `self`.
template
<
class
T
>
auto
typed_actor_view_flow_access
(
caf
::
scheduled_actor
*
self
)
{
using
Self
=
flow
::
assert_scheduled_actor_hdr_t
<
T
,
caf
::
scheduled_actor
*>
;
return
static_cast
<
Self
>
(
self
);
}
/// Decorates a pointer to a @ref scheduled_actor with a statically typed actor
/// interface.
template
<
class
...
Sigs
>
...
...
@@ -296,6 +308,101 @@ public:
return
self_
;
}
// -- flow API ---------------------------------------------------------------
/// @copydoc flow::coordinator::make_observable
template
<
class
T
=
none_t
>
auto
make_observable
()
{
// Note: the template parameter T serves no purpose other than forcing the
// compiler to delay evaluation of this function body by having
// *something* to pass to `typed_actor_view_flow_access`.
auto
self
=
typed_actor_view_flow_access
<
T
>
(
self_
);
return
self
->
make_observable
();
}
/// @copydoc scheduled_actor::to_stream
template
<
class
Observable
>
auto
to_stream
(
cow_string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
,
Observable
&&
obs
)
{
auto
self
=
typed_actor_view_flow_access
<
Observable
>
(
self_
);
return
self
->
to_stream
(
std
::
move
(
name
),
max_delay
,
max_items_per_batch
,
std
::
forward
<
Observable
>
(
obs
));
}
/// @copydoc scheduled_actor::to_stream
template
<
class
Observable
>
auto
to_stream
(
std
::
string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
,
Observable
&&
obs
)
{
return
to_stream
(
cow_string
{
std
::
move
(
name
)},
max_delay
,
max_items_per_batch
,
std
::
forward
<
Observable
>
(
obs
));
}
/// Returns a function object for passing it to @c compose.
scheduled_actor
::
to_stream_t
to_stream
(
cow_string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
)
{
return
{
self_
,
std
::
move
(
name
),
max_delay
,
max_items_per_batch
};
}
/// Returns a function object for passing it to @c compose.
scheduled_actor
::
to_stream_t
to_stream
(
std
::
string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
)
{
return
to_stream
(
cow_string
{
std
::
move
(
name
)},
max_delay
,
max_items_per_batch
);
}
/// @copydoc scheduled_actor::to_typed_stream
template
<
class
Observable
>
auto
to_typed_stream
(
cow_string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
,
Observable
obs
)
{
auto
self
=
typed_actor_view_flow_access
<
Observable
>
(
self_
);
return
self
->
to_typed_stream
(
std
::
move
(
name
),
max_delay
,
max_items_per_batch
,
std
::
move
(
obs
));
}
/// @copydoc scheduled_actor::to_typed_stream
template
<
class
Observable
>
auto
to_typed_stream
(
std
::
string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
,
Observable
obs
)
{
return
to_typed_stream
(
cow_string
{
std
::
move
(
name
)},
max_delay
,
max_items_per_batch
,
std
::
move
(
obs
));
}
/// Returns a function object for passing it to @c compose.
scheduled_actor
::
to_typed_stream_t
to_typed_stream
(
cow_string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
)
{
return
{
self_
,
std
::
move
(
name
),
max_delay
,
max_items_per_batch
};
}
/// Returns a function object for passing it to @c compose.
scheduled_actor
::
to_typed_stream_t
to_typed_stream
(
std
::
string
name
,
timespan
max_delay
,
size_t
max_items_per_batch
)
{
return
to_typed_stream
(
cow_string
{
std
::
move
(
name
)},
max_delay
,
max_items_per_batch
);
}
/// @copydoc scheduled_actor::observe
template
<
class
T
>
auto
observe
(
typed_stream
<
T
>
what
,
size_t
buf_capacity
,
size_t
demand_threshold
)
{
auto
self
=
typed_actor_view_flow_access
<
T
>
(
self_
);
return
self
->
observe
(
std
::
move
(
what
),
buf_capacity
,
demand_threshold
);
}
/// @copydoc scheduled_actor::observe_as
template
<
class
T
>
auto
observe_as
(
stream
what
,
size_t
buf_capacity
,
size_t
demand_threshold
)
{
auto
self
=
typed_actor_view_flow_access
<
T
>
(
self_
);
return
self
->
template
observe_as
<
T
>(
std
::
move
(
what
),
buf_capacity
,
demand_threshold
);
}
/// @copydoc scheduled_actor::deregister_stream
void
deregister_stream
(
uint64_t
stream_id
)
{
self_
->
deregister_stream
(
stream_id
);
}
private:
scheduled_actor
*
self_
;
};
...
...
libcaf_core/src/async/batch.cpp
View file @
0e5a90e1
...
...
@@ -35,17 +35,17 @@ bool batch::data::save(Inspector& sink) const {
sink
.
emplace_error
(
sec
::
unsafe_type
);
return
false
;
}
auto
meta
=
detail
::
global_meta_object
(
item_type_
);
auto
&
meta
=
detail
::
global_meta_object
(
item_type_
);
auto
ptr
=
storage_
;
if
(
!
sink
.
begin_sequence
(
size_
))
return
false
;
auto
len
=
size_
;
do
{
if
constexpr
(
std
::
is_same_v
<
Inspector
,
binary_serializer
>
)
{
if
(
!
meta
->
save_binary
(
sink
,
ptr
))
if
(
!
meta
.
save_binary
(
sink
,
ptr
))
return
false
;
}
else
{
if
(
!
meta
->
save
(
sink
,
ptr
))
if
(
!
meta
.
save
(
sink
,
ptr
))
return
false
;
}
ptr
+=
item_size_
;
...
...
libcaf_core/src/config_value.cpp
View file @
0e5a90e1
...
...
@@ -194,12 +194,12 @@ error_code<sec> config_value::default_construct(type_id_t id) {
set
(
uri
{});
return
sec
::
none
;
default:
if
(
auto
meta
=
detail
::
global_meta_object
(
id
))
{
if
(
auto
meta
=
detail
::
global_meta_object_or_null
(
id
))
{
using
detail
::
make_scope_guard
;
auto
ptr
=
malloc
(
meta
->
padded_size
);
auto
free_guard
=
detail
::
make_scope_guard
([
ptr
]
{
free
(
ptr
);
});
auto
free_guard
=
make_scope_guard
([
ptr
]
{
free
(
ptr
);
});
meta
->
default_construct
(
ptr
);
auto
destroy_guard
=
detail
::
make_scope_guard
([
=
]
{
meta
->
destroy
(
ptr
);
});
auto
destroy_guard
=
make_scope_guard
([
=
]
{
meta
->
destroy
(
ptr
);
});
config_value_writer
writer
{
this
};
if
(
meta
->
save
(
writer
,
ptr
))
{
return
sec
::
none
;
...
...
@@ -519,13 +519,12 @@ config_value::parse_msg_impl(std::string_view str,
return
false
;
auto
pos
=
ptr
->
storage
();
for
(
auto
type
:
ls
)
{
auto
meta
=
detail
::
global_meta_object
(
type
);
CAF_ASSERT
(
meta
!=
nullptr
);
meta
->
default_construct
(
pos
);
auto
&
meta
=
detail
::
global_meta_object
(
type
);
meta
.
default_construct
(
pos
);
ptr
->
inc_constructed_elements
();
if
(
!
meta
->
load
(
reader
,
pos
))
if
(
!
meta
.
load
(
reader
,
pos
))
return
false
;
pos
+=
meta
->
padded_size
;
pos
+=
meta
.
padded_size
;
}
result
.
reset
(
ptr
.
release
(),
false
);
return
reader
.
end_sequence
();
...
...
libcaf_core/src/detail/meta_object.cpp
View file @
0e5a90e1
...
...
@@ -51,10 +51,28 @@ span<const meta_object> global_meta_objects() {
return
{
meta_objects
,
meta_objects_size
};
}
const
meta_object
*
global_meta_object
(
type_id_t
id
)
{
const
meta_object
&
global_meta_object
(
type_id_t
id
)
{
if
(
id
<
meta_objects_size
)
{
auto
&
meta
=
meta_objects
[
id
];
return
!
meta
.
type_name
.
empty
()
?
&
meta
:
nullptr
;
if
(
!
meta
.
type_name
.
empty
())
return
meta
;
}
CAF_CRITICAL_FMT
(
"found no meta object for type ID %d!
\n
"
" This usually means that run-time type initialization is missing.
\n
"
" With CAF_MAIN, make sure to pass all custom type ID blocks.
\n
"
" With a custom main, call (before any other CAF function):
\n
"
" - caf::core::init_global_meta_objects()
\n
"
" - <module>::init_global_meta_objects() for all loaded modules
\n
"
" - caf::init_global_meta_objects<T>() for all custom ID blocks"
,
static_cast
<
int
>
(
id
));
}
const
meta_object
*
global_meta_object_or_null
(
type_id_t
id
)
{
if
(
id
<
meta_objects_size
)
{
auto
&
meta
=
meta_objects
[
id
];
if
(
!
meta
.
type_name
.
empty
())
return
&
meta
;
}
return
nullptr
;
}
...
...
libcaf_core/src/error.cpp
View file @
0e5a90e1
...
...
@@ -64,21 +64,19 @@ int error::compare(uint8_t code, type_id_t category) const noexcept {
// -- inspection support -----------------------------------------------------
std
::
string
to_string
(
const
error
&
x
)
{
using
const_void_ptr
=
const
void
*
;
using
const_meta_ptr
=
const
detail
::
meta_object
*
;
if
(
!
x
)
return
"none"
;
std
::
string
result
;
auto
append
=
[
&
result
](
const
_void_ptr
ptr
,
const
_meta_ptr
meta
)
->
const_void_ptr
{
meta
->
stringify
(
result
,
ptr
);
return
static_cast
<
const
std
::
byte
*>
(
ptr
)
+
meta
->
padded_size
;
auto
append
=
[
&
result
](
const
void
*
ptr
,
const
detail
::
meta_object
&
meta
)
->
const
void
*
{
meta
.
stringify
(
result
,
ptr
);
return
static_cast
<
const
std
::
byte
*>
(
ptr
)
+
meta
.
padded_size
;
};
auto
code
=
x
.
code
();
append
(
&
code
,
detail
::
global_meta_object
(
x
.
category
()));
if
(
auto
&
ctx
=
x
.
context
())
{
result
+=
'('
;
auto
ptr
=
static_cast
<
const
_void_ptr
>
(
ctx
.
cdata
().
storage
());
auto
ptr
=
static_cast
<
const
void
*
>
(
ctx
.
cdata
().
storage
());
auto
types
=
ctx
.
types
();
ptr
=
append
(
ptr
,
detail
::
global_meta_object
(
types
[
0
]));
for
(
size_t
index
=
1
;
index
<
types
.
size
();
++
index
)
{
...
...
libcaf_core/src/message.cpp
View file @
0e5a90e1
...
...
@@ -71,8 +71,8 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
CAF_ASSERT
(
ids
.
size
()
==
msg_size
);
size_t
data_size
=
0
;
for
(
auto
id
:
ids
)
{
if
(
auto
meta
_obj
=
detail
::
global_meta_object
(
id
))
data_size
+=
meta
_obj
->
padded_size
;
if
(
auto
meta
=
detail
::
global_meta_object_or_null
(
id
))
data_size
+=
meta
->
padded_size
;
else
STOP
(
sec
::
unknown_type
);
}
...
...
@@ -155,7 +155,7 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
for
(
size_t
i
=
0
;
i
<
msg_size
;
++
i
)
{
auto
type
=
type_id_t
{
0
};
GUARDED
(
source
.
fetch_next_object_type
(
type
));
if
(
auto
meta_obj
=
detail
::
global_meta_object
(
type
))
{
if
(
auto
meta_obj
=
detail
::
global_meta_object
_or_null
(
type
))
{
ids
.
push_back
(
type
);
auto
obj_size
=
meta_obj
->
padded_size
;
data_size
+=
obj_size
;
...
...
@@ -289,14 +289,12 @@ std::string to_string(const message& msg) {
auto
types
=
msg
.
types
();
if
(
!
types
.
empty
())
{
auto
ptr
=
msg
.
cdata
().
storage
();
auto
meta
=
detail
::
global_meta_object
(
types
[
0
]);
CAF_ASSERT
(
meta
!=
nullptr
);
auto
*
meta
=
&
detail
::
global_meta_object
(
types
[
0
]);
meta
->
stringify
(
result
,
ptr
);
ptr
+=
meta
->
padded_size
;
for
(
size_t
index
=
1
;
index
<
types
.
size
();
++
index
)
{
result
+=
", "
;
meta
=
detail
::
global_meta_object
(
types
[
index
]);
CAF_ASSERT
(
meta
!=
nullptr
);
meta
=
&
detail
::
global_meta_object
(
types
[
index
]);
meta
->
stringify
(
result
,
ptr
);
ptr
+=
meta
->
padded_size
;
}
...
...
libcaf_core/src/message_builder.cpp
View file @
0e5a90e1
...
...
@@ -52,9 +52,9 @@ public:
}
std
::
byte
*
copy_init
(
std
::
byte
*
storage
)
const
override
{
auto
*
meta
=
global_meta_object
(
src_
.
type_at
(
index_
));
meta
->
copy_construct
(
storage
,
src_
.
data
().
at
(
index_
));
return
storage
+
meta
->
padded_size
;
auto
&
meta
=
global_meta_object
(
src_
.
type_at
(
index_
));
meta
.
copy_construct
(
storage
,
src_
.
data
().
at
(
index_
));
return
storage
+
meta
.
padded_size
;
}
std
::
byte
*
move_init
(
std
::
byte
*
storage
)
override
{
...
...
@@ -75,8 +75,8 @@ message_builder& message_builder::append_from(const caf::message& msg,
auto
end
=
std
::
min
(
msg
.
size
(),
first
+
n
);
for
(
size_t
index
=
first
;
index
<
end
;
++
index
)
{
auto
tid
=
msg
.
type_at
(
index
);
auto
*
meta
=
detail
::
global_meta_object
(
tid
);
storage_size_
+=
meta
->
padded_size
;
auto
&
meta
=
detail
::
global_meta_object
(
tid
);
storage_size_
+=
meta
.
padded_size
;
types_
.
push_back
(
tid
);
elements_
.
emplace_back
(
std
::
make_unique
<
message_builder_element_adapter
>
(
msg
,
index
));
...
...
libcaf_core/src/scheduled_actor.cpp
View file @
0e5a90e1
...
...
@@ -587,21 +587,45 @@ scheduled_actor::categorize(mailbox_element& x) {
return
message_category
::
internal
;
}
case
type_id_v
<
stream_open_msg
>
:
{
// Try to subscribe the sink to the observable.
auto
&
[
str_id
,
ptr
,
sink_id
]
=
content
.
get_as
<
stream_open_msg
>
(
0
);
if
(
!
ptr
)
{
CAF_LOG_ERROR
(
"received a stream_open_msg with a null sink"
);
return
message_category
::
internal
;
}
auto
sink_hdl
=
actor_cast
<
actor
>
(
ptr
);
if
(
auto
i
=
stream_sources_
.
find
(
str_id
);
i
!=
stream_sources_
.
end
())
{
// Create a forwarder that turns observed items into batches.
auto
fwd
=
make_counted
<
batch_forwarder_impl
>
(
this
,
sink_hdl
,
sink_id
);
auto
sub
=
i
->
second
.
obs
->
subscribe
(
flow
::
observer
<
async
::
batch
>
{
fwd
});
if
(
fwd
->
subscribed
())
{
// Inform the sink that the stream is now open.
auto
flow_id
=
new_u64_id
();
stream_subs_
.
emplace
(
flow_id
,
std
::
move
(
fwd
));
auto
mipb
=
static_cast
<
uint32_t
>
(
i
->
second
.
max_items_per_batch
);
unsafe_send_as
(
this
,
sink_hdl
,
stream_ack_msg
{
ctrl
(),
sink_id
,
flow_id
,
mipb
});
if
(
sink_hdl
.
node
()
!=
node
())
{
// Actors cancel any pending streams when they terminate. However,
// remote actors may terminate without sending us a proper goodbye.
// Hence, we add a function object to remote actors to make sure we
// get a cancel in all cases.
auto
weak_self
=
weak_actor_ptr
{
ctrl
()};
sink_hdl
->
attach_functor
([
weak_self
,
flow_id
]
{
if
(
auto
sptr
=
weak_self
.
lock
())
anon_send
(
actor_cast
<
actor
>
(
sptr
),
stream_cancel_msg
{
flow_id
});
});
}
}
else
{
CAF_LOG_ERROR
(
"failed to subscribe a batch forwarder"
);
sub
.
dispose
();
}
}
else
{
// Abort the flow immediately.
CAF_LOG_DEBUG
(
"requested stream does not exist"
);
auto
err
=
make_error
(
sec
::
invalid_stream
);
unsafe_send_as
(
this
,
sink_hdl
,
stream_abort_msg
{
sink_id
,
std
::
move
(
err
)});
}
return
message_category
::
internal
;
}
...
...
@@ -615,6 +639,7 @@ scheduled_actor::categorize(mailbox_element& x) {
case
type_id_v
<
stream_cancel_msg
>
:
{
auto
[
sub_id
]
=
content
.
get_as
<
stream_cancel_msg
>
(
0
);
if
(
auto
i
=
stream_subs_
.
find
(
sub_id
);
i
!=
stream_subs_
.
end
())
{
CAF_LOG_DEBUG
(
"canceled stream "
<<
sub_id
);
i
->
second
->
cancel
();
stream_subs_
.
erase
(
i
);
}
...
...
libcaf_core/src/type_id.cpp
View file @
0e5a90e1
...
...
@@ -9,8 +9,8 @@
namespace
caf
{
std
::
string_view
query_type_name
(
type_id_t
type
)
{
if
(
auto
ptr
=
detail
::
global_meta_object
(
type
))
return
ptr
->
type_name
;
if
(
auto
meta
=
detail
::
global_meta_object_or_null
(
type
))
return
meta
->
type_name
;
return
{};
}
...
...
libcaf_core/src/type_id_list.cpp
View file @
0e5a90e1
...
...
@@ -12,10 +12,8 @@ namespace caf {
size_t
type_id_list
::
data_size
()
const
noexcept
{
auto
result
=
size_t
{
0
};
for
(
auto
type
:
*
this
)
{
auto
meta_obj
=
detail
::
global_meta_object
(
type
);
result
+=
meta_obj
->
padded_size
;
}
for
(
auto
type
:
*
this
)
result
+=
detail
::
global_meta_object
(
type
).
padded_size
;
return
result
;
}
...
...
@@ -25,12 +23,12 @@ std::string to_string(type_id_list xs) {
std
::
string
result
;
result
+=
'['
;
{
auto
tn
=
detail
::
global_meta_object
(
xs
[
0
])
->
type_name
;
auto
tn
=
detail
::
global_meta_object
(
xs
[
0
])
.
type_name
;
result
.
insert
(
result
.
end
(),
tn
.
begin
(),
tn
.
end
());
}
for
(
size_t
index
=
1
;
index
<
xs
.
size
();
++
index
)
{
result
+=
", "
;
auto
tn
=
detail
::
global_meta_object
(
xs
[
index
])
->
type_name
;
auto
tn
=
detail
::
global_meta_object
(
xs
[
index
])
.
type_name
;
result
.
insert
(
result
.
end
(),
tn
.
begin
(),
tn
.
end
());
}
result
+=
']'
;
...
...
libcaf_core/test/messaging.cpp
0 → 100644
View file @
0e5a90e1
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE messaging
#include "caf/event_based_actor.hpp"
#include "core-test.hpp"
using
namespace
caf
;
using
namespace
std
::
literals
;
using
self_ptr
=
event_based_actor
*
;
namespace
{
struct
fixture
:
test_coordinator_fixture
<>
{
actor
uut1
;
actor
uut2
;
disposable
dis
;
bool
had_message
=
false
;
};
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"send transfers a message from one actor to another"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"sending a message from uu2 to uu1"
)
{
THEN
(
"uut1 calls the appropriate message handler"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
i
)
{
had_message
=
true
;
CHECK_EQ
(
i
,
42
);
CHECK_EQ
(
self
->
current_sender
(),
uut2
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
self
->
send
(
uut1
,
42
);
});
run
();
CHECK
(
had_message
);
}
}
}
}
SCENARIO
(
"delayed_send transfers the message after a relative timeout"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"sending a message from uu2 to uu1"
)
{
THEN
(
"uut1 calls the appropriate message handler"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
i
)
{
had_message
=
true
;
CHECK_EQ
(
i
,
42
);
CHECK_EQ
(
self
->
current_sender
(),
uut2
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
self
->
delayed_send
(
uut1
,
1s
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
advance_time
(
1s
);
sched
.
run
();
CHECK
(
had_message
);
}
}
}
}
SCENARIO
(
"scheduled_send transfers the message after an absolute timeout"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"sending a message from uu2 to uu1"
)
{
THEN
(
"uut1 calls the appropriate message handler"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
i
)
{
had_message
=
true
;
CHECK_EQ
(
i
,
42
);
CHECK_EQ
(
self
->
current_sender
(),
uut2
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
auto
timeout
=
self
->
clock
().
now
()
+
1s
;
self
->
scheduled_send
(
uut1
,
timeout
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
advance_time
(
1s
);
sched
.
run
();
CHECK
(
had_message
);
}
}
}
}
SCENARIO
(
"anon_send hides the sender of a message"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"sending a message from uu2 to uu1"
)
{
THEN
(
"uut1 calls the appropriate message handler"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
i
)
{
had_message
=
true
;
CHECK_EQ
(
i
,
42
);
CHECK_EQ
(
self
->
current_sender
(),
nullptr
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
self
->
anon_send
(
uut1
,
42
);
});
run
();
CHECK
(
had_message
);
}
}
}
}
SCENARIO
(
"delayed_anon_send hides the sender of a message"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"sending a message from uu2 to uu1"
)
{
THEN
(
"uut1 calls the appropriate message handler"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
i
)
{
had_message
=
true
;
CHECK_EQ
(
i
,
42
);
CHECK_EQ
(
self
->
current_sender
(),
nullptr
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
self
->
delayed_anon_send
(
uut1
,
1s
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
advance_time
(
1s
);
sched
.
run
();
CHECK
(
had_message
);
}
}
}
}
SCENARIO
(
"scheduled_anon_send hides the sender of a message"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"sending a message from uu2 to uu1"
)
{
THEN
(
"uut1 calls the appropriate message handler"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
i
)
{
had_message
=
true
;
CHECK_EQ
(
i
,
42
);
CHECK_EQ
(
self
->
current_sender
(),
nullptr
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
auto
timeout
=
self
->
clock
().
now
()
+
1s
;
self
->
scheduled_anon_send
(
uut1
,
timeout
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
advance_time
(
1s
);
sched
.
run
();
CHECK
(
had_message
);
}
}
}
}
SCENARIO
(
"a delayed message may be canceled before its timeout"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"when disposing the message of delayed_send before it arrives"
)
{
THEN
(
"uut1 receives no message"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
)
{
had_message
=
true
;
CHECK_EQ
(
self
->
current_sender
(),
uut2
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
dis
=
self
->
delayed_send
(
uut1
,
1s
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
dis
.
dispose
();
advance_time
(
1s
);
run
();
CHECK
(
!
had_message
);
}
}
WHEN
(
"when disposing the message of delayed_anon_send before it arrives"
)
{
THEN
(
"uut1 receives no message"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
)
{
had_message
=
true
;
CHECK_EQ
(
self
->
current_sender
(),
uut2
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
dis
=
self
->
delayed_anon_send
(
uut1
,
1s
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
dis
.
dispose
();
advance_time
(
1s
);
run
();
CHECK
(
!
had_message
);
}
}
}
}
SCENARIO
(
"a scheduled message may be canceled before its timeout"
)
{
GIVEN
(
"two actors: uut1 and uut2"
)
{
WHEN
(
"when disposing the message of scheduled_send before it arrives"
)
{
THEN
(
"uut1 receives no message"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
)
{
had_message
=
true
;
CHECK_EQ
(
self
->
current_sender
(),
uut2
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
auto
timeout
=
self
->
clock
().
now
()
+
1s
;
dis
=
self
->
scheduled_send
(
uut1
,
timeout
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
dis
.
dispose
();
advance_time
(
1s
);
run
();
CHECK
(
!
had_message
);
}
}
WHEN
(
"when disposing the message of scheduled_anon_send before it arrives"
)
{
THEN
(
"uut1 receives no message"
)
{
uut1
=
sys
.
spawn
([
this
](
self_ptr
self
)
->
behavior
{
return
{
[
this
,
self
](
int
)
{
had_message
=
true
;
CHECK_EQ
(
self
->
current_sender
(),
uut2
);
},
[](
float
)
{
CAF_FAIL
(
"float handler called"
);
},
};
});
uut2
=
sys
.
spawn
([
this
](
self_ptr
self
)
{
//
auto
timeout
=
self
->
clock
().
now
()
+
1s
;
dis
=
self
->
scheduled_anon_send
(
uut1
,
timeout
,
42
);
});
sched
.
run
();
CHECK
(
!
had_message
);
dis
.
dispose
();
advance_time
(
1s
);
run
();
CHECK
(
!
had_message
);
}
}
}
}
END_FIXTURE_SCOPE
()
libcaf_core/test/response_handle.cpp
View file @
0e5a90e1
...
...
@@ -14,6 +14,8 @@ using namespace caf;
namespace
{
using
i32_worker
=
typed_actor
<
result
<
int32_t
>
(
int32_t
)
>
;
struct
dummy_state
{
static
inline
const
char
*
name
=
"dummy"
;
...
...
@@ -33,9 +35,11 @@ using dummy_actor = stateful_actor<dummy_state>;
struct
fixture
:
test_coordinator_fixture
<>
{
actor
dummy
;
i32_worker
typed_dummy
;
fixture
()
{
dummy
=
sys
.
spawn
<
dummy_actor
>
();
typed_dummy
=
actor_cast
<
i32_worker
>
(
sys
.
spawn
<
dummy_actor
>
());
sched
.
run
();
}
};
...
...
@@ -45,7 +49,7 @@ struct fixture : test_coordinator_fixture<> {
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"response handles are convertible to observables and singles"
)
{
GIVEN
(
"a response handle that produces a valid result"
)
{
GIVEN
(
"a response handle
with dynamic typing
that produces a valid result"
)
{
WHEN
(
"calling as_single"
)
{
THEN
(
"observers see the result"
)
{
using
result_t
=
std
::
variant
<
none_t
,
int32_t
,
caf
::
error
>
;
...
...
@@ -89,7 +93,51 @@ SCENARIO("response handles are convertible to observables and singles") {
}
}
}
GIVEN
(
"a response handle that produces an error"
)
{
GIVEN
(
"a response handle with static typing that produces a valid result"
)
{
WHEN
(
"calling as_single"
)
{
THEN
(
"observers see the result"
)
{
using
result_t
=
std
::
variant
<
none_t
,
int32_t
,
caf
::
error
>
;
result_t
result
;
auto
[
self
,
launch
]
=
sys
.
spawn_inactive
<
event_based_actor
>
();
self
->
request
(
typed_dummy
,
infinite
,
int32_t
{
42
})
.
as_single
<
int32_t
>
()
.
subscribe
([
&
result
](
int32_t
val
)
{
result
=
val
;
},
[
&
result
](
const
error
&
what
)
{
result
=
what
;
});
auto
aut
=
actor
{
self
};
launch
();
expect
((
int32_t
),
from
(
aut
).
to
(
typed_dummy
).
with
(
42
));
expect
((
int32_t
),
from
(
typed_dummy
).
to
(
aut
).
with
(
84
));
CHECK
(
!
sched
.
has_job
());
CHECK_EQ
(
result
,
result_t
{
84
});
}
}
WHEN
(
"calling as_observable"
)
{
THEN
(
"observers see the result"
)
{
using
result_t
=
std
::
variant
<
none_t
,
int32_t
,
caf
::
error
>
;
size_t
on_next_calls
=
0
;
bool
completed
=
false
;
result_t
result
;
auto
[
self
,
launch
]
=
sys
.
spawn_inactive
<
event_based_actor
>
();
self
->
request
(
typed_dummy
,
infinite
,
int32_t
{
42
})
.
as_observable
<
int32_t
>
()
.
do_on_error
([
&
](
const
error
&
what
)
{
result
=
what
;
})
.
do_on_complete
([
&
]
{
completed
=
true
;
})
.
for_each
([
&
](
int32_t
val
)
{
result
=
val
;
++
on_next_calls
;
});
auto
aut
=
actor
{
self
};
launch
();
expect
((
int32_t
),
from
(
aut
).
to
(
typed_dummy
).
with
(
42
));
expect
((
int32_t
),
from
(
typed_dummy
).
to
(
aut
).
with
(
84
));
CHECK
(
!
sched
.
has_job
());
CHECK_EQ
(
result
,
result_t
{
84
});
CHECK_EQ
(
on_next_calls
,
1u
);
CHECK
(
completed
);
}
}
}
GIVEN
(
"a response handle with dynamic typing that produces an error"
)
{
WHEN
(
"calling as_single"
)
{
THEN
(
"observers see an error"
)
{
using
result_t
=
std
::
variant
<
none_t
,
int32_t
,
caf
::
error
>
;
...
...
@@ -133,6 +181,50 @@ SCENARIO("response handles are convertible to observables and singles") {
}
}
}
GIVEN
(
"a response handle with static typing that produces an error"
)
{
WHEN
(
"calling as_single"
)
{
THEN
(
"observers see an error"
)
{
using
result_t
=
std
::
variant
<
none_t
,
int32_t
,
caf
::
error
>
;
result_t
result
;
auto
[
self
,
launch
]
=
sys
.
spawn_inactive
<
event_based_actor
>
();
self
->
request
(
typed_dummy
,
infinite
,
int32_t
{
13
})
.
as_single
<
int32_t
>
()
.
subscribe
([
&
result
](
int32_t
val
)
{
result
=
val
;
},
[
&
result
](
const
error
&
what
)
{
result
=
what
;
});
auto
aut
=
actor
{
self
};
launch
();
expect
((
int32_t
),
from
(
aut
).
to
(
typed_dummy
).
with
(
13
));
expect
((
error
),
from
(
typed_dummy
).
to
(
aut
));
CHECK
(
!
sched
.
has_job
());
CHECK_EQ
(
result
,
result_t
{
make_error
(
sec
::
invalid_argument
)});
}
}
WHEN
(
"calling as_observable"
)
{
THEN
(
"observers see an error"
)
{
using
result_t
=
std
::
variant
<
none_t
,
int32_t
,
caf
::
error
>
;
size_t
on_next_calls
=
0
;
bool
completed
=
false
;
result_t
result
;
auto
[
self
,
launch
]
=
sys
.
spawn_inactive
<
event_based_actor
>
();
self
->
request
(
typed_dummy
,
infinite
,
int32_t
{
13
})
.
as_observable
<
int32_t
>
()
.
do_on_error
([
&
](
const
error
&
what
)
{
result
=
what
;
})
.
do_on_complete
([
&
]
{
completed
=
true
;
})
.
for_each
([
&
](
int32_t
val
)
{
result
=
val
;
++
on_next_calls
;
});
auto
aut
=
actor
{
self
};
launch
();
expect
((
int32_t
),
from
(
aut
).
to
(
typed_dummy
).
with
(
13
));
expect
((
error
),
from
(
typed_dummy
).
to
(
aut
));
CHECK
(
!
sched
.
has_job
());
CHECK_EQ
(
result
,
result_t
{
make_error
(
sec
::
invalid_argument
)});
CHECK_EQ
(
on_next_calls
,
0u
);
CHECK
(
!
completed
);
}
}
}
}
END_FIXTURE_SCOPE
()
libcaf_core/test/typed_actor_view.cpp
0 → 100644
View file @
0e5a90e1
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE typed_actor_view
#include "caf/typed_actor_view.hpp"
#include "core-test.hpp"
#include "caf/scheduled_actor/flow.hpp"
#include "caf/typed_actor.hpp"
using
namespace
caf
;
using
namespace
std
::
literals
;
namespace
{
using
shared_int
=
std
::
shared_ptr
<
int
>
;
using
shared_err
=
std
::
shared_ptr
<
error
>
;
using
int_actor
=
typed_actor
<
result
<
void
>
(
int
)
>
;
using
int_actor_ptr
=
int_actor
::
pointer_view
;
struct
int_actor_state
{
using
init_fn
=
std
::
function
<
void
(
int_actor_ptr
)
>
;
int_actor_state
(
int_actor_ptr
ptr
,
init_fn
fn
)
:
self
(
ptr
),
init
(
std
::
move
(
fn
))
{
// nop
}
int_actor
::
behavior_type
make_behavior
()
{
init
(
self
);
return
{
[](
int
)
{},
};
}
int_actor_ptr
self
;
init_fn
init
;
};
using
int_actor_impl
=
int_actor
::
stateful_impl
<
int_actor_state
>
;
void
stream_observer
(
event_based_actor
*
self
,
stream
str
,
shared_int
res
,
shared_err
err
)
{
// Access `self` through the view to check correct forwarding of `observe_as`.
auto
view
=
typed_actor_view
<
result
<
void
>
(
int
)
>
{
self
};
view
.
observe_as
<
int
>
(
str
,
30
,
10
)
.
do_on_error
([
err
](
const
error
&
what
)
{
*
err
=
what
;
})
.
for_each
([
res
](
int
value
)
{
*
res
+=
value
;
});
}
void
typed_stream_observer
(
event_based_actor
*
self
,
typed_stream
<
int
>
str
,
shared_int
res
,
shared_err
err
)
{
// Access `self` through the view to check correct forwarding of `observe`.
auto
view
=
typed_actor_view
<
result
<
void
>
(
int
)
>
{
self
};
view
.
observe
(
str
,
30
,
10
)
.
do_on_error
([
err
](
const
error
&
what
)
{
*
err
=
what
;
})
.
for_each
([
res
](
int
value
)
{
*
res
+=
value
;
});
}
struct
fixture
:
test_coordinator_fixture
<>
{
int_actor
spawn_int_actor
(
int_actor_state
::
init_fn
init
)
{
return
sys
.
spawn
<
int_actor_impl
>
(
std
::
move
(
init
));
}
};
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"typed actors may use the flow API"
)
{
GIVEN
(
"a typed actor"
)
{
WHEN
(
"the actor calls make_observable"
)
{
THEN
(
"the function returns a flow that lives on the typed actor"
)
{
auto
res
=
std
::
make_shared
<
int
>
(
0
);
spawn_int_actor
([
=
](
int_actor_ptr
self
)
{
self
->
make_observable
()
//
.
iota
(
1
)
.
take
(
10
)
.
for_each
([
res
](
int
value
)
{
*
res
+=
value
;
});
});
run
();
CHECK_EQ
(
*
res
,
55
);
}
}
WHEN
(
"the actor creates a stream via compose"
)
{
THEN
(
"other actors may observe the values"
)
{
auto
res
=
std
::
make_shared
<
int
>
(
0
);
auto
err
=
std
::
make_shared
<
error
>
();
spawn_int_actor
([
=
](
int_actor_ptr
self
)
{
auto
str
=
self
->
make_observable
()
//
.
iota
(
1
)
.
take
(
10
)
.
compose
(
self
->
to_stream
(
"foo"
,
10ms
,
10
));
self
->
spawn
(
stream_observer
,
str
,
res
,
err
);
});
run
();
CHECK_EQ
(
*
res
,
55
);
CHECK_EQ
(
*
err
,
sec
::
none
);
}
}
WHEN
(
"the actor creates a stream via to_stream"
)
{
THEN
(
"other actors may observe the values"
)
{
auto
res
=
std
::
make_shared
<
int
>
(
0
);
auto
err
=
std
::
make_shared
<
error
>
();
spawn_int_actor
([
=
](
int_actor_ptr
self
)
{
auto
obs
=
self
->
make_observable
()
//
.
iota
(
1
)
.
take
(
10
)
.
as_observable
();
auto
str
=
self
->
to_stream
(
"foo"
,
10ms
,
10
,
obs
);
self
->
spawn
(
stream_observer
,
str
,
res
,
err
);
});
run
();
CHECK_EQ
(
*
res
,
55
);
CHECK
(
!*
err
);
}
}
WHEN
(
"the actor creates a typed stream via compose"
)
{
THEN
(
"other actors may observe the values"
)
{
auto
res
=
std
::
make_shared
<
int
>
(
0
);
auto
err
=
std
::
make_shared
<
error
>
();
spawn_int_actor
([
=
](
int_actor_ptr
self
)
{
auto
str
=
self
->
make_observable
()
//
.
iota
(
1
)
.
take
(
10
)
.
compose
(
self
->
to_typed_stream
(
"foo"
,
10ms
,
10
));
self
->
spawn
(
typed_stream_observer
,
str
,
res
,
err
);
});
run
();
CHECK_EQ
(
*
res
,
55
);
CHECK
(
!*
err
);
}
}
WHEN
(
"the actor creates a typed stream via to_typed_stream"
)
{
THEN
(
"other actors may observe the values"
)
{
auto
res
=
std
::
make_shared
<
int
>
(
0
);
auto
err
=
std
::
make_shared
<
error
>
();
spawn_int_actor
([
=
](
int_actor_ptr
self
)
{
auto
obs
=
self
->
make_observable
()
//
.
iota
(
1
)
.
take
(
10
)
.
as_observable
();
auto
str
=
self
->
to_typed_stream
(
"foo"
,
10ms
,
10
,
obs
);
self
->
spawn
(
typed_stream_observer
,
str
,
res
,
err
);
});
run
();
CHECK_EQ
(
*
res
,
55
);
CHECK
(
!*
err
);
}
}
}
WHEN
(
"the actor creates a stream and deregisters it immediately"
)
{
THEN
(
"other actors receive an error when observing the stream"
)
{
auto
res
=
std
::
make_shared
<
int
>
(
0
);
auto
err
=
std
::
make_shared
<
error
>
();
spawn_int_actor
([
=
](
int_actor_ptr
self
)
{
auto
str
=
self
->
make_observable
()
//
.
iota
(
1
)
.
take
(
10
)
.
compose
(
self
->
to_stream
(
"foo"
,
10ms
,
10
));
self
->
spawn
(
stream_observer
,
str
,
res
,
err
);
self
->
deregister_stream
(
str
.
id
());
});
run
();
CHECK_EQ
(
*
res
,
0
);
CHECK_EQ
(
*
err
,
sec
::
invalid_stream
);
}
}
}
END_FIXTURE_SCOPE
()
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