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
86d0fb7e
Commit
86d0fb7e
authored
Feb 07, 2023
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More op::buffer coverage; make test code private
parent
335d3a2d
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
732 additions
and
177 deletions
+732
-177
libcaf_core/caf/error.hpp
libcaf_core/caf/error.hpp
+12
-0
libcaf_core/caf/flow/observer.hpp
libcaf_core/caf/flow/observer.hpp
+0
-129
libcaf_core/caf/flow/op/buffer.hpp
libcaf_core/caf/flow/op/buffer.hpp
+51
-45
libcaf_core/test/core-test.cpp
libcaf_core/test/core-test.cpp
+61
-0
libcaf_core/test/core-test.hpp
libcaf_core/test/core-test.hpp
+221
-0
libcaf_core/test/flow/buffer.cpp
libcaf_core/test/flow/buffer.cpp
+387
-3
No files found.
libcaf_core/caf/error.hpp
View file @
86d0fb7e
...
@@ -145,6 +145,18 @@ public:
...
@@ -145,6 +145,18 @@ public:
int
compare
(
uint8_t
code
,
type_id_t
category
)
const
noexcept
;
int
compare
(
uint8_t
code
,
type_id_t
category
)
const
noexcept
;
/// Returns a copy of `this` if `!empty()` or else returns a new error from
/// given arguments.
template
<
class
Enum
,
class
...
Ts
>
error
or_else
(
Enum
code
,
Ts
&&
...
args
)
const
{
if
(
!
empty
())
return
*
this
;
if
constexpr
(
sizeof
...(
Ts
)
>
0
)
return
error
{
code
,
make_message
(
std
::
forward
<
Ts
>
(
args
)...)};
else
return
error
{
code
};
}
// -- modifiers --------------------------------------------------------------
// -- modifiers --------------------------------------------------------------
/// Reverts this error to "not an error" as if calling `*this = error{}`.
/// Reverts this error to "not an error" as if calling `*this = error{}`.
...
...
libcaf_core/caf/flow/observer.hpp
View file @
86d0fb7e
...
@@ -522,133 +522,4 @@ private:
...
@@ -522,133 +522,4 @@ private:
Token
token_
;
Token
token_
;
};
};
/// An observer with minimal internal logic. Useful for writing unit tests.
template
<
class
T
>
class
passive_observer
:
public
observer_impl_base
<
T
>
{
public:
// -- implementation of observer_impl<T> -------------------------------------
void
on_complete
()
override
{
if
(
sub
)
{
sub
.
dispose
();
sub
=
nullptr
;
}
state
=
observer_state
::
completed
;
}
void
on_error
(
const
error
&
what
)
override
{
if
(
sub
)
{
sub
.
dispose
();
sub
=
nullptr
;
}
err
=
what
;
state
=
observer_state
::
aborted
;
}
void
on_subscribe
(
subscription
new_sub
)
override
{
if
(
state
==
observer_state
::
idle
)
{
CAF_ASSERT
(
!
sub
);
sub
=
std
::
move
(
new_sub
);
state
=
observer_state
::
subscribed
;
}
else
{
new_sub
.
dispose
();
}
}
void
on_next
(
const
T
&
item
)
override
{
buf
.
emplace_back
(
item
);
}
// -- convenience functions --------------------------------------------------
bool
request
(
size_t
demand
)
{
if
(
sub
)
{
sub
.
request
(
demand
);
return
true
;
}
else
{
return
false
;
}
}
void
unsubscribe
()
{
if
(
sub
)
{
sub
.
dispose
();
state
=
observer_state
::
idle
;
}
}
bool
idle
()
const
noexcept
{
return
state
==
observer_state
::
idle
;
}
bool
subscribed
()
const
noexcept
{
return
state
==
observer_state
::
subscribed
;
}
bool
completed
()
const
noexcept
{
return
state
==
observer_state
::
completed
;
}
bool
aborted
()
const
noexcept
{
return
state
==
observer_state
::
aborted
;
}
std
::
vector
<
T
>
sorted_buf
()
const
{
auto
result
=
buf
;
std
::
sort
(
result
.
begin
(),
result
.
end
());
return
result
;
}
// -- member variables -------------------------------------------------------
/// The subscription for requesting additional items.
subscription
sub
;
/// Default-constructed unless on_error was called.
error
err
;
/// Represents the current state of this observer.
observer_state
state
=
observer_state
::
idle
;
/// Stores all items received via `on_next`.
std
::
vector
<
T
>
buf
;
};
/// @relates passive_observer
template
<
class
T
>
intrusive_ptr
<
passive_observer
<
T
>>
make_passive_observer
()
{
return
make_counted
<
passive_observer
<
T
>>
();
}
/// Similar to @ref passive_observer but automatically requests items until
/// completed. Useful for writing unit tests.
template
<
class
T
>
class
auto_observer
:
public
passive_observer
<
T
>
{
public:
// -- implementation of observer_impl<T> -------------------------------------
void
on_subscribe
(
subscription
new_sub
)
override
{
if
(
this
->
state
==
observer_state
::
idle
)
{
CAF_ASSERT
(
!
this
->
sub
);
this
->
sub
=
std
::
move
(
new_sub
);
this
->
state
=
observer_state
::
subscribed
;
this
->
sub
.
request
(
64
);
}
else
{
new_sub
.
dispose
();
}
}
void
on_next
(
const
T
&
item
)
override
{
this
->
buf
.
emplace_back
(
item
);
if
(
this
->
sub
)
this
->
sub
.
request
(
1
);
}
};
/// @relates auto_observer
template
<
class
T
>
intrusive_ptr
<
auto_observer
<
T
>>
make_auto_observer
()
{
return
make_counted
<
auto_observer
<
T
>>
();
}
}
// namespace caf::flow
}
// namespace caf::flow
libcaf_core/caf/flow/op/buffer.hpp
View file @
86d0fb7e
...
@@ -69,6 +69,24 @@ public:
...
@@ -69,6 +69,24 @@ public:
// nop
// nop
}
}
// -- properties -------------------------------------------------------------
bool
running
()
const
noexcept
{
return
state_
==
state
::
running
;
}
const
error
&
err
()
const
noexcept
{
return
err_
;
}
size_t
pending
()
const
noexcept
{
return
buf_
.
size
();
}
bool
can_emit
()
const
noexcept
{
return
buf_
.
size
()
==
max_buf_size_
||
has_shut_down
(
state_
);
}
// -- callbacks for the parent -----------------------------------------------
// -- callbacks for the parent -----------------------------------------------
void
init
(
observable
<
input_type
>
vals
,
observable
<
select_token_type
>
ctrl
)
{
void
init
(
observable
<
input_type
>
vals
,
observable
<
select_token_type
>
ctrl
)
{
...
@@ -76,21 +94,22 @@ public:
...
@@ -76,21 +94,22 @@ public:
using
ctrl_fwd_t
=
forwarder
<
select_token_type
,
buffer_sub
,
buffer_emit_t
>
;
using
ctrl_fwd_t
=
forwarder
<
select_token_type
,
buffer_sub
,
buffer_emit_t
>
;
vals
.
subscribe
(
vals
.
subscribe
(
make_counted
<
val_fwd_t
>
(
this
,
buffer_input_t
{})
->
as_observer
());
make_counted
<
val_fwd_t
>
(
this
,
buffer_input_t
{})
->
as_observer
());
ctrl
.
subscribe
(
// Note: the previous subscribe might call on_error, in which case we don't
make_counted
<
ctrl_fwd_t
>
(
this
,
buffer_emit_t
{})
->
as_observer
());
// need to try to subscribe to the control observable.
if
(
running
())
ctrl
.
subscribe
(
make_counted
<
ctrl_fwd_t
>
(
this
,
buffer_emit_t
{})
->
as_observer
());
}
}
// -- callbacks for the forwarders -------------------------------------------
// -- callbacks for the forwarders -------------------------------------------
void
fwd_on_subscribe
(
buffer_input_t
,
subscription
sub
)
{
void
fwd_on_subscribe
(
buffer_input_t
,
subscription
sub
)
{
if
(
state_
!=
state
::
idle
||
value_sub_
||
!
out_
)
{
if
(
!
running
()
||
value_sub_
||
!
out_
)
{
sub
.
dispose
();
sub
.
dispose
();
return
;
return
;
}
}
value_sub_
=
std
::
move
(
sub
);
value_sub_
=
std
::
move
(
sub
);
value_sub_
.
request
(
max_buf_size_
);
value_sub_
.
request
(
max_buf_size_
);
if
(
control_sub_
)
state_
=
state
::
running
;
}
}
void
fwd_on_complete
(
buffer_input_t
)
{
void
fwd_on_complete
(
buffer_input_t
)
{
...
@@ -105,27 +124,20 @@ public:
...
@@ -105,27 +124,20 @@ public:
}
}
void
fwd_on_next
(
buffer_input_t
,
const
input_type
&
item
)
{
void
fwd_on_next
(
buffer_input_t
,
const
input_type
&
item
)
{
switch
(
state_
)
{
if
(
running
())
{
case
state
:
:
idle
:
buf_
.
push_back
(
item
);
case
state
:
:
running
:
if
(
buf_
.
size
()
==
max_buf_size_
)
buf_
.
push_back
(
item
);
do_emit
();
if
(
buf_
.
size
()
==
max_buf_size_
)
do_emit
();
break
;
default:
break
;
}
}
}
}
void
fwd_on_subscribe
(
buffer_emit_t
,
subscription
sub
)
{
void
fwd_on_subscribe
(
buffer_emit_t
,
subscription
sub
)
{
if
(
state_
!=
state
::
idle
||
control_sub_
||
!
out_
)
{
if
(
!
running
()
||
control_sub_
||
!
out_
)
{
sub
.
dispose
();
sub
.
dispose
();
return
;
return
;
}
}
control_sub_
=
std
::
move
(
sub
);
control_sub_
=
std
::
move
(
sub
);
control_sub_
.
request
(
1
);
control_sub_
.
request
(
1
);
if
(
value_sub_
)
state_
=
state
::
running
;
}
}
void
fwd_on_complete
(
buffer_emit_t
)
{
void
fwd_on_complete
(
buffer_emit_t
)
{
...
@@ -177,15 +189,10 @@ public:
...
@@ -177,15 +189,10 @@ public:
}
}
private:
private:
bool
can_emit
()
const
noexcept
{
return
buf_
.
size
()
==
max_buf_size_
||
has_shut_down
(
state_
);
}
void
shutdown
()
{
void
shutdown
()
{
value_sub_
.
dispose
();
value_sub_
.
dispose
();
control_sub_
.
dispose
();
control_sub_
.
dispose
();
switch
(
state_
)
{
switch
(
state_
)
{
case
state
:
:
idle
:
case
state
:
:
running
:
case
state
:
:
running
:
if
(
!
buf_
.
empty
())
{
if
(
!
buf_
.
empty
())
{
if
(
demand_
==
0
)
{
if
(
demand_
==
0
)
{
...
@@ -211,33 +218,27 @@ private:
...
@@ -211,33 +218,27 @@ private:
void
on_request
()
{
void
on_request
()
{
if
(
demand_
==
0
||
!
can_emit
())
if
(
demand_
==
0
||
!
can_emit
())
return
;
return
;
switch
(
state_
)
{
if
(
running
())
{
case
state
:
:
idle
:
CAF_ASSERT
(
buf_
.
size
()
==
max_buf_size_
);
case
state
:
:
running
:
do_emit
();
CAF_ASSERT
(
buf_
.
size
()
==
max_buf_size_
);
return
;
do_emit
();
break
;
case
state
:
:
completed
:
case
state
:
:
aborted
:
if
(
!
buf_
.
empty
())
do_emit
();
if
(
err_
)
out_
.
on_error
(
err_
);
else
out_
.
on_complete
();
out_
=
nullptr
;
break
;
default:
break
;
}
}
if
(
!
buf_
.
empty
())
do_emit
();
if
(
err_
)
out_
.
on_error
(
err_
);
else
out_
.
on_complete
();
out_
=
nullptr
;
}
}
void
do_emit
()
{
void
do_emit
()
{
CAF_ASSERT
(
demand_
>
0
);
if
(
demand_
==
0
)
return
;
Trait
f
;
Trait
f
;
--
demand_
;
--
demand_
;
out_
.
on_next
(
f
(
buf_
));
auto
buffered
=
buf_
.
size
();
auto
buffered
=
buf_
.
size
();
out_
.
on_next
(
f
(
buf_
));
buf_
.
clear
();
buf_
.
clear
();
if
(
value_sub_
&&
buffered
>
0
)
if
(
value_sub_
&&
buffered
>
0
)
value_sub_
.
request
(
buffered
);
value_sub_
.
request
(
buffered
);
...
@@ -277,12 +278,11 @@ private:
...
@@ -277,12 +278,11 @@ private:
size_t
demand_
=
0
;
size_t
demand_
=
0
;
/// Our current state.
/// Our current state.
/// - idle: until we have received both subscriptions.
/// - running: alive and ready to emit batches.
/// - running: emitting batches.
/// - completed: on_complete was called but some data is still buffered.
/// - completed: on_complete was called but some data is still buffered.
/// - aborted: on_error was called but some data is still buffered.
/// - aborted: on_error was called but some data is still buffered.
/// - disposed: inactive.
/// - disposed: inactive.
state
state_
=
state
::
idle
;
state
state_
=
state
::
running
;
/// Caches the abort reason.
/// Caches the abort reason.
error
err_
;
error
err_
;
...
@@ -318,6 +318,12 @@ public:
...
@@ -318,6 +318,12 @@ public:
disposable
subscribe
(
observer
<
output_type
>
out
)
override
{
disposable
subscribe
(
observer
<
output_type
>
out
)
override
{
auto
ptr
=
make_counted
<
buffer_sub
<
Trait
>>
(
super
::
ctx_
,
max_items_
,
out
);
auto
ptr
=
make_counted
<
buffer_sub
<
Trait
>>
(
super
::
ctx_
,
max_items_
,
out
);
ptr
->
init
(
in_
,
select_
);
ptr
->
init
(
in_
,
select_
);
if
(
!
ptr
->
running
())
{
auto
err
=
ptr
->
err
().
or_else
(
sec
::
runtime_error
,
"failed to initialize buffer subscription"
);
out
.
on_error
(
err
);
return
{};
}
out
.
on_subscribe
(
subscription
{
ptr
});
out
.
on_subscribe
(
subscription
{
ptr
});
return
ptr
->
as_disposable
();
return
ptr
->
as_disposable
();
}
}
...
...
libcaf_core/test/core-test.cpp
View file @
86d0fb7e
...
@@ -4,6 +4,67 @@
...
@@ -4,6 +4,67 @@
#include "core-test.hpp"
#include "core-test.hpp"
#include <atomic>
namespace
{
/// A trivial disposable with an atomic flag.
class
trivial_impl
:
public
caf
::
ref_counted
,
public
caf
::
disposable
::
impl
{
public:
trivial_impl
()
:
flag_
(
false
)
{
// nop
}
void
dispose
()
override
{
flag_
=
true
;
}
bool
disposed
()
const
noexcept
override
{
return
flag_
.
load
();
}
void
ref_disposable
()
const
noexcept
override
{
ref
();
}
void
deref_disposable
()
const
noexcept
override
{
deref
();
}
friend
void
intrusive_ptr_add_ref
(
const
trivial_impl
*
ptr
)
noexcept
{
ptr
->
ref
();
}
friend
void
intrusive_ptr_release
(
const
trivial_impl
*
ptr
)
noexcept
{
ptr
->
deref
();
}
private:
std
::
atomic
<
bool
>
flag_
;
};
}
// namespace
namespace
caf
::
flow
{
disposable
make_trivial_disposable
()
{
return
disposable
{
make_counted
<
trivial_impl
>
()};
}
void
passive_subscription_impl
::
request
(
size_t
n
)
{
demand
+=
n
;
}
void
passive_subscription_impl
::
dispose
()
{
disposed_flag
=
true
;
}
bool
passive_subscription_impl
::
disposed
()
const
noexcept
{
return
disposed_flag
;
}
}
// namespace caf::flow
std
::
string
to_string
(
level
lvl
)
{
std
::
string
to_string
(
level
lvl
)
{
switch
(
lvl
)
{
switch
(
lvl
)
{
case
level
:
:
all
:
case
level
:
:
all
:
...
...
libcaf_core/test/core-test.hpp
View file @
86d0fb7e
...
@@ -16,6 +16,227 @@
...
@@ -16,6 +16,227 @@
#include <string>
#include <string>
#include <utility>
#include <utility>
// -- utility for testing flows ------------------------------------------------
namespace
caf
::
flow
{
/// Returns a trivial disposable that wraps an atomic flag.
disposable
make_trivial_disposable
();
/// An observer with minimal internal logic. Useful for writing unit tests.
template
<
class
T
>
class
passive_observer
:
public
observer_impl_base
<
T
>
{
public:
// -- implementation of observer_impl<T> -------------------------------------
void
on_complete
()
override
{
if
(
sub
)
{
sub
.
dispose
();
sub
=
nullptr
;
}
state
=
observer_state
::
completed
;
}
void
on_error
(
const
error
&
what
)
override
{
if
(
sub
)
{
sub
.
dispose
();
sub
=
nullptr
;
}
err
=
what
;
state
=
observer_state
::
aborted
;
}
void
on_subscribe
(
subscription
new_sub
)
override
{
if
(
state
==
observer_state
::
idle
)
{
CAF_ASSERT
(
!
sub
);
sub
=
std
::
move
(
new_sub
);
state
=
observer_state
::
subscribed
;
}
else
{
new_sub
.
dispose
();
}
}
void
on_next
(
const
T
&
item
)
override
{
buf
.
emplace_back
(
item
);
}
// -- convenience functions --------------------------------------------------
bool
request
(
size_t
demand
)
{
if
(
sub
)
{
sub
.
request
(
demand
);
return
true
;
}
else
{
return
false
;
}
}
void
unsubscribe
()
{
if
(
sub
)
{
sub
.
dispose
();
state
=
observer_state
::
idle
;
}
}
bool
idle
()
const
noexcept
{
return
state
==
observer_state
::
idle
;
}
bool
subscribed
()
const
noexcept
{
return
state
==
observer_state
::
subscribed
;
}
bool
completed
()
const
noexcept
{
return
state
==
observer_state
::
completed
;
}
bool
aborted
()
const
noexcept
{
return
state
==
observer_state
::
aborted
;
}
std
::
vector
<
T
>
sorted_buf
()
const
{
auto
result
=
buf
;
std
::
sort
(
result
.
begin
(),
result
.
end
());
return
result
;
}
// -- member variables -------------------------------------------------------
/// The subscription for requesting additional items.
subscription
sub
;
/// Default-constructed unless on_error was called.
error
err
;
/// Represents the current state of this observer.
observer_state
state
=
observer_state
::
idle
;
/// Stores all items received via `on_next`.
std
::
vector
<
T
>
buf
;
};
/// @relates passive_observer
template
<
class
T
>
intrusive_ptr
<
passive_observer
<
T
>>
make_passive_observer
()
{
return
make_counted
<
passive_observer
<
T
>>
();
}
/// Similar to @ref passive_observer but automatically requests items until
/// completed. Useful for writing unit tests.
template
<
class
T
>
class
auto_observer
:
public
passive_observer
<
T
>
{
public:
// -- implementation of observer_impl<T> -------------------------------------
void
on_subscribe
(
subscription
new_sub
)
override
{
if
(
this
->
state
==
observer_state
::
idle
)
{
CAF_ASSERT
(
!
this
->
sub
);
this
->
sub
=
std
::
move
(
new_sub
);
this
->
state
=
observer_state
::
subscribed
;
this
->
sub
.
request
(
64
);
}
else
{
new_sub
.
dispose
();
}
}
void
on_next
(
const
T
&
item
)
override
{
this
->
buf
.
emplace_back
(
item
);
if
(
this
->
sub
)
this
->
sub
.
request
(
1
);
}
};
/// @relates auto_observer
template
<
class
T
>
intrusive_ptr
<
auto_observer
<
T
>>
make_auto_observer
()
{
return
make_counted
<
auto_observer
<
T
>>
();
}
/// A subscription implementation without internal logic.
class
passive_subscription_impl
final
:
public
subscription
::
impl_base
{
public:
/// Incremented by `request`.
size_t
demand
=
0
;
/// Flipped by `dispose`.
bool
disposed_flag
=
false
;
void
request
(
size_t
n
)
override
;
void
dispose
()
override
;
bool
disposed
()
const
noexcept
override
;
};
namespace
op
{
/// An observable that does nothing when subscribed except returning a trivial
/// disposable. Allows tests to call on_subscribe some time later.
template
<
class
T
>
class
nil_observable
:
public
op
::
cold
<
T
>
{
public:
using
super
=
op
::
cold
<
T
>
;
using
shared_count
=
std
::
shared_ptr
<
size_t
>
;
nil_observable
(
coordinator
*
ctx
,
shared_count
subscribe_count
)
:
super
(
ctx
),
subscribe_count_
(
std
::
move
(
subscribe_count
))
{
// nop
}
disposable
subscribe
(
observer
<
T
>
)
override
{
if
(
subscribe_count_
)
*
subscribe_count_
+=
1
;
return
make_trivial_disposable
();
}
shared_count
subscribe_count_
;
};
/// An observable that passes a trivial disposable to any observer.
template
<
class
T
>
class
trivial_observable
:
public
op
::
cold
<
T
>
{
public:
using
super
=
op
::
cold
<
T
>
;
using
shared_count
=
std
::
shared_ptr
<
size_t
>
;
trivial_observable
(
coordinator
*
ctx
,
shared_count
subscribe_count
)
:
super
(
ctx
),
subscribe_count_
(
std
::
move
(
subscribe_count
))
{
// nop
}
disposable
subscribe
(
observer
<
T
>
out
)
override
{
if
(
subscribe_count_
)
*
subscribe_count_
+=
1
;
auto
ptr
=
make_counted
<
passive_subscription_impl
>
();
out
.
on_subscribe
(
subscription
{
ptr
});
return
make_trivial_disposable
();
}
shared_count
subscribe_count_
;
};
}
// namespace op
template
<
class
T
>
observable
<
T
>
make_nil_observable
(
coordinator
*
ctx
,
std
::
shared_ptr
<
size_t
>
subscribe_count
=
nullptr
)
{
auto
ptr
=
make_counted
<
op
::
nil_observable
<
T
>>
(
ctx
,
subscribe_count
);
return
observable
<
T
>
{
std
::
move
(
ptr
)};
}
template
<
class
T
>
observable
<
T
>
make_trivial_observable
(
coordinator
*
ctx
,
std
::
shared_ptr
<
size_t
>
subscribe_count
=
nullptr
)
{
auto
ptr
=
make_counted
<
op
::
trivial_observable
<
T
>>
(
ctx
,
subscribe_count
);
return
observable
<
T
>
{
std
::
move
(
ptr
)};
}
}
// namespace caf::flow
// -- utility for testing serialization round-trips ----------------------------
// -- utility for testing serialization round-trips ----------------------------
template
<
class
T
>
template
<
class
T
>
...
...
libcaf_core/test/flow/buffer.cpp
View file @
86d0fb7e
This diff is collapsed.
Click to expand it.
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