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
a6cf6260
Commit
a6cf6260
authored
Jan 10, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Detect and report broken response promises
parent
c089c764
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
326 additions
and
196 deletions
+326
-196
CHANGELOG.md
CHANGELOG.md
+4
-0
libcaf_core/caf/response_promise.hpp
libcaf_core/caf/response_promise.hpp
+131
-89
libcaf_core/caf/sec.hpp
libcaf_core/caf/sec.hpp
+2
-0
libcaf_core/caf/typed_response_promise.hpp
libcaf_core/caf/typed_response_promise.hpp
+55
-42
libcaf_core/src/response_promise.cpp
libcaf_core/src/response_promise.cpp
+98
-65
libcaf_core/src/sec_strings.cpp
libcaf_core/src/sec_strings.cpp
+6
-0
libcaf_core/test/response_promise.cpp
libcaf_core/test/response_promise.cpp
+15
-0
libcaf_core/test/typed_response_promise.cpp
libcaf_core/test/typed_response_promise.cpp
+15
-0
No files found.
CHANGELOG.md
View file @
a6cf6260
...
...
@@ -46,6 +46,10 @@ is based on [Keep a Changelog](https://keepachangelog.com).
string input on the command-line that starts with quotes in the same way it
would parse strings from a config file, leading to very unintuitive results in
some cases (#1113).
-
Response promises now implicitly share their state when copied. Once the
reference count for the state reaches zero, CAF now produces a
`broken_promise`
error if the actor failed to fulfill the promise by calling
either
`dispatch`
or
`delegate`
.
### Fixed
...
...
libcaf_core/caf/response_promise.hpp
View file @
a6cf6260
...
...
@@ -11,68 +11,129 @@
#include "caf/actor_cast.hpp"
#include "caf/check_typed_input.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/message.hpp"
#include "caf/message_id.hpp"
#include "caf/response_type.hpp"
namespace
caf
{
///
A response promise can be used to deliver a uniquely identifiable
/// re
sponse message from the server (i.e. receiver of the request)
///
to the client (i.e. the sender of the request)
.
///
Enables actors to delay a response message by capturing the context of a
/// re
quest message. This is particularly useful when an actor needs to
///
communicate to other actors in order to fulfill a request for a client
.
class
CAF_CORE_EXPORT
response_promise
{
public:
using
forwarding_stack
=
std
::
vector
<
strong_actor_ptr
>
;
// -- member types -----------------------------------------------------------
/// Constructs an invalid response promise.
response_promise
();
using
forwarding_stack
=
std
::
vector
<
strong_actor_ptr
>
;
response_promise
(
none_t
);
// -- constructors, destructors, and assignment operators --------------------
response_promise
(
strong_actor_ptr
self
,
strong_actor_ptr
source
,
forwarding_stack
stages
,
message_id
id
);
response_promise
(
strong_actor_ptr
self
,
mailbox_element
&
src
);
response_promise
()
=
default
;
response_promise
(
response_promise
&&
)
=
default
;
response_promise
(
const
response_promise
&
)
=
default
;
response_promise
&
operator
=
(
response_promise
&&
)
=
default
;
response_promise
&
operator
=
(
const
response_promise
&
)
=
default
;
/// Satisfies the promise by sending a non-error response message.
template
<
class
T
,
class
...
Ts
>
detail
::
enable_if_t
<
((
sizeof
...(
Ts
)
>
0
)
||
(
!
std
::
is_convertible
<
T
,
error
>::
value
&&
!
std
::
is_same
<
detail
::
decay_t
<
T
>
,
unit_t
>::
value
))
&&
!
detail
::
is_expected
<
detail
::
decay_t
<
T
>>::
value
>
deliver
(
T
&&
x
,
Ts
&&
...
xs
)
{
using
ts
=
detail
::
type_list
<
detail
::
decay_t
<
T
>
,
detail
::
decay_t
<
Ts
>
...
>
;
static_assert
(
!
detail
::
tl_exists
<
ts
,
detail
::
is_result
>::
value
,
"it is not possible to deliver objects of type result<T>"
);
static_assert
(
!
detail
::
tl_exists
<
ts
,
detail
::
is_expected
>::
value
,
[[
deprecated
(
"use the default constructor instead"
)]]
response_promise
(
none_t
)
:
response_promise
()
{
// nop
}
// -- properties -------------------------------------------------------------
/// Returns whether this response promise replies to an asynchronous message.
bool
async
()
const
noexcept
;
/// Queries whether this promise is a valid promise that is not satisfied yet.
bool
pending
()
const
noexcept
;
/// Returns the source of the corresponding request.
strong_actor_ptr
source
()
const
noexcept
;
/// Returns the remaining stages for the corresponding request.
forwarding_stack
stages
()
const
;
/// Returns the actor that will receive the response, i.e.,
/// `stages().front()` if `!stages().empty()` or `source()` otherwise.
strong_actor_ptr
next
()
const
noexcept
;
/// Returns the message ID of the corresponding request.
message_id
id
()
const
noexcept
;
// -- delivery ---------------------------------------------------------------
/// Satisfies the promise by sending the given message.
/// @note Drops empty messages silently when responding to an asynchronous
/// request message, i.e., if `async() == true`.
/// @post `pending() == false`
void
deliver
(
message
msg
);
/// Satisfies the promise by sending an error message.
/// @post `pending() == false`
void
deliver
(
error
x
);
/// Satisfies the promise by sending an empty message.
/// @note Sends no message if the request message was asynchronous, i.e., if
/// `async() == true`.
/// @post `pending() == false`
void
deliver
();
/// Satisfies the promise by sending an empty message.
/// @note Sends no message if the request message was asynchronous, i.e., if
/// `async() == true`.
/// @post `pending() == false`
void
deliver
(
unit_t
)
{
deliver
();
}
/// Satisfies the promise by sending `make_message(xs...)`.
/// @post `pending() == false`
template
<
class
...
Ts
>
void
deliver
(
Ts
...
xs
)
{
using
arg_types
=
detail
::
type_list
<
Ts
...
>
;
static_assert
(
!
detail
::
tl_exists
<
arg_types
,
detail
::
is_result
>::
value
,
"delivering a result<T> is not supported"
);
static_assert
(
!
detail
::
tl_exists
<
arg_types
,
detail
::
is_expected
>::
value
,
"mixing expected<T> with regular values is not supported"
);
if
constexpr
(
sizeof
...(
Ts
)
==
0
&&
std
::
is_same
<
message
,
std
::
decay_t
<
T
>>::
value
)
deliver_impl
(
std
::
forward
<
T
>
(
x
));
else
deliver_impl
(
make_message
(
std
::
forward
<
T
>
(
x
),
std
::
forward
<
Ts
>
(
xs
)...));
if
(
pending
())
{
state_
->
deliver_impl
(
make_message
(
std
::
move
(
xs
)...));
state_
.
reset
();
}
}
/// Satisfies the promise by sending either an error or a non-error response
/// message.
/// Satisfies the promise by sending the content of `x`, i.e., either a value
/// of type `T` or an @ref error.
/// @post `pending() == false`
template
<
class
T
>
void
deliver
(
expected
<
T
>
x
)
{
if
(
pending
())
{
if
(
x
)
{
if
constexpr
(
std
::
is_same
<
T
,
void
>::
value
)
deliver
();
if
constexpr
(
std
::
is_same
<
T
,
void
>::
value
||
std
::
is_same
<
T
,
unit_t
>::
value
)
state_
->
deliver_impl
(
make_message
());
else
deliver
(
std
::
move
(
*
x
));
state_
->
deliver_impl
(
make_message
(
std
::
move
(
*
x
)
));
}
else
{
deliver
(
std
::
move
(
x
.
error
(
)));
state_
->
deliver_impl
(
make_message
(
std
::
move
(
x
.
error
()
)));
}
state_
.
reset
();
}
}
// -- delegation -------------------------------------------------------------
/// Satisfies the promise by delegating to another actor.
/// @post `pending() == false`
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
class
...
Ts
>
delegated_response_type_t
<
...
...
@@ -84,73 +145,54 @@ public:
typename
std
::
decay
<
Ts
>::
type
>::
type
...
>
;
static_assert
(
response_type_unbox
<
signatures_of_t
<
Handle
>
,
token
>::
valid
,
"receiver does not accept given message"
);
if
(
pending
())
{
if
constexpr
(
P
==
message_priority
::
high
)
id_
=
id_
.
with_high_priority
();
state_
->
id
=
state_
->
id
.
with_high_priority
();
if
constexpr
(
std
::
is_same
<
detail
::
type_list
<
message
>
,
detail
::
type_list
<
std
::
decay_t
<
Ts
>
...
>>::
value
)
delegate_impl
(
actor_cast
<
abstract_actor
*>
(
dest
),
std
::
forward
<
Ts
>
(
xs
)...);
state_
->
delegate_impl
(
actor_cast
<
abstract_actor
*>
(
dest
),
std
::
forward
<
Ts
>
(
xs
)...);
else
delegate_impl
(
actor_cast
<
abstract_actor
*>
(
dest
),
state_
->
delegate_impl
(
actor_cast
<
abstract_actor
*>
(
dest
),
make_message
(
std
::
forward
<
Ts
>
(
xs
)...));
state_
.
reset
();
}
return
{};
}
/// Satisfies the promise by sending an error response message.
void
deliver
(
error
x
);
/// Satisfies the promise by sending an empty message if this promise has a
/// valid message ID, i.e., `async() == false`.
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
();
}
private:
// Note: response promises must remain local to their owner. Hence, we don't
// need a thread-safe reference count for the state.
class
state
{
public:
state
()
=
default
;
state
(
const
state
&
)
=
delete
;
state
&
operator
=
(
const
state
&
)
=
delete
;
~
state
();
/// Returns whether this response promise replies to an asynchronous message.
bool
async
()
const
;
void
cancel
();
/// Queries whether this promise is a valid promise that is not satisfied yet.
bool
pending
()
const
{
return
source_
!=
nullptr
||
!
stages_
.
empty
();
}
void
deliver_impl
(
message
msg
);
/// Returns the source of the corresponding request.
const
strong_actor_ptr
&
source
()
const
{
return
source_
;
}
void
delegate_impl
(
abstract_actor
*
receiver
,
message
msg
);
/// Returns the remaining stages for the corresponding request.
const
forwarding_stack
&
stages
()
const
{
return
stages_
;
}
mutable
size_t
ref_count
=
1
;
strong_actor_ptr
self
;
strong_actor_ptr
source
;
forwarding_stack
stages
;
message_id
id
;
/// Returns the actor that will receive the response, i.e.,
/// `stages().front()` if `!stages().empty()` or `source()` otherwise.
strong_actor_ptr
next
()
const
{
return
stages_
.
empty
()
?
source_
:
stages_
.
front
();
friend
void
intrusive_ptr_add_ref
(
const
state
*
ptr
)
{
++
ptr
->
ref_count
;
}
/// Returns the message ID of the corresponding request.
message_id
id
()
const
{
return
id_
;
friend
void
intrusive_ptr_release
(
const
state
*
ptr
)
{
if
(
--
ptr
->
ref_count
==
0
)
delete
ptr
;
}
};
private:
/// Returns a downcasted version of `self_`.
local_actor
*
self_dptr
()
const
;
execution_unit
*
context
();
void
deliver_impl
(
message
msg
);
void
delegate_impl
(
abstract_actor
*
receiver
,
message
msg
);
strong_actor_ptr
self_
;
strong_actor_ptr
source_
;
forwarding_stack
stages_
;
message_id
id_
;
intrusive_ptr
<
state
>
state_
;
};
}
// namespace caf
libcaf_core/caf/sec.hpp
View file @
a6cf6260
...
...
@@ -159,6 +159,8 @@ enum class sec : uint8_t {
unsupported_operation
,
/// A key lookup failed.
no_such_key
=
65
,
/// An destroyed a response promise without calling deliver or delegate on it.
broken_promise
,
};
// --(rst-sec-end)--
...
...
libcaf_core/caf/typed_response_promise.hpp
View file @
a6cf6260
...
...
@@ -12,20 +12,18 @@
namespace
caf
{
/// A response promise can be used to deliver a uniquely identifiable
/// response message from the server (i.e. receiver of the request)
/// to the client (i.e. the sender of the request).
/// Enables statically typed actors to delay a response message by capturing the
/// context of a request message. This is particularly useful when an actor
/// needs to communicate to other actors in order to fulfill a request for a
/// client.
template
<
class
...
Ts
>
class
typed_response_promise
{
public:
using
forwarding_stack
=
response_promise
::
forwarding_stack
;
// -- member types -----------------------------------------------------------
/// Constructs an invalid response promise.
typed_response_promise
()
=
default
;
using
forwarding_stack
=
response_promise
::
forwarding_stack
;
typed_response_promise
(
none_t
x
)
:
promise_
(
x
)
{
// nop
}
// -- constructors, destructors, and assignment operators --------------------
typed_response_promise
(
strong_actor_ptr
self
,
strong_actor_ptr
source
,
forwarding_stack
stages
,
message_id
id
)
...
...
@@ -38,17 +36,61 @@ public:
// nop
}
typed_response_promise
()
=
default
;
typed_response_promise
(
typed_response_promise
&&
)
=
default
;
typed_response_promise
(
const
typed_response_promise
&
)
=
default
;
typed_response_promise
&
operator
=
(
typed_response_promise
&&
)
=
default
;
typed_response_promise
&
operator
=
(
const
typed_response_promise
&
)
=
default
;
/// Implicitly convertible to untyped response promise.
[[
deprecated
(
"Use the typed_response_promise directly."
)]]
[[
deprecated
(
"use the default constructor instead"
)]]
//
typed_response_promise
(
none_t
x
)
:
promise_
(
x
)
{
// nop
}
// -- properties -------------------------------------------------------------
/// @copydoc response_promise::async
bool
async
()
const
{
return
promise_
.
async
();
}
/// @copydoc response_promise::pending
bool
pending
()
const
{
return
promise_
.
pending
();
}
/// @copydoc response_promise::source
strong_actor_ptr
source
()
const
{
return
promise_
.
source
();
}
/// @copydoc response_promise::stages
forwarding_stack
stages
()
const
{
return
promise_
.
stages
();
}
/// @copydoc response_promise::next
strong_actor_ptr
next
()
const
{
return
promise_
.
next
();
}
/// @copydoc response_promise::id
message_id
id
()
const
{
return
promise_
.
id
();
}
[[
deprecated
(
"use the typed_response_promise directly"
)]]
operator
response_promise
&
()
{
return
promise_
;
}
// -- delivery ---------------------------------------------------------------
/// Satisfies the promise by sending a non-error response message.
template
<
class
...
Us
>
std
::
enable_if_t
<
(
std
::
is_constructible
<
Ts
,
Us
>::
value
&&
...)
>
...
...
@@ -77,6 +119,8 @@ public:
promise_
.
deliver
(
std
::
move
(
x
));
}
// -- delegation -------------------------------------------------------------
/// Satisfies the promise by delegating to another actor.
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
class
...
Us
>
...
...
@@ -84,37 +128,6 @@ public:
return
promise_
.
template
delegate
<
P
>(
dest
,
std
::
forward
<
Us
>
(
xs
)...);
}
/// Returns whether this response promise replies to an asynchronous message.
bool
async
()
const
{
return
promise_
.
async
();
}
/// Queries whether this promise is a valid promise that is not satisfied yet.
bool
pending
()
const
{
return
promise_
.
pending
();
}
/// Returns the source of the corresponding request.
const
strong_actor_ptr
&
source
()
const
{
return
promise_
.
source
();
}
/// Returns the remaining stages for the corresponding request.
const
forwarding_stack
&
stages
()
const
{
return
promise_
.
stages
();
}
/// Returns the actor that will receive the response, i.e.,
/// `stages().front()` if `!stages().empty()` or `source()` otherwise.
strong_actor_ptr
next
()
const
{
return
promise_
.
next
();
}
/// Returns the message ID of the corresponding request.
message_id
id
()
const
{
return
promise_
.
id
();
}
private:
response_promise
promise_
;
};
...
...
libcaf_core/src/response_promise.cpp
View file @
a6cf6260
...
...
@@ -14,25 +14,21 @@
namespace
caf
{
response_promise
::
response_promise
()
:
self_
(
nullptr
)
{
// nop
}
response_promise
::
response_promise
(
none_t
)
:
response_promise
()
{
// nop
}
// -- constructors, destructors, and assignment operators ----------------------
response_promise
::
response_promise
(
strong_actor_ptr
self
,
strong_actor_ptr
source
,
forwarding_stack
stages
,
message_id
mid
)
:
id_
(
mid
)
{
forwarding_stack
stages
,
message_id
mid
)
{
CAF_ASSERT
(
self
!=
nullptr
);
// Form an invalid request promise when initialized from a response ID, since
// we always drop messages in this case.
if
(
!
mid
.
is_response
())
{
self_
.
swap
(
self
);
source_
.
swap
(
source
);
stages_
.
swap
(
stages
);
// we always drop messages in this case. Also don't create promises for
// anonymous messages since there's nowhere to send the message to anyway.
if
(
!
mid
.
is_response
()
&&
(
source
||
!
stages
.
empty
()))
{
state_
=
make_counted
<
state
>
();
state_
->
self
.
swap
(
self
);
state_
->
source
.
swap
(
source
);
state_
->
stages
.
swap
(
stages
);
state_
->
id
=
mid
;
}
}
...
...
@@ -42,77 +38,114 @@ response_promise::response_promise(strong_actor_ptr self, mailbox_element& src)
// nop
}
void
response_promise
::
deliver
(
error
x
)
{
deliver_impl
(
make_message
(
std
::
move
(
x
)));
// -- properties ---------------------------------------------------------------
bool
response_promise
::
async
()
const
noexcept
{
return
id
().
is_async
();
}
void
response_promise
::
deliver
()
{
deliver_impl
(
make_message
())
;
bool
response_promise
::
pending
()
const
noexcept
{
return
state_
!=
nullptr
&&
state_
->
self
!=
nullptr
;
}
bool
response_promise
::
async
()
const
{
return
id_
.
is_async
();
strong_actor_ptr
response_promise
::
source
()
const
noexcept
{
if
(
state_
)
return
state_
->
source
;
else
return
nullptr
;
}
local_actor
*
response_promise
::
self_dptr
()
const
{
// TODO: We require that self_ was constructed by using a local_actor*. The
// type erasure performed by strong_actor_ptr hides that fact. We
// probably should use a different pointer type such as
// intrusive_ptr<local_actor>. However, that would mean we would have to
// include local_actor.hpp in response_promise.hpp or provide overloads
// for intrusive_ptr_add_ref and intrusive_ptr_release.
auto
self_baseptr
=
actor_cast
<
abstract_actor
*>
(
self_
);
return
static_cast
<
local_actor
*>
(
self_baseptr
);
response_promise
::
forwarding_stack
response_promise
::
stages
()
const
{
if
(
state_
)
return
state_
->
stages
;
else
return
{};
}
execution_unit
*
response_promise
::
context
()
{
return
self_
==
nullptr
?
nullptr
:
self_dptr
()
->
context
();
strong_actor_ptr
response_promise
::
next
()
const
noexcept
{
if
(
state_
)
return
state_
->
stages
.
empty
()
?
state_
->
source
:
state_
->
stages
[
0
];
else
return
nullptr
;
}
void
response_promise
::
deliver_impl
(
message
msg
)
{
message_id
response_promise
::
id
()
const
noexcept
{
if
(
state_
)
return
state_
->
id
;
else
return
make_message_id
();
}
// -- delivery -----------------------------------------------------------------
void
response_promise
::
deliver
(
message
msg
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
msg
));
if
(
self_
==
nullptr
)
{
CAF_LOG_DEBUG
(
"drop response: invalid promise"
);
return
;
if
(
pending
()
)
{
state_
->
deliver_impl
(
std
::
move
(
msg
)
);
state_
.
reset
()
;
}
if
(
msg
.
empty
()
&&
id_
.
is_async
())
{
CAF_LOG_DEBUG
(
"drop response: empty response to asynchronous input"
);
self_
.
reset
();
return
;
}
void
response_promise
::
deliver
(
error
x
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
x
));
if
(
pending
())
{
state_
->
deliver_impl
(
make_message
(
std
::
move
(
x
)));
state_
.
reset
();
}
auto
dptr
=
self_dptr
();
if
(
!
stages_
.
empty
())
{
auto
next
=
std
::
move
(
stages_
.
back
());
stages_
.
pop_back
();
detail
::
profiled_send
(
dptr
,
std
::
move
(
source_
),
next
,
id_
,
std
::
move
(
stages_
),
dptr
->
context
(),
std
::
move
(
msg
));
self_
.
reset
();
return
;
}
void
response_promise
::
deliver
()
{
CAF_LOG_TRACE
(
CAF_ARG
(
""
));
if
(
pending
())
{
state_
->
deliver_impl
(
make_message
());
state_
.
reset
();
}
if
(
source_
)
{
detail
::
profiled_send
(
dptr
,
self_
,
source_
,
id_
.
response_id
(),
no_stages
,
dptr
->
context
(),
std
::
move
(
msg
));
self_
.
reset
();
source_
.
reset
();
return
;
}
// -- state --------------------------------------------------------------------
response_promise
::
state
::~
state
()
{
if
(
self
)
{
CAF_LOG_DEBUG
(
"broken promise!"
);
deliver_impl
(
make_message
(
make_error
(
sec
::
broken_promise
)));
}
CAF_LOG_WARNING
(
"malformed response promise: self != nullptr && !pending()"
);
}
void
response_promise
::
delegate_impl
(
abstract_actor
*
receiver
,
message
msg
)
{
void
response_promise
::
state
::
cancel
()
{
self
.
reset
();
}
void
response_promise
::
state
::
deliver_impl
(
message
msg
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
msg
));
if
(
receiver
==
nullptr
)
{
CAF_LOG_DEBUG
(
"drop response: invalid delegation target"
);
return
;
if
(
msg
.
empty
()
&&
id
.
is_async
())
{
CAF_LOG_DEBUG
(
"drop response: empty response to asynchronous input"
);
}
else
{
auto
dptr
=
actor_cast
<
local_actor
*>
(
self
);
if
(
stages
.
empty
())
{
detail
::
profiled_send
(
dptr
,
self
,
source
,
id
.
response_id
(),
forwarding_stack
{},
dptr
->
context
(),
std
::
move
(
msg
));
}
else
{
auto
next
=
std
::
move
(
stages
.
back
());
stages
.
pop_back
();
detail
::
profiled_send
(
dptr
,
std
::
move
(
source
),
next
,
id
,
std
::
move
(
stages
),
dptr
->
context
(),
std
::
move
(
msg
));
}
if
(
self_
==
nullptr
)
{
CAF_LOG_DEBUG
(
"drop response: invalid promise"
);
return
;
}
auto
dptr
=
self_dptr
();
detail
::
profiled_send
(
dptr
,
std
::
move
(
source_
),
receiver
,
id_
,
std
::
move
(
stages_
),
dptr
->
context
(),
std
::
move
(
msg
));
self_
.
reset
();
cancel
();
}
void
response_promise
::
state
::
delegate_impl
(
abstract_actor
*
receiver
,
message
msg
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
msg
));
if
(
receiver
!=
nullptr
)
{
auto
dptr
=
actor_cast
<
local_actor
*>
(
self
);
detail
::
profiled_send
(
dptr
,
std
::
move
(
source
),
receiver
,
id
,
std
::
move
(
stages
),
dptr
->
context
(),
std
::
move
(
msg
));
}
else
{
CAF_LOG_DEBUG
(
"drop response: invalid delegation target"
);
}
cancel
();
}
}
// namespace caf
libcaf_core/src/sec_strings.cpp
View file @
a6cf6260
...
...
@@ -148,6 +148,8 @@ std::string to_string(sec x) {
return
"unsupported_operation"
;
case
sec
:
:
no_such_key
:
return
"no_such_key"
;
case
sec
:
:
broken_promise
:
return
"broken_promise"
;
};
}
...
...
@@ -350,6 +352,9 @@ bool from_string(string_view in, sec& out) {
}
else
if
(
in
==
"no_such_key"
)
{
out
=
sec
::
no_such_key
;
return
true
;
}
else
if
(
in
==
"broken_promise"
)
{
out
=
sec
::
broken_promise
;
return
true
;
}
else
{
return
false
;
}
...
...
@@ -427,6 +432,7 @@ bool from_integer(std::underlying_type_t<sec> in,
case
sec
:
:
type_clash
:
case
sec
:
:
unsupported_operation
:
case
sec
:
:
no_such_key
:
case
sec
:
:
broken_promise
:
out
=
result
;
return
true
;
};
...
...
libcaf_core/test/response_promise.cpp
View file @
a6cf6260
...
...
@@ -140,6 +140,21 @@ SCENARIO("response promises allow delaying of response messages") {
}
}
SCENARIO
(
"response promises send errors when broken"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
auto
hdl
=
sys
.
spawn
(
requester_v1
,
adder_hdl
);
GIVEN
(
"a dispatcher, and adder and a client"
)
{
WHEN
(
"the dispatcher terminates before calling deliver on its promise"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
inject
((
exit_msg
),
to
(
hdl
).
with
(
exit_msg
{
hdl
.
address
(),
exit_reason
::
kill
}));
THEN
(
"clients receive a broken_promise error"
)
{
expect
((
error
),
from
(
hdl
).
to
(
self
).
with
(
sec
::
broken_promise
));
}
}
}
}
SCENARIO
(
"response promises allow delegation"
)
{
GIVEN
(
"a dispatcher that calls delegate on its promise"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
...
...
libcaf_core/test/typed_response_promise.cpp
View file @
a6cf6260
...
...
@@ -145,6 +145,21 @@ SCENARIO("response promises allow delaying of response messages") {
}
}
SCENARIO
(
"response promises send errors when broken"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
auto
hdl
=
sys
.
spawn
(
requester_v1
,
adder_hdl
);
GIVEN
(
"a dispatcher, and adder and a client"
)
{
WHEN
(
"the dispatcher terminates before calling deliver on its promise"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
inject
((
exit_msg
),
to
(
hdl
).
with
(
exit_msg
{
hdl
.
address
(),
exit_reason
::
kill
}));
THEN
(
"clients receive a broken_promise error"
)
{
expect
((
error
),
from
(
hdl
).
to
(
self
).
with
(
sec
::
broken_promise
));
}
}
}
}
SCENARIO
(
"response promises allow delegation"
)
{
GIVEN
(
"a dispatcher that calls delegate on its promise"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
...
...
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