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
4f917888
Commit
4f917888
authored
May 12, 2018
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow stream managers to close individual slots
parent
58b281ec
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
189 additions
and
31 deletions
+189
-31
libcaf_core/caf/broadcast_downstream_manager.hpp
libcaf_core/caf/broadcast_downstream_manager.hpp
+24
-18
libcaf_core/caf/detail/algorithms.hpp
libcaf_core/caf/detail/algorithms.hpp
+23
-1
libcaf_core/caf/downstream_manager.hpp
libcaf_core/caf/downstream_manager.hpp
+24
-3
libcaf_core/caf/outbound_path.hpp
libcaf_core/caf/outbound_path.hpp
+12
-0
libcaf_core/src/downstream_manager.cpp
libcaf_core/src/downstream_manager.cpp
+42
-3
libcaf_core/src/outbound_path.cpp
libcaf_core/src/outbound_path.cpp
+2
-1
libcaf_core/src/stream_manager.cpp
libcaf_core/src/stream_manager.cpp
+3
-0
libcaf_core/test/continuous_streaming.cpp
libcaf_core/test/continuous_streaming.cpp
+59
-5
No files found.
libcaf_core/caf/broadcast_downstream_manager.hpp
View file @
4f917888
...
@@ -74,6 +74,12 @@ public:
...
@@ -74,6 +74,12 @@ public:
return
central_buf
+
max_path_buf
;
return
central_buf
+
max_path_buf
;
}
}
size_t
buffered
(
stream_slot
slot
)
const
noexcept
override
{
auto
i
=
state_map_
.
find
(
slot
);
return
this
->
buf_
.
size
()
+
(
i
!=
state_map_
.
end
()
?
i
->
second
.
buf
.
size
()
:
0u
);
}
/// Sets the filter for `slot` to `filter`. Inserts a new element if `slot`
/// Sets the filter for `slot` to `filter`. Inserts a new element if `slot`
/// is a new path.
/// is a new path.
void
set_filter
(
stream_slot
slot
,
filter_type
filter
)
{
void
set_filter
(
stream_slot
slot
,
filter_type
filter
)
{
...
@@ -220,31 +226,30 @@ private:
...
@@ -220,31 +226,30 @@ private:
return
;
return
;
// Calculate the chunk size, i.e., how many more items we can put to our
// Calculate the chunk size, i.e., how many more items we can put to our
// caches at the most.
// caches at the most.
struct
get_credit
{
auto
not_closing
=
[
&
](
typename
map_type
::
value_type
&
x
,
inline
size_t
operator
()(
typename
map_type
::
value_type
&
x
)
{
typename
state_map_type
::
value_type
&
)
{
return
static_cast
<
size_t
>
(
x
.
second
->
open_credit
);
return
!
x
.
second
->
closing
;
}
};
};
struct
get_cache_size
{
// Returns the minimum of `interim` and `get_credit(x) - get_cache_size(y)`.
inline
size_t
operator
()(
typename
state_map_type
::
value_type
&
x
)
{
auto
f
=
[
&
](
size_t
interim
,
typename
map_type
::
value_type
&
x
,
return
x
.
second
.
buf
.
size
();
typename
state_map_type
::
value_type
&
y
)
{
}
auto
credit
=
static_cast
<
size_t
>
(
x
.
second
->
open_credit
);
};
auto
cache_size
=
y
.
second
.
buf
.
size
();
auto
f
=
[](
size_t
x
,
size_t
credit
,
size_t
cache_size
)
{
return
std
::
min
(
interim
,
credit
>
cache_size
?
credit
-
cache_size
:
0u
);
auto
y
=
credit
>
cache_size
?
credit
-
cache_size
:
0u
;
return
x
<
y
?
x
:
y
;
};
};
auto
chunk_size
=
detail
::
zip_fold
(
auto
chunk_size
=
detail
::
zip_fold_if
(
f
,
not_closing
,
f
,
std
::
numeric_limits
<
size_t
>::
max
(),
std
::
numeric_limits
<
size_t
>::
max
(),
detail
::
make_container_view
<
get_credit
>
(
this
->
paths_
.
container
()),
this
->
paths_
.
container
(),
detail
::
make_container_view
<
get_cache_size
>
(
state_map_
.
container
()));
state_map_
.
container
());
auto
chunk
=
this
->
get_chunk
(
chunk_size
);
auto
chunk
=
this
->
get_chunk
(
chunk_size
);
if
(
chunk
.
empty
())
{
if
(
chunk
.
empty
())
{
auto
g
=
[
&
](
typename
map_type
::
value_type
&
x
,
auto
g
=
[
&
](
typename
map_type
::
value_type
&
x
,
typename
state_map_type
::
value_type
&
y
)
{
typename
state_map_type
::
value_type
&
y
)
{
x
.
second
->
emit_batches
(
this
->
self
(),
y
.
second
.
buf
,
force_underfull
);
x
.
second
->
emit_batches
(
this
->
self
(),
y
.
second
.
buf
,
force_underfull
);
};
};
detail
::
zip_foreach
(
g
,
this
->
paths_
.
container
(),
state_map_
.
container
());
detail
::
zip_foreach_if
(
g
,
not_closing
,
this
->
paths_
.
container
(),
state_map_
.
container
());
}
else
{
}
else
{
auto
g
=
[
&
](
typename
map_type
::
value_type
&
x
,
auto
g
=
[
&
](
typename
map_type
::
value_type
&
x
,
typename
state_map_type
::
value_type
&
y
)
{
typename
state_map_type
::
value_type
&
y
)
{
...
@@ -259,7 +264,8 @@ private:
...
@@ -259,7 +264,8 @@ private:
}
}
x
.
second
->
emit_batches
(
this
->
self
(),
st
.
buf
,
force_underfull
);
x
.
second
->
emit_batches
(
this
->
self
(),
st
.
buf
,
force_underfull
);
};
};
detail
::
zip_foreach
(
g
,
this
->
paths_
.
container
(),
state_map_
.
container
());
detail
::
zip_foreach_if
(
g
,
not_closing
,
this
->
paths_
.
container
(),
state_map_
.
container
());
}
}
}
}
...
...
libcaf_core/caf/detail/algorithms.hpp
View file @
4f917888
...
@@ -25,7 +25,8 @@
...
@@ -25,7 +25,8 @@
namespace
caf
{
namespace
caf
{
namespace
detail
{
namespace
detail
{
/// Like `std::for_each`, but for multiple containers.
/// Like `std::for_each`, but for multiple containers and filters elements by
/// predicate.
/// @pre `x.size() <= y.size()` for each `y` in `xs`
/// @pre `x.size() <= y.size()` for each `y` in `xs`
template
<
class
F
,
class
Container
,
class
...
Containers
>
template
<
class
F
,
class
Container
,
class
...
Containers
>
void
zip_foreach
(
F
f
,
Container
&&
x
,
Containers
&&
...
xs
)
{
void
zip_foreach
(
F
f
,
Container
&&
x
,
Containers
&&
...
xs
)
{
...
@@ -33,6 +34,15 @@ void zip_foreach(F f, Container&& x, Containers&&... xs) {
...
@@ -33,6 +34,15 @@ void zip_foreach(F f, Container&& x, Containers&&... xs) {
f
(
x
[
i
],
xs
[
i
]...);
f
(
x
[
i
],
xs
[
i
]...);
}
}
/// Like `std::for_each`, but for multiple containers.
/// @pre `x.size() <= y.size()` for each `y` in `xs`
template
<
class
F
,
class
Predicate
,
class
Container
,
class
...
Containers
>
void
zip_foreach_if
(
F
f
,
Predicate
p
,
Container
&&
x
,
Containers
&&
...
xs
)
{
for
(
size_t
i
=
0
;
i
<
x
.
size
();
++
i
)
if
(
p
(
x
[
i
],
xs
[
i
]...))
f
(
x
[
i
],
xs
[
i
]...);
}
/// Like `std::accumulate`, but for multiple containers.
/// Like `std::accumulate`, but for multiple containers.
/// @pre `x.size() <= y.size()` for each `y` in `xs`
/// @pre `x.size() <= y.size()` for each `y` in `xs`
template
<
class
F
,
class
T
,
class
Container
,
class
...
Containers
>
template
<
class
F
,
class
T
,
class
Container
,
class
...
Containers
>
...
@@ -42,6 +52,18 @@ T zip_fold(F f, T init, Container&& x, Containers&&... xs) {
...
@@ -42,6 +52,18 @@ T zip_fold(F f, T init, Container&& x, Containers&&... xs) {
return
init
;
return
init
;
}
}
/// Like `std::accumulate`, but for multiple containers and filters elements by
/// predicate.
/// @pre `x.size() <= y.size()` for each `y` in `xs`
template
<
class
F
,
class
Predicate
,
class
T
,
class
Container
,
class
...
Containers
>
T
zip_fold_if
(
F
f
,
Predicate
p
,
T
init
,
Container
&&
x
,
Containers
&&
...
xs
)
{
for
(
size_t
i
=
0
;
i
<
x
.
size
();
++
i
)
if
(
p
(
x
[
i
],
xs
[
i
]...))
init
=
f
(
init
,
x
[
i
],
xs
[
i
]...);
return
init
;
}
/// Decorates a container of type `T` to appear as container of type `U`.
/// Decorates a container of type `T` to appear as container of type `U`.
template
<
class
F
,
class
Container
>
template
<
class
F
,
class
Container
>
struct
container_view
{
struct
container_view
{
...
...
libcaf_core/caf/downstream_manager.hpp
View file @
4f917888
...
@@ -39,6 +39,9 @@ public:
...
@@ -39,6 +39,9 @@ public:
/// Pointer to an outbound path.
/// Pointer to an outbound path.
using
path_ptr
=
path_type
*
;
using
path_ptr
=
path_type
*
;
/// Pointer to an immutable outbound path.
using
const_path_ptr
=
const
path_type
*
;
/// Unique pointer to an outbound path.
/// Unique pointer to an outbound path.
using
unique_path_ptr
=
std
::
unique_ptr
<
path_type
>
;
using
unique_path_ptr
=
std
::
unique_ptr
<
path_type
>
;
...
@@ -95,6 +98,9 @@ public:
...
@@ -95,6 +98,9 @@ public:
for_each_path_impl
(
g
);
for_each_path_impl
(
g
);
}
}
/// Returns all used slots.
std
::
vector
<
stream_slot
>
path_slots
();
/// Checks whether `predicate` holds true for all paths.
/// Checks whether `predicate` holds true for all paths.
template
<
class
Predicate
>
template
<
class
Predicate
>
bool
all_paths
(
Predicate
predicate
)
const
noexcept
{
bool
all_paths
(
Predicate
predicate
)
const
noexcept
{
...
@@ -120,20 +126,32 @@ public:
...
@@ -120,20 +126,32 @@ public:
/// @returns The added path on success, `nullptr` otherwise.
/// @returns The added path on success, `nullptr` otherwise.
path_ptr
add_path
(
stream_slot
slot
,
strong_actor_ptr
target
);
path_ptr
add_path
(
stream_slot
slot
,
strong_actor_ptr
target
);
/// Removes a path from the manager
and returns it
.
/// Removes a path from the manager.
virtual
bool
remove_path
(
stream_slot
slot
,
error
reason
,
virtual
bool
remove_path
(
stream_slot
slot
,
error
reason
,
bool
silent
)
noexcept
;
bool
silent
)
noexcept
;
/// Returns the path associated to `slot` or `nullptr`.
/// Returns the path associated to `slot` or `nullptr`.
virtual
path_ptr
path
(
stream_slot
slot
)
noexcept
;
virtual
path_ptr
path
(
stream_slot
slot
)
noexcept
;
/// Returns `true` if there is no data pending and no unacknowledged batch on
/// Returns the path associated to `slot` or `nullptr`.
/// any path.
const_path_ptr
path
(
stream_slot
slot
)
const
noexcept
;
/// Returns `true` if there is no data pending and all batches are
/// acknowledged batch on all paths.
bool
clean
()
const
noexcept
;
bool
clean
()
const
noexcept
;
/// Returns `true` if `slot` is unknown or if there is no data pending and
/// all batches are acknowledged on `slot`. The default implementation
/// returns `false` for all paths, even if `clean()` return `true`.
bool
clean
(
stream_slot
slot
)
const
noexcept
;
/// Removes all paths gracefully.
/// Removes all paths gracefully.
virtual
void
close
();
virtual
void
close
();
/// Removes path `slot` gracefully by sending pending batches before removing
/// it. Effectively calls `path(slot)->closing = true`.
virtual
void
close
(
stream_slot
slot
);
/// Removes all paths with an error message.
/// Removes all paths with an error message.
virtual
void
abort
(
error
reason
);
virtual
void
abort
(
error
reason
);
...
@@ -165,6 +183,9 @@ public:
...
@@ -165,6 +183,9 @@ public:
/// Queries the size of the output buffer.
/// Queries the size of the output buffer.
virtual
size_t
buffered
()
const
noexcept
;
virtual
size_t
buffered
()
const
noexcept
;
/// Queries an estimate of the size of the output buffer for `slot`.
virtual
size_t
buffered
(
stream_slot
slot
)
const
noexcept
;
/// Queries whether the manager cannot make any progress, because its buffer
/// Queries whether the manager cannot make any progress, because its buffer
/// is full and no more credit is available.
/// is full and no more credit is available.
bool
stalled
()
const
noexcept
;
bool
stalled
()
const
noexcept
;
...
...
libcaf_core/caf/outbound_path.hpp
View file @
4f917888
...
@@ -140,6 +140,11 @@ public:
...
@@ -140,6 +140,11 @@ public:
return
slots
.
receiver
==
invalid_stream_slot
;
return
slots
.
receiver
==
invalid_stream_slot
;
}
}
/// Returns whether no pending ACKs exist.
inline
bool
clean
()
const
noexcept
{
return
next_batch_id
==
next_ack_id
;
}
// -- member variables -------------------------------------------------------
// -- member variables -------------------------------------------------------
/// Slot IDs for sender (self) and receiver (hdl).
/// Slot IDs for sender (self) and receiver (hdl).
...
@@ -160,6 +165,13 @@ public:
...
@@ -160,6 +165,13 @@ public:
/// ID of the first unacknowledged batch. Note that CAF uses accumulative
/// ID of the first unacknowledged batch. Note that CAF uses accumulative
/// ACKs, i.e., receiving an ACK with a higher ID is not an error.
/// ACKs, i.e., receiving an ACK with a higher ID is not an error.
int64_t
next_ack_id
;
int64_t
next_ack_id
;
/// Stores whether an outbound path is marked for removal. The
/// `downstream_manger` no longer sends new batches to a closing path, but
/// buffered batches are still shipped. The owning `stream_manager` removes
/// the path when receiving an `upstream_msg::ack_batch` and no pending
/// batches for this path exist.
bool
closing
;
};
};
/// @relates outbound_path
/// @relates outbound_path
...
...
libcaf_core/src/downstream_manager.cpp
View file @
4f917888
...
@@ -64,6 +64,15 @@ bool downstream_manager::terminal() const noexcept {
...
@@ -64,6 +64,15 @@ bool downstream_manager::terminal() const noexcept {
// -- path management ----------------------------------------------------------
// -- path management ----------------------------------------------------------
std
::
vector
<
stream_slot
>
downstream_manager
::
path_slots
()
{
std
::
vector
<
stream_slot
>
xs
;
xs
.
reserve
(
num_paths
());
for_each_path
([
&
](
outbound_path
&
x
)
{
xs
.
emplace_back
(
x
.
slots
.
sender
);
});
return
xs
;
}
size_t
downstream_manager
::
num_paths
()
const
noexcept
{
size_t
downstream_manager
::
num_paths
()
const
noexcept
{
return
0
;
return
0
;
}
}
...
@@ -80,21 +89,47 @@ bool downstream_manager::remove_path(stream_slot, error, bool) noexcept {
...
@@ -80,21 +89,47 @@ bool downstream_manager::remove_path(stream_slot, error, bool) noexcept {
return
false
;
return
false
;
}
}
downstream_manager
::
const_path_ptr
downstream_manager
::
path
(
stream_slot
slot
)
const
noexcept
{
// The `const` is restored when returning the pointer.
return
const_cast
<
downstream_manager
&>
(
*
this
).
path
(
slot
);
}
auto
downstream_manager
::
path
(
stream_slot
)
noexcept
->
path_ptr
{
auto
downstream_manager
::
path
(
stream_slot
)
noexcept
->
path_ptr
{
return
nullptr
;
return
nullptr
;
}
}
bool
downstream_manager
::
clean
()
const
noexcept
{
bool
downstream_manager
::
clean
()
const
noexcept
{
auto
pred
=
[](
const
outbound_path
&
x
)
{
auto
pred
=
[](
const
outbound_path
&
x
)
{
return
x
.
next_batch_id
==
x
.
next_ack_id
;
return
x
.
clean
()
;
};
};
return
buffered
()
==
0
&&
all_paths
(
pred
);
return
buffered
()
==
0
&&
all_paths
(
pred
);
}
}
bool
downstream_manager
::
clean
(
stream_slot
slot
)
const
noexcept
{
auto
ptr
=
path
(
slot
);
return
ptr
!=
nullptr
?
buffered
(
slot
)
==
0
&&
ptr
->
clean
()
:
false
;
}
void
downstream_manager
::
close
()
{
void
downstream_manager
::
close
()
{
CAF_LOG_TRACE
(
""
);
CAF_LOG_TRACE
(
""
);
for_each_path
([
&
](
outbound_path
&
x
)
{
about_to_erase
(
&
x
,
false
,
nullptr
);
});
if
(
clean
())
{
clear_paths
();
for_each_path
([
&
](
outbound_path
&
x
)
{
about_to_erase
(
&
x
,
false
,
nullptr
);
});
clear_paths
();
}
else
{
for_each_path
([
&
](
outbound_path
&
x
)
{
x
.
closing
=
true
;
});
}
}
void
downstream_manager
::
close
(
stream_slot
slot
)
{
CAF_LOG_TRACE
(
CAF_ARG
(
slot
));
if
(
clean
(
slot
))
remove_path
(
slot
,
none
,
false
);
else
{
auto
ptr
=
path
(
slot
);
if
(
ptr
!=
nullptr
)
ptr
->
closing
=
true
;
}
}
}
void
downstream_manager
::
abort
(
error
reason
)
{
void
downstream_manager
::
abort
(
error
reason
)
{
...
@@ -148,6 +183,10 @@ size_t downstream_manager::buffered() const noexcept {
...
@@ -148,6 +183,10 @@ size_t downstream_manager::buffered() const noexcept {
return
0
;
return
0
;
}
}
size_t
downstream_manager
::
buffered
(
stream_slot
)
const
noexcept
{
return
0
;
}
bool
downstream_manager
::
stalled
()
const
noexcept
{
bool
downstream_manager
::
stalled
()
const
noexcept
{
auto
no_credit
=
[](
const
outbound_path
&
x
)
{
auto
no_credit
=
[](
const
outbound_path
&
x
)
{
return
x
.
open_credit
==
0
;
return
x
.
open_credit
==
0
;
...
...
libcaf_core/src/outbound_path.cpp
View file @
4f917888
...
@@ -32,7 +32,8 @@ outbound_path::outbound_path(stream_slot sender_slot,
...
@@ -32,7 +32,8 @@ outbound_path::outbound_path(stream_slot sender_slot,
next_batch_id
(
1
),
next_batch_id
(
1
),
open_credit
(
0
),
open_credit
(
0
),
desired_batch_size
(
50
),
desired_batch_size
(
50
),
next_ack_id
(
1
)
{
next_ack_id
(
1
),
closing
(
false
)
{
// nop
// nop
}
}
...
...
libcaf_core/src/stream_manager.cpp
View file @
4f917888
...
@@ -92,6 +92,9 @@ void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) {
...
@@ -92,6 +92,9 @@ void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) {
path
->
open_credit
+=
x
.
new_capacity
;
path
->
open_credit
+=
x
.
new_capacity
;
path
->
desired_batch_size
=
x
.
desired_batch_size
;
path
->
desired_batch_size
=
x
.
desired_batch_size
;
path
->
next_ack_id
=
x
.
acknowledged_id
+
1
;
path
->
next_ack_id
=
x
.
acknowledged_id
+
1
;
// Gravefully remove path after receiving its final ACK.
if
(
path
->
closing
&&
out
().
clean
(
slots
.
receiver
))
out
().
remove_path
(
slots
.
receiver
,
none
,
false
);
push
();
push
();
}
}
}
}
...
...
libcaf_core/test/continuous_streaming.cpp
View file @
4f917888
...
@@ -34,10 +34,18 @@ using namespace caf;
...
@@ -34,10 +34,18 @@ using namespace caf;
namespace
{
namespace
{
/// Returns the sum of natural numbers up until `n`, i.e., 1 + 2 + ... + n.
int
sum
(
int
n
)
{
return
(
n
*
(
n
+
1
))
/
2
;
}
TESTEE_SETUP
();
TESTEE_SETUP
();
TESTEE_STATE
(
file_reader
)
{
std
::
vector
<
int
>
buf
;
};
VARARGS_TESTEE
(
file_reader
,
size_t
buf_size
)
{
VARARGS_TESTEE
(
file_reader
,
size_t
buf_size
)
{
using
buf
=
std
::
deque
<
int
>
;
return
{
return
{
[
=
](
string
&
fname
)
->
output_stream
<
int
,
string
>
{
[
=
](
string
&
fname
)
->
output_stream
<
int
,
string
>
{
CAF_CHECK_EQUAL
(
fname
,
"numbers.txt"
);
CAF_CHECK_EQUAL
(
fname
,
"numbers.txt"
);
...
@@ -46,12 +54,14 @@ VARARGS_TESTEE(file_reader, size_t buf_size) {
...
@@ -46,12 +54,14 @@ VARARGS_TESTEE(file_reader, size_t buf_size) {
// forward file name in handshake to next stage
// forward file name in handshake to next stage
std
::
forward_as_tuple
(
std
::
move
(
fname
)),
std
::
forward_as_tuple
(
std
::
move
(
fname
)),
// initialize state
// initialize state
[
=
](
buf
&
xs
)
{
[
=
](
unit_t
&
)
{
auto
&
xs
=
self
->
state
.
buf
;
xs
.
resize
(
buf_size
);
xs
.
resize
(
buf_size
);
std
::
iota
(
xs
.
begin
(),
xs
.
end
(),
1
);
std
::
iota
(
xs
.
begin
(),
xs
.
end
(),
1
);
},
},
// get next element
// get next element
[](
buf
&
xs
,
downstream
<
int
>&
out
,
size_t
num
)
{
[
=
](
unit_t
&
,
downstream
<
int
>&
out
,
size_t
num
)
{
auto
&
xs
=
self
->
state
.
buf
;
CAF_MESSAGE
(
"push "
<<
num
<<
" messages downstream"
);
CAF_MESSAGE
(
"push "
<<
num
<<
" messages downstream"
);
auto
n
=
std
::
min
(
num
,
xs
.
size
());
auto
n
=
std
::
min
(
num
,
xs
.
size
());
for
(
size_t
i
=
0
;
i
<
n
;
++
i
)
for
(
size_t
i
=
0
;
i
<
n
;
++
i
)
...
@@ -59,8 +69,8 @@ VARARGS_TESTEE(file_reader, size_t buf_size) {
...
@@ -59,8 +69,8 @@ VARARGS_TESTEE(file_reader, size_t buf_size) {
xs
.
erase
(
xs
.
begin
(),
xs
.
begin
()
+
static_cast
<
ptrdiff_t
>
(
n
));
xs
.
erase
(
xs
.
begin
(),
xs
.
begin
()
+
static_cast
<
ptrdiff_t
>
(
n
));
},
},
// check whether we reached the end
// check whether we reached the end
[
=
](
const
buf
&
xs
)
{
[
=
](
const
unit_t
&
)
{
if
(
xs
.
empty
())
{
if
(
self
->
state
.
buf
.
empty
())
{
CAF_MESSAGE
(
self
->
name
()
<<
" is done"
);
CAF_MESSAGE
(
self
->
name
()
<<
" is done"
);
return
true
;
return
true
;
}
}
...
@@ -132,6 +142,10 @@ TESTEE(stream_multiplexer) {
...
@@ -132,6 +142,10 @@ TESTEE(stream_multiplexer) {
CAF_CHECK_EQUAL
(
fname
,
"numbers.txt"
);
CAF_CHECK_EQUAL
(
fname
,
"numbers.txt"
);
return
self
->
state
.
stage
->
add_inbound_path
(
in
);
return
self
->
state
.
stage
->
add_inbound_path
(
in
);
},
},
[
=
](
close_atom
,
int
sink_index
)
{
auto
&
out
=
self
->
state
.
stage
->
out
();
out
.
close
(
out
.
path_slots
().
at
(
static_cast
<
size_t
>
(
sink_index
)));
},
};
};
}
}
...
@@ -190,4 +204,44 @@ CAF_TEST(depth_3_pipeline_with_join) {
...
@@ -190,4 +204,44 @@ CAF_TEST(depth_3_pipeline_with_join) {
self
->
send_exit
(
stg
,
exit_reason
::
kill
);
self
->
send_exit
(
stg
,
exit_reason
::
kill
);
}
}
CAF_TEST
(
closing_downstreams_before_end_of_stream
)
{
auto
src
=
sys
.
spawn
(
file_reader
,
10000u
);
auto
stg
=
sys
.
spawn
(
stream_multiplexer
);
auto
snk1
=
sys
.
spawn
(
sum_up
);
auto
snk2
=
sys
.
spawn
(
sum_up
);
auto
&
st
=
deref
<
stream_multiplexer_actor
>
(
stg
).
state
;
CAF_MESSAGE
(
"connect sinks to the stage (fork)"
);
self
->
send
(
snk1
,
join_atom
::
value
,
stg
);
self
->
send
(
snk2
,
join_atom
::
value
,
stg
);
sched
.
run
();
CAF_CHECK_EQUAL
(
st
.
stage
->
out
().
num_paths
(),
2u
);
CAF_MESSAGE
(
"connect source to the stage (fork)"
);
self
->
send
(
stg
*
src
,
"numbers.txt"
);
sched
.
run
();
CAF_CHECK_EQUAL
(
st
.
stage
->
out
().
num_paths
(),
2u
);
CAF_CHECK_EQUAL
(
st
.
stage
->
inbound_paths
().
size
(),
1u
);
CAF_MESSAGE
(
"do a single round of credit"
);
sched
.
clock
().
current_time
+=
streaming_cycle
;
sched
.
dispatch
();
sched
.
run
();
CAF_MESSAGE
(
"make sure the stream isn't done yet"
);
CAF_REQUIRE
(
!
deref
<
file_reader_actor
>
(
src
).
state
.
buf
.
empty
());
CAF_CHECK_EQUAL
(
st
.
stage
->
out
().
num_paths
(),
2u
);
CAF_CHECK_EQUAL
(
st
.
stage
->
inbound_paths
().
size
(),
1u
);
CAF_MESSAGE
(
"get the next not-yet-buffered integer"
);
auto
next_pending
=
deref
<
file_reader_actor
>
(
src
).
state
.
buf
.
front
();
CAF_REQUIRE_GREATER
(
next_pending
,
0
);
auto
sink1_result
=
sum
(
next_pending
-
1
);
CAF_MESSAGE
(
"gracefully close sink 1, next pending: "
<<
next_pending
);
self
->
send
(
stg
,
close_atom
::
value
,
0
);
expect
((
atom_value
,
int
),
from
(
self
).
to
(
stg
));
CAF_MESSAGE
(
"ship remaining elements"
);
run_exhaustively
();
CAF_CHECK_EQUAL
(
st
.
stage
->
out
().
num_paths
(),
1u
);
CAF_CHECK_EQUAL
(
st
.
stage
->
inbound_paths
().
size
(),
0u
);
CAF_CHECK_LESS
(
deref
<
sum_up_actor
>
(
snk1
).
state
.
x
,
sink1_result
);
CAF_CHECK_EQUAL
(
deref
<
sum_up_actor
>
(
snk2
).
state
.
x
,
sum
(
10000
));
self
->
send_exit
(
stg
,
exit_reason
::
kill
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
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