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
e3d69731
Commit
e3d69731
authored
Jan 05, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix handling of expected<T> in response promises
parent
6b2cbd4f
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
369 additions
and
208 deletions
+369
-208
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
+171
-0
libcaf_core/test/typed_response_promise.cpp
libcaf_core/test/typed_response_promise.cpp
+137
-168
No files found.
libcaf_core/CMakeLists.txt
View file @
e3d69731
...
...
@@ -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 @
e3d69731
...
...
@@ -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_impl
(
make_message
());
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 @
e3d69731
...
...
@@ -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 @
e3d69731
...
...
@@ -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 @
e3d69731
...
...
@@ -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 @
e3d69731
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2021 Dominik Charousset *
* *
* 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. *
******************************************************************************/
#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 @
e3d69731
...
...
@@ -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