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
899766cd
Commit
899766cd
authored
Nov 30, 2016
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add test coordinator and per-actor configs
parent
fdd24706
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
631 additions
and
102 deletions
+631
-102
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+1
-0
libcaf_core/caf/actor_system.hpp
libcaf_core/caf/actor_system.hpp
+10
-0
libcaf_core/caf/all.hpp
libcaf_core/caf/all.hpp
+1
-1
libcaf_core/caf/named_actor_config.hpp
libcaf_core/caf/named_actor_config.hpp
+51
-0
libcaf_core/caf/scheduler/test_coordinator.hpp
libcaf_core/caf/scheduler/test_coordinator.hpp
+91
-0
libcaf_core/src/actor_system.cpp
libcaf_core/src/actor_system.cpp
+7
-0
libcaf_core/src/test_coordinator.cpp
libcaf_core/src/test_coordinator.cpp
+173
-0
libcaf_core/test/request_timeout.cpp
libcaf_core/test/request_timeout.cpp
+269
-92
libcaf_core/test/simple_timeout.cpp
libcaf_core/test/simple_timeout.cpp
+28
-9
No files found.
libcaf_core/CMakeLists.txt
View file @
899766cd
...
@@ -88,6 +88,7 @@ set (LIBCAF_CORE_SRCS
...
@@ -88,6 +88,7 @@ set (LIBCAF_CORE_SRCS
src/splitter.cpp
src/splitter.cpp
src/sync_request_bouncer.cpp
src/sync_request_bouncer.cpp
src/stringification_inspector.cpp
src/stringification_inspector.cpp
src/test_coordinator.cpp
src/timestamp.cpp
src/timestamp.cpp
src/try_match.cpp
src/try_match.cpp
src/type_erased_value.cpp
src/type_erased_value.cpp
...
...
libcaf_core/caf/actor_system.hpp
View file @
899766cd
...
@@ -39,6 +39,7 @@
...
@@ -39,6 +39,7 @@
#include "caf/abstract_actor.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/actor_registry.hpp"
#include "caf/actor_registry.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/named_actor_config.hpp"
#include "caf/scoped_execution_unit.hpp"
#include "caf/scoped_execution_unit.hpp"
#include "caf/uniform_type_info_map.hpp"
#include "caf/uniform_type_info_map.hpp"
#include "caf/composable_behavior_based_actor.hpp"
#include "caf/composable_behavior_based_actor.hpp"
...
@@ -163,6 +164,9 @@ public:
...
@@ -163,6 +164,9 @@ public:
using
module_array
=
std
::
array
<
module_ptr
,
module
::
num_ids
>
;
using
module_array
=
std
::
array
<
module_ptr
,
module
::
num_ids
>
;
using
named_actor_config_map
=
std
::
unordered_map
<
std
::
string
,
named_actor_config
>
;
/// @warning The system stores a reference to `cfg`, which means the
/// @warning The system stores a reference to `cfg`, which means the
/// config object must outlive the actor system.
/// config object must outlive the actor system.
explicit
actor_system
(
actor_system_config
&
cfg
);
explicit
actor_system
(
actor_system_config
&
cfg
);
...
@@ -454,6 +458,11 @@ public:
...
@@ -454,6 +458,11 @@ public:
return
cfg_
;
return
cfg_
;
}
}
/// Returns configuration parameters for individual named actors types.
inline
const
named_actor_config_map
&
named_actor_configs
()
const
{
return
named_actor_configs_
;
}
/// @cond PRIVATE
/// @cond PRIVATE
/// Increases running-detached-threads-count by one.
/// Increases running-detached-threads-count by one.
...
@@ -521,6 +530,7 @@ private:
...
@@ -521,6 +530,7 @@ private:
std
::
mutex
logger_dtor_mtx_
;
std
::
mutex
logger_dtor_mtx_
;
std
::
condition_variable
logger_dtor_cv_
;
std
::
condition_variable
logger_dtor_cv_
;
volatile
bool
logger_dtor_done_
;
volatile
bool
logger_dtor_done_
;
named_actor_config_map
named_actor_configs_
;
};
};
}
// namespace caf
}
// namespace caf
...
...
libcaf_core/caf/all.hpp
View file @
899766cd
...
@@ -92,7 +92,6 @@
...
@@ -92,7 +92,6 @@
#include "caf/composable_behavior.hpp"
#include "caf/composable_behavior.hpp"
#include "caf/typed_actor_pointer.hpp"
#include "caf/typed_actor_pointer.hpp"
#include "caf/scoped_execution_unit.hpp"
#include "caf/scoped_execution_unit.hpp"
#include "caf/typed_continue_helper.hpp"
#include "caf/typed_response_promise.hpp"
#include "caf/typed_response_promise.hpp"
#include "caf/typed_event_based_actor.hpp"
#include "caf/typed_event_based_actor.hpp"
#include "caf/abstract_composable_behavior.hpp"
#include "caf/abstract_composable_behavior.hpp"
...
@@ -105,6 +104,7 @@
...
@@ -105,6 +104,7 @@
#include "caf/meta/load_callback.hpp"
#include "caf/meta/load_callback.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/meta/omittable_if_empty.hpp"
#include "caf/scheduler/test_coordinator.hpp"
#include "caf/scheduler/abstract_coordinator.hpp"
#include "caf/scheduler/abstract_coordinator.hpp"
/// @author Dominik Charousset <dominik.charousset (at) haw-hamburg.de>
/// @author Dominik Charousset <dominik.charousset (at) haw-hamburg.de>
...
...
libcaf_core/caf/named_actor_config.hpp
0 → 100644
View file @
899766cd
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_NAMED_ACTOR_CONFIG_HPP
#define CAF_NAMED_ACTOR_CONFIG_HPP
#include <cstddef>
#include "caf/atom.hpp"
#include "caf/deep_to_string.hpp"
namespace
caf
{
/// Stores a flow-control configuration.
struct
named_actor_config
{
atom_value
strategy
;
size_t
low_watermark
;
size_t
max_pending
;
};
template
<
class
Processor
>
void
serialize
(
Processor
&
proc
,
named_actor_config
&
x
,
const
unsigned
int
)
{
proc
&
x
.
strategy
;
proc
&
x
.
low_watermark
;
proc
&
x
.
max_pending
;
}
inline
std
::
string
to_string
(
const
named_actor_config
&
x
)
{
return
"named_actor_config"
+
deep_to_string_as_tuple
(
x
.
strategy
,
x
.
low_watermark
,
x
.
max_pending
);
}
}
// namespace caf
#endif //CAF_NAMED_ACTOR_CONFIG_HPP
libcaf_core/caf/scheduler/test_coordinator.hpp
0 → 100644
View file @
899766cd
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_TEST_COORDINATOR_HPP
#define CAF_TEST_COORDINATOR_HPP
#include "caf/config.hpp"
#include <deque>
#include <chrono>
#include <cstddef>
#include "caf/scheduler/abstract_coordinator.hpp"
namespace
caf
{
namespace
scheduler
{
/// Coordinator for testing purposes.
class
test_coordinator
:
public
abstract_coordinator
{
public:
using
super
=
abstract_coordinator
;
test_coordinator
(
actor_system
&
sys
);
std
::
deque
<
resumable
*>
jobs
;
struct
delayed_msg
{
strong_actor_ptr
from
;
strong_actor_ptr
to
;
message_id
mid
;
message
msg
;
};
using
hrc
=
std
::
chrono
::
high_resolution_clock
;
std
::
multimap
<
hrc
::
time_point
,
delayed_msg
>
delayed_messages
;
template
<
class
T
=
resumable
>
T
&
next_job
()
{
if
(
jobs
.
empty
())
CAF_RAISE_ERROR
(
"jobs.empty())"
);
return
dynamic_cast
<
T
&>
(
*
jobs
.
front
());
}
/// Tries to execute a single event.
bool
run_once
();
/// Executes events until the job queue is empty and no pending timeouts are
/// left. Returns the number of processed events.
size_t
run
();
/// Tries to dispatch a single delayed message.
bool
dispatch_once
();
/// Dispatches all pending delayed messages. Returns the number of dispatched
/// messages.
size_t
dispatch
();
/// Loops until no job or delayed message remains. Returns the total number of
/// events (first) and dispatched delayed messages (second).
std
::
pair
<
size_t
,
size_t
>
run_dispatch_loop
();
protected:
void
start
()
override
;
void
stop
()
override
;
void
enqueue
(
resumable
*
ptr
)
override
;
};
}
// namespace scheduler
}
// namespace caf
#endif // CAF_TEST_COORDINATOR_HPP
libcaf_core/src/actor_system.cpp
View file @
899766cd
...
@@ -30,6 +30,7 @@
...
@@ -30,6 +30,7 @@
#include "caf/policy/work_stealing.hpp"
#include "caf/policy/work_stealing.hpp"
#include "caf/scheduler/coordinator.hpp"
#include "caf/scheduler/coordinator.hpp"
#include "caf/scheduler/test_coordinator.hpp"
#include "caf/scheduler/abstract_coordinator.hpp"
#include "caf/scheduler/abstract_coordinator.hpp"
#include "caf/scheduler/profiled_coordinator.hpp"
#include "caf/scheduler/profiled_coordinator.hpp"
...
@@ -205,6 +206,7 @@ actor_system::actor_system(actor_system_config& cfg)
...
@@ -205,6 +206,7 @@ actor_system::actor_system(actor_system_config& cfg)
if
(
clptr
)
if
(
clptr
)
opencl_manager_
=
reinterpret_cast
<
opencl
::
manager
*>
(
clptr
->
subtype_ptr
());
opencl_manager_
=
reinterpret_cast
<
opencl
::
manager
*>
(
clptr
->
subtype_ptr
());
auto
&
sched
=
modules_
[
module
::
scheduler
];
auto
&
sched
=
modules_
[
module
::
scheduler
];
using
test
=
scheduler
::
test_coordinator
;
using
share
=
scheduler
::
coordinator
<
policy
::
work_sharing
>
;
using
share
=
scheduler
::
coordinator
<
policy
::
work_sharing
>
;
using
steal
=
scheduler
::
coordinator
<
policy
::
work_stealing
>
;
using
steal
=
scheduler
::
coordinator
<
policy
::
work_stealing
>
;
using
profiled_share
=
scheduler
::
profiled_coordinator
<
policy
::
work_sharing
>
;
using
profiled_share
=
scheduler
::
profiled_coordinator
<
policy
::
work_sharing
>
;
...
@@ -214,6 +216,7 @@ actor_system::actor_system(actor_system_config& cfg)
...
@@ -214,6 +216,7 @@ actor_system::actor_system(actor_system_config& cfg)
enum
sched_conf
{
enum
sched_conf
{
stealing
=
0x0001
,
stealing
=
0x0001
,
sharing
=
0x0002
,
sharing
=
0x0002
,
testing
=
0x0003
,
profiled
=
0x0100
,
profiled
=
0x0100
,
profiled_stealing
=
0x0101
,
profiled_stealing
=
0x0101
,
profiled_sharing
=
0x0102
profiled_sharing
=
0x0102
...
@@ -221,6 +224,8 @@ actor_system::actor_system(actor_system_config& cfg)
...
@@ -221,6 +224,8 @@ actor_system::actor_system(actor_system_config& cfg)
sched_conf
sc
=
stealing
;
sched_conf
sc
=
stealing
;
if
(
cfg
.
scheduler_policy
==
atom
(
"sharing"
))
if
(
cfg
.
scheduler_policy
==
atom
(
"sharing"
))
sc
=
sharing
;
sc
=
sharing
;
else
if
(
cfg
.
scheduler_policy
==
atom
(
"testing"
))
sc
=
testing
;
else
if
(
cfg
.
scheduler_policy
!=
atom
(
"stealing"
))
else
if
(
cfg
.
scheduler_policy
!=
atom
(
"stealing"
))
std
::
cerr
<<
"[WARNING] "
<<
deep_to_string
(
cfg
.
scheduler_policy
)
std
::
cerr
<<
"[WARNING] "
<<
deep_to_string
(
cfg
.
scheduler_policy
)
<<
" is an unrecognized scheduler pollicy, "
<<
" is an unrecognized scheduler pollicy, "
...
@@ -241,6 +246,8 @@ actor_system::actor_system(actor_system_config& cfg)
...
@@ -241,6 +246,8 @@ actor_system::actor_system(actor_system_config& cfg)
case
profiled_sharing
:
case
profiled_sharing
:
sched
.
reset
(
new
profiled_share
(
*
this
));
sched
.
reset
(
new
profiled_share
(
*
this
));
break
;
break
;
case
testing
:
sched
.
reset
(
new
test
(
*
this
));
}
}
}
}
// initialize state for each module and give each module the opportunity
// initialize state for each module and give each module the opportunity
...
...
libcaf_core/src/test_coordinator.cpp
0 → 100644
View file @
899766cd
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2016 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/scheduler/test_coordinator.hpp"
#include "caf/resumable.hpp"
#include "caf/monitorable_actor.hpp"
namespace
caf
{
namespace
scheduler
{
namespace
{
class
dummy_worker
:
public
execution_unit
{
public:
dummy_worker
(
test_coordinator
*
parent
)
:
execution_unit
(
&
parent
->
system
()),
parent_
(
parent
)
{
// nop
}
void
exec_later
(
resumable
*
ptr
)
override
{
parent_
->
jobs
.
push_back
(
ptr
);
}
private:
test_coordinator
*
parent_
;
};
class
dummy_printer
:
public
monitorable_actor
{
public:
dummy_printer
(
actor_config
&
cfg
)
:
monitorable_actor
(
cfg
)
{
mh_
.
assign
(
[
&
](
add_atom
,
actor_id
,
const
std
::
string
&
str
)
{
std
::
cout
<<
str
;
}
);
}
void
enqueue
(
mailbox_element_ptr
what
,
execution_unit
*
)
override
{
mh_
(
what
->
content
());
}
private:
message_handler
mh_
;
};
class
dummy_timer
:
public
monitorable_actor
{
public:
dummy_timer
(
actor_config
&
cfg
,
test_coordinator
*
parent
)
:
monitorable_actor
(
cfg
),
parent_
(
parent
)
{
mh_
.
assign
(
[
&
](
const
duration
&
d
,
strong_actor_ptr
&
from
,
strong_actor_ptr
&
to
,
message_id
mid
,
message
&
msg
)
{
auto
tout
=
test_coordinator
::
hrc
::
now
();
tout
+=
d
;
using
delayed_msg
=
test_coordinator
::
delayed_msg
;
parent_
->
delayed_messages
.
emplace
(
tout
,
delayed_msg
{
std
::
move
(
from
),
std
::
move
(
to
),
mid
,
std
::
move
(
msg
)});
}
);
}
void
enqueue
(
mailbox_element_ptr
what
,
execution_unit
*
)
override
{
mh_
(
what
->
content
());
}
private:
test_coordinator
*
parent_
;
message_handler
mh_
;
};
}
// namespace <anonymous>
test_coordinator
::
test_coordinator
(
actor_system
&
sys
)
:
super
(
sys
)
{
// nop
}
void
test_coordinator
::
start
()
{
dummy_worker
worker
{
this
};
actor_config
cfg
{
&
worker
};
auto
&
sys
=
system
();
timer_
=
make_actor
<
dummy_timer
,
strong_actor_ptr
>
(
sys
.
next_actor_id
(),
sys
.
node
(),
&
sys
,
cfg
,
this
);
printer_
=
make_actor
<
dummy_printer
,
strong_actor_ptr
>
(
sys
.
next_actor_id
(),
sys
.
node
(),
&
sys
,
cfg
);
}
void
test_coordinator
::
stop
()
{
run_dispatch_loop
();
}
void
test_coordinator
::
enqueue
(
resumable
*
ptr
)
{
jobs
.
push_back
(
ptr
);
}
bool
test_coordinator
::
run_once
()
{
if
(
jobs
.
empty
())
return
false
;
auto
job
=
jobs
.
front
();
jobs
.
pop_front
();
dummy_worker
worker
{
this
};
switch
(
job
->
resume
(
&
worker
,
1
))
{
case
resumable
:
:
resume_later
:
jobs
.
push_front
(
job
);
break
;
case
resumable
:
:
done
:
case
resumable
:
:
awaiting_message
:
intrusive_ptr_release
(
job
);
break
;
case
resumable
:
:
shutdown_execution_unit
:
break
;
}
return
true
;
}
size_t
test_coordinator
::
run
()
{
size_t
res
=
0
;
while
(
run_once
())
++
res
;
return
res
;
}
bool
test_coordinator
::
dispatch_once
()
{
auto
i
=
delayed_messages
.
begin
();
if
(
i
==
delayed_messages
.
end
())
return
false
;
auto
&
dm
=
i
->
second
;
dm
.
to
->
enqueue
(
dm
.
from
,
dm
.
mid
,
std
::
move
(
dm
.
msg
),
nullptr
);
delayed_messages
.
erase
(
i
);
return
true
;
}
size_t
test_coordinator
::
dispatch
()
{
size_t
res
=
0
;
while
(
dispatch_once
())
++
res
;
return
res
;
}
std
::
pair
<
size_t
,
size_t
>
test_coordinator
::
run_dispatch_loop
()
{
std
::
pair
<
size_t
,
size_t
>
res
;
size_t
i
=
0
;
do
{
auto
x
=
run
();
auto
y
=
dispatch
();
res
.
first
+=
x
;
res
.
second
+=
y
;
i
=
x
+
y
;
}
while
(
i
>
0
);
return
res
;
}
}
// namespace caf
}
// namespace scheduler
libcaf_core/test/request_timeout.cpp
View file @
899766cd
...
@@ -29,6 +29,7 @@
...
@@ -29,6 +29,7 @@
using
namespace
caf
;
using
namespace
caf
;
using
std
::
string
;
using
std
::
chrono
::
seconds
;
using
std
::
chrono
::
seconds
;
using
std
::
chrono
::
milliseconds
;
using
std
::
chrono
::
milliseconds
;
...
@@ -36,142 +37,256 @@ namespace {
...
@@ -36,142 +37,256 @@ namespace {
using
ping_atom
=
atom_constant
<
atom
(
"ping"
)
>
;
using
ping_atom
=
atom_constant
<
atom
(
"ping"
)
>
;
using
pong_atom
=
atom_constant
<
atom
(
"pong"
)
>
;
using
pong_atom
=
atom_constant
<
atom
(
"pong"
)
>
;
using
send_ping_atom
=
atom_constant
<
atom
(
"send_ping
"
)
>
;
using
timeout_atom
=
atom_constant
<
atom
(
"timeout
"
)
>
;
behavior
pong
()
{
struct
pong_state
{
static
const
char
*
name
;
};
const
char
*
pong_state
::
name
=
"pong"
;
behavior
pong
(
stateful_actor
<
pong_state
>*
)
{
return
{
return
{
[
=
]
(
ping_atom
)
{
[
=
]
(
ping_atom
)
{
std
::
this_thread
::
sleep_for
(
seconds
(
1
));
return
pong_atom
::
value
;
return
pong_atom
::
value
;
}
}
};
};
}
}
behavior
ping1
(
event_based_actor
*
self
,
const
actor
&
pong_actor
)
{
struct
ping_state
{
self
->
link_to
(
pong_actor
);
static
const
char
*
name
;
self
->
send
(
self
,
send_ping_atom
::
value
);
bool
had_first_timeout
=
false
;
// unused in ping_singleN functions
};
const
char
*
ping_state
::
name
=
"ping"
;
using
ping_actor
=
stateful_actor
<
ping_state
>
;
using
fptr
=
behavior
(
*
)(
ping_actor
*
,
bool
*
,
const
actor
&
);
using
test_vec
=
std
::
vector
<
std
::
pair
<
fptr
,
string
>>
;
// assumes to receive a timeout (sent via delayed_send) before pong replies
behavior
ping_single1
(
ping_actor
*
self
,
bool
*
had_timeout
,
const
actor
&
buddy
)
{
self
->
send
(
buddy
,
ping_atom
::
value
);
self
->
delayed_send
(
self
,
std
::
chrono
::
seconds
(
1
),
timeout_atom
::
value
);
return
{
return
{
[
=
](
send_ping_atom
)
{
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
CAF_ERROR
(
"received pong atom"
);
self
->
quit
(
exit_reason
::
user_shutdown
);
},
},
[
=
](
const
error
&
err
)
{
[
=
](
timeout_atom
)
{
CAF_REQUIRE
(
err
==
sec
::
request_timeout
);
*
had_timeout
=
true
;
self
->
quit
(
exit_reason
::
user_shutdown
);
self
->
quit
();
}
);
}
}
};
};
}
}
behavior
ping2
(
event_based_actor
*
self
,
const
actor
&
pong_actor
)
{
// assumes to receive a timeout (via after()) before pong replies
self
->
link_to
(
pong_actor
);
behavior
ping_single2
(
ping_actor
*
self
,
bool
*
had_timeout
,
const
actor
&
buddy
)
{
self
->
send
(
self
,
send_ping_atom
::
value
);
self
->
send
(
buddy
,
ping_atom
::
value
);
auto
received_inner
=
std
::
make_shared
<
bool
>
(
false
);
return
{
return
{
[
=
](
send_ping_atom
)
{
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
CAF_ERROR
(
"received pong atom"
);
self
->
quit
(
exit_reason
::
user_shutdown
);
},
after
(
std
::
chrono
::
seconds
(
1
))
>>
[
=
]
{
*
had_timeout
=
true
;
self
->
quit
();
}
};
}
// assumes to receive a timeout (via request error handler) before pong replies
behavior
ping_single3
(
ping_actor
*
self
,
bool
*
had_timeout
,
const
actor
&
buddy
)
{
self
->
request
(
buddy
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
},
},
[
=
](
const
error
&
err
)
{
[
=
](
const
error
&
err
)
{
CAF_REQUIRE
(
err
==
sec
::
request_timeout
);
CAF_REQUIRE
(
err
==
sec
::
request_timeout
);
CAF_MESSAGE
(
"inner timeout: check"
);
*
had_timeout
=
true
;
*
received_inner
=
true
;
}
}
);
);
return
{};
// dummy value in order to give all 3 variants the same fun sig
}
// assumes to receive an inner timeout (sent via delayed_send) before pong
// replies, then second timeout fires
behavior
ping_nested1
(
ping_actor
*
self
,
bool
*
had_timeout
,
const
actor
&
buddy
)
{
self
->
send
(
buddy
,
ping_atom
::
value
);
self
->
delayed_send
(
self
,
std
::
chrono
::
seconds
(
1
),
timeout_atom
::
value
);
return
{
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
},
},
[
=
](
timeout_atom
)
{
self
->
state
.
had_first_timeout
=
true
;
self
->
become
(
after
(
milliseconds
(
100
))
>>
[
=
]
{
after
(
milliseconds
(
100
))
>>
[
=
]
{
CAF_CHECK_EQUAL
(
*
received_inner
,
true
);
CAF_CHECK
(
self
->
state
.
had_first_timeout
);
self
->
quit
(
exit_reason
::
user_shutdown
);
*
had_timeout
=
true
;
self
->
quit
();
}
);
}
}
};
};
}
}
behavior
ping3
(
event_based_actor
*
self
,
const
actor
&
pong_actor
)
{
// assumes to receive an inner timeout (via after()) before pong replies, then a
self
->
link_to
(
pong_actor
);
// second timeout fires
self
->
send
(
self
,
send_ping_atom
::
value
);
behavior
ping_nested2
(
ping_actor
*
self
,
bool
*
had_timeout
,
const
actor
&
buddy
)
{
self
->
send
(
buddy
,
ping_atom
::
value
);
return
{
return
{
[
=
](
send_ping_atom
)
{
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
CAF_ERROR
(
"received pong atom"
);
self
->
quit
(
exit_reason
::
user_shutdown
);
},
},
[
=
](
const
error
&
err
)
{
after
(
std
::
chrono
::
seconds
(
1
))
>>
[
=
]
{
CAF_REQUIRE
(
err
==
sec
::
request_timeout
);
self
->
state
.
had_first_timeout
=
true
;
CAF_MESSAGE
(
"async timeout: check"
);
self
->
become
(
self
->
quit
(
exit_reason
::
user_shutdown
);
after
(
milliseconds
(
100
))
>>
[
=
]
{
CAF_CHECK
(
self
->
state
.
had_first_timeout
);
*
had_timeout
=
true
;
self
->
quit
();
}
}
);
);
}
}
};
};
}
}
behavior
ping4
(
event_based_actor
*
self
,
const
actor
&
pong_actor
)
{
// assumes to receive an inner timeout (via request error handler) before pong
self
->
link_to
(
pong_actor
);
// replies, then a second timeout fires
self
->
send
(
self
,
send_ping_atom
::
value
);
behavior
ping_nested3
(
ping_actor
*
self
,
bool
*
had_timeout
,
const
actor
&
buddy
)
{
auto
received_outer
=
std
::
make_shared
<
bool
>
(
false
);
self
->
request
(
buddy
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
self
->
quit
(
sec
::
unexpected_message
);
},
[
=
](
const
error
&
err
)
{
CAF_REQUIRE_EQUAL
(
err
,
sec
::
request_timeout
);
self
->
state
.
had_first_timeout
=
true
;
}
);
return
{
return
{
[
=
](
send_ping_atom
)
{
after
(
milliseconds
(
100
))
>>
[
=
]
{
self
->
request
(
pong_actor
,
milliseconds
(
100
),
CAF_CHECK
(
self
->
state
.
had_first_timeout
);
ping_atom
::
value
).
then
(
*
had_timeout
=
true
;
self
->
quit
();
}
};
}
// uses .then on both requests
behavior
ping_multiplexed1
(
ping_actor
*
self
,
bool
*
had_timeout
,
const
actor
&
pong_actor
)
{
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
CAF_ERROR
(
"received pong atom"
);
self
->
quit
(
exit_reason
::
user_shutdown
);
},
},
[
=
](
const
error
&
err
)
{
[
=
](
const
error
&
err
)
{
CAF_REQUIRE
(
err
==
sec
::
request_timeout
);
CAF_REQUIRE_EQUAL
(
err
,
sec
::
request_timeout
);
CAF_CHECK_EQUAL
(
*
received_outer
,
true
);
if
(
!
self
->
state
.
had_first_timeout
)
self
->
quit
(
exit_reason
::
user_shutdown
);
self
->
state
.
had_first_timeout
=
true
;
else
*
had_timeout
=
true
;
}
}
);
);
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
},
},
after
(
milliseconds
(
50
))
>>
[
=
]
{
[
=
](
const
error
&
err
)
{
CAF_MESSAGE
(
"outer timeout: check"
);
CAF_REQUIRE_EQUAL
(
err
,
sec
::
request_timeout
);
*
received_outer
=
true
;
if
(
!
self
->
state
.
had_first_timeout
)
self
->
state
.
had_first_timeout
=
true
;
else
*
had_timeout
=
true
;
}
}
};
);
return
{};
}
}
void
ping5
(
event_based_actor
*
self
,
const
actor
&
pong_actor
)
{
// uses .await on both requests
self
->
link_to
(
pong_actor
);
behavior
ping_multiplexed2
(
ping_actor
*
self
,
bool
*
had_timeout
,
auto
timeouts
=
std
::
make_shared
<
int
>
(
0
);
const
actor
&
pong_actor
)
{
self
->
request
(
pong_actor
,
milliseconds
(
100
),
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
await
(
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
CAF_ERROR
(
"received pong atom"
);
},
},
[
=
](
const
error
&
err
)
{
[
=
](
const
error
&
err
)
{
CAF_REQUIRE
(
err
==
sec
::
request_timeout
);
CAF_REQUIRE_EQUAL
(
err
,
sec
::
request_timeout
);
if
(
++*
timeouts
==
2
)
if
(
!
self
->
state
.
had_first_timeout
)
self
->
quit
();
self
->
state
.
had_first_timeout
=
true
;
else
*
had_timeout
=
true
;
}
}
);
);
self
->
request
(
pong_actor
,
milliseconds
(
100
),
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
await
(
ping_atom
::
value
).
await
(
[
=
](
pong_atom
)
{
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
CAF_ERROR
(
"received pong atom"
);
},
},
[
=
](
const
error
&
err
)
{
[
=
](
const
error
&
err
)
{
CAF_REQUIRE
(
err
==
sec
::
request_timeout
);
CAF_REQUIRE_EQUAL
(
err
,
sec
::
request_timeout
);
if
(
++*
timeouts
==
2
)
if
(
!
self
->
state
.
had_first_timeout
)
self
->
quit
();
self
->
state
.
had_first_timeout
=
true
;
else
*
had_timeout
=
true
;
}
}
);
);
return
{};
}
}
struct
fixture
{
// uses .await and .then
fixture
()
:
system
(
cfg
)
{
behavior
ping_multiplexed3
(
ping_actor
*
self
,
bool
*
had_timeout
,
// nop
const
actor
&
pong_actor
)
{
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
then
(
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
},
[
=
](
const
error
&
err
)
{
CAF_REQUIRE_EQUAL
(
err
,
sec
::
request_timeout
);
if
(
!
self
->
state
.
had_first_timeout
)
self
->
state
.
had_first_timeout
=
true
;
else
*
had_timeout
=
true
;
}
}
);
self
->
request
(
pong_actor
,
milliseconds
(
100
),
ping_atom
::
value
).
await
(
[
=
](
pong_atom
)
{
CAF_ERROR
(
"received pong atom"
);
},
[
=
](
const
error
&
err
)
{
CAF_REQUIRE_EQUAL
(
err
,
sec
::
request_timeout
);
if
(
!
self
->
state
.
had_first_timeout
)
self
->
state
.
had_first_timeout
=
true
;
else
*
had_timeout
=
true
;
}
);
return
{};
}
actor_system_config
cfg
;
struct
config
:
actor_system_config
{
config
()
{
scheduler_policy
=
atom
(
"testing"
);
}
};
struct
fixture
{
config
cfg
;
actor_system
system
;
actor_system
system
;
scoped_actor
self
;
scheduler
::
test_coordinator
&
sched
;
fixture
()
:
system
(
cfg
),
self
(
system
),
sched
(
dynamic_cast
<
scheduler
::
test_coordinator
&>
(
system
.
scheduler
()))
{
CAF_REQUIRE
(
sched
.
jobs
.
empty
());
CAF_REQUIRE
(
sched
.
delayed_messages
.
empty
());
}
~
fixture
()
{
sched
.
run_dispatch_loop
();
}
};
};
}
// namespace <anonymous>
}
// namespace <anonymous>
...
@@ -179,17 +294,79 @@ struct fixture {
...
@@ -179,17 +294,79 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE
(
request_timeout_tests
,
fixture
)
CAF_TEST_FIXTURE_SCOPE
(
request_timeout_tests
,
fixture
)
CAF_TEST
(
single_timeout
)
{
CAF_TEST
(
single_timeout
)
{
system
.
spawn
(
ping1
,
system
.
spawn
(
pong
));
test_vec
fs
{{
ping_single1
,
"ping_single1"
},
system
.
spawn
(
ping3
,
system
.
spawn
(
pong
));
{
ping_single2
,
"ping_single2"
},
{
ping_single3
,
"ping_single3"
}};
for
(
auto
f
:
fs
)
{
bool
had_timeout
=
false
;
CAF_MESSAGE
(
"test implemenation "
<<
f
.
second
);
auto
testee
=
system
.
spawn
(
f
.
first
,
&
had_timeout
,
system
.
spawn
<
lazy_init
>
(
pong
));
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
1
);
CAF_REQUIRE_EQUAL
(
sched
.
next_job
<
local_actor
>
().
name
(),
string
{
"ping"
});
sched
.
run_once
();
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
1
);
CAF_REQUIRE_EQUAL
(
sched
.
next_job
<
local_actor
>
().
name
(),
string
{
"pong"
});
sched
.
dispatch
();
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
2
);
// now, the timeout message is already dispatched, while pong did
// not respond to the message yet, i.e., timeout arrives before response
CAF_CHECK_EQUAL
(
sched
.
run
(),
2
);
CAF_CHECK
(
had_timeout
);
}
}
}
CAF_TEST
(
scoped_timeout
)
{
CAF_TEST
(
nested_timeout
)
{
system
.
spawn
(
ping2
,
system
.
spawn
(
pong
));
test_vec
fs
{{
ping_nested1
,
"ping_nested1"
},
system
.
spawn
(
ping4
,
system
.
spawn
(
pong
));
{
ping_nested2
,
"ping_nested2"
},
{
ping_nested3
,
"ping_nested3"
}};
for
(
auto
f
:
fs
)
{
bool
had_timeout
=
false
;
CAF_MESSAGE
(
"test implemenation "
<<
f
.
second
);
auto
testee
=
system
.
spawn
(
f
.
first
,
&
had_timeout
,
system
.
spawn
<
lazy_init
>
(
pong
));
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
1
);
CAF_REQUIRE_EQUAL
(
sched
.
next_job
<
local_actor
>
().
name
(),
string
{
"ping"
});
sched
.
run_once
();
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
1
);
CAF_REQUIRE_EQUAL
(
sched
.
next_job
<
local_actor
>
().
name
(),
string
{
"pong"
});
sched
.
dispatch
();
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
2
);
// now, the timeout message is already dispatched, while pong did
// not respond to the message yet, i.e., timeout arrives before response
sched
.
run
();
// dispatch second timeout
CAF_REQUIRE
(
!
sched
.
delayed_messages
.
empty
());
sched
.
dispatch
();
CAF_REQUIRE_EQUAL
(
sched
.
next_job
<
local_actor
>
().
name
(),
string
{
"ping"
});
CAF_CHECK
(
!
had_timeout
);
CAF_CHECK
(
sched
.
next_job
<
ping_actor
>
().
state
.
had_first_timeout
);
sched
.
run
();
CAF_CHECK
(
had_timeout
);
}
}
}
CAF_TEST
(
awaited_multiplexed_timeout
)
{
CAF_TEST
(
multiplexed_timeout
)
{
system
.
spawn
(
ping5
,
system
.
spawn
(
pong
));
test_vec
fs
{{
ping_multiplexed1
,
"ping_multiplexed1"
},
{
ping_multiplexed2
,
"ping_multiplexed2"
},
{
ping_multiplexed3
,
"ping_multiplexed3"
}};
for
(
auto
f
:
fs
)
{
bool
had_timeout
=
false
;
CAF_MESSAGE
(
"test implemenation "
<<
f
.
second
);
auto
testee
=
system
.
spawn
(
f
.
first
,
&
had_timeout
,
system
.
spawn
<
lazy_init
>
(
pong
));
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
1
);
CAF_REQUIRE_EQUAL
(
sched
.
next_job
<
local_actor
>
().
name
(),
string
{
"ping"
});
sched
.
run_once
();
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
1
);
CAF_REQUIRE_EQUAL
(
sched
.
next_job
<
local_actor
>
().
name
(),
string
{
"pong"
});
sched
.
dispatch
();
CAF_REQUIRE_EQUAL
(
sched
.
jobs
.
size
(),
2
);
// now, the timeout message is already dispatched, while pong did
// not respond to the message yet, i.e., timeout arrives before response
sched
.
run
();
CAF_CHECK
(
had_timeout
);
}
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/simple_timeout.cpp
View file @
899766cd
...
@@ -36,17 +36,20 @@ using ms = std::chrono::milliseconds;
...
@@ -36,17 +36,20 @@ using ms = std::chrono::milliseconds;
using
reset_atom
=
atom_constant
<
atom
(
"reset"
)
>
;
using
reset_atom
=
atom_constant
<
atom
(
"reset"
)
>
;
using
timer
=
typed_actor
<
reacts_to
<
reset_atom
>>
;
using
timer
=
typed_actor
<
reacts_to
<
reset_atom
>>
;
timer
::
behavior_type
timer_impl
(
timer
::
pointer
self
)
{
struct
timer_state
{
auto
had_reset
=
std
::
make_shared
<
bool
>
(
false
);
bool
had_reset
=
false
;
};
timer
::
behavior_type
timer_impl
(
timer
::
stateful_pointer
<
timer_state
>
self
)
{
self
->
delayed_send
(
self
,
ms
(
100
),
reset_atom
::
value
);
self
->
delayed_send
(
self
,
ms
(
100
),
reset_atom
::
value
);
return
{
return
{
[
=
](
reset_atom
)
{
[
=
](
reset_atom
)
{
CAF_MESSAGE
(
"timer reset"
);
CAF_MESSAGE
(
"timer reset"
);
*
had_reset
=
true
;
self
->
state
.
had_reset
=
true
;
},
},
after
(
ms
(
600
))
>>
[
=
]
{
after
(
ms
(
600
))
>>
[
=
]
{
CAF_MESSAGE
(
"timer expired"
);
CAF_MESSAGE
(
"timer expired"
);
CAF_REQUIRE
(
*
had_reset
);
CAF_REQUIRE
(
self
->
state
.
had_reset
);
self
->
quit
();
self
->
quit
();
}
}
};
};
...
@@ -68,18 +71,34 @@ timer::behavior_type timer_impl2(timer::pointer self) {
...
@@ -68,18 +71,34 @@ timer::behavior_type timer_impl2(timer::pointer self) {
};
};
}
}
struct
fixture
{
struct
config
:
actor_system_config
{
fixture
()
:
system
(
cfg
)
{
config
(
)
{
// nop
scheduler_policy
=
atom
(
"testing"
);
}
}
};
actor_system_config
cfg
;
struct
fixture
{
config
cfg
;
actor_system
system
;
actor_system
system
;
scoped_actor
self
;
scheduler
::
test_coordinator
&
sched
;
fixture
()
:
system
(
cfg
),
self
(
system
),
sched
(
dynamic_cast
<
scheduler
::
test_coordinator
&>
(
system
.
scheduler
()))
{
CAF_REQUIRE
(
sched
.
jobs
.
empty
());
CAF_REQUIRE
(
sched
.
delayed_messages
.
empty
());
}
~
fixture
()
{
sched
.
run_dispatch_loop
();
}
};
};
}
// namespace <anonymous>
}
// namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE
(
timeout_tests
,
fixture
)
CAF_TEST_FIXTURE_SCOPE
(
simple_
timeout_tests
,
fixture
)
CAF_TEST
(
duration_conversion
)
{
CAF_TEST
(
duration_conversion
)
{
duration
d1
{
time_unit
::
milliseconds
,
100
};
duration
d1
{
time_unit
::
milliseconds
,
100
};
...
...
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