Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
actor-incubator
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
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-incubator
Commits
e1473d7f
Commit
e1473d7f
authored
Sep 22, 2020
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement round-trip from string input to response
parent
f2395f58
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
160 additions
and
17 deletions
+160
-17
libcaf_net/caf/net/actor_shell.hpp
libcaf_net/caf/net/actor_shell.hpp
+17
-5
libcaf_net/src/net/actor_shell.cpp
libcaf_net/src/net/actor_shell.cpp
+39
-8
libcaf_net/test/net/actor_shell.cpp
libcaf_net/test/net/actor_shell.cpp
+104
-4
No files found.
libcaf_net/caf/net/actor_shell.hpp
View file @
e1473d7f
...
...
@@ -19,11 +19,13 @@
#pragma once
#include "caf/actor_traits.hpp"
#include "caf/detail/unordered_flat_map.hpp"
#include "caf/extend.hpp"
#include "caf/fwd.hpp"
#include "caf/intrusive/drr_queue.hpp"
#include "caf/intrusive/fifo_inbox.hpp"
#include "caf/local_actor.hpp"
#include "caf/mixin/requester.hpp"
#include "caf/mixin/sender.hpp"
#include "caf/net/fwd.hpp"
#include "caf/none.hpp"
...
...
@@ -33,7 +35,8 @@ namespace caf::net {
///
class
actor_shell
:
public
extend
<
local_actor
,
actor_shell
>::
with
<
mixin
::
sender
>
,
:
public
extend
<
local_actor
,
actor_shell
>::
with
<
mixin
::
sender
,
mixin
::
requester
>
,
public
dynamically_typed_actor_base
,
public
non_blocking_actor_base
{
public:
...
...
@@ -43,7 +46,8 @@ public:
// -- member types -----------------------------------------------------------
using
super
=
extend
<
local_actor
,
actor_shell
>::
with
<
mixin
::
sender
>
;
using
super
=
extend
<
local_actor
,
actor_shell
>::
with
<
mixin
::
sender
,
mixin
::
requester
>
;
using
signatures
=
none_t
;
...
...
@@ -82,6 +86,12 @@ public:
/// `nullptr` if the mailbox is empty.
mailbox_element_ptr
next_message
();
/// Tries to put the mailbox into the `blocked` state, causing the next
/// enqueue to register the owning socket manager for write events.
bool
try_block_mailbox
();
// -- message processing -----------------------------------------------------
/// Dequeues and processes the next message from the mailbox.
/// @param bhvr Available message handlers.
/// @param fallback Callback for processing message that failed to match
...
...
@@ -91,9 +101,8 @@ public:
bool
consume_message
(
behavior
&
bhvr
,
callback
<
result
<
message
>
(
message
&
)
>&
fallback
);
/// Tries to put the mailbox into the `blocked` state, causing the next
/// enqueue to register the owning socket manager for write events.
bool
try_block_mailbox
();
/// Adds a callback for a multiplexed response.
void
add_multiplexed_response_handler
(
message_id
response_id
,
behavior
bhvr
);
// -- overridden functions of abstract_actor ---------------------------------
...
...
@@ -120,6 +129,9 @@ private:
// Points to the owning manager (nullptr after quit was called).
socket_manager
*
owner_
;
// Stores callbacks for multiplexed responses.
detail
::
unordered_flat_map
<
message_id
,
behavior
>
multiplexed_responses_
;
};
/// An "owning" pointer to an actor shell in the sense that it calls `quit()` on
...
...
libcaf_net/src/net/actor_shell.cpp
View file @
e1473d7f
...
...
@@ -29,6 +29,8 @@
namespace
caf
::
net
{
// -- constructors, destructors, and assignment operators ----------------------
actor_shell
::
actor_shell
(
actor_config
&
cfg
,
socket_manager
*
owner
)
:
super
(
cfg
),
mailbox_
(
policy
::
normal_messages
{}),
owner_
(
owner
)
{
mailbox_
.
try_block
();
...
...
@@ -38,10 +40,14 @@ actor_shell::~actor_shell() {
// nop
}
// -- state modifiers ----------------------------------------------------------
void
actor_shell
::
quit
(
error
reason
)
{
cleanup
(
std
::
move
(
reason
),
nullptr
);
}
// -- mailbox access -----------------------------------------------------------
mailbox_element_ptr
actor_shell
::
next_message
()
{
mailbox_
.
fetch_more
();
auto
&
q
=
mailbox_
.
queue
();
...
...
@@ -52,6 +58,12 @@ mailbox_element_ptr actor_shell::next_message() {
return
nullptr
;
}
bool
actor_shell
::
try_block_mailbox
()
{
return
mailbox_
.
try_block
();
}
// -- message processing -------------------------------------------------------
bool
actor_shell
::
consume_message
(
behavior
&
bhvr
,
callback
<
result
<
message
>
(
message
&
)
>&
fallback
)
{
CAF_LOG_TRACE
(
""
);
...
...
@@ -59,6 +71,8 @@ bool actor_shell::consume_message(
current_element_
=
msg
.
get
();
CAF_LOG_RECEIVE_EVENT
(
current_element_
);
CAF_BEFORE_PROCESSING
(
this
,
*
msg
);
auto
mid
=
msg
->
mid
;
if
(
!
mid
.
is_response
())
{
detail
::
default_invoke_result_visitor
<
actor_shell
>
visitor
{
this
};
if
(
auto
result
=
bhvr
(
msg
->
payload
))
{
visitor
(
*
result
);
...
...
@@ -66,6 +80,18 @@ bool actor_shell::consume_message(
auto
fallback_result
=
fallback
(
msg
->
payload
);
visit
(
visitor
,
fallback_result
);
}
}
else
if
(
auto
i
=
multiplexed_responses_
.
find
(
mid
);
i
!=
multiplexed_responses_
.
end
())
{
auto
bhvr
=
std
::
move
(
i
->
second
);
multiplexed_responses_
.
erase
(
i
);
auto
res
=
bhvr
(
msg
->
payload
);
if
(
!
res
)
{
CAF_LOG_DEBUG
(
"got unexpected_response"
);
auto
err_msg
=
make_message
(
make_error
(
sec
::
unexpected_response
,
std
::
move
(
msg
->
payload
)));
bhvr
(
err_msg
);
}
}
CAF_AFTER_PROCESSING
(
this
,
invoke_message_result
::
consumed
);
CAF_LOG_SKIP_OR_FINALIZE_EVENT
(
invoke_message_result
::
consumed
);
return
true
;
...
...
@@ -73,10 +99,15 @@ bool actor_shell::consume_message(
return
false
;
}
bool
actor_shell
::
try_block_mailbox
()
{
return
mailbox_
.
try_block
();
void
actor_shell
::
add_multiplexed_response_handler
(
message_id
response_id
,
behavior
bhvr
)
{
if
(
bhvr
.
timeout
()
!=
infinite
)
request_response_timeout
(
bhvr
.
timeout
(),
response_id
);
multiplexed_responses_
.
emplace
(
response_id
,
std
::
move
(
bhvr
));
}
// -- overridden functions of abstract_actor -----------------------------------
void
actor_shell
::
enqueue
(
mailbox_element_ptr
ptr
,
execution_unit
*
)
{
CAF_ASSERT
(
ptr
!=
nullptr
);
CAF_ASSERT
(
!
getf
(
is_blocking_flag
));
...
...
libcaf_net/test/net/actor_shell.cpp
View file @
e1473d7f
...
...
@@ -30,6 +30,8 @@
using
namespace
caf
;
using
namespace
std
::
string_literals
;
namespace
{
using
byte_span
=
span
<
const
byte
>
;
...
...
@@ -39,8 +41,14 @@ using svec = std::vector<std::string>;
struct
app_t
{
using
input_tag
=
tag
::
stream_oriented
;
app_t
()
=
default
;
explicit
app_t
(
actor
hdl
)
:
worker
(
std
::
move
(
hdl
))
{
// nop
}
template
<
class
LowerLayer
>
error
init
(
net
::
socket_manager
*
mgr
,
LowerLayer
&
,
const
settings
&
)
{
error
init
(
net
::
socket_manager
*
mgr
,
LowerLayer
&
down
,
const
settings
&
)
{
self
=
mgr
->
make_actor_shell
();
bhvr
=
behavior
{
[
this
](
std
::
string
&
line
)
{
lines
.
emplace_back
(
std
::
move
(
line
));
},
...
...
@@ -49,6 +57,7 @@ struct app_t {
CAF_FAIL
(
"unexpected message: "
<<
msg
);
return
make_error
(
sec
::
unexpected_message
);
});
down
.
configure_read
(
net
::
receive_policy
::
up_to
(
2048
));
return
none
;
}
...
...
@@ -70,18 +79,80 @@ struct app_t {
}
template
<
class
LowerLayer
>
ptrdiff_t
consume
(
LowerLayer
&
,
byte_span
,
byte_span
)
{
CAF_FAIL
(
"received unexpected data"
);
ptrdiff_t
consume
(
LowerLayer
&
down
,
byte_span
buf
,
byte_span
)
{
// Seek newline character.
constexpr
auto
nl
=
byte
{
'\n'
};
if
(
auto
i
=
std
::
find
(
buf
.
begin
(),
buf
.
end
(),
nl
);
i
!=
buf
.
end
())
{
// Skip empty lines.
if
(
i
==
buf
.
begin
())
{
consumed_bytes
+=
1
;
auto
sub_res
=
consume
(
down
,
buf
.
subspan
(
1
),
{});
return
sub_res
>=
0
?
sub_res
+
1
:
sub_res
;
}
// Deserialize config value from received line.
auto
num_bytes
=
std
::
distance
(
buf
.
begin
(),
i
)
+
1
;
string_view
line
{
reinterpret_cast
<
const
char
*>
(
buf
.
data
()),
static_cast
<
size_t
>
(
num_bytes
)
-
1
};
config_value
val
;
if
(
auto
parsed_res
=
config_value
::
parse
(
line
))
{
val
=
std
::
move
(
*
parsed_res
);
}
else
{
down
.
abort_reason
(
std
::
move
(
parsed_res
.
error
()));
return
-
1
;
}
if
(
!
holds_alternative
<
settings
>
(
val
))
{
down
.
abort_reason
(
make_error
(
pec
::
type_mismatch
,
"expected a dictionary, got a "
s
+
val
.
type_name
()));
return
-
1
;
}
// Deserialize message from received dictionary.
config_value_reader
reader
{
&
val
};
caf
::
message
msg
;
if
(
!
reader
.
apply_object
(
msg
))
{
down
.
abort_reason
(
reader
.
get_error
());
return
-
1
;
}
// Dispatch message to worker.
CAF_MESSAGE
(
"app received a message from its socket: "
<<
msg
);
self
->
request
(
worker
,
std
::
chrono
::
seconds
{
1
},
std
::
move
(
msg
))
.
then
(
[
this
](
int32_t
value
)
{
++
received_responses
;
CAF_CHECK_EQUAL
(
value
,
246
);
},
[](
error
&
)
{
// TODO: implement me
});
// Try consuming more from the buffer.
consumed_bytes
+=
static_cast
<
size_t
>
(
num_bytes
);
auto
sub_buf
=
buf
.
subspan
(
num_bytes
);
auto
sub_res
=
consume
(
down
,
sub_buf
,
{});
return
sub_res
>=
0
?
num_bytes
+
sub_res
:
sub_res
;
}
return
0
;
}
// Handle to the worker-under-test.
actor
worker
;
// Lines received as asynchronous messages.
std
::
vector
<
std
::
string
>
lines
;
// Actor shell representing this app.
net
::
actor_shell_ptr
self
;
// Handler for consuming messages from the mailbox.
behavior
bhvr
;
// Handler for unexpected messages.
unique_callback_ptr
<
result
<
message
>
(
message
&
)
>
fallback
;
// Counts how many bytes we've consumed in total.
size_t
consumed_bytes
=
0
;
// Counts how many response messages we've received from the worker.
size_t
received_responses
=
0
;
};
struct
fixture
:
host_fixture
,
test_coordinator_fixture
<>
{
...
...
@@ -108,12 +179,23 @@ struct fixture : host_fixture, test_coordinator_fixture<> {
}
CAF_FAIL
(
"reached max repeat rate without meeting the predicate"
);
}
void
send
(
string_view
str
)
{
auto
res
=
write
(
self_socket_guard
.
socket
(),
as_bytes
(
make_span
(
str
)));
if
(
res
!=
static_cast
<
ptrdiff_t
>
(
str
.
size
()))
CAF_FAIL
(
"expected write() to return "
<<
str
.
size
()
<<
", got: "
<<
res
);
}
net
::
middleman
mm
;
net
::
multiplexer
mpx
;
net
::
socket_guard
<
net
::
stream_socket
>
self_socket_guard
;
net
::
socket_guard
<
net
::
stream_socket
>
testee_socket_guard
;
};
constexpr
std
::
string_view
input
=
R"__(
{ values = [ { "@type" : "int32_t", value: 123 } ] }
)__"
;
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
actor_shell_tests
,
fixture
)
...
...
@@ -132,4 +214,22 @@ CAF_TEST(actor shells expose their mailbox to their owners) {
CAF_CHECK_EQUAL
(
app
.
lines
,
svec
({
"line 1"
,
"line 2"
,
"line 3"
}));
}
CAF_TEST
(
actor
shells
can
send
requests
and
receive
responses
)
{
auto
worker
=
sys
.
spawn
([]
{
return
behavior
{
[](
int32_t
value
)
{
return
value
*
2
;
},
};
});
auto
sck
=
testee_socket_guard
.
release
();
auto
mgr
=
net
::
make_socket_manager
<
app_t
,
net
::
stream_transport
>
(
sck
,
&
mpx
,
worker
);
if
(
auto
err
=
mgr
->
init
(
content
(
cfg
)))
CAF_FAIL
(
"mgr->init() failed: "
<<
err
);
auto
&
app
=
mgr
->
top_layer
();
send
(
input
);
run_while
([
&
]
{
return
app
.
consumed_bytes
!=
input
.
size
();
});
expect
((
int32_t
),
to
(
worker
).
with
(
123
));
run_while
([
&
]
{
return
app
.
received_responses
!=
1
;
});
}
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