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
c089c764
Unverified
Commit
c089c764
authored
Jan 11, 2021
by
Noir
Committed by
GitHub
Jan 11, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1198
Fix handling of expected<T> in response promises
parents
99972912
fb854f73
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
359 additions
and
208 deletions
+359
-208
CHANGELOG.md
CHANGELOG.md
+4
-0
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+1
-0
libcaf_core/caf/response_promise.hpp
libcaf_core/caf/response_promise.hpp
+20
-10
libcaf_core/caf/response_type.hpp
libcaf_core/caf/response_type.hpp
+9
-2
libcaf_core/caf/typed_response_promise.hpp
libcaf_core/caf/typed_response_promise.hpp
+25
-27
libcaf_core/src/response_promise.cpp
libcaf_core/src/response_promise.cpp
+6
-1
libcaf_core/test/response_promise.cpp
libcaf_core/test/response_promise.cpp
+157
-0
libcaf_core/test/typed_response_promise.cpp
libcaf_core/test/typed_response_promise.cpp
+137
-168
No files found.
CHANGELOG.md
View file @
c089c764
...
...
@@ -22,6 +22,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
trying to parse the input of
`x`
if it contains a string. The function
`get_or`
already existed for
`settings`
, but we have added new overloads for
generalizing the function to
`config_value`
as well.
-
The
`typed_response_promise`
received additional member functions to mirror
the interface of the untyped
`response_promise`
.
### Deprecated
...
...
@@ -84,6 +86,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
-
Skipping high-priority messages resulted in CAF lowering the priority to
normal. This unintentional demotion has been fixed (#1171).
-
Fix undefined behavior in the experimental datagram brokers (#1174).
-
Response promises no longer send empty messages in response to asynchronous
messages.
## [0.18.0-rc.1] - 2020-09-09
...
...
libcaf_core/CMakeLists.txt
View file @
c089c764
...
...
@@ -308,6 +308,7 @@ caf_add_component(
policy.select_all
policy.select_any
request_timeout
response_promise
result
save_inspector
selective_streaming
...
...
libcaf_core/caf/response_promise.hpp
View file @
c089c764
...
...
@@ -53,27 +53,31 @@ public:
"mixing expected<T> with regular values is not supported"
);
if
constexpr
(
sizeof
...(
Ts
)
==
0
&&
std
::
is_same
<
message
,
std
::
decay_t
<
T
>>::
value
)
return
deliver_impl
(
std
::
forward
<
T
>
(
x
));
deliver_impl
(
std
::
forward
<
T
>
(
x
));
else
return
deliver_impl
(
make_message
(
std
::
forward
<
T
>
(
x
),
std
::
forward
<
Ts
>
(
xs
)...));
deliver_impl
(
make_message
(
std
::
forward
<
T
>
(
x
),
std
::
forward
<
Ts
>
(
xs
)...));
}
/// Satisfies the promise by sending either an error or a non-error response
/// message.
template
<
class
T
>
void
deliver
(
expected
<
T
>
x
)
{
if
(
x
)
return
deliver
(
std
::
move
(
*
x
));
return
deliver
(
std
::
move
(
x
.
error
()));
if
(
x
)
{
if
constexpr
(
std
::
is_same
<
T
,
void
>::
value
)
deliver
();
else
deliver
(
std
::
move
(
*
x
));
}
else
{
deliver
(
std
::
move
(
x
.
error
()));
}
}
/// Satisfies the promise by delegating to another actor.
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
class
...
Ts
>
typename
response_type
<
typename
Handle
::
signatures
,
detail
::
implicit_conversions_t
<
typename
std
::
decay
<
Ts
>::
type
>
...
>::
delegated_type
delegated_response_type_t
<
typename
Handle
::
signatures
,
detail
::
implicit_conversions_t
<
typename
std
::
decay
<
Ts
>::
type
>
...
>
delegate
(
const
Handle
&
dest
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"nothing to delegate"
);
using
token
=
detail
::
type_list
<
typename
detail
::
implicit_conversions
<
...
...
@@ -96,7 +100,13 @@ public:
/// Satisfies the promise by sending an empty message if this promise has a
/// valid message ID, i.e., `async() == false`.
void
deliver
(
unit_t
x
);
void
deliver
();
/// Satisfies the promise by sending an empty message if this promise has a
/// valid message ID, i.e., `async() == false`.
void
deliver
(
unit_t
)
{
deliver
();
}
/// Returns whether this response promise replies to an asynchronous message.
bool
async
()
const
;
...
...
libcaf_core/caf/response_type.hpp
View file @
c089c764
...
...
@@ -58,11 +58,18 @@ struct response_type<detail::type_list<Out(In...), Fs...>, In...> {
using
delegated_type
=
delegated
<
Out
>
;
};
/// Computes the response message
for input `In...` from the list of message
/// passing interfaces `Fs`.
/// Computes the response message
type for input `In...` from the list of
///
message
passing interfaces `Fs`.
template
<
class
Fs
,
class
...
In
>
using
response_type_t
=
typename
response_type
<
Fs
,
In
...
>::
type
;
/// Computes the response message type for input `In...` from the list of
/// message passing interfaces `Fs` and returns the corresponding
/// `delegated<T>`.
template
<
class
Fs
,
class
...
In
>
using
delegated_response_type_t
=
typename
response_type
<
Fs
,
In
...
>::
delegated_type
;
/// Unboxes `Xs` and calls `response_type`.
template
<
class
Ts
,
class
Xs
>
struct
response_type_unbox
;
...
...
libcaf_core/caf/typed_response_promise.hpp
View file @
c089c764
...
...
@@ -4,9 +4,11 @@
#pragma once
#include
"caf/response_promise.hpp"
#include
<type_traits>
#include "caf/detail/type_list.hpp"
#include "caf/make_message.hpp"
#include "caf/response_promise.hpp"
namespace
caf
{
...
...
@@ -48,42 +50,38 @@ public:
}
/// Satisfies the promise by sending a non-error response message.
template
<
class
U
,
class
...
Us
>
typename
std
::
enable_if
<
(
sizeof
...(
Us
)
>
0
)
||
!
std
::
is_convertible
<
U
,
error
>::
value
,
typed_response_promise
>::
type
deliver
(
U
&&
x
,
Us
&&
...
xs
)
{
static_assert
(
std
::
is_same
<
detail
::
type_list
<
Ts
...
>
,
detail
::
type_list
<
typename
std
::
decay
<
U
>::
type
,
typename
std
::
decay
<
Us
>::
type
...
>>::
value
,
"typed_response_promise: message type mismatched"
);
promise_
.
deliver
(
std
::
forward
<
U
>
(
x
),
std
::
forward
<
Us
>
(
xs
)...);
return
*
this
;
template
<
class
...
Us
>
std
::
enable_if_t
<
(
std
::
is_constructible
<
Ts
,
Us
>::
value
&&
...)
>
deliver
(
Us
...
xs
)
{
promise_
.
deliver
(
make_message
(
Ts
{
std
::
forward
<
Us
>
(
xs
)}...));
}
/// Satisfies the promise by sending an empty response message.
template
<
class
L
=
detail
::
type_list
<
Ts
...>
>
std
::
enable_if_t
<
std
::
is_same
<
L
,
detail
::
type_list
<
void
>>::
value
>
deliver
()
{
promise_
.
deliver
();
}
/// Satisfies the promise by sending an error response message.
/// For non-requests, nothing is done.
void
deliver
(
error
x
)
{
promise_
.
deliver
(
std
::
move
(
x
));
}
/// Satisfies the promise by sending either an error or a non-error response
/// message.
template
<
class
T
>
void
deliver
(
expected
<
T
>
x
)
{
if
(
x
)
return
deliver
(
std
::
move
(
*
x
));
return
deliver
(
std
::
move
(
x
.
error
()
));
std
::
enable_if_t
<
std
::
is_same
<
detail
::
type_list
<
T
>
,
detail
::
type_list
<
Ts
...
>>::
value
>
deliver
(
expected
<
T
>
x
)
{
promise_
.
deliver
(
std
::
move
(
x
));
}
/// Satisfies the promise by delegating to another actor.
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
class
...
Us
>
typed_response_promise
delegate
(
const
Handle
&
dest
,
Us
&&
...
xs
)
{
promise_
.
template
delegate
<
P
>(
dest
,
std
::
forward
<
Us
>
(
xs
)...);
return
*
this
;
}
/// Satisfies the promise by sending an error response message.
/// For non-requests, nothing is done.
typed_response_promise
deliver
(
error
x
)
{
promise_
.
deliver
(
std
::
move
(
x
));
return
*
this
;
auto
delegate
(
const
Handle
&
dest
,
Us
&&
...
xs
)
{
return
promise_
.
template
delegate
<
P
>(
dest
,
std
::
forward
<
Us
>
(
xs
)...);
}
/// Returns whether this response promise replies to an asynchronous message.
...
...
libcaf_core/src/response_promise.cpp
View file @
c089c764
...
...
@@ -46,7 +46,7 @@ void response_promise::deliver(error x) {
deliver_impl
(
make_message
(
std
::
move
(
x
)));
}
void
response_promise
::
deliver
(
unit_t
)
{
void
response_promise
::
deliver
()
{
deliver_impl
(
make_message
());
}
...
...
@@ -75,6 +75,11 @@ void response_promise::deliver_impl(message msg) {
CAF_LOG_DEBUG
(
"drop response: invalid promise"
);
return
;
}
if
(
msg
.
empty
()
&&
id_
.
is_async
())
{
CAF_LOG_DEBUG
(
"drop response: empty response to asynchronous input"
);
self_
.
reset
();
return
;
}
auto
dptr
=
self_dptr
();
if
(
!
stages_
.
empty
())
{
auto
next
=
std
::
move
(
stages_
.
back
());
...
...
libcaf_core/test/response_promise.cpp
0 → 100644
View file @
c089c764
// 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 response_promise
#include "caf/response_promise.hpp"
#include "core-test.hpp"
using
namespace
caf
;
namespace
{
behavior
adder
()
{
return
{
[](
int
x
,
int
y
)
{
return
x
+
y
;
},
[](
ok_atom
)
{},
};
}
behavior
delegator
(
event_based_actor
*
self
,
actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
promise
=
self
->
make_response_promise
();
return
promise
.
delegate
(
worker
,
x
,
y
);
},
[
=
](
ok_atom
)
{
auto
promise
=
self
->
make_response_promise
();
return
promise
.
delegate
(
worker
,
ok_atom_v
);
},
};
}
behavior
requester_v1
(
event_based_actor
*
self
,
actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
rp
=
self
->
make_response_promise
();
self
->
request
(
worker
,
infinite
,
x
,
y
)
.
then
(
[
rp
](
int
result
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
result
);
},
[
rp
](
error
err
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
[
=
](
ok_atom
)
{
auto
rp
=
self
->
make_response_promise
();
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
.
then
(
[
rp
]()
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
();
},
[
rp
](
error
err
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
};
}
behavior
requester_v2
(
event_based_actor
*
self
,
actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
rp
=
self
->
make_response_promise
();
auto
deliver
=
[
rp
](
expected
<
int
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
x
,
y
)
.
then
([
deliver
](
int
result
)
mutable
{
deliver
(
result
);
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
[
=
](
ok_atom
)
{
auto
rp
=
self
->
make_response_promise
();
auto
deliver
=
[
rp
](
expected
<
void
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
.
then
([
deliver
]()
mutable
{
deliver
({});
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
};
}
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
response_promise_tests
,
test_coordinator_fixture
<>
)
SCENARIO
(
"response promises allow delaying of response messages"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
std
::
map
<
std
::
string
,
actor
>
impls
;
impls
[
"with a value or an error"
]
=
sys
.
spawn
(
requester_v1
,
adder_hdl
);
impls
[
"with an expected<T>"
]
=
sys
.
spawn
(
requester_v2
,
adder_hdl
);
for
(
auto
&
[
desc
,
hdl
]
:
impls
)
{
GIVEN
(
"a dispatcher that calls deliver "
<<
desc
<<
" on its promise"
)
{
WHEN
(
"sending a request with two integers to the dispatcher"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
THEN
(
"clients receive the response from the dispatcher"
)
{
expect
((
int
,
int
),
from
(
hdl
).
to
(
adder_hdl
).
with
(
3
,
4
));
expect
((
int
),
from
(
adder_hdl
).
to
(
hdl
).
with
(
7
));
expect
((
int
),
from
(
hdl
).
to
(
self
).
with
(
7
));
}
}
WHEN
(
"sending ok_atom to the dispatcher synchronously"
)
{
auto
res
=
self
->
request
(
hdl
,
infinite
,
ok_atom_v
);
auto
fetch_result
=
[
&
]
{
message
result
;
res
.
receive
([]
{},
// void result
[
&
](
const
error
&
reason
)
{
result
=
make_message
(
reason
);
});
return
result
;
};
THEN
(
"clients receive an empty response from the dispatcher"
)
{
expect
((
ok_atom
),
from
(
self
).
to
(
hdl
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
fetch_result
().
empty
());
}
}
WHEN
(
"sending ok_atom to the dispatcher asynchronously"
)
{
THEN
(
"clients receive no response from the dispatcher"
)
{
inject
((
ok_atom
),
from
(
self
).
to
(
hdl
).
with
(
ok_atom_v
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
self
->
mailbox
().
empty
());
}
}
}
}
}
SCENARIO
(
"response promises allow delegation"
)
{
GIVEN
(
"a dispatcher that calls delegate on its promise"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
auto
hdl
=
sys
.
spawn
(
delegator
,
adder_hdl
);
WHEN
(
"sending a request to the dispatcher"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
THEN
(
"clients receive the response from the adder"
)
{
expect
((
int
,
int
),
from
(
self
).
to
(
adder_hdl
).
with
(
3
,
4
));
expect
((
int
),
from
(
adder_hdl
).
to
(
self
).
with
(
7
));
}
}
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/typed_response_promise.cpp
View file @
c089c764
...
...
@@ -4,190 +4,159 @@
#define CAF_SUITE typed_response_promise
#include "core-test.hpp"
#include <map>
#include "caf/typed_response_promise.hpp"
#include "c
af/all
.hpp"
#include "c
ore-test
.hpp"
using
namespace
caf
;
namespace
{
using
promise_actor
=
typed_actor
<
replies_to
<
int
>::
with
<
int
>
,
replies_to
<
get_atom
,
int
>::
with
<
int
>
,
replies_to
<
get_atom
,
int
,
int
>::
with
<
int
,
int
>
,
replies_to
<
get_atom
,
double
>::
with
<
double
>
,
replies_to
<
get_atom
,
double
,
double
>::
with
<
double
,
double
>
,
reacts_to
<
put_atom
,
int
,
int
>
,
reacts_to
<
put_atom
,
int
,
int
,
int
>>
;
using
foo_promise
=
typed_response_promise
<
int
>
;
using
foo2_promise
=
typed_response_promise
<
int
,
int
>
;
using
foo3_promise
=
typed_response_promise
<
double
>
;
using
get1_helper
=
typed_actor
<
replies_to
<
int
,
int
>::
with
<
put_atom
,
int
,
int
>>
;
using
get2_helper
=
typed_actor
<
replies_to
<
int
,
int
,
int
>::
with
<
put_atom
,
int
,
int
,
int
>>
;
class
promise_actor_impl
:
public
promise_actor
::
base
{
public:
promise_actor_impl
(
actor_config
&
cfg
)
:
promise_actor
::
base
(
cfg
)
{
// nop
}
behavior_type
make_behavior
()
override
{
return
{
[
=
](
int
x
)
->
foo_promise
{
auto
resp
=
response
(
x
*
2
);
CAF_CHECK
(
!
resp
.
pending
());
return
resp
.
deliver
(
x
*
4
);
// has no effect
},
[
=
](
get_atom
,
int
x
)
->
foo_promise
{
auto
calculator
=
spawn
([]()
->
get1_helper
::
behavior_type
{
return
{[](
int
promise_id
,
int
value
)
->
result
<
put_atom
,
int
,
int
>
{
return
{
put_atom_v
,
promise_id
,
value
*
2
};
}};
});
send
(
calculator
,
next_id_
,
x
);
auto
&
entry
=
promises_
[
next_id_
++
];
entry
=
make_response_promise
<
foo_promise
>
();
return
entry
;
},
[
=
](
get_atom
,
int
x
,
int
y
)
->
foo2_promise
{
auto
calculator
=
spawn
([]()
->
get2_helper
::
behavior_type
{
return
{[](
int
promise_id
,
int
v0
,
int
v1
)
->
result
<
put_atom
,
int
,
int
,
int
>
{
return
{
put_atom_v
,
promise_id
,
v0
*
2
,
v1
*
2
};
}};
});
send
(
calculator
,
next_id_
,
x
,
y
);
auto
&
entry
=
promises2_
[
next_id_
++
];
entry
=
make_response_promise
<
foo2_promise
>
();
// verify move semantics
CAF_CHECK
(
entry
.
pending
());
foo2_promise
tmp
(
std
::
move
(
entry
));
CAF_CHECK
(
!
entry
.
pending
());
CAF_CHECK
(
tmp
.
pending
());
entry
=
std
::
move
(
tmp
);
CAF_CHECK
(
entry
.
pending
());
CAF_CHECK
(
!
tmp
.
pending
());
return
entry
;
},
[
=
](
get_atom
,
double
)
->
foo3_promise
{
auto
resp
=
make_response_promise
<
double
>
();
return
resp
.
deliver
(
make_error
(
sec
::
unexpected_message
));
},
[
=
](
get_atom
,
double
x
,
double
y
)
{
return
response
(
x
*
2
,
y
*
2
);
},
[
=
](
put_atom
,
int
promise_id
,
int
x
)
{
auto
i
=
promises_
.
find
(
promise_id
);
if
(
i
==
promises_
.
end
())
return
;
i
->
second
.
deliver
(
x
);
promises_
.
erase
(
i
);
},
[
=
](
put_atom
,
int
promise_id
,
int
x
,
int
y
)
{
auto
i
=
promises2_
.
find
(
promise_id
);
if
(
i
==
promises2_
.
end
())
return
;
i
->
second
.
deliver
(
x
,
y
);
promises2_
.
erase
(
i
);
},
};
}
private:
int
next_id_
=
0
;
std
::
map
<
int
,
foo_promise
>
promises_
;
std
::
map
<
int
,
foo2_promise
>
promises2_
;
};
struct
fixture
{
fixture
()
:
system
(
cfg
),
self
(
system
,
true
),
foo
(
system
.
spawn
<
promise_actor_impl
>
())
{
// nop
}
actor_system_config
cfg
;
actor_system
system
;
scoped_actor
self
;
promise_actor
foo
;
};
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
typed_spawn_tests
,
fixture
)
using
testee_actor
=
typed_actor
<
result
<
int
>
(
int
,
int
),
result
<
void
>
(
ok_atom
)
>
;
CAF_TEST
(
typed_response_promise
)
{
typed_response_promise
<
int
>
resp
;
CAF_MESSAGE
(
"trigger 'invalid response promise' error"
);
resp
.
deliver
(
1
);
// delivers on an invalid promise has no effect
auto
f
=
make_function_view
(
foo
);
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
42
),
84
);
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
42
,
52
),
std
::
make_tuple
(
84
,
104
));
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
3.14
,
3.14
),
std
::
make_tuple
(
6.28
,
6.28
));
testee_actor
::
behavior_type
adder
()
{
return
{
[](
int
x
,
int
y
)
{
return
x
+
y
;
},
[](
ok_atom
)
{},
};
}
CAF_TEST
(
typed_response_promise_chained
)
{
auto
f
=
make_function_view
(
foo
*
foo
*
foo
);
CAF_CHECK_EQUAL
(
f
(
1
),
8
);
testee_actor
::
behavior_type
delegator
(
testee_actor
::
pointer
self
,
testee_actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
promise
=
self
->
make_response_promise
<
int
>
();
return
promise
.
delegate
(
worker
,
x
,
y
);
},
[
=
](
ok_atom
)
{
auto
promise
=
self
->
make_response_promise
<
void
>
();
return
promise
.
delegate
(
worker
,
ok_atom_v
);
},
};
}
// verify that only requests get an error response message
CAF_TEST
(
error_response_message
)
{
auto
f
=
make_function_view
(
foo
);
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
3.14
),
sec
::
unexpected_message
);
self
->
send
(
foo
,
get_atom_v
,
42
);
self
->
receive
([](
int
x
)
{
CAF_CHECK_EQUAL
(
x
,
84
);
},
[](
double
x
)
{
CAF_ERROR
(
"unexpected ordinary response message received: "
<<
x
);
});
self
->
send
(
foo
,
get_atom_v
,
3.14
);
self
->
receive
([
&
](
error
&
err
)
{
CAF_CHECK_EQUAL
(
err
,
sec
::
unexpected_message
);
self
->
send
(
self
,
message
{});
});
testee_actor
::
behavior_type
requester_v1
(
testee_actor
::
pointer
self
,
testee_actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
rp
=
self
->
make_response_promise
<
int
>
();
self
->
request
(
worker
,
infinite
,
x
,
y
)
.
then
(
[
rp
](
int
result
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
result
);
},
[
rp
](
error
err
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
[
=
](
ok_atom
)
{
auto
rp
=
self
->
make_response_promise
<
void
>
();
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
.
then
(
[
rp
]()
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
();
},
[
rp
](
error
err
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
};
}
// verify that delivering to a satisfied promise has no effect
CAF_TEST
(
satisfied_promise
)
{
self
->
send
(
foo
,
1
);
self
->
send
(
foo
,
get_atom_v
,
3.14
,
3.14
);
int
i
=
0
;
self
->
receive_for
(
i
,
2
)([](
int
x
)
{
CAF_CHECK_EQUAL
(
x
,
1
*
2
);
},
[](
double
x
,
double
y
)
{
CAF_CHECK_EQUAL
(
x
,
3.14
*
2
);
CAF_CHECK_EQUAL
(
y
,
3.14
*
2
);
});
testee_actor
::
behavior_type
requester_v2
(
testee_actor
::
pointer
self
,
testee_actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
rp
=
self
->
make_response_promise
<
int
>
();
auto
deliver
=
[
rp
](
expected
<
int
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
x
,
y
)
.
then
([
deliver
](
int
result
)
mutable
{
deliver
(
result
);
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
[
=
](
ok_atom
)
{
auto
rp
=
self
->
make_response_promise
<
void
>
();
auto
deliver
=
[
rp
](
expected
<
void
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
.
then
([
deliver
]()
mutable
{
deliver
({});
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
};
}
CAF_TEST
(
delegating_promises
)
{
using
task
=
std
::
pair
<
typed_response_promise
<
int
>
,
int
>
;
struct
state
{
std
::
vector
<
task
>
tasks
;
};
using
bar_actor
=
typed_actor
<
replies_to
<
int
>::
with
<
int
>
,
reacts_to
<
ok_atom
>>
;
auto
bar_fun
=
[](
bar_actor
::
stateful_pointer
<
state
>
self
,
promise_actor
worker
)
->
bar_actor
::
behavior_type
{
return
{
[
=
](
int
x
)
->
typed_response_promise
<
int
>
{
auto
&
tasks
=
self
->
state
.
tasks
;
tasks
.
emplace_back
(
self
->
make_response_promise
<
int
>
(),
x
);
self
->
send
(
self
,
ok_atom_v
);
return
tasks
.
back
().
first
;
},
[
=
](
ok_atom
)
{
auto
&
tasks
=
self
->
state
.
tasks
;
if
(
!
tasks
.
empty
())
{
auto
&
task
=
tasks
.
back
();
task
.
first
.
delegate
(
worker
,
task
.
second
);
tasks
.
pop_back
();
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
typed_response_promise_tests
,
test_coordinator_fixture
<>
)
SCENARIO
(
"response promises allow delaying of response messages"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
std
::
map
<
std
::
string
,
testee_actor
>
impls
;
impls
[
"with a value or an error"
]
=
sys
.
spawn
(
requester_v1
,
adder_hdl
);
impls
[
"with an expected<T>"
]
=
sys
.
spawn
(
requester_v2
,
adder_hdl
);
for
(
auto
&
[
desc
,
hdl
]
:
impls
)
{
GIVEN
(
"a dispatcher that calls deliver "
<<
desc
<<
" on its promise"
)
{
WHEN
(
"sending a request with two integers to the dispatcher"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
THEN
(
"clients receive the response from the dispatcher"
)
{
expect
((
int
,
int
),
from
(
hdl
).
to
(
adder_hdl
).
with
(
3
,
4
));
expect
((
int
),
from
(
adder_hdl
).
to
(
hdl
).
with
(
7
));
expect
((
int
),
from
(
hdl
).
to
(
self
).
with
(
7
));
}
},
};
};
auto
f
=
make_function_view
(
system
.
spawn
(
bar_fun
,
foo
));
CAF_CHECK_EQUAL
(
f
(
42
),
84
);
}
WHEN
(
"sending ok_atom to the dispatcher synchronously"
)
{
auto
res
=
self
->
request
(
hdl
,
infinite
,
ok_atom_v
);
auto
fetch_result
=
[
&
]
{
message
result
;
res
.
receive
([]
{},
// void result
[
&
](
const
error
&
reason
)
{
result
=
make_message
(
reason
);
});
return
result
;
};
THEN
(
"clients receive an empty response from the dispatcher"
)
{
expect
((
ok_atom
),
from
(
self
).
to
(
hdl
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
fetch_result
().
empty
());
}
}
WHEN
(
"sending ok_atom to the dispatcher asynchronously"
)
{
THEN
(
"clients receive no response from the dispatcher"
)
{
inject
((
ok_atom
),
from
(
self
).
to
(
hdl
).
with
(
ok_atom_v
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
self
->
mailbox
().
empty
());
}
}
}
}
}
SCENARIO
(
"response promises allow delegation"
)
{
GIVEN
(
"a dispatcher that calls delegate on its promise"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
auto
hdl
=
sys
.
spawn
(
delegator
,
adder_hdl
);
WHEN
(
"sending a request to the dispatcher"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
THEN
(
"clients receive the response from the adder"
)
{
expect
((
int
,
int
),
from
(
self
).
to
(
adder_hdl
).
with
(
3
,
4
));
expect
((
int
),
from
(
adder_hdl
).
to
(
self
).
with
(
7
));
}
}
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
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