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
900049d2
Commit
900049d2
authored
Nov 19, 2022
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve coverage of from_resource tests
parent
3635f1c8
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
146 additions
and
7 deletions
+146
-7
libcaf_core/caf/flow/op/from_resource.hpp
libcaf_core/caf/flow/op/from_resource.hpp
+8
-6
libcaf_core/test/flow/generation.cpp
libcaf_core/test/flow/generation.cpp
+138
-1
No files found.
libcaf_core/caf/flow/op/from_resource.hpp
View file @
900049d2
...
...
@@ -35,8 +35,10 @@ public:
}
~
from_resource_sub
()
{
if
(
buf_
)
buf_
->
cancel
();
// The buffer points back to this object as consumer, so this cannot be
// destroyed unless we have called buf_->cancel(). All code paths that do
// call cancel() on the buffer also must set the variable to `nullptr`.
CAF_ASSERT
(
buf_
==
nullptr
);
ctx_
->
deref_execution_context
();
}
...
...
@@ -51,7 +53,7 @@ public:
if
(
!
disposed_
)
{
disposed_
=
true
;
if
(
!
running_
)
do_
cancel
();
do_
dispose
();
}
}
...
...
@@ -114,7 +116,7 @@ private:
}
}
void
do_
cancel
()
{
void
do_
dispose
()
{
if
(
buf_
)
{
buf_
->
cancel
();
buf_
=
nullptr
;
...
...
@@ -129,7 +131,7 @@ private:
CAF_LOG_TRACE
(
""
);
auto
guard
=
detail
::
make_scope_guard
([
this
]
{
running_
=
false
;
});
if
(
disposed_
)
{
do_
cancel
();
do_
dispose
();
return
;
}
CAF_ASSERT
(
out_
);
...
...
@@ -142,7 +144,7 @@ private:
disposed_
=
true
;
return
;
}
else
if
(
disposed_
)
{
do_
cancel
();
do_
dispose
();
return
;
}
else
if
(
pulled
==
0
)
{
return
;
...
...
libcaf_core/test/flow/generation.cpp
View file @
900049d2
...
...
@@ -8,6 +8,7 @@
#include "core-test.hpp"
#include "caf/async/blocking_producer.hpp"
#include "caf/flow/coordinator.hpp"
#include "caf/flow/merge.hpp"
#include "caf/flow/observable_builder.hpp"
...
...
@@ -192,6 +193,142 @@ SCENARIO("callable sources stream values generated from a function object") {
}
}
SCENARIO
(
"asynchronous buffers can generate flow items"
)
{
GIVEN
(
"a background thread writing into an async buffer"
)
{
auto
producer_impl
=
[](
async
::
producer_resource
<
int
>
res
,
bool
*
cancelled
)
{
auto
producer
=
async
::
make_blocking_producer
(
std
::
move
(
res
));
if
(
!
producer
)
CAF_FAIL
(
"make_blocking_producer failed"
);
for
(
int
i
=
1
;
i
<=
713
;
++
i
)
{
if
(
!
producer
->
push
(
i
))
{
*
cancelled
=
true
;
return
;
}
}
*
cancelled
=
false
;
};
WHEN
(
"reading all values from the buffer"
)
{
THEN
(
"the observer receives all produced values"
)
{
auto
cancelled
=
false
;
auto
[
pull
,
push
]
=
async
::
make_spsc_buffer_resource
<
int
>
();
auto
bg_thread
=
std
::
thread
{
producer_impl
,
push
,
&
cancelled
};
auto
res
=
ivec
{};
ctx
->
make_observable
()
//
.
from_resource
(
pull
)
.
take
(
777
)
.
for_each
([
&
res
](
int
val
)
{
res
.
push_back
(
val
);
});
ctx
->
run
();
CHECK_EQ
(
res
,
iota_vec
(
713
));
bg_thread
.
join
();
CHECK
(
!
cancelled
);
}
}
WHEN
(
"reading only a subset of values from the buffer"
)
{
THEN
(
"producer receives a cancel event after the selected items"
)
{
auto
cancelled
=
false
;
auto
[
pull
,
push
]
=
async
::
make_spsc_buffer_resource
<
int
>
();
auto
bg_thread
=
std
::
thread
{
producer_impl
,
push
,
&
cancelled
};
auto
res
=
ivec
{};
ctx
->
make_observable
()
//
.
from_resource
(
pull
)
.
take
(
20
)
.
for_each
([
&
res
](
int
val
)
{
res
.
push_back
(
val
);
});
ctx
->
run
();
CHECK_EQ
(
res
,
iota_vec
(
20
));
bg_thread
.
join
();
CHECK
(
cancelled
);
}
}
WHEN
(
"canceling the subscription to the buffer"
)
{
THEN
(
"the producer receives a cancel event"
)
{
auto
cancelled
=
false
;
auto
[
pull
,
push
]
=
async
::
make_spsc_buffer_resource
<
int
>
();
auto
res
=
ivec
{};
auto
sub
=
ctx
->
make_observable
()
//
.
from_resource
(
pull
)
.
take
(
777
)
.
for_each
([
&
res
](
int
val
)
{
res
.
push_back
(
val
);
});
// Run initial actions to handle events from the initial request()
// calls. Without this step, from_resource is in `running_` state and we
// won't hit the code paths for disposing a "cold" object. This is also
// why we spin up the thread later: making sure we're hitting the code
// paths we want to test here.
ctx
->
run_some
();
auto
bg_thread
=
std
::
thread
{
producer_impl
,
push
,
&
cancelled
};
sub
.
dispose
();
ctx
->
run
();
CHECK
(
res
.
empty
());
bg_thread
.
join
();
CHECK
(
cancelled
);
}
}
}
GIVEN
(
"a null-resource"
)
{
WHEN
(
"trying to read from it"
)
{
THEN
(
"the observer receives an error"
)
{
auto
res
=
ivec
{};
auto
err
=
error
{};
auto
pull
=
async
::
consumer_resource
<
int
>
{};
ctx
->
make_observable
()
//
.
from_resource
(
pull
)
.
take
(
713
)
.
do_on_error
([
&
err
](
const
error
&
what
)
{
err
=
what
;
})
.
for_each
([
&
res
](
int
val
)
{
res
.
push_back
(
val
);
});
ctx
->
run
();
CHECK
(
res
.
empty
());
CHECK
(
err
);
}
}
}
GIVEN
(
"a resource that has already been accessed"
)
{
WHEN
(
"trying to read from it"
)
{
THEN
(
"the observer receives an error"
)
{
auto
[
pull
,
push
]
=
async
::
make_spsc_buffer_resource
<
int
>
();
auto
pull_cpy
=
pull
;
auto
buf
=
pull_cpy
.
try_open
();
CHECK
(
buf
!=
nullptr
);
auto
res
=
ivec
{};
auto
err
=
error
{};
ctx
->
make_observable
()
//
.
from_resource
(
pull
)
.
take
(
713
)
.
do_on_error
([
&
err
](
const
error
&
what
)
{
err
=
what
;
})
.
for_each
([
&
res
](
int
val
)
{
res
.
push_back
(
val
);
});
ctx
->
run
();
CHECK
(
res
.
empty
());
CHECK
(
err
);
}
}
}
GIVEN
(
"a from_resource_sub object"
)
{
WHEN
(
"manipulating its ref count as consumer or disposable"
)
{
THEN
(
"the different pointer types manipulate the same ref count"
)
{
using
buf_t
=
async
::
spsc_buffer
<
int
>
;
using
impl_t
=
flow
::
op
::
from_resource_sub
<
buf_t
>
;
auto
ptr
=
make_counted
<
impl_t
>
(
ctx
.
get
(),
nullptr
,
flow
::
observer
<
int
>::
ignore
());
CHECK_EQ
(
ptr
->
get_reference_count
(),
1u
);
{
auto
sub
=
flow
::
subscription
{
ptr
.
get
()};
CHECK_EQ
(
ptr
->
get_reference_count
(),
2u
);
}
CHECK_EQ
(
ptr
->
get_reference_count
(),
1u
);
{
auto
cptr
=
async
::
consumer_ptr
{
ptr
.
get
()};
CHECK_EQ
(
ptr
->
get_reference_count
(),
2u
);
}
CHECK_EQ
(
ptr
->
get_reference_count
(),
1u
);
}
}
}
}
namespace
{
class
custom_generator
{
...
...
@@ -216,7 +353,7 @@ private:
}
// namespace
SCENARIO
(
"
lifting converts a generator into an observable
"
)
{
SCENARIO
(
"
users can provide custom generators
"
)
{
GIVEN
(
"a lifted implementation of the generator concept"
)
{
WHEN
(
"subscribing to its output"
)
{
THEN
(
"the observer receives the generated values"
)
{
...
...
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