Merge lp:~andreas-pokorny/mir/split-main-loop-and-fix-races into lp:mir
- split-main-loop-and-fix-races
- Merge into development-branch
Status: | Work in progress |
---|---|
Proposed branch: | lp:~andreas-pokorny/mir/split-main-loop-and-fix-races |
Merge into: | lp:mir |
Diff against target: |
3020 lines (+1675/-617) 31 files modified
include/platform/mir/graphics/event_handler_register.h (+3/-0) include/server/mir/default_server_configuration.h (+5/-0) include/server/mir/loop.h (+37/-0) include/server/mir/main_loop.h (+1/-2) include/server/mir/server_configuration.h (+2/-0) include/server/mir/time/timer_loop.h (+37/-0) include/test/mir_test_doubles/mock_event_handler_register.h (+53/-0) include/test/mir_test_doubles/mock_loop.h (+44/-0) include/test/mir_test_doubles/mock_server_action_queue.h (+44/-0) src/platform/graphics/mesa/display.cpp (+1/-0) src/server/CMakeLists.txt (+3/-0) src/server/asio_main_loop.cpp (+88/-307) src/server/asio_main_loop.h (+21/-27) src/server/asio_server_action_queue.cpp (+91/-0) src/server/asio_server_action_queue.h (+58/-0) src/server/asio_timer_service.cpp (+260/-0) src/server/asio_timer_service.h (+58/-0) src/server/compositor/default_configuration.cpp (+1/-1) src/server/default_server_configuration.cpp (+47/-2) src/server/display_server.cpp (+14/-1) src/server/synchronous_server_action.cpp (+50/-0) src/server/synchronous_server_action.h (+48/-0) tests/acceptance-tests/test_display_configuration.cpp (+1/-0) tests/integration-tests/test_display_server_main_loop_events.cpp (+44/-0) tests/unit-tests/CMakeLists.txt (+3/-0) tests/unit-tests/graphics/mesa/test_display.cpp (+5/-17) tests/unit-tests/graphics/mesa/test_linux_virtual_terminal.cpp (+2/-16) tests/unit-tests/test_asio_main_loop.cpp (+45/-244) tests/unit-tests/test_asio_server_action_queue.cpp (+219/-0) tests/unit-tests/test_asio_timer_service.cpp (+312/-0) tests/unit-tests/test_synchronous_server_action.cpp (+78/-0) |
To merge this branch: | bzr merge lp:~andreas-pokorny/mir/split-main-loop-and-fix-races |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alberto Aguirre (community) | Needs Fixing | ||
Alan Griffiths | Needs Fixing | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Alexandros Frantzis (community) | Needs Fixing | ||
Robert Carr (community) | Approve | ||
Andreas Pokorny (community) | Approve | ||
Review via email: mp+220571@code.launchpad.net |
Commit message
Add the ability to unregister fd callbacks from AsioMainLoop
Moves Timer handling out of MainLoop into a separate TimerService
To identify previously registered callbacks the caller has to submit a void pointer. The supplied fd set does need to match the full registered set, only those supplied will be removed.
Reconfiguration operations are now executed sequentially together with the action handlers in both the TimerService and MainLoop.
Description of the change
Adds a method to unregister some of the file descriptors from a FDhandler in mir::AsioMainLoop. This is needed by the upcoming InputSender to unregister from InputChannels of removed surfaces.
The FDHandler is associated with a void const* on registry and when removing a file descriptor again from the main loop.
Since Asio copies handlers, cancelling operations does not really stop it
from execution. This can cause update issues in AlarmImpl:
that lead to alarms not being triggered again. The solution applied here
is sequential execution of all handlers inside the ServerActionQueues and reconfigurations of any kind are executed using the utility class SentinelAction which blocks until the action completes. This utility either executes inside the queue or on the callers thread, depending on the current thread id.
PS Jenkins bot (ps-jenkins) wrote : | # |
Alexandros Frantzis (afrantzis) wrote : | # |
What's the use case for this?
166 + remove_
Do we also want to erase the items?
203 + end(stream_
204 + end(stream_
205 + [s](stream_
206 + {return item.get() == s;} ))
Doesn't this code need to be protected from concurrent access to stream descriptors? For example, handling an fd while concurrently reducing the fd set?
Also the code would be clearer if this piece of code was placed in a function.
Needs Information/Needs Fixing
Andreas Pokorny (andreas-pokorny) wrote : | # |
> What's the use case for this?
The input sender, I am just preparing, observes those fds for incoming input ACK messages, which contain Conumed/NotConsumed flags. Those fds are closed and opened together with surfaces.
> 166 + remove_
>
> Do we also want to erase the items?
I thought that would happen here. The destruction of the unique_ptr instances should delete the instance.
>
> 203 + end(stream_
> 204 + end(stream_
> 205 + [s](stream_
> 206 + {return item.get() == s;} ))
>
> Doesn't this code need to be protected from concurrent access to stream
> descriptors? For example, handling an fd while concurrently reducing the fd
> set?
Yes. Thanks! Just fixed.
> Also the code would be clearer if this piece of code was placed in a function.
>
> Needs Information/Needs Fixing
ACK
Alexandros Frantzis (afrantzis) wrote : | # |
> > 166 + remove_
> >
> > Do we also want to erase the items?
>
> I thought that would happen here. The destruction of the unique_ptr instances should delete the
> instance.
I was referring to the erase-remove idiom: std::remove and remove_if just move the items to the end of the data structure (invalidating them in the process), so we also need to erase them like:
v.erase(
(Unfortunately the name 'remove' is not very descriptive of what the function is actually doing.)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1644
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Andreas Pokorny (andreas-pokorny) wrote : | # |
> I was referring to the erase-remove idiom: std::remove and remove_if just move
> the items to the end of the data structure (invalidating them in the process),
> so we also need to erase them like:
>
> v.erase(
>
> (Unfortunately the name 'remove' is not very descriptive of what the function
> is actually doing.)
Maybe not descriptive but it was not thoughtful to assume that an algorithm could make that kind of changes to the container. Now that I fixed it I have to deal with the issue that asio might still execute a destroyed callback that refers to a recently destroyed Handler... fixing.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1645
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1646
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) wrote : | # |
Is the current plan to use the server main loop, or will we have a different object?
I wonder if using the main loop mechanism to handle the FDs for input is appropriate. Do we need total serialization of the messages? I could imagine using a more focused class for this purpose (but I am not familiar with the details).
This is not a NACK, just a call for information.
Andreas Pokorny (andreas-pokorny) wrote : | # |
> Is the current plan to use the server main loop, or will we have a different
> object?
>
> I wonder if using the main loop mechanism to handle the FDs for input is
> appropriate. Do we need total serialization of the messages? I could imagine
> using a more focused class for this purpose (but I am not familiar with the
> details).
It is not strictly necessary to handles those responses in a specific or dedicated thread. Handling user input sequentially is . Up to now the android based input dispatcher forwards user input notifications into another thread for dispatch. This thread was also used to receive those responses.
In the InputSender implementation I assumed the main loop to be a natural pick as I did not assume a lot of load caused by those notifications and assumed that creating another thread for that use case is not necessary. A lot of assumptions... so to resolve that, how about:
class EventLoop : public graphics:
{
public:
virtual void run() = 0;
virtual void stop() = 0;
}; // or WorkLoop or just Loop ..
class MainLoop : EventLoop
{
};
class AsioMainLoop : MainLoop
{
...
};
That way the decision which thread should handle that could be done inside the DefaultServerCo
Andreas Pokorny (andreas-pokorny) wrote : | # |
> Is the current plan to use the server main loop, or will we have a different
> object?
>
> I wonder if using the main loop mechanism to handle the FDs for input is
> appropriate. Do we need total serialization of the messages? I could imagine
> using a more focused class for this purpose (but I am not familiar with the
> details).
>
> This is not a NACK, just a call for information.
Now you can have a look at lp:~andreas-pokorny/mir/input-sender ~> lines 600ff
Andreas Pokorny (andreas-pokorny) wrote : | # |
There are races inside reconfiguration options like timer reschedule and unregistering the fd handler (the code around 270 + races for "still_
- 1649. By Andreas Pokorny
-
cleaning up empty lines and removing debug code
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1648
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1650. By Andreas Pokorny
-
fix missing update of alarm state to pending and missing timer_thread update
- 1651. By Andreas Pokorny
-
make AsioTimerServic
e::stop synchronous
Andreas Pokorny (andreas-pokorny) wrote : | # |
The code is slightly simpler than the intermediate combined solution, but has grown in a way that replacing asio with a different solution looks like a good idea.
Andreas Pokorny (andreas-pokorny) wrote : | # |
+ in a later step (for main loop and timer fd)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1650
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1651
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) : | # |
- 1652. By Andreas Pokorny
-
Renamed SentinelAction to SynchronousServ
erAction and all remaining references of main loop in timer service tests got renamed to timer service.
- 1653. By Andreas Pokorny
-
renamed test again
Andreas Pokorny (andreas-pokorny) : | # |
- 1654. By Andreas Pokorny
-
Getting rid of InternalState in FDHandler
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1652
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1655. By Andreas Pokorny
-
remove action queue again
- 1656. By Andreas Pokorny
-
switching from mutex to atomic<State>
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1654
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alexandros Frantzis (afrantzis) : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1656
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Andreas Pokorny (andreas-pokorny) : | # |
- 1657. By Andreas Pokorny
-
Explain synchronizatio aspects of main loop and fix c++ style guide issues
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1657
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
First pass basically looks ok...
Not sure about handling the finished signals in main loop...while they aren't high bandwidth they are timing sensitive and my intuition is a thread will provide better average latency. Of course this is all just speculation without benchmarking of some realistic input scenarios.
The timeouts in the unit test of a few hundred milliseconds may need bumping, such timeouts frequently fail under QEMU on Jenkins.
oh
Robert Carr (robertcarr) wrote : | # |
Not sure where "oh" came from at the end there ;)
Alan Griffiths (alan-griffiths) wrote : | # |
Why do we have one MP coupling two changes?
1. "Add the ability to unregister fd callbacks from AsioMainLoop" and
2. "Moves Timer handling out of MainLoop into a separate TimerService"
Alan Griffiths (alan-griffiths) wrote : | # |
We've used the base "mir" namespace for the utilities that help bring up the display server implemented by components in nested namespaces. I.e. it contains display_server, run_mir and default_
I don't think src/server is the right place for asio_timer_service or synchronous_
Andreas Pokorny (andreas-pokorny) wrote : | # |
> Why do we have one MP coupling two changes?
>
> 1. "Add the ability to unregister fd callbacks from AsioMainLoop" and
> 2. "Moves Timer handling out of MainLoop into a separate TimerService"
It started with just 1. and when giving the rationale for 1, doing 2. was suggested, for keepig those things out of the main loop.
Alexandros Frantzis (afrantzis) wrote : | # |
833 + boost::
1486 + boost::
No need for boost::optional, since std::thread::id() constructs a thread id guaranteed to not represent any valid thread (it will compare unequal to any valid thread ids).
1511 + if (done)
1512 + return;
Unprotected access to 'done'.
1540 +#ifndef MIR_SENTINEL_
1541 +#define MIR_SENTINEL_
Wrong guard name (also missing '_' at the end of the name).
2240 + std::atomic<int> p1_handler_
2241 + std::atomic<int> p2_handler_
No need for atomic, since the handlers are guaranteed not to run after run() returns.
2850 +TEST(Synchrono
It would be better to use an expectation on mtd::MockServer
currently we don't check that the queue is actually used. For example, if we mess up the code and call action() directly instead of enqueuing this test will still succeed.
mtd::MockServer
EXPECT_
.WillOnce(
Andreas Pokorny (andreas-pokorny) wrote : | # |
> 833 + boost::
> 1486 + boost::
>
> No need for boost::optional, since std::thread::id() constructs a thread id
> guaranteed to not represent any valid thread (it will compare unequal to any
> valid thread ids).
I use those three states to differ between - queue is not running, queue is running on a different thread, queue is running on the same thread.
> 1511 + if (done)
> 1512 + return;
>
> Unprotected access to 'done'.
>
> 1540 +#ifndef MIR_SENTINEL_
> 1541 +#define MIR_SENTINEL_
>
> Wrong guard name (also missing '_' at the end of the name).
>
> 2240 + std::atomic<int> p1_handler_
> 2241 + std::atomic<int> p2_handler_
>
> No need for atomic, since the handlers are guaranteed not to run after run()
> returns.
ack fixing.
> 2850 +TEST(Synchrono
>
> It would be better to use an expectation on
> mtd::MockServer
> currently we don't check that the queue is actually used. For example, if we
> mess up the code and call action() directly instead of enqueuing this test
> will still succeed.
>
> mtd::MockServer
> EXPECT_
> .WillOnce(...invoke arg2...);
See above, this is tested by the first unit test.
- 1658. By Andreas Pokorny
-
merged devel, and addressed findings
- 1659. By Andreas Pokorny
-
fixes strange indent
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1658
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Andreas Pokorny (andreas-pokorny) wrote : | # |
fixed the weird indent
> We've used the base "mir" namespace for the utilities that help bring up the
> display server implemented by components in nested namespaces. I.e. it
> contains display_server, run_mir and default_
> files to support this.
>
> I don't think src/server is the right place for asio_timer_service or
> synchronous_
> existing.) Whether these belong in "time" (or a new "scheduling" namespace) is
> less clear to me.
I proposed one solution (with this branch as prerequisite) here: https:/
Alan Griffiths (alan-griffiths) wrote : | # |
111 +class TimerService : public Timer
112 +{
113 +public:
114 + virtual void run() = 0;
115 + virtual void stop() = 0;
116 +};
I don't find "TimerService" a particularly helpful name. Saying "TimerService *is a* Timer" seems wrong.
Suggestions: "GlobalTimer", "MainTimer" or "SystemTimer"?
OTOH looking at mt::Timer ... that could be plausibly renamed "AlarmService".
Anyway, something about the naming needs fixing. ;)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1659
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Andreas Pokorny (andreas-pokorny) wrote : | # |
> 111 +class TimerService : public Timer
> 112 +{
> 113 +public:
> 114 + virtual void run() = 0;
> 115 + virtual void stop() = 0;
> 116 +};
>
> I don't find "TimerService" a particularly helpful name. Saying "TimerService
> *is a* Timer" seems wrong.
> Suggestions: "GlobalTimer", "MainTimer" or "SystemTimer"?
That part of the interface is all about starting (run) and stopping.. like in MainLoop so:
AlarmLoop?
> OTOH looking at mt::Timer ... that could be plausibly renamed "AlarmService".
>
> Anyway, something about the naming needs fixing. ;)
Agreed Timer is a bad pick for something that provides Alarms (aka Timers).
AlarmService sounds good ..
- 1660. By Andreas Pokorny
-
restore fix for alarm unit tests
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1660
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Andreas Pokorny (andreas-pokorny) wrote : | # |
> [...]
>
> Anyway, something about the naming needs fixing.
I tried to address that in the MP that also moves the classes into a better namespace: https:/
Alan Griffiths (alan-griffiths) wrote : | # |
1061 + // of boost::asio only allowing static methods inside the taits type.
s/taits/traits/
~~~~
1372 + return std::make_
TimeoutFrameDro
1363 +#include "mir/time/
- 1661. By Andreas Pokorny
-
typo
- 1662. By Andreas Pokorny
-
Removed mir::timer:
:TimerService the run/stop part of the interface is now represented by mir::Loop
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1662
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1663. By Andreas Pokorny
-
merged lp:mir/development-branch
- 1664. By Andreas Pokorny
-
merged lp:mir/development-branch
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1664
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Andreas Pokorny (andreas-pokorny) wrote : | # |
> 1372 + return
> std::make_
>
> TimeoutFrameDro
> shared_
> are wrong. It also requires an extra header here that shouldn't be needed.
> Vis:
>
> 1363 +#include "mir/time/
I think we already had that before, but with MainLoop. I think it was wrong to extend Timer with start/stop and use it as "the_timer_
Alan Griffiths (alan-griffiths) wrote : | # |
> assuming that every operator() call of CachedPtr
> has identical behaviour.
Responding to just this part of the comment (not the MP): it is hard to ensure this as users of Mir can override these calls.
Alan Griffiths (alan-griffiths) wrote : | # |
41 + virtual std::shared_
"mir::" is not needed.
~~~~
79 +namespace mir
80 +{
81 +class Loop
I think "Loop" is an uninformative name (what is it?) in the wrong namespace (what role it has in setting up the server)?
Should it be time::Scheduler or todo::Scheduler?
Andreas Pokorny (andreas-pokorny) wrote : | # |
> > assuming that every operator() call of CachedPtr
> > has identical behaviour.
>
> Responding to just this part of the comment (not the MP): it is hard to ensure
> this as users of Mir can override these calls.
Hm I thought I would be safe from that kind of abuse by making the CachedPtr private - but it still felt like a hack so I did not do it.
Andreas Pokorny (andreas-pokorny) wrote : | # |
> 41 + virtual std::shared_
>
> "mir::" is not needed.
>
> ~~~~
>
> 79 +namespace mir
> 80 +{
> 81 +class Loop
>
> I think "Loop" is an uninformative name (what is it?) in the wrong namespace
> (what role it has in setting up the server)?
A blocking but stoppable thing? I thought within that domain (to be moved to scheduler namespace) it would make at least some artificial sense. I.e a horrible implementation of Loop::run could be a simple loop: while(!done) { execute_
> Should it be time::Scheduler or todo::Scheduler?
Scheduler sounds ok too.
It is in the wrong namespace still, because I thought I should to the moving in a separate MP.
- 1665. By Andreas Pokorny
-
unnecessary namespace
Alexandros Frantzis (afrantzis) wrote : | # |
> > 833 + boost::
> > 1486 + boost::
> >
> > No need for boost::optional, since std::thread::id() constructs a thread id
> > guaranteed to not represent any valid thread (it will compare unequal to any
> > valid thread ids).
>
> I use those three states to differ between - queue is not running,
> queue is running on a different thread, queue is running on the same thread.
In the current code at least, I don't see a need to differentiate states (1) and (3):
if (current_thread == queue_thread) //state 2
{
...
}
else // no queue_thread or current_thread != queue_thread i.e. state 1 && state 3
{
...
}
Anyway, not a blocker.
+ virtual std::shared_
...and other timer related stuff
How about something like?
TimerService
{
void run() = 0;
void stop() = 0;
std::
};
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1665
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
Mostly happy I think.
Not sure I see the point in the mir::Loop interface? Yes the main loop and the timer service both have run/stop methods, but which component really benefits from the loop interface?
Alexandros Frantzis (afrantzis) wrote : | # |
1959 -class MockEventRegister : public mg::EventHandle
1960 +class MockVirtualTerminal : public mgm::VirtualTer
This (new) class is not needed (we have mtd::MockVirtua
> 2850 +TEST(Synchrono
>
> > It would be better to use an expectation on
> > mtd::MockServer
> > currently we don't check that the queue is actually used. For example, if we
> > mess up the code and call action() directly instead of enqueuing this test
> > will still succeed.
>
> See above, this is tested by the first unit test.
I don't see how this is covered by the first test. All other tests check that enqueue is never called. If we mess up the code in the SynchronousServ
done = true;
action();
all tests will still pass. Regardless of the details, the bottom line is that this test is called "enqueues_
- 1666. By Andreas Pokorny
-
review findings
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1666
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
> Mostly happy I think.
>
> Not sure I see the point in the mir::Loop interface? Yes the main loop and the
> timer service both have run/stop methods, but which component really benefits
> from the loop interface?
Mmmm.
Adding an interface whose purpose is unclear to the wrong namespace is enough to make me unhappy with the MP.
Alan Griffiths (alan-griffiths) wrote : | # |
OK I started looking at the dependent MP and decided that the comment really needs to be addressed here. It seems that the "scheduler" namespace proposed there is is going to be about dispatching events.
That implies that the Loop interface is about an EventLoop?
It makes more sense to start/stop "the event loop" than, as proposed here, to start and stop "the loop". ("Loop" is meaninglessly generic.)
A lot of the problem understanding this stack of MPs comes down to misleading or ambiguous names.
Maybe the name "scheduler" in the next MP is also setting wrong expectations. Only some of those events are schedule based - others are based on fds and interrupts. Perhaps "dispatcher" would be better.
But if both TimerLoop and MainLoop are dispatching events (albeit from different sources) I am still unsure that this is a right approach.
Alberto Aguirre (albaguirre) wrote : | # |
Loop as an interface doesn't seem necessary.
It seems the timer and the "thing" in which the timer runs are coupled together so it doesn't make sense to expose "the_timer" and "the_timer_service" as the interfaces do not express the coupling between them.
Instead TimerService and Timer interfaces should fold into an AlarmService interface, expose it in the config as the_alarm_service() :
namespace mir
{
namespace time
{
class AlarmService
{
void run() = 0;
void stop() = 0;
virtual std::unique_
std:
virtual std::unique_
std:
virtual std::unique_
};
Then a mir user that would like to replace the alarm service can use any mechanism it wants - threads, timer fds, mainloops...etc..
Unmerged revisions
- 1666. By Andreas Pokorny
-
review findings
- 1665. By Andreas Pokorny
-
unnecessary namespace
- 1664. By Andreas Pokorny
-
merged lp:mir/development-branch
- 1663. By Andreas Pokorny
-
merged lp:mir/development-branch
- 1662. By Andreas Pokorny
-
Removed mir::timer:
:TimerService the run/stop part of the interface is now represented by mir::Loop - 1661. By Andreas Pokorny
-
typo
- 1660. By Andreas Pokorny
-
restore fix for alarm unit tests
- 1659. By Andreas Pokorny
-
fixes strange indent
- 1658. By Andreas Pokorny
-
merged devel, and addressed findings
- 1657. By Andreas Pokorny
-
Explain synchronizatio aspects of main loop and fix c++ style guide issues
Preview Diff
1 | === modified file 'include/platform/mir/graphics/event_handler_register.h' |
2 | --- include/platform/mir/graphics/event_handler_register.h 2013-08-28 03:41:48 +0000 |
3 | +++ include/platform/mir/graphics/event_handler_register.h 2014-06-16 22:41:10 +0000 |
4 | @@ -37,8 +37,11 @@ |
5 | |
6 | virtual void register_fd_handler( |
7 | std::initializer_list<int> fds, |
8 | + void const* owner, |
9 | std::function<void(int)> const& handler) = 0; |
10 | |
11 | + virtual void unregister_fd_handler(void const* owner) = 0; |
12 | + |
13 | protected: |
14 | EventHandlerRegister() = default; |
15 | virtual ~EventHandlerRegister() = default; |
16 | |
17 | === modified file 'include/server/mir/default_server_configuration.h' |
18 | --- include/server/mir/default_server_configuration.h 2014-06-12 15:35:08 +0000 |
19 | +++ include/server/mir/default_server_configuration.h 2014-06-16 22:41:10 +0000 |
20 | @@ -73,6 +73,8 @@ |
21 | namespace time |
22 | { |
23 | class Clock; |
24 | +class Timer; |
25 | +class TimerLoop; |
26 | } |
27 | namespace scene |
28 | { |
29 | @@ -159,6 +161,7 @@ |
30 | virtual std::shared_ptr<compositor::Compositor> the_compositor(); |
31 | virtual std::shared_ptr<input::InputManager> the_input_manager(); |
32 | virtual std::shared_ptr<MainLoop> the_main_loop(); |
33 | + virtual std::shared_ptr<Loop> the_timer_loop(); |
34 | virtual std::shared_ptr<ServerStatusListener> the_server_status_listener(); |
35 | virtual std::shared_ptr<DisplayChanger> the_display_changer(); |
36 | virtual std::shared_ptr<graphics::Platform> the_graphics_platform(); |
37 | @@ -279,6 +282,7 @@ |
38 | |
39 | virtual std::shared_ptr<time::Clock> the_clock(); |
40 | virtual std::shared_ptr<ServerActionQueue> the_server_action_queue(); |
41 | + virtual std::shared_ptr<time::Timer> the_timer(); |
42 | |
43 | protected: |
44 | std::shared_ptr<options::Option> the_options() const; |
45 | @@ -363,6 +367,7 @@ |
46 | CachedPtr<graphics::DisplayReport> display_report; |
47 | CachedPtr<time::Clock> clock; |
48 | CachedPtr<MainLoop> main_loop; |
49 | + CachedPtr<time::TimerLoop> timer_loop; |
50 | CachedPtr<ServerStatusListener> server_status_listener; |
51 | CachedPtr<graphics::DisplayConfigurationPolicy> display_configuration_policy; |
52 | CachedPtr<graphics::nested::HostConnection> host_connection; |
53 | |
54 | === added file 'include/server/mir/loop.h' |
55 | --- include/server/mir/loop.h 1970-01-01 00:00:00 +0000 |
56 | +++ include/server/mir/loop.h 2014-06-16 22:41:10 +0000 |
57 | @@ -0,0 +1,37 @@ |
58 | +/* |
59 | + * Copyright © 2014 Canonical Ltd. |
60 | + * |
61 | + * This program is free software: you can redistribute it and/or modify it |
62 | + * under the terms of the GNU General Public License version 3, |
63 | + * as published by the Free Software Foundation. |
64 | + * |
65 | + * This program is distributed in the hope that it will be useful, |
66 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
67 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
68 | + * GNU General Public License for more details. |
69 | + * |
70 | + * You should have received a copy of the GNU General Public License |
71 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
72 | + * |
73 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
74 | + */ |
75 | + |
76 | +#ifndef MIR_LOOP_H_ |
77 | +#define MIR_LOOP_H_ |
78 | + |
79 | +namespace mir |
80 | +{ |
81 | +class Loop |
82 | +{ |
83 | +public: |
84 | + Loop() = default; |
85 | + virtual ~Loop() = default; |
86 | + virtual void run() = 0; |
87 | + virtual void stop() = 0; |
88 | +protected: |
89 | + Loop(Loop const&) = delete; |
90 | + Loop& operator=(Loop const&) = delete; |
91 | +}; |
92 | +} |
93 | + |
94 | +#endif |
95 | |
96 | === modified file 'include/server/mir/main_loop.h' |
97 | --- include/server/mir/main_loop.h 2014-06-03 11:04:15 +0000 |
98 | +++ include/server/mir/main_loop.h 2014-06-16 22:41:10 +0000 |
99 | @@ -20,13 +20,12 @@ |
100 | #define MIR_MAIN_LOOP_H_ |
101 | |
102 | #include "mir/graphics/event_handler_register.h" |
103 | -#include "mir/time/timer.h" |
104 | #include "mir/server_action_queue.h" |
105 | |
106 | namespace mir |
107 | { |
108 | |
109 | -class MainLoop : public graphics::EventHandlerRegister, public time::Timer, |
110 | +class MainLoop : public graphics::EventHandlerRegister, |
111 | public ServerActionQueue |
112 | { |
113 | public: |
114 | |
115 | === modified file 'include/server/mir/server_configuration.h' |
116 | --- include/server/mir/server_configuration.h 2014-06-02 17:07:02 +0000 |
117 | +++ include/server/mir/server_configuration.h 2014-06-16 22:41:10 +0000 |
118 | @@ -50,6 +50,7 @@ |
119 | } |
120 | |
121 | class MainLoop; |
122 | +class Loop; |
123 | class ServerStatusListener; |
124 | class DisplayChanger; |
125 | class EmergencyCleanup; |
126 | @@ -65,6 +66,7 @@ |
127 | virtual std::shared_ptr<input::InputManager> the_input_manager() = 0; |
128 | virtual std::shared_ptr<input::InputDispatcher> the_input_dispatcher() = 0; |
129 | virtual std::shared_ptr<MainLoop> the_main_loop() = 0; |
130 | + virtual std::shared_ptr<Loop> the_timer_loop() = 0; |
131 | virtual std::shared_ptr<ServerStatusListener> the_server_status_listener() = 0; |
132 | virtual std::shared_ptr<DisplayChanger> the_display_changer() = 0; |
133 | virtual std::shared_ptr<graphics::Platform> the_graphics_platform() = 0; |
134 | |
135 | === added file 'include/server/mir/time/timer_loop.h' |
136 | --- include/server/mir/time/timer_loop.h 1970-01-01 00:00:00 +0000 |
137 | +++ include/server/mir/time/timer_loop.h 2014-06-16 22:41:10 +0000 |
138 | @@ -0,0 +1,37 @@ |
139 | +/* |
140 | + * Copyright © 2014 Canonical Ltd. |
141 | + * |
142 | + * This program is free software: you can redistribute it and/or modify it |
143 | + * under the terms of the GNU General Public License version 3, |
144 | + * as published by the Free Software Foundation. |
145 | + * |
146 | + * This program is distributed in the hope that it will be useful, |
147 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
148 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
149 | + * GNU General Public License for more details. |
150 | + * |
151 | + * You should have received a copy of the GNU General Public License |
152 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
153 | + * |
154 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
155 | + */ |
156 | + |
157 | +#ifndef MIR_TIME_TIMER_LOOP_H_ |
158 | +#define MIR_TIME_TIMER_LOOP_H_ |
159 | + |
160 | +#include "mir/time/timer.h" |
161 | +#include "mir/loop.h" |
162 | + |
163 | +namespace mir |
164 | +{ |
165 | +namespace time |
166 | +{ |
167 | + |
168 | +class TimerLoop : public Timer, public Loop |
169 | +{ |
170 | +}; |
171 | + |
172 | +} |
173 | +} |
174 | + |
175 | +#endif |
176 | |
177 | === added file 'include/test/mir_test_doubles/mock_event_handler_register.h' |
178 | --- include/test/mir_test_doubles/mock_event_handler_register.h 1970-01-01 00:00:00 +0000 |
179 | +++ include/test/mir_test_doubles/mock_event_handler_register.h 2014-06-16 22:41:10 +0000 |
180 | @@ -0,0 +1,53 @@ |
181 | +/* |
182 | + * Copyright © 2014 Canonical Ltd. |
183 | + * |
184 | + * This program is free software: you can redistribute it and/or modify |
185 | + * it under the terms of the GNU General Public License version 3 as |
186 | + * published by the Free Software Foundation. |
187 | + * |
188 | + * This program is distributed in the hope that it will be useful, |
189 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
190 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
191 | + * GNU General Public License for more details. |
192 | + * |
193 | + * You should have received a copy of the GNU General Public License |
194 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
195 | + * |
196 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
197 | + */ |
198 | + |
199 | +#ifndef MOCK_EVENT_HANDLER_REGISTER_H_ |
200 | +#define MOCK_EVENT_HANDLER_REGISTER_H_ |
201 | + |
202 | +#include "mir/graphics/event_handler_register.h" |
203 | + |
204 | +#include <gmock/gmock.h> |
205 | + |
206 | +namespace mir |
207 | +{ |
208 | +namespace test |
209 | +{ |
210 | +namespace doubles |
211 | +{ |
212 | + |
213 | +class MockEventHandlerRegister : public graphics::EventHandlerRegister |
214 | +{ |
215 | +public: |
216 | + ~MockEventHandlerRegister() noexcept {} |
217 | + |
218 | + MOCK_METHOD2(register_signal_handler, |
219 | + void(std::initializer_list<int>, |
220 | + std::function<void(int)> const&)); |
221 | + |
222 | + MOCK_METHOD3(register_fd_handler, |
223 | + void(std::initializer_list<int>, void const*, |
224 | + std::function<void(int)> const&)); |
225 | + |
226 | + MOCK_METHOD1(unregister_fd_handler, void(void const*)); |
227 | +}; |
228 | + |
229 | +} |
230 | +} |
231 | +} |
232 | + |
233 | +#endif |
234 | |
235 | === added file 'include/test/mir_test_doubles/mock_loop.h' |
236 | --- include/test/mir_test_doubles/mock_loop.h 1970-01-01 00:00:00 +0000 |
237 | +++ include/test/mir_test_doubles/mock_loop.h 2014-06-16 22:41:10 +0000 |
238 | @@ -0,0 +1,44 @@ |
239 | +/* |
240 | + * Copyright © 2014 Canonical Ltd. |
241 | + * |
242 | + * This program is free software: you can redistribute it and/or modify it |
243 | + * under the terms of the GNU General Public License version 3, |
244 | + * as published by the Free Software Foundation. |
245 | + * |
246 | + * This program is distributed in the hope that it will be useful, |
247 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
248 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
249 | + * GNU General Public License for more details. |
250 | + * |
251 | + * You should have received a copy of the GNU General Public License |
252 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
253 | + * |
254 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
255 | + */ |
256 | + |
257 | +#ifndef MIR_MOCK_LOOP_H_ |
258 | +#define MIR_MOCK_LOOP_H_ |
259 | + |
260 | +#include "mir/loop.h" |
261 | + |
262 | +#include "mir_test/gmock_fixes.h" |
263 | + |
264 | +namespace mir |
265 | +{ |
266 | +namespace test |
267 | +{ |
268 | +namespace doubles |
269 | +{ |
270 | + |
271 | +class MockLoop : public mir::Loop |
272 | +{ |
273 | +public: |
274 | + MOCK_METHOD0(run, void()); |
275 | + MOCK_METHOD0(stop, void()); |
276 | +}; |
277 | + |
278 | +} |
279 | +} |
280 | +} |
281 | + |
282 | +#endif |
283 | |
284 | === added file 'include/test/mir_test_doubles/mock_server_action_queue.h' |
285 | --- include/test/mir_test_doubles/mock_server_action_queue.h 1970-01-01 00:00:00 +0000 |
286 | +++ include/test/mir_test_doubles/mock_server_action_queue.h 2014-06-16 22:41:10 +0000 |
287 | @@ -0,0 +1,44 @@ |
288 | +/* |
289 | + * Copyright © 2014 Canonical Ltd. |
290 | + * |
291 | + * This program is free software: you can redistribute it and/or modify it |
292 | + * under the terms of the GNU General Public License version 3, |
293 | + * as published by the Free Software Foundation. |
294 | + * |
295 | + * This program is distributed in the hope that it will be useful, |
296 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
297 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
298 | + * GNU General Public License for more details. |
299 | + * |
300 | + * You should have received a copy of the GNU General Public License |
301 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
302 | + * |
303 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
304 | + */ |
305 | + |
306 | +#ifndef MIR_MOCK_SERVER_ACTION_QUEUE_H_ |
307 | +#define MIR_MOCK_SERVER_ACTION_QUEUE_H_ |
308 | + |
309 | +#include "mir/server_action_queue.h" |
310 | +#include <gmock/gmock.h> |
311 | + |
312 | +namespace mir |
313 | +{ |
314 | +namespace test |
315 | +{ |
316 | +namespace doubles |
317 | +{ |
318 | + |
319 | +class MockServerActionQueue : public mir::ServerActionQueue |
320 | +{ |
321 | +public: |
322 | + MOCK_METHOD2(enqueue, void (void const* /*owner*/, mir::ServerAction const& /*action*/)); |
323 | + MOCK_METHOD1(pause_processing_for, void(void const* /*owner*/)); |
324 | + MOCK_METHOD1(resume_processing_for, void(void const* /*owner*/)); |
325 | +}; |
326 | + |
327 | +} |
328 | +} |
329 | +} |
330 | + |
331 | +#endif |
332 | |
333 | === modified file 'src/platform/graphics/mesa/display.cpp' |
334 | --- src/platform/graphics/mesa/display.cpp 2014-06-10 14:40:23 +0000 |
335 | +++ src/platform/graphics/mesa/display.cpp 2014-06-16 22:41:10 +0000 |
336 | @@ -233,6 +233,7 @@ |
337 | { |
338 | handlers.register_fd_handler( |
339 | {monitor.fd()}, |
340 | + this, |
341 | [conf_change_handler, this](int) |
342 | { |
343 | monitor.process_events([conf_change_handler] |
344 | |
345 | === modified file 'src/server/CMakeLists.txt' |
346 | --- src/server/CMakeLists.txt 2014-06-06 10:03:54 +0000 |
347 | +++ src/server/CMakeLists.txt 2014-06-16 22:41:10 +0000 |
348 | @@ -30,6 +30,9 @@ |
349 | display_server.cpp |
350 | default_server_configuration.cpp |
351 | asio_main_loop.cpp |
352 | + asio_server_action_queue.cpp |
353 | + asio_timer_service.cpp |
354 | + synchronous_server_action.cpp |
355 | ) |
356 | |
357 | set(MIRSERVER_LINKAGE SHARED) |
358 | |
359 | === modified file 'src/server/asio_main_loop.cpp' |
360 | --- src/server/asio_main_loop.cpp 2014-06-12 15:35:08 +0000 |
361 | +++ src/server/asio_main_loop.cpp 2014-06-16 22:41:10 +0000 |
362 | @@ -16,87 +16,15 @@ |
363 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
364 | */ |
365 | |
366 | -#include "mir/asio_main_loop.h" |
367 | +#include "asio_main_loop.h" |
368 | |
369 | -#include "boost/date_time/posix_time/conversion.hpp" |
370 | +#include "synchronous_server_action.h" |
371 | |
372 | #include <cassert> |
373 | #include <mutex> |
374 | #include <condition_variable> |
375 | |
376 | -namespace |
377 | -{ |
378 | -struct MirClockTimerTraits |
379 | -{ |
380 | - // TODO the clock used by the main loop is a global setting, this is a restriction |
381 | - // of boost::asio only allowing static methods inside the taits type. |
382 | - struct TimerServiceClockStorage |
383 | - { |
384 | - public: |
385 | - void set_clock(std::shared_ptr<mir::time::Clock const> const& clock) |
386 | - { |
387 | - std::lock_guard<std::mutex> lock(timer_service_mutex); |
388 | - auto stored_clock = timer_service_clock.lock(); |
389 | - if (stored_clock && stored_clock != clock) |
390 | - BOOST_THROW_EXCEPTION(std::logic_error("A clock is already in use as time source for mir::AsioMainLoop")); |
391 | - timer_service_clock = clock; |
392 | - } |
393 | - mir::time::Timestamp now() |
394 | - { |
395 | - std::lock_guard<std::mutex> lock(timer_service_mutex); |
396 | - auto clock = timer_service_clock.lock(); |
397 | - if (!clock) |
398 | - BOOST_THROW_EXCEPTION(std::logic_error("No clock available to create time stamp")); |
399 | - return clock->sample(); |
400 | - } |
401 | - private: |
402 | - std::mutex timer_service_mutex; |
403 | - std::weak_ptr<mir::time::Clock const> timer_service_clock; |
404 | - }; |
405 | - |
406 | - static TimerServiceClockStorage clock_storage; |
407 | - |
408 | - static void set_clock(std::shared_ptr<mir::time::Clock const> const& clock) |
409 | - { |
410 | - clock_storage.set_clock(clock); |
411 | - } |
412 | - |
413 | - // time_traits interface required by boost::asio::deadline_timer{_service} |
414 | - typedef mir::time::Timestamp time_type; |
415 | - typedef std::chrono::milliseconds duration_type; |
416 | - |
417 | - |
418 | - static time_type now() |
419 | - { |
420 | - return clock_storage.now(); |
421 | - } |
422 | - |
423 | - static time_type add(const time_type& t, const duration_type& d) |
424 | - { |
425 | - return t + d; |
426 | - } |
427 | - |
428 | - static duration_type subtract(const time_type& t1, const time_type& t2) |
429 | - { |
430 | - return std::chrono::duration_cast<duration_type>(t1 - t2); |
431 | - } |
432 | - |
433 | - static bool less_than(const time_type& t1, const time_type& t2) |
434 | - { |
435 | - return t1 < t2; |
436 | - } |
437 | - |
438 | - static boost::posix_time::time_duration to_posix_duration( |
439 | - const duration_type& d) |
440 | - { |
441 | - return boost::posix_time::millisec(d.count()); |
442 | - } |
443 | -}; |
444 | - |
445 | -MirClockTimerTraits::TimerServiceClockStorage MirClockTimerTraits::clock_storage; |
446 | - |
447 | -typedef boost::asio::basic_deadline_timer<mir::time::Timestamp, MirClockTimerTraits> deadline_timer; |
448 | -} |
449 | +namespace bap = boost::asio::posix; |
450 | |
451 | class mir::AsioMainLoop::SignalHandler |
452 | { |
453 | @@ -137,46 +65,64 @@ |
454 | class mir::AsioMainLoop::FDHandler |
455 | { |
456 | public: |
457 | + typedef std::unique_ptr<bap::stream_descriptor> stream_descriptor_ptr; |
458 | + |
459 | FDHandler(boost::asio::io_service& io, |
460 | std::initializer_list<int> fds, |
461 | + void const* owner, |
462 | std::function<void(int)> const& handler) |
463 | - : handler{handler} |
464 | + : handler{handler}, owner{owner} |
465 | { |
466 | for (auto fd : fds) |
467 | - { |
468 | - auto raw = new boost::asio::posix::stream_descriptor{io, fd}; |
469 | - auto s = std::unique_ptr<boost::asio::posix::stream_descriptor>(raw); |
470 | - stream_descriptors.push_back(std::move(s)); |
471 | - } |
472 | - } |
473 | - |
474 | - void async_wait() |
475 | - { |
476 | - for (auto const& s : stream_descriptors) |
477 | - { |
478 | - s->async_read_some( |
479 | - boost::asio::null_buffers(), |
480 | - std::bind(&FDHandler::handle, this, |
481 | - std::placeholders::_1, std::placeholders::_2, s.get())); |
482 | - } |
483 | + stream_descriptors.emplace_back(new bap::stream_descriptor{io, fd}); |
484 | + } |
485 | + |
486 | + bool is_owned_by(void const* possible_owner) const |
487 | + { |
488 | + return owner == possible_owner; |
489 | + } |
490 | + |
491 | + static void async_wait(std::shared_ptr<FDHandler> const& fd_handler, ServerActionQueue & queue) |
492 | + { |
493 | + for (auto const& s : fd_handler->stream_descriptors) |
494 | + read_some(s.get(), fd_handler, queue); |
495 | } |
496 | |
497 | private: |
498 | - void handle(boost::system::error_code err, size_t /*bytes*/, |
499 | - boost::asio::posix::stream_descriptor* s) |
500 | + static void read_some(bap::stream_descriptor* s, std::weak_ptr<FDHandler> const& possible_fd_handler, ServerActionQueue & queue) |
501 | { |
502 | - if (!err) |
503 | - { |
504 | - handler(s->native_handle()); |
505 | - s->async_read_some( |
506 | - boost::asio::null_buffers(), |
507 | - std::bind(&FDHandler::handle, this, |
508 | - std::placeholders::_1, std::placeholders::_2, s)); |
509 | - } |
510 | + s->async_read_some( |
511 | + boost::asio::null_buffers(), |
512 | + [possible_fd_handler,s,&queue](boost::system::error_code err, size_t /*bytes*/) |
513 | + { |
514 | + if (err) |
515 | + return; |
516 | + |
517 | + // The actual execution of the fd handler is moved to the action queue.This allows us to synchronously |
518 | + // unregister FDHandlers even when they are about to be executed. We do this because of the way Asio |
519 | + // copies and executes pending completion handlers. |
520 | + // In worst case during the call to unregister the FDHandler, it may still be executed, but not after |
521 | + // the unregister call returned. |
522 | + queue.enqueue( |
523 | + s, |
524 | + [possible_fd_handler, s, &queue]() |
525 | + { |
526 | + auto fd_handler = possible_fd_handler.lock(); |
527 | + if (!fd_handler) |
528 | + return; |
529 | + |
530 | + fd_handler->handler(s->native_handle()); |
531 | + fd_handler.reset(); |
532 | + |
533 | + if (possible_fd_handler.lock()) |
534 | + read_some(s, possible_fd_handler, queue); |
535 | + }); |
536 | + }); |
537 | } |
538 | |
539 | - std::vector<std::unique_ptr<boost::asio::posix::stream_descriptor>> stream_descriptors; |
540 | - std::function<void(int)> handler; |
541 | + std::vector<stream_descriptor_ptr> stream_descriptors; |
542 | + std::function<void(int)> const handler; |
543 | + void const* owner; |
544 | }; |
545 | |
546 | /* |
547 | @@ -186,10 +132,9 @@ |
548 | * don't have complete type information for SignalHandler and fail |
549 | * to compile. |
550 | */ |
551 | -mir::AsioMainLoop::AsioMainLoop(std::shared_ptr<time::Clock> const& clock) |
552 | - : work{io}, clock(clock) |
553 | +mir::AsioMainLoop::AsioMainLoop() |
554 | + : work{io}, action_queue{io} |
555 | { |
556 | - MirClockTimerTraits::set_clock(clock); |
557 | } |
558 | |
559 | mir::AsioMainLoop::~AsioMainLoop() noexcept(true) |
560 | @@ -198,12 +143,14 @@ |
561 | |
562 | void mir::AsioMainLoop::run() |
563 | { |
564 | + main_loop_thread = std::this_thread::get_id(); |
565 | io.run(); |
566 | } |
567 | |
568 | void mir::AsioMainLoop::stop() |
569 | { |
570 | io.stop(); |
571 | + main_loop_thread.reset(); |
572 | } |
573 | |
574 | void mir::AsioMainLoop::register_signal_handler( |
575 | @@ -222,220 +169,54 @@ |
576 | |
577 | void mir::AsioMainLoop::register_fd_handler( |
578 | std::initializer_list<int> fds, |
579 | + void const* owner, |
580 | std::function<void(int)> const& handler) |
581 | { |
582 | assert(handler); |
583 | |
584 | - auto fd_handler = std::unique_ptr<FDHandler>{ |
585 | - new FDHandler{io, fds, handler}}; |
586 | - |
587 | - fd_handler->async_wait(); |
588 | - |
589 | - fd_handlers.push_back(std::move(fd_handler)); |
590 | -} |
591 | - |
592 | -namespace |
593 | -{ |
594 | -class AlarmImpl : public mir::time::Alarm |
595 | -{ |
596 | -public: |
597 | - AlarmImpl(boost::asio::io_service& io, |
598 | - std::chrono::milliseconds delay, |
599 | - std::function<void(void)> callback); |
600 | - |
601 | - AlarmImpl(boost::asio::io_service& io, |
602 | - mir::time::Timestamp time_point, |
603 | - std::function<void(void)> callback); |
604 | - |
605 | - AlarmImpl(boost::asio::io_service& io, |
606 | - std::function<void(void)> callback); |
607 | - |
608 | - ~AlarmImpl() noexcept override; |
609 | - |
610 | - bool cancel() override; |
611 | - State state() const override; |
612 | - |
613 | - bool reschedule_in(std::chrono::milliseconds delay) override; |
614 | - bool reschedule_for(mir::time::Timestamp time_point) override; |
615 | -private: |
616 | - void update_timer(); |
617 | - struct InternalState |
618 | - { |
619 | - explicit InternalState(std::function<void(void)> callback) |
620 | - : callback{callback} |
621 | - { |
622 | - } |
623 | - |
624 | - mutable std::mutex m; |
625 | - std::function<void(void)> callback; |
626 | - State state; |
627 | - }; |
628 | - |
629 | - ::deadline_timer timer; |
630 | - std::shared_ptr<InternalState> data; |
631 | -}; |
632 | - |
633 | -AlarmImpl::AlarmImpl(boost::asio::io_service& io, |
634 | - std::chrono::milliseconds delay, |
635 | - std::function<void ()> callback) |
636 | - : AlarmImpl(io, callback) |
637 | -{ |
638 | - reschedule_in(delay); |
639 | -} |
640 | - |
641 | -AlarmImpl::AlarmImpl(boost::asio::io_service& io, |
642 | - mir::time::Timestamp time_point, |
643 | - std::function<void ()> callback) |
644 | - : AlarmImpl(io, callback) |
645 | -{ |
646 | - reschedule_for(time_point); |
647 | -} |
648 | - |
649 | -AlarmImpl::AlarmImpl(boost::asio::io_service& io, |
650 | - std::function<void(void)> callback) |
651 | - : timer{io}, |
652 | - data{std::make_shared<InternalState>(callback)} |
653 | -{ |
654 | - data->state = triggered; |
655 | -} |
656 | - |
657 | -AlarmImpl::~AlarmImpl() noexcept |
658 | -{ |
659 | - AlarmImpl::cancel(); |
660 | -} |
661 | - |
662 | -bool AlarmImpl::cancel() |
663 | -{ |
664 | - std::lock_guard<decltype(data->m)> lock(data->m); |
665 | - if (data->state == triggered) |
666 | - return false; |
667 | - |
668 | - data->state = cancelled; |
669 | - timer.cancel(); |
670 | - return true; |
671 | -} |
672 | - |
673 | -mir::time::Alarm::State AlarmImpl::state() const |
674 | -{ |
675 | - std::lock_guard<decltype(data->m)> lock(data->m); |
676 | - |
677 | - return data->state; |
678 | -} |
679 | - |
680 | -bool AlarmImpl::reschedule_in(std::chrono::milliseconds delay) |
681 | -{ |
682 | - bool cancelling = timer.expires_from_now(delay); |
683 | - update_timer(); |
684 | - return cancelling; |
685 | -} |
686 | - |
687 | -bool AlarmImpl::reschedule_for(mir::time::Timestamp time_point) |
688 | -{ |
689 | - bool cancelling = timer.expires_at(time_point); |
690 | - update_timer(); |
691 | - return cancelling; |
692 | -} |
693 | - |
694 | -void AlarmImpl::update_timer() |
695 | -{ |
696 | - std::lock_guard<decltype(data->m)> lock(data->m); |
697 | - // Awkwardly, we can't stop the async_wait handler from being called |
698 | - // on a destroyed AlarmImpl. This means we need to wedge a shared_ptr |
699 | - // into the async_wait callback. |
700 | - std::weak_ptr<InternalState> possible_data = data; |
701 | - timer.async_wait([possible_data](boost::system::error_code const& ec) |
702 | - { |
703 | - auto data = possible_data.lock(); |
704 | - if (!data) |
705 | - return; |
706 | - |
707 | - std::unique_lock<decltype(data->m)> lock(data->m); |
708 | - if (!ec && data->state == pending) |
709 | - { |
710 | - data->state = triggered; |
711 | - lock.unlock(); |
712 | - data->callback(); |
713 | - } |
714 | - }); |
715 | - data->state = pending; |
716 | -} |
717 | -} |
718 | - |
719 | -std::unique_ptr<mir::time::Alarm> mir::AsioMainLoop::notify_in(std::chrono::milliseconds delay, |
720 | - std::function<void()> callback) |
721 | -{ |
722 | - return std::unique_ptr<mir::time::Alarm>{new AlarmImpl{io, delay, callback}}; |
723 | -} |
724 | - |
725 | -std::unique_ptr<mir::time::Alarm> mir::AsioMainLoop::notify_at(mir::time::Timestamp time_point, |
726 | - std::function<void()> callback) |
727 | -{ |
728 | - return std::unique_ptr<mir::time::Alarm>{new AlarmImpl{io, time_point, callback}}; |
729 | -} |
730 | - |
731 | -std::unique_ptr<mir::time::Alarm> mir::AsioMainLoop::create_alarm(std::function<void()> callback) |
732 | -{ |
733 | - return std::unique_ptr<mir::time::Alarm>{new AlarmImpl{io, callback}}; |
734 | + auto fd_handler = std::make_shared<FDHandler>(io, fds, owner, handler); |
735 | + |
736 | + FDHandler::async_wait(fd_handler, action_queue); |
737 | + |
738 | + std::lock_guard<std::mutex> lock(fd_handlers_mutex); |
739 | + fd_handlers.push_back(fd_handler); |
740 | +} |
741 | + |
742 | +void mir::AsioMainLoop::unregister_fd_handler(void const* owner) |
743 | +{ |
744 | + // The SynchronousServerAction makes sure that with the |
745 | + // completion of the method unregister_fd_handler the |
746 | + // handler will no longer be called. |
747 | + // There is a chance for a fd handler callback to happen before |
748 | + // the completion of this method. |
749 | + SynchronousServerAction unregister{ |
750 | + action_queue, |
751 | + main_loop_thread, |
752 | + [this,owner]() |
753 | + { |
754 | + std::lock_guard<std::mutex> lock(fd_handlers_mutex); |
755 | + auto end_of_valid = remove_if( |
756 | + begin(fd_handlers), |
757 | + end(fd_handlers), |
758 | + [owner](std::shared_ptr<FDHandler> const& item) |
759 | + { |
760 | + return item->is_owned_by(owner); |
761 | + }); |
762 | + fd_handlers.erase(end_of_valid, end(fd_handlers)); |
763 | + }}; |
764 | } |
765 | |
766 | void mir::AsioMainLoop::enqueue(void const* owner, ServerAction const& action) |
767 | { |
768 | - { |
769 | - std::lock_guard<std::mutex> lock{server_actions_mutex}; |
770 | - server_actions.push_back({owner, action}); |
771 | - } |
772 | - |
773 | - io.post([this] { process_server_actions(); }); |
774 | + action_queue.enqueue(owner, action); |
775 | } |
776 | |
777 | void mir::AsioMainLoop::pause_processing_for(void const* owner) |
778 | { |
779 | - std::lock_guard<std::mutex> lock{server_actions_mutex}; |
780 | - do_not_process.insert(owner); |
781 | + action_queue.pause_processing_for(owner); |
782 | } |
783 | |
784 | void mir::AsioMainLoop::resume_processing_for(void const* owner) |
785 | { |
786 | - { |
787 | - std::lock_guard<std::mutex> lock{server_actions_mutex}; |
788 | - do_not_process.erase(owner); |
789 | - } |
790 | - |
791 | - io.post([this] { process_server_actions(); }); |
792 | -} |
793 | - |
794 | -void mir::AsioMainLoop::process_server_actions() |
795 | -{ |
796 | - std::unique_lock<std::mutex> lock{server_actions_mutex}; |
797 | - |
798 | - size_t i = 0; |
799 | - |
800 | - while (i < server_actions.size()) |
801 | - { |
802 | - /* |
803 | - * It's safe to use references to elements, since std::deque<> |
804 | - * guarantees that references remain valid after appends, which is |
805 | - * the only operation that can be performed on server_actions outside |
806 | - * this function (in AsioMainLoop::post()). |
807 | - */ |
808 | - auto const& owner = server_actions[i].first; |
809 | - auto const& action = server_actions[i].second; |
810 | - |
811 | - if (do_not_process.find(owner) == do_not_process.end()) |
812 | - { |
813 | - lock.unlock(); |
814 | - action(); |
815 | - lock.lock(); |
816 | - /* |
817 | - * This erase is always ok, since outside this function |
818 | - * we only append to server_actions, i.e., our index i |
819 | - * is guaranteed to remain valid and correct. |
820 | - */ |
821 | - server_actions.erase(server_actions.begin() + i); |
822 | - } |
823 | - else |
824 | - { |
825 | - ++i; |
826 | - } |
827 | - } |
828 | + action_queue.resume_processing_for(owner); |
829 | } |
830 | |
831 | === renamed file 'include/server/mir/asio_main_loop.h' => 'src/server/asio_main_loop.h' |
832 | --- include/server/mir/asio_main_loop.h 2014-06-12 15:35:08 +0000 |
833 | +++ src/server/asio_main_loop.h 2014-06-16 22:41:10 +0000 |
834 | @@ -21,8 +21,13 @@ |
835 | |
836 | #include "mir/main_loop.h" |
837 | |
838 | +#include "asio_server_action_queue.h" |
839 | + |
840 | #include <boost/asio.hpp> |
841 | +#include <boost/optional.hpp> |
842 | + |
843 | #include <memory> |
844 | +#include <thread> |
845 | #include <vector> |
846 | #include <mutex> |
847 | #include <utility> |
848 | @@ -32,52 +37,41 @@ |
849 | namespace mir |
850 | { |
851 | |
852 | -namespace time |
853 | -{ |
854 | -class Clock; |
855 | -} |
856 | - |
857 | class AsioMainLoop : public MainLoop |
858 | { |
859 | public: |
860 | - explicit AsioMainLoop(std::shared_ptr<time::Clock> const& clock); |
861 | + AsioMainLoop(); |
862 | ~AsioMainLoop() noexcept(true); |
863 | |
864 | - void run(); |
865 | - void stop(); |
866 | + void run() override; |
867 | + void stop() override; |
868 | |
869 | void register_signal_handler( |
870 | std::initializer_list<int> signals, |
871 | - std::function<void(int)> const& handler); |
872 | + std::function<void(int)> const& handler) override; |
873 | |
874 | void register_fd_handler( |
875 | std::initializer_list<int> fd, |
876 | - std::function<void(int)> const& handler); |
877 | - |
878 | - std::unique_ptr<time::Alarm> notify_in(std::chrono::milliseconds delay, |
879 | - std::function<void()> callback) override; |
880 | - std::unique_ptr<time::Alarm> notify_at(mir::time::Timestamp time_point, |
881 | - std::function<void()> callback) override; |
882 | - std::unique_ptr<time::Alarm> create_alarm(std::function<void()> callback) override; |
883 | - |
884 | - void enqueue(void const* owner, ServerAction const& action); |
885 | - void pause_processing_for(void const* owner); |
886 | - void resume_processing_for(void const* owner); |
887 | + void const* owner, |
888 | + std::function<void(int)> const& handler) override; |
889 | + |
890 | + void unregister_fd_handler(void const* owner) override; |
891 | + |
892 | + void enqueue(void const* owner, ServerAction const& action) override; |
893 | + void pause_processing_for(void const* owner) override; |
894 | + void resume_processing_for(void const* owner) override; |
895 | |
896 | private: |
897 | class SignalHandler; |
898 | class FDHandler; |
899 | - bool should_process(void const*); |
900 | - void process_server_actions(); |
901 | |
902 | boost::asio::io_service io; |
903 | boost::asio::io_service::work work; |
904 | + boost::optional<std::thread::id> main_loop_thread; |
905 | std::vector<std::unique_ptr<SignalHandler>> signal_handlers; |
906 | - std::vector<std::unique_ptr<FDHandler>> fd_handlers; |
907 | - std::mutex server_actions_mutex; |
908 | - std::deque<std::pair<void const*,ServerAction>> server_actions; |
909 | - std::set<void const*> do_not_process; |
910 | - std::shared_ptr<time::Clock> const clock; |
911 | + std::vector<std::shared_ptr<FDHandler>> fd_handlers; |
912 | + std::mutex fd_handlers_mutex; |
913 | + AsioServerActionQueue action_queue; |
914 | }; |
915 | |
916 | } |
917 | |
918 | === added file 'src/server/asio_server_action_queue.cpp' |
919 | --- src/server/asio_server_action_queue.cpp 1970-01-01 00:00:00 +0000 |
920 | +++ src/server/asio_server_action_queue.cpp 2014-06-16 22:41:10 +0000 |
921 | @@ -0,0 +1,91 @@ |
922 | +/* |
923 | + * Copyright © 2014 Canonical Ltd. |
924 | + * |
925 | + * This program is free software: you can redistribute it and/or modify it |
926 | + * under the terms of the GNU General Public License version 3, |
927 | + * as published by the Free Software Foundation. |
928 | + * |
929 | + * This program is distributed in the hope that it will be useful, |
930 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
931 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
932 | + * GNU General Public License for more details. |
933 | + * |
934 | + * You should have received a copy of the GNU General Public License |
935 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
936 | + * |
937 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
938 | + * Andreas Pokorny <andreas.pokorny@canonical.com> |
939 | + */ |
940 | + |
941 | +#include "asio_server_action_queue.h" |
942 | + |
943 | +mir::AsioServerActionQueue::AsioServerActionQueue(boost::asio::io_service & service) |
944 | + : io(service) |
945 | +{ |
946 | +} |
947 | + |
948 | +mir::AsioServerActionQueue::~AsioServerActionQueue() noexcept(true) |
949 | +{ |
950 | +} |
951 | + |
952 | +void mir::AsioServerActionQueue::enqueue(void const* owner, ServerAction const& action) |
953 | +{ |
954 | + { |
955 | + std::lock_guard<std::mutex> lock{server_actions_mutex}; |
956 | + server_actions.push_back({owner, action}); |
957 | + } |
958 | + |
959 | + io.post([this] { process_server_actions(); }); |
960 | +} |
961 | + |
962 | +void mir::AsioServerActionQueue::pause_processing_for(void const* owner) |
963 | +{ |
964 | + std::lock_guard<std::mutex> lock{server_actions_mutex}; |
965 | + do_not_process.insert(owner); |
966 | +} |
967 | + |
968 | +void mir::AsioServerActionQueue::resume_processing_for(void const* owner) |
969 | +{ |
970 | + { |
971 | + std::lock_guard<std::mutex> lock{server_actions_mutex}; |
972 | + do_not_process.erase(owner); |
973 | + } |
974 | + |
975 | + io.post([this] { process_server_actions(); }); |
976 | +} |
977 | + |
978 | +void mir::AsioServerActionQueue::process_server_actions() |
979 | +{ |
980 | + std::unique_lock<std::mutex> lock{server_actions_mutex}; |
981 | + |
982 | + size_t i = 0; |
983 | + |
984 | + while (i < server_actions.size()) |
985 | + { |
986 | + /* |
987 | + * It's safe to use references to elements, since std::deque<> |
988 | + * guarantees that references remain valid after appends, which is |
989 | + * the only operation that can be performed on server_actions outside |
990 | + * this function (in AsioServerActionQueue::post()). |
991 | + */ |
992 | + auto const& owner = server_actions[i].first; |
993 | + auto const& action = server_actions[i].second; |
994 | + |
995 | + if (do_not_process.find(owner) == do_not_process.end()) |
996 | + { |
997 | + lock.unlock(); |
998 | + action(); |
999 | + lock.lock(); |
1000 | + /* |
1001 | + * This erase is always ok, since outside this function |
1002 | + * we only append to server_actions, i.e., our index i |
1003 | + * is guaranteed to remain valid and correct. |
1004 | + */ |
1005 | + server_actions.erase(server_actions.begin() + i); |
1006 | + } |
1007 | + else |
1008 | + { |
1009 | + ++i; |
1010 | + } |
1011 | + } |
1012 | +} |
1013 | |
1014 | === added file 'src/server/asio_server_action_queue.h' |
1015 | --- src/server/asio_server_action_queue.h 1970-01-01 00:00:00 +0000 |
1016 | +++ src/server/asio_server_action_queue.h 2014-06-16 22:41:10 +0000 |
1017 | @@ -0,0 +1,58 @@ |
1018 | +/* |
1019 | + * Copyright © 2014 Canonical Ltd. |
1020 | + * |
1021 | + * This program is free software: you can redistribute it and/or modify it |
1022 | + * under the terms of the GNU General Public License version 3, |
1023 | + * as published by the Free Software Foundation. |
1024 | + * |
1025 | + * This program is distributed in the hope that it will be useful, |
1026 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1027 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1028 | + * GNU General Public License for more details. |
1029 | + * |
1030 | + * You should have received a copy of the GNU General Public License |
1031 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1032 | + * |
1033 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
1034 | + * Andreas Pokorny <andreas.pokorny@canonical.com> |
1035 | + */ |
1036 | + |
1037 | +#ifndef MIR_ASIO_SERVER_ACTION_QUEUE |
1038 | +#define MIR_ASIO_SERVER_ACTION_QUEUE |
1039 | + |
1040 | +#include "mir/server_action_queue.h" |
1041 | + |
1042 | +#include <boost/asio.hpp> |
1043 | + |
1044 | +#include <mutex> |
1045 | +#include <condition_variable> |
1046 | +#include <deque> |
1047 | +#include <set> |
1048 | + |
1049 | +namespace mir |
1050 | +{ |
1051 | + |
1052 | +class AsioServerActionQueue : public ServerActionQueue |
1053 | +{ |
1054 | +public: |
1055 | + explicit AsioServerActionQueue(boost::asio::io_service & service); |
1056 | + ~AsioServerActionQueue() noexcept(true); |
1057 | + |
1058 | + void enqueue(void const* owner, ServerAction const& action) override; |
1059 | + void pause_processing_for(void const* owner) override; |
1060 | + void resume_processing_for(void const* owner) override; |
1061 | + |
1062 | +private: |
1063 | + |
1064 | + bool should_process(void const*); |
1065 | + void process_server_actions(); |
1066 | + |
1067 | + boost::asio::io_service & io; |
1068 | + std::mutex server_actions_mutex; |
1069 | + std::deque<std::pair<void const*,ServerAction>> server_actions; |
1070 | + std::set<void const*> do_not_process; |
1071 | +}; |
1072 | + |
1073 | +} |
1074 | + |
1075 | +#endif |
1076 | |
1077 | === added file 'src/server/asio_timer_service.cpp' |
1078 | --- src/server/asio_timer_service.cpp 1970-01-01 00:00:00 +0000 |
1079 | +++ src/server/asio_timer_service.cpp 2014-06-16 22:41:10 +0000 |
1080 | @@ -0,0 +1,260 @@ |
1081 | +/* |
1082 | + * Copyright © 2014 Canonical Ltd. |
1083 | + * |
1084 | + * This program is free software: you can redistribute it and/or modify it |
1085 | + * under the terms of the GNU General Public License version 3, |
1086 | + * as published by the Free Software Foundation. |
1087 | + * |
1088 | + * This program is distributed in the hope that it will be useful, |
1089 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1090 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1091 | + * GNU General Public License for more details. |
1092 | + * |
1093 | + * You should have received a copy of the GNU General Public License |
1094 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1095 | + * |
1096 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
1097 | + */ |
1098 | + |
1099 | +#include "asio_timer_service.h" |
1100 | + |
1101 | +#include "boost/date_time/posix_time/conversion.hpp" |
1102 | +#include <mutex> |
1103 | +#include <atomic> |
1104 | + |
1105 | +namespace |
1106 | +{ |
1107 | +struct MirClockTimerTraits |
1108 | +{ |
1109 | + // TODO the clock used by the main loop is a global setting, this is a restriction |
1110 | + // of boost::asio only allowing static methods inside the traits type. |
1111 | + struct TimerServiceClockStorage |
1112 | + { |
1113 | + public: |
1114 | + void set_clock(std::shared_ptr<mir::time::Clock const> const& clock) |
1115 | + { |
1116 | + std::lock_guard<std::mutex> lock(timer_service_mutex); |
1117 | + auto stored_clock = timer_service_clock.lock(); |
1118 | + if (stored_clock && stored_clock != clock) |
1119 | + BOOST_THROW_EXCEPTION(std::logic_error("A clock is already in use as time source for mir::AsioTimerService")); |
1120 | + timer_service_clock = clock; |
1121 | + } |
1122 | + mir::time::Timestamp now() |
1123 | + { |
1124 | + std::lock_guard<std::mutex> lock(timer_service_mutex); |
1125 | + auto clock = timer_service_clock.lock(); |
1126 | + if (!clock) |
1127 | + BOOST_THROW_EXCEPTION(std::logic_error("No clock available to create time stamp")); |
1128 | + return clock->sample(); |
1129 | + } |
1130 | + private: |
1131 | + std::mutex timer_service_mutex; |
1132 | + std::weak_ptr<mir::time::Clock const> timer_service_clock; |
1133 | + }; |
1134 | + |
1135 | + static TimerServiceClockStorage clock_storage; |
1136 | + |
1137 | + static void set_clock(std::shared_ptr<mir::time::Clock const> const& clock) |
1138 | + { |
1139 | + clock_storage.set_clock(clock); |
1140 | + } |
1141 | + |
1142 | + // time_traits interface required by boost::asio::deadline_timer{_service} |
1143 | + typedef mir::time::Timestamp time_type; |
1144 | + typedef std::chrono::milliseconds duration_type; |
1145 | + |
1146 | + |
1147 | + static time_type now() |
1148 | + { |
1149 | + return clock_storage.now(); |
1150 | + } |
1151 | + |
1152 | + static time_type add(const time_type& t, const duration_type& d) |
1153 | + { |
1154 | + return t + d; |
1155 | + } |
1156 | + |
1157 | + static duration_type subtract(const time_type& t1, const time_type& t2) |
1158 | + { |
1159 | + return std::chrono::duration_cast<duration_type>(t1 - t2); |
1160 | + } |
1161 | + |
1162 | + static bool less_than(const time_type& t1, const time_type& t2) |
1163 | + { |
1164 | + return t1 < t2; |
1165 | + } |
1166 | + |
1167 | + static boost::posix_time::time_duration to_posix_duration( |
1168 | + const duration_type& d) |
1169 | + { |
1170 | + return boost::posix_time::millisec(d.count()); |
1171 | + } |
1172 | +}; |
1173 | + |
1174 | +MirClockTimerTraits::TimerServiceClockStorage MirClockTimerTraits::clock_storage; |
1175 | + |
1176 | +typedef boost::asio::basic_deadline_timer<mir::time::Timestamp, MirClockTimerTraits> deadline_timer; |
1177 | +} |
1178 | + |
1179 | +class mir::AsioTimerService::AlarmImpl : public mir::time::Alarm |
1180 | +{ |
1181 | +public: |
1182 | + AlarmImpl(boost::asio::io_service& io, |
1183 | + std::chrono::milliseconds delay, |
1184 | + std::function<void(void)> callback); |
1185 | + |
1186 | + AlarmImpl(boost::asio::io_service& io, |
1187 | + mir::time::Timestamp time_point, |
1188 | + std::function<void(void)> callback); |
1189 | + |
1190 | + AlarmImpl(boost::asio::io_service& io, |
1191 | + std::function<void(void)> callback); |
1192 | + |
1193 | + ~AlarmImpl() noexcept override; |
1194 | + |
1195 | + // mir::time::Alarm: |
1196 | + bool cancel() override; |
1197 | + State state() const override; |
1198 | + |
1199 | + bool reschedule_in(std::chrono::milliseconds delay) override; |
1200 | + bool reschedule_for(mir::time::Timestamp time_point) override; |
1201 | + |
1202 | +private: |
1203 | + void update_timer(); |
1204 | + |
1205 | + struct InternalState |
1206 | + { |
1207 | + InternalState(std::function<void(void)> callback) |
1208 | + : callback{callback}, state{pending} |
1209 | + { |
1210 | + } |
1211 | + |
1212 | + std::function<void(void)> callback; |
1213 | + std::atomic<State> state; |
1214 | + }; |
1215 | + |
1216 | + ::deadline_timer timer; |
1217 | + std::shared_ptr<InternalState> data; |
1218 | +}; |
1219 | + |
1220 | +mir::AsioTimerService::AlarmImpl::AlarmImpl(boost::asio::io_service& io, |
1221 | + std::chrono::milliseconds delay, |
1222 | + std::function<void()> callback) |
1223 | + : AlarmImpl(io, callback) |
1224 | +{ |
1225 | + reschedule_in(delay); |
1226 | +} |
1227 | + |
1228 | +mir::AsioTimerService::AlarmImpl::AlarmImpl(boost::asio::io_service& io, |
1229 | + mir::time::Timestamp time_point, |
1230 | + std::function<void()> callback) |
1231 | + : AlarmImpl(io, callback) |
1232 | +{ |
1233 | + reschedule_for(time_point); |
1234 | +} |
1235 | + |
1236 | +mir::AsioTimerService::AlarmImpl::AlarmImpl(boost::asio::io_service& io, |
1237 | + std::function<void(void)> callback) |
1238 | + : timer{io}, |
1239 | + data{std::make_shared<InternalState>(callback)} |
1240 | +{ |
1241 | + data->state = triggered; |
1242 | +} |
1243 | + |
1244 | +mir::AsioTimerService::AlarmImpl::~AlarmImpl() noexcept |
1245 | +{ |
1246 | + AlarmImpl::cancel(); |
1247 | +} |
1248 | + |
1249 | +bool mir::AsioTimerService::AlarmImpl::cancel() |
1250 | +{ |
1251 | + State expected_state = pending; |
1252 | + if (data->state.compare_exchange_strong(expected_state, cancelled)) |
1253 | + { |
1254 | + timer.cancel(); |
1255 | + return true; |
1256 | + } |
1257 | + return false; |
1258 | +} |
1259 | + |
1260 | +mir::time::Alarm::State mir::AsioTimerService::AlarmImpl::state() const |
1261 | +{ |
1262 | + return data->state; |
1263 | +} |
1264 | + |
1265 | +bool mir::AsioTimerService::AlarmImpl::reschedule_in(std::chrono::milliseconds delay) |
1266 | +{ |
1267 | + bool cancelling = timer.expires_from_now(delay); |
1268 | + update_timer(); |
1269 | + return cancelling; |
1270 | +} |
1271 | + |
1272 | +bool mir::AsioTimerService::AlarmImpl::reschedule_for(mir::time::Timestamp time_point) |
1273 | +{ |
1274 | + bool cancelling = timer.expires_at(time_point); |
1275 | + update_timer(); |
1276 | + return cancelling; |
1277 | +} |
1278 | + |
1279 | +void mir::AsioTimerService::AlarmImpl::update_timer() |
1280 | +{ |
1281 | + auto new_internal_state = std::make_shared<InternalState>(data->callback); |
1282 | + |
1283 | + // Awkwardly, we can't stop the async_wait handler from being called |
1284 | + // on a destroyed AlarmImpl. This means we need to wedge a shared_ptr |
1285 | + // into the async_wait callback. |
1286 | + std::weak_ptr<InternalState> possible_data = new_internal_state; |
1287 | + timer.async_wait([possible_data](boost::system::error_code const& ec) |
1288 | + { |
1289 | + if (ec) |
1290 | + return; |
1291 | + |
1292 | + auto data = possible_data.lock(); |
1293 | + if (!data) |
1294 | + return; |
1295 | + |
1296 | + State expected_state = pending; |
1297 | + if (data->state.compare_exchange_strong(expected_state, triggered)) |
1298 | + data->callback(); |
1299 | + }); |
1300 | + |
1301 | + data = new_internal_state; |
1302 | +} |
1303 | + |
1304 | +mir::AsioTimerService::AsioTimerService(std::shared_ptr<time::Clock> const& clock) |
1305 | + : work{io}, clock(clock) |
1306 | +{ |
1307 | + MirClockTimerTraits::set_clock(clock); |
1308 | +} |
1309 | + |
1310 | +mir::AsioTimerService::~AsioTimerService() noexcept(true) |
1311 | +{ |
1312 | +} |
1313 | + |
1314 | +void mir::AsioTimerService::run() |
1315 | +{ |
1316 | + io.run(); |
1317 | +} |
1318 | + |
1319 | +void mir::AsioTimerService::stop() |
1320 | +{ |
1321 | + io.stop(); |
1322 | +} |
1323 | + |
1324 | +std::unique_ptr<mir::time::Alarm> mir::AsioTimerService::notify_in(std::chrono::milliseconds delay, |
1325 | + std::function<void()> callback) |
1326 | +{ |
1327 | + return std::unique_ptr<mir::time::Alarm>{new AlarmImpl{io, delay, callback}}; |
1328 | +} |
1329 | + |
1330 | +std::unique_ptr<mir::time::Alarm> mir::AsioTimerService::notify_at(mir::time::Timestamp time_point, |
1331 | + std::function<void()> callback) |
1332 | +{ |
1333 | + return std::unique_ptr<mir::time::Alarm>{new AlarmImpl{io, time_point, callback}}; |
1334 | + |
1335 | +} |
1336 | + |
1337 | +std::unique_ptr<mir::time::Alarm> mir::AsioTimerService::create_alarm(std::function<void()> callback) |
1338 | +{ |
1339 | + return std::unique_ptr<mir::time::Alarm>{new AlarmImpl{io, callback}}; |
1340 | +} |
1341 | |
1342 | === added file 'src/server/asio_timer_service.h' |
1343 | --- src/server/asio_timer_service.h 1970-01-01 00:00:00 +0000 |
1344 | +++ src/server/asio_timer_service.h 2014-06-16 22:41:10 +0000 |
1345 | @@ -0,0 +1,58 @@ |
1346 | +/* |
1347 | + * Copyright © 2014 Canonical Ltd. |
1348 | + * |
1349 | + * This program is free software: you can redistribute it and/or modify it |
1350 | + * under the terms of the GNU General Public License version 3, |
1351 | + * as published by the Free Software Foundation. |
1352 | + * |
1353 | + * This program is distributed in the hope that it will be useful, |
1354 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1355 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1356 | + * GNU General Public License for more details. |
1357 | + * |
1358 | + * You should have received a copy of the GNU General Public License |
1359 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1360 | + * |
1361 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
1362 | + */ |
1363 | + |
1364 | +#ifndef MIR_ASIO_TIMER_SERVICE_H_ |
1365 | +#define MIR_ASIO_TIMER_SERVICE_H_ |
1366 | + |
1367 | +#include "mir/time/timer_loop.h" |
1368 | + |
1369 | +#include <boost/asio.hpp> |
1370 | +#include <boost/optional.hpp> |
1371 | + |
1372 | +#include <memory> |
1373 | +#include <thread> |
1374 | +#include <mutex> |
1375 | + |
1376 | +namespace mir |
1377 | +{ |
1378 | + |
1379 | +class AsioTimerService : public time::TimerLoop |
1380 | +{ |
1381 | +public: |
1382 | + explicit AsioTimerService(std::shared_ptr<time::Clock> const& clock); |
1383 | + ~AsioTimerService() noexcept(true); |
1384 | + |
1385 | + void run() override; |
1386 | + void stop() override; |
1387 | + |
1388 | + std::unique_ptr<time::Alarm> notify_in(std::chrono::milliseconds delay, |
1389 | + std::function<void()> callback) override; |
1390 | + std::unique_ptr<time::Alarm> notify_at(mir::time::Timestamp time_point, |
1391 | + std::function<void()> callback) override; |
1392 | + std::unique_ptr<time::Alarm> create_alarm(std::function<void()> callback) override; |
1393 | +private: |
1394 | + class AlarmImpl; |
1395 | + boost::asio::io_service io; |
1396 | + boost::asio::io_service::work work; |
1397 | + |
1398 | + std::shared_ptr<time::Clock> const clock; |
1399 | +}; |
1400 | + |
1401 | +} |
1402 | + |
1403 | +#endif |
1404 | |
1405 | === modified file 'src/server/compositor/default_configuration.cpp' |
1406 | --- src/server/compositor/default_configuration.cpp 2014-06-09 17:16:32 +0000 |
1407 | +++ src/server/compositor/default_configuration.cpp 2014-06-16 22:41:10 +0000 |
1408 | @@ -52,7 +52,7 @@ |
1409 | return frame_dropping_policy_factory( |
1410 | [this]() |
1411 | { |
1412 | - return std::make_shared<mc::TimeoutFrameDroppingPolicyFactory>(the_main_loop(), |
1413 | + return std::make_shared<mc::TimeoutFrameDroppingPolicyFactory>(the_timer(), |
1414 | std::chrono::milliseconds{100}); |
1415 | }); |
1416 | } |
1417 | |
1418 | === modified file 'src/server/default_server_configuration.cpp' |
1419 | --- src/server/default_server_configuration.cpp 2014-06-11 16:11:32 +0000 |
1420 | +++ src/server/default_server_configuration.cpp 2014-06-16 22:41:10 +0000 |
1421 | @@ -19,7 +19,6 @@ |
1422 | #include "mir/default_server_configuration.h" |
1423 | #include "mir/options/default_configuration.h" |
1424 | #include "mir/abnormal_exit.h" |
1425 | -#include "mir/asio_main_loop.h" |
1426 | #include "mir/default_server_status_listener.h" |
1427 | #include "mir/emergency_cleanup.h" |
1428 | #include "mir/default_configuration.h" |
1429 | @@ -39,6 +38,9 @@ |
1430 | #include "mir/default_configuration.h" |
1431 | #include "mir/scene/null_prompt_session_listener.h" |
1432 | |
1433 | +#include "asio_timer_service.h" |
1434 | +#include "asio_main_loop.h" |
1435 | + |
1436 | #include <map> |
1437 | #include <vector> |
1438 | #include <mutex> |
1439 | @@ -52,6 +54,31 @@ |
1440 | namespace msh = mir::shell; |
1441 | namespace mi = mir::input; |
1442 | |
1443 | +namespace |
1444 | +{ |
1445 | +class AsioTimerServiceThread : public mir::AsioTimerService |
1446 | +{ |
1447 | +public: |
1448 | + AsioTimerServiceThread(std::shared_ptr<mir::time::Clock> const& clock) |
1449 | + : AsioTimerService(clock) |
1450 | + {} |
1451 | + void run() override |
1452 | + { |
1453 | + service_thread = std::thread([this]{mir::AsioTimerService::run();}); |
1454 | + } |
1455 | + void stop() override |
1456 | + { |
1457 | + if (service_thread.joinable()) |
1458 | + { |
1459 | + mir::AsioTimerService::stop(); |
1460 | + service_thread.join(); |
1461 | + } |
1462 | + } |
1463 | +private: |
1464 | + std::thread service_thread; |
1465 | +}; |
1466 | +} |
1467 | + |
1468 | mir::DefaultServerConfiguration::DefaultServerConfiguration(int argc, char const* argv[]) : |
1469 | DefaultServerConfiguration(std::make_shared<mo::DefaultConfiguration>(argc, argv)) |
1470 | { |
1471 | @@ -185,7 +212,25 @@ |
1472 | return main_loop( |
1473 | [this]() |
1474 | { |
1475 | - return std::make_shared<mir::AsioMainLoop>(the_clock()); |
1476 | + return std::make_shared<mir::AsioMainLoop>(); |
1477 | + }); |
1478 | +} |
1479 | + |
1480 | +std::shared_ptr<mir::time::Timer> mir::DefaultServerConfiguration::the_timer() |
1481 | +{ |
1482 | + return timer_loop( |
1483 | + [this]() |
1484 | + { |
1485 | + return std::make_shared<AsioTimerServiceThread>(the_clock()); |
1486 | + }); |
1487 | +} |
1488 | + |
1489 | +std::shared_ptr<mir::Loop> mir::DefaultServerConfiguration::the_timer_loop() |
1490 | +{ |
1491 | + return timer_loop( |
1492 | + [this]() |
1493 | + { |
1494 | + return std::make_shared<AsioTimerServiceThread>(the_clock()); |
1495 | }); |
1496 | } |
1497 | |
1498 | |
1499 | === modified file 'src/server/display_server.cpp' |
1500 | --- src/server/display_server.cpp 2014-06-03 11:04:15 +0000 |
1501 | +++ src/server/display_server.cpp 2014-06-16 22:41:10 +0000 |
1502 | @@ -21,6 +21,7 @@ |
1503 | #include "mir/display_server.h" |
1504 | #include "mir/server_configuration.h" |
1505 | #include "mir/main_loop.h" |
1506 | +#include "mir/loop.h" |
1507 | #include "mir/server_status_listener.h" |
1508 | #include "mir/display_changer.h" |
1509 | |
1510 | @@ -80,7 +81,8 @@ |
1511 | input_manager{config.the_input_manager()}, |
1512 | main_loop{config.the_main_loop()}, |
1513 | server_status_listener{config.the_server_status_listener()}, |
1514 | - display_changer{config.the_display_changer()} |
1515 | + display_changer{config.the_display_changer()}, |
1516 | + timer_loop{config.the_timer_loop()} |
1517 | { |
1518 | display->register_configuration_change_handler( |
1519 | *main_loop, |
1520 | @@ -96,6 +98,10 @@ |
1521 | { |
1522 | try |
1523 | { |
1524 | + TryButRevertIfUnwinding timers{ |
1525 | + [this] { timer_loop->stop(); }, |
1526 | + [this] { timer_loop->run(); }}; |
1527 | + |
1528 | TryButRevertIfUnwinding dispatcher{ |
1529 | [this] { input_dispatcher->stop(); }, |
1530 | [this] { input_dispatcher->start(); }}; |
1531 | @@ -152,6 +158,10 @@ |
1532 | [this] { input_dispatcher->start(); }, |
1533 | [this] { input_dispatcher->stop(); }}; |
1534 | |
1535 | + TryButRevertIfUnwinding timers{ |
1536 | + [this] { timer_loop->run(); }, |
1537 | + [this] { timer_loop->stop(); }}; |
1538 | + |
1539 | compositor->start(); |
1540 | } |
1541 | catch(std::runtime_error const&) |
1542 | @@ -184,6 +194,7 @@ |
1543 | std::shared_ptr<mir::MainLoop> const main_loop; |
1544 | std::shared_ptr<mir::ServerStatusListener> const server_status_listener; |
1545 | std::shared_ptr<mir::DisplayChanger> const display_changer; |
1546 | + std::shared_ptr<mir::Loop> const timer_loop; |
1547 | }; |
1548 | |
1549 | mir::DisplayServer::DisplayServer(ServerConfiguration& config) : |
1550 | @@ -206,11 +217,13 @@ |
1551 | p->compositor->start(); |
1552 | p->input_manager->start(); |
1553 | p->input_dispatcher->start(); |
1554 | + p->timer_loop->run(); |
1555 | |
1556 | p->server_status_listener->started(); |
1557 | |
1558 | p->main_loop->run(); |
1559 | |
1560 | + p->timer_loop->stop(); |
1561 | p->input_dispatcher->stop(); |
1562 | p->input_manager->stop(); |
1563 | p->compositor->stop(); |
1564 | |
1565 | === added file 'src/server/synchronous_server_action.cpp' |
1566 | --- src/server/synchronous_server_action.cpp 1970-01-01 00:00:00 +0000 |
1567 | +++ src/server/synchronous_server_action.cpp 2014-06-16 22:41:10 +0000 |
1568 | @@ -0,0 +1,50 @@ |
1569 | +/* |
1570 | + * Copyright © 2014 Canonical Ltd. |
1571 | + * |
1572 | + * This program is free software: you can redistribute it and/or modify it |
1573 | + * under the terms of the GNU General Public License version 3, |
1574 | + * as published by the Free Software Foundation. |
1575 | + * |
1576 | + * This program is distributed in the hope that it will be useful, |
1577 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1578 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1579 | + * GNU General Public License for more details. |
1580 | + * |
1581 | + * You should have received a copy of the GNU General Public License |
1582 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1583 | + * |
1584 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
1585 | + */ |
1586 | + |
1587 | +#include "synchronous_server_action.h" |
1588 | + |
1589 | +mir::SynchronousServerAction::SynchronousServerAction( |
1590 | + ServerActionQueue & queue, |
1591 | + boost::optional<std::thread::id> queue_thread_id, |
1592 | + ServerAction const& action) : |
1593 | + done{false} |
1594 | +{ |
1595 | + if (queue_thread_id && |
1596 | + *queue_thread_id != std::this_thread::get_id()) |
1597 | + { |
1598 | + queue.enqueue(this, |
1599 | + [this,action]() |
1600 | + { |
1601 | + action(); |
1602 | + std::lock_guard<std::mutex> lock(done_mutex); |
1603 | + done = true; |
1604 | + done_condition.notify_one(); |
1605 | + }); |
1606 | + } |
1607 | + else |
1608 | + { |
1609 | + done = true; |
1610 | + action(); |
1611 | + } |
1612 | +} |
1613 | + |
1614 | +mir::SynchronousServerAction::~SynchronousServerAction() |
1615 | +{ |
1616 | + std::unique_lock<std::mutex> lock(done_mutex); |
1617 | + while(!done) done_condition.wait(lock); |
1618 | +} |
1619 | |
1620 | === added file 'src/server/synchronous_server_action.h' |
1621 | --- src/server/synchronous_server_action.h 1970-01-01 00:00:00 +0000 |
1622 | +++ src/server/synchronous_server_action.h 2014-06-16 22:41:10 +0000 |
1623 | @@ -0,0 +1,48 @@ |
1624 | +/* |
1625 | + * Copyright © 2014 Canonical Ltd. |
1626 | + * |
1627 | + * This program is free software: you can redistribute it and/or modify it |
1628 | + * under the terms of the GNU General Public License version 3, |
1629 | + * as published by the Free Software Foundation. |
1630 | + * |
1631 | + * This program is distributed in the hope that it will be useful, |
1632 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1633 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1634 | + * GNU General Public License for more details. |
1635 | + * |
1636 | + * You should have received a copy of the GNU General Public License |
1637 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1638 | + * |
1639 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
1640 | + */ |
1641 | + |
1642 | +#ifndef MIR_SYNCHRONOUS_SERVER_ACTION_H_ |
1643 | +#define MIR_SYNCHRONOUS_SERVER_ACTION_H_ |
1644 | + |
1645 | +#include "mir/server_action_queue.h" |
1646 | + |
1647 | +#include <boost/optional.hpp> |
1648 | + |
1649 | +#include <thread> |
1650 | +#include <mutex> |
1651 | +#include <condition_variable> |
1652 | + |
1653 | +namespace mir |
1654 | +{ |
1655 | + |
1656 | +class SynchronousServerAction |
1657 | +{ |
1658 | +public: |
1659 | + SynchronousServerAction(ServerActionQueue & queue, |
1660 | + boost::optional<std::thread::id> queue_thread_id, |
1661 | + ServerAction const& action); |
1662 | + ~SynchronousServerAction(); |
1663 | +private: |
1664 | + std::mutex done_mutex; |
1665 | + bool done; |
1666 | + std::condition_variable done_condition; |
1667 | +}; |
1668 | + |
1669 | +} |
1670 | + |
1671 | +#endif |
1672 | |
1673 | === modified file 'tests/acceptance-tests/test_display_configuration.cpp' |
1674 | --- tests/acceptance-tests/test_display_configuration.cpp 2014-06-02 17:07:02 +0000 |
1675 | +++ tests/acceptance-tests/test_display_configuration.cpp 2014-06-16 22:41:10 +0000 |
1676 | @@ -103,6 +103,7 @@ |
1677 | { |
1678 | handlers.register_fd_handler( |
1679 | {p.read_fd()}, |
1680 | + this, |
1681 | [this, handler](int fd) |
1682 | { |
1683 | char c; |
1684 | |
1685 | === modified file 'tests/integration-tests/test_display_server_main_loop_events.cpp' |
1686 | --- tests/integration-tests/test_display_server_main_loop_events.cpp 2014-06-03 11:04:15 +0000 |
1687 | +++ tests/integration-tests/test_display_server_main_loop_events.cpp 2014-06-16 22:41:10 +0000 |
1688 | @@ -30,6 +30,7 @@ |
1689 | #include "mir_test_doubles/mock_input_manager.h" |
1690 | #include "mir_test_doubles/mock_input_dispatcher.h" |
1691 | #include "mir_test_doubles/mock_compositor.h" |
1692 | +#include "mir_test_doubles/mock_loop.h" |
1693 | #include "mir_test_doubles/null_display.h" |
1694 | #include "mir_test_doubles/mock_server_status_listener.h" |
1695 | #include "mir/run_mir.h" |
1696 | @@ -104,6 +105,7 @@ |
1697 | { |
1698 | handlers.register_fd_handler( |
1699 | {fd}, |
1700 | + this, |
1701 | [this,conf_change_handler](int fd) |
1702 | { |
1703 | char c; |
1704 | @@ -253,6 +255,14 @@ |
1705 | return mock_input_dispatcher; |
1706 | } |
1707 | |
1708 | + std::shared_ptr<mir::Loop> the_timer_loop() override |
1709 | + { |
1710 | + if (!mock_timer_loop) |
1711 | + mock_timer_loop = std::make_shared<mtd::MockLoop>(); |
1712 | + |
1713 | + return mock_timer_loop; |
1714 | + } |
1715 | + |
1716 | std::shared_ptr<MockDisplay> the_mock_display() |
1717 | { |
1718 | the_display(); |
1719 | @@ -271,6 +281,12 @@ |
1720 | return mock_connector; |
1721 | } |
1722 | |
1723 | + std::shared_ptr<mtd::MockLoop> the_mock_timer_loop() |
1724 | + { |
1725 | + the_timer_loop(); |
1726 | + return mock_timer_loop; |
1727 | + } |
1728 | + |
1729 | std::shared_ptr<mtd::MockInputManager> the_mock_input_manager() |
1730 | { |
1731 | the_input_manager(); |
1732 | @@ -319,6 +335,7 @@ |
1733 | std::shared_ptr<MockConnector> mock_connector; |
1734 | std::shared_ptr<mtd::MockInputManager> mock_input_manager; |
1735 | std::shared_ptr<mtd::MockInputDispatcher> mock_input_dispatcher; |
1736 | + std::shared_ptr<mtd::MockLoop> mock_timer_loop; |
1737 | |
1738 | mt::Pipe p; |
1739 | int const pause_signal; |
1740 | @@ -381,6 +398,7 @@ |
1741 | auto mock_connector = server_config.the_mock_connector(); |
1742 | auto mock_input_manager = server_config.the_mock_input_manager(); |
1743 | auto mock_input_dispatcher = server_config.the_mock_input_dispatcher(); |
1744 | + auto mock_timer_loop = server_config.the_mock_timer_loop(); |
1745 | |
1746 | { |
1747 | InSequence s; |
1748 | @@ -390,8 +408,10 @@ |
1749 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1750 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1751 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1752 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1753 | |
1754 | /* Pause */ |
1755 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1756 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1757 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1758 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1759 | @@ -403,9 +423,11 @@ |
1760 | EXPECT_CALL(*mock_connector, start()).Times(1); |
1761 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1762 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1763 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1764 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1765 | |
1766 | /* Stop */ |
1767 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1768 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1769 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1770 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1771 | @@ -437,6 +459,7 @@ |
1772 | auto mock_connector = server_config.the_mock_connector(); |
1773 | auto mock_input_manager = server_config.the_mock_input_manager(); |
1774 | auto mock_input_dispatcher = server_config.the_mock_input_dispatcher(); |
1775 | + auto mock_timer_loop = server_config.the_mock_timer_loop(); |
1776 | |
1777 | { |
1778 | InSequence s; |
1779 | @@ -446,8 +469,10 @@ |
1780 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1781 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1782 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1783 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1784 | |
1785 | /* Pause */ |
1786 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1787 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1788 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1789 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1790 | @@ -455,6 +480,7 @@ |
1791 | EXPECT_CALL(*mock_display, pause()).Times(1); |
1792 | |
1793 | /* Stop */ |
1794 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1795 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1796 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1797 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1798 | @@ -485,6 +511,7 @@ |
1799 | auto mock_connector = server_config.the_mock_connector(); |
1800 | auto mock_input_manager = server_config.the_mock_input_manager(); |
1801 | auto mock_input_dispatcher = server_config.the_mock_input_dispatcher(); |
1802 | + auto mock_timer_loop = server_config.the_mock_timer_loop(); |
1803 | |
1804 | { |
1805 | InSequence s; |
1806 | @@ -494,8 +521,10 @@ |
1807 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1808 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1809 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1810 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1811 | |
1812 | /* Pause failure */ |
1813 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1814 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1815 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1816 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1817 | @@ -508,8 +537,10 @@ |
1818 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1819 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1820 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1821 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1822 | |
1823 | /* Stop */ |
1824 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1825 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1826 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1827 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1828 | @@ -540,6 +571,7 @@ |
1829 | auto mock_connector = server_config.the_mock_connector(); |
1830 | auto mock_input_manager = server_config.the_mock_input_manager(); |
1831 | auto mock_input_dispatcher = server_config.the_mock_input_dispatcher(); |
1832 | + auto mock_timer_loop = server_config.the_mock_timer_loop(); |
1833 | |
1834 | { |
1835 | InSequence s; |
1836 | @@ -549,6 +581,7 @@ |
1837 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1838 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1839 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1840 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1841 | |
1842 | /* Change configuration */ |
1843 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1844 | @@ -556,6 +589,7 @@ |
1845 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1846 | |
1847 | /* Stop */ |
1848 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1849 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1850 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1851 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1852 | @@ -587,6 +621,7 @@ |
1853 | auto mock_connector = server_config.the_mock_connector(); |
1854 | auto mock_input_manager = server_config.the_mock_input_manager(); |
1855 | auto mock_input_dispatcher = server_config.the_mock_input_dispatcher(); |
1856 | + auto mock_timer_loop = server_config.the_mock_timer_loop(); |
1857 | |
1858 | { |
1859 | InSequence s; |
1860 | @@ -596,8 +631,10 @@ |
1861 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1862 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1863 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1864 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1865 | |
1866 | /* Pause event */ |
1867 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1868 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1869 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1870 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1871 | @@ -609,6 +646,7 @@ |
1872 | EXPECT_CALL(*mock_connector, start()).Times(1); |
1873 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1874 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1875 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1876 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1877 | |
1878 | /* Change configuration (after resuming) */ |
1879 | @@ -617,6 +655,7 @@ |
1880 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1881 | |
1882 | /* Stop */ |
1883 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1884 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1885 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1886 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1887 | @@ -652,6 +691,7 @@ |
1888 | auto mock_input_manager = server_config.the_mock_input_manager(); |
1889 | auto mock_input_dispatcher = server_config.the_mock_input_dispatcher(); |
1890 | auto mock_server_status_listener = server_config.the_mock_server_status_listener(); |
1891 | + auto mock_timer_loop = server_config.the_mock_timer_loop(); |
1892 | |
1893 | { |
1894 | InSequence s; |
1895 | @@ -661,9 +701,11 @@ |
1896 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1897 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1898 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1899 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1900 | EXPECT_CALL(*mock_server_status_listener, started()).Times(1); |
1901 | |
1902 | /* "paused" is emitted after all components have been paused/stopped */ |
1903 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1904 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1905 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1906 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1907 | @@ -676,10 +718,12 @@ |
1908 | EXPECT_CALL(*mock_connector, start()).Times(1); |
1909 | EXPECT_CALL(*mock_input_manager, start()).Times(1); |
1910 | EXPECT_CALL(*mock_input_dispatcher, start()).Times(1); |
1911 | + EXPECT_CALL(*mock_timer_loop, run()).Times(1); |
1912 | EXPECT_CALL(*mock_compositor, start()).Times(1); |
1913 | EXPECT_CALL(*mock_server_status_listener, resumed()).Times(1); |
1914 | |
1915 | /* Stop */ |
1916 | + EXPECT_CALL(*mock_timer_loop, stop()).Times(1); |
1917 | EXPECT_CALL(*mock_input_dispatcher, stop()).Times(1); |
1918 | EXPECT_CALL(*mock_input_manager, stop()).Times(1); |
1919 | EXPECT_CALL(*mock_compositor, stop()).Times(1); |
1920 | |
1921 | === modified file 'tests/unit-tests/CMakeLists.txt' |
1922 | --- tests/unit-tests/CMakeLists.txt 2014-06-04 15:30:21 +0000 |
1923 | +++ tests/unit-tests/CMakeLists.txt 2014-06-16 22:41:10 +0000 |
1924 | @@ -8,7 +8,10 @@ |
1925 | UNIT_TEST_SOURCES |
1926 | |
1927 | test_gmock_fixes.cpp |
1928 | + test_synchronous_server_action.cpp |
1929 | test_asio_main_loop.cpp |
1930 | + test_asio_server_action_queue.cpp |
1931 | + test_asio_timer_service.cpp |
1932 | shared_library_test.cpp |
1933 | test_raii.cpp |
1934 | test_udev_wrapper.cpp |
1935 | |
1936 | === modified file 'tests/unit-tests/graphics/mesa/test_display.cpp' |
1937 | --- tests/unit-tests/graphics/mesa/test_display.cpp 2014-06-10 14:40:23 +0000 |
1938 | +++ tests/unit-tests/graphics/mesa/test_display.cpp 2014-06-16 22:41:10 +0000 |
1939 | @@ -23,13 +23,13 @@ |
1940 | #include "mir/logging/logger.h" |
1941 | #include "mir/graphics/display_buffer.h" |
1942 | #include "src/server/graphics/default_display_configuration_policy.h" |
1943 | -#include "mir/time/high_resolution_clock.h" |
1944 | -#include "mir/asio_main_loop.h" |
1945 | +#include "src/server/asio_main_loop.h" |
1946 | |
1947 | #include "mir_test_doubles/mock_egl.h" |
1948 | #include "mir_test_doubles/mock_gl.h" |
1949 | #include "src/server/report/null_report_factory.h" |
1950 | #include "mir_test_doubles/mock_display_report.h" |
1951 | +#include "mir_test_doubles/mock_event_handler_register.h" |
1952 | #include "mir_test_doubles/null_virtual_terminal.h" |
1953 | #include "mir_test_doubles/stub_gl_config.h" |
1954 | #include "mir_test_doubles/mock_gl_config.h" |
1955 | @@ -69,18 +69,6 @@ |
1956 | ~MockLogger() noexcept(true) {} |
1957 | }; |
1958 | |
1959 | -class MockEventRegister : public mg::EventHandlerRegister |
1960 | -{ |
1961 | -public: |
1962 | - MOCK_METHOD2(register_signal_handler, |
1963 | - void(std::initializer_list<int>, |
1964 | - std::function<void(int)> const&)); |
1965 | - MOCK_METHOD2(register_fd_handler, |
1966 | - void(std::initializer_list<int>, |
1967 | - std::function<void(int)> const&)); |
1968 | -}; |
1969 | - |
1970 | - |
1971 | class MesaDisplayTest : public ::testing::Test |
1972 | { |
1973 | public: |
1974 | @@ -706,9 +694,9 @@ |
1975 | using namespace testing; |
1976 | |
1977 | auto display = create_display(create_platform()); |
1978 | - MockEventRegister mock_register; |
1979 | + mtd::MockEventHandlerRegister mock_register; |
1980 | |
1981 | - EXPECT_CALL(mock_register, register_fd_handler(_,_)); |
1982 | + EXPECT_CALL(mock_register, register_fd_handler(_,_,_)); |
1983 | |
1984 | display->register_configuration_change_handler(mock_register, []{}); |
1985 | } |
1986 | @@ -719,7 +707,7 @@ |
1987 | |
1988 | auto display = create_display(create_platform()); |
1989 | |
1990 | - mir::AsioMainLoop ml{std::make_shared<mir::time::HighResolutionClock>()}; |
1991 | + mir::AsioMainLoop ml; |
1992 | std::condition_variable done; |
1993 | |
1994 | int const device_add_count{1}; |
1995 | |
1996 | === modified file 'tests/unit-tests/graphics/mesa/test_linux_virtual_terminal.cpp' |
1997 | --- tests/unit-tests/graphics/mesa/test_linux_virtual_terminal.cpp 2014-06-14 19:52:52 +0000 |
1998 | +++ tests/unit-tests/graphics/mesa/test_linux_virtual_terminal.cpp 2014-06-16 22:41:10 +0000 |
1999 | @@ -18,10 +18,10 @@ |
2000 | |
2001 | #include "src/platform/graphics/mesa/linux_virtual_terminal.h" |
2002 | #include "src/server/report/null_report_factory.h" |
2003 | -#include "mir/graphics/event_handler_register.h" |
2004 | |
2005 | #include "mir_test/fake_shared.h" |
2006 | #include "mir_test_doubles/mock_display_report.h" |
2007 | +#include "mir_test_doubles/mock_event_handler_register.h" |
2008 | #include "mir_test/gmock_fixes.h" |
2009 | |
2010 | #include <gtest/gtest.h> |
2011 | @@ -70,20 +70,6 @@ |
2012 | // Add a typedef to aid clarity. |
2013 | typedef testing::NiceMock<MockPosixProcessOperations> StubPosixProcessOperations; |
2014 | |
2015 | -class MockEventHandlerRegister : public mg::EventHandlerRegister |
2016 | -{ |
2017 | -public: |
2018 | - ~MockEventHandlerRegister() noexcept {} |
2019 | - |
2020 | - MOCK_METHOD2(register_signal_handler, |
2021 | - void(std::initializer_list<int>, |
2022 | - std::function<void(int)> const&)); |
2023 | - |
2024 | - MOCK_METHOD2(register_fd_handler, |
2025 | - void(std::initializer_list<int>, |
2026 | - std::function<void(int)> const&)); |
2027 | -}; |
2028 | - |
2029 | ACTION_TEMPLATE(SetIoctlPointee, |
2030 | HAS_1_TEMPLATE_PARAMS(typename, T), |
2031 | AND_1_VALUE_PARAMS(param)) |
2032 | @@ -254,7 +240,7 @@ |
2033 | struct termios fake_tc_attr; |
2034 | std::function<void(int)> sig_handler; |
2035 | MockVTFileOperations mock_fops; |
2036 | - MockEventHandlerRegister mock_event_handler_register; |
2037 | + mtd::MockEventHandlerRegister mock_event_handler_register; |
2038 | }; |
2039 | |
2040 | |
2041 | |
2042 | === modified file 'tests/unit-tests/test_asio_main_loop.cpp' |
2043 | --- tests/unit-tests/test_asio_main_loop.cpp 2014-06-12 15:35:08 +0000 |
2044 | +++ tests/unit-tests/test_asio_main_loop.cpp 2014-06-16 22:41:10 +0000 |
2045 | @@ -16,8 +16,8 @@ |
2046 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
2047 | */ |
2048 | |
2049 | -#include "mir/asio_main_loop.h" |
2050 | -#include "mir/time/high_resolution_clock.h" |
2051 | +#include "src/server/asio_main_loop.h" |
2052 | + |
2053 | #include "mir_test/pipe.h" |
2054 | #include "mir_test/auto_unblock_thread.h" |
2055 | #include "mir_test/signal.h" |
2056 | @@ -45,68 +45,7 @@ |
2057 | class AsioMainLoopTest : public ::testing::Test |
2058 | { |
2059 | public: |
2060 | - mir::AsioMainLoop ml{std::make_shared<mir::time::HighResolutionClock>()}; |
2061 | -}; |
2062 | - |
2063 | -class AdvanceableClock : public mir::time::Clock |
2064 | -{ |
2065 | -public: |
2066 | - mir::time::Timestamp sample() const override |
2067 | - { |
2068 | - std::lock_guard<std::mutex> lock(time_mutex); |
2069 | - return current_time; |
2070 | - } |
2071 | - void advance_by(std::chrono::milliseconds const step, mir::AsioMainLoop & ml) |
2072 | - { |
2073 | - bool done = false; |
2074 | - std::mutex checkpoint_mutex; |
2075 | - std::condition_variable checkpoint; |
2076 | - |
2077 | - { |
2078 | - std::lock_guard<std::mutex> lock(time_mutex); |
2079 | - current_time += step; |
2080 | - } |
2081 | - auto evaluate_clock_alarm = ml.notify_in( |
2082 | - std::chrono::milliseconds{0}, |
2083 | - [&done, &checkpoint_mutex, &checkpoint] |
2084 | - { |
2085 | - std::unique_lock<std::mutex> lock(checkpoint_mutex); |
2086 | - done = true; |
2087 | - checkpoint.notify_one(); |
2088 | - }); |
2089 | - |
2090 | - std::unique_lock<std::mutex> lock(checkpoint_mutex); |
2091 | - while(!done) checkpoint.wait(lock); |
2092 | - |
2093 | - } |
2094 | -private: |
2095 | - mutable std::mutex time_mutex; |
2096 | - mir::time::Timestamp current_time{ |
2097 | - [] |
2098 | - { |
2099 | - mir::time::HighResolutionClock clock; |
2100 | - return clock.sample(); |
2101 | - }() |
2102 | - }; |
2103 | -}; |
2104 | - |
2105 | - |
2106 | -class AsioMainLoopAlarmTest : public ::testing::Test |
2107 | -{ |
2108 | -public: |
2109 | - std::shared_ptr<AdvanceableClock> clock = std::make_shared<AdvanceableClock>(); |
2110 | - mir::AsioMainLoop ml{clock}; |
2111 | - int call_count{0}; |
2112 | - mt::WaitObject wait; |
2113 | - std::chrono::milliseconds delay{50}; |
2114 | - |
2115 | - struct UnblockMainLoop : mt::AutoUnblockThread |
2116 | - { |
2117 | - UnblockMainLoop(mir::AsioMainLoop & loop) |
2118 | - : mt::AutoUnblockThread([&loop]() {loop.stop();}, |
2119 | - [&loop]() {loop.run();}) |
2120 | - {} |
2121 | - }; |
2122 | + mir::AsioMainLoop ml; |
2123 | }; |
2124 | |
2125 | class Counter |
2126 | @@ -258,6 +197,7 @@ |
2127 | |
2128 | ml.register_fd_handler( |
2129 | {p.read_fd()}, |
2130 | + this, |
2131 | [&handled_fd, &data_read, this](int fd) |
2132 | { |
2133 | handled_fd = fd; |
2134 | @@ -282,6 +222,7 @@ |
2135 | |
2136 | ml.register_fd_handler( |
2137 | {pipes[0].read_fd(), pipes[1].read_fd()}, |
2138 | + this, |
2139 | [&handled_fds, &elems_read, &num_handled_fds](int fd) |
2140 | { |
2141 | handled_fds.push_back(fd); |
2142 | @@ -329,6 +270,7 @@ |
2143 | |
2144 | ml.register_fd_handler( |
2145 | {pipes[0].read_fd()}, |
2146 | + this, |
2147 | [&handled_fds, &elems_read, this](int fd) |
2148 | { |
2149 | EXPECT_EQ(static_cast<ssize_t>(sizeof(elems_read[0])), |
2150 | @@ -344,6 +286,7 @@ |
2151 | |
2152 | ml.register_fd_handler( |
2153 | {pipes[1].read_fd()}, |
2154 | + this, |
2155 | [&handled_fds, &elems_read, this](int fd) |
2156 | { |
2157 | EXPECT_EQ(static_cast<ssize_t>(sizeof(elems_read[1])), |
2158 | @@ -359,6 +302,7 @@ |
2159 | |
2160 | ml.register_fd_handler( |
2161 | {pipes[2].read_fd()}, |
2162 | + this, |
2163 | [&handled_fds, &elems_read, this](int fd) |
2164 | { |
2165 | EXPECT_EQ(static_cast<ssize_t>(sizeof(elems_read[2])), |
2166 | @@ -390,185 +334,42 @@ |
2167 | EXPECT_EQ(elems_to_send[2], elems_read[2]); |
2168 | } |
2169 | |
2170 | -TEST_F(AsioMainLoopAlarmTest, main_loop_runs_until_stop_called) |
2171 | -{ |
2172 | - auto mainloop_started = std::make_shared<mt::Signal>(); |
2173 | - |
2174 | - auto fire_on_mainloop_start = ml.notify_in(std::chrono::milliseconds{0}, |
2175 | - [mainloop_started]() |
2176 | - { |
2177 | - mainloop_started->raise(); |
2178 | - }); |
2179 | - |
2180 | - UnblockMainLoop unblocker(ml); |
2181 | - |
2182 | - ASSERT_TRUE(mainloop_started->wait_for(std::chrono::milliseconds{100})); |
2183 | - |
2184 | - auto timer_fired = std::make_shared<mt::Signal>(); |
2185 | - auto alarm = ml.notify_in(std::chrono::milliseconds{10}, [timer_fired] |
2186 | - { |
2187 | - timer_fired->raise(); |
2188 | - }); |
2189 | - |
2190 | - clock->advance_by(std::chrono::milliseconds{10}, ml); |
2191 | - EXPECT_TRUE(timer_fired->wait_for(std::chrono::milliseconds{500})); |
2192 | - |
2193 | - ml.stop(); |
2194 | - // Main loop should be stopped now |
2195 | - |
2196 | - timer_fired = std::make_shared<mt::Signal>(); |
2197 | - auto should_not_fire = ml.notify_in(std::chrono::milliseconds{0}, |
2198 | - [timer_fired]() |
2199 | - { |
2200 | - timer_fired->raise(); |
2201 | - }); |
2202 | - |
2203 | - EXPECT_FALSE(timer_fired->wait_for(std::chrono::milliseconds{100})); |
2204 | -} |
2205 | - |
2206 | -TEST_F(AsioMainLoopAlarmTest, alarm_starts_in_pending_state) |
2207 | -{ |
2208 | - auto alarm = ml.notify_in(delay, [this]() {}); |
2209 | - |
2210 | - UnblockMainLoop unblocker(ml); |
2211 | - |
2212 | - EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2213 | -} |
2214 | - |
2215 | -TEST_F(AsioMainLoopAlarmTest, alarm_fires_with_correct_delay) |
2216 | -{ |
2217 | - UnblockMainLoop unblocker(ml); |
2218 | - |
2219 | - auto alarm = ml.notify_in(delay, [](){}); |
2220 | - |
2221 | - clock->advance_by(delay - std::chrono::milliseconds{1}, ml); |
2222 | - EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2223 | - |
2224 | - clock->advance_by(delay, ml); |
2225 | - EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2226 | -} |
2227 | - |
2228 | -TEST_F(AsioMainLoopAlarmTest, multiple_alarms_fire) |
2229 | -{ |
2230 | - using namespace testing; |
2231 | - |
2232 | - int const alarm_count{10}; |
2233 | - Counter call_count; |
2234 | - std::array<std::unique_ptr<mir::time::Alarm>, alarm_count> alarms; |
2235 | - |
2236 | - for (auto& alarm : alarms) |
2237 | - alarm = ml.notify_in(delay, [&call_count](){++call_count;}); |
2238 | - |
2239 | - UnblockMainLoop unblocker(ml); |
2240 | - clock->advance_by(delay, ml); |
2241 | - |
2242 | - call_count.wait_for(delay, alarm_count); |
2243 | - EXPECT_THAT(call_count, Eq(alarm_count)); |
2244 | - |
2245 | - for (auto const& alarm : alarms) |
2246 | - EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2247 | -} |
2248 | - |
2249 | -TEST_F(AsioMainLoopAlarmTest, alarm_changes_to_triggered_state) |
2250 | -{ |
2251 | - auto alarm_fired = std::make_shared<mt::Signal>(); |
2252 | - auto alarm = ml.notify_in(std::chrono::milliseconds{5}, [alarm_fired]() |
2253 | - { |
2254 | - alarm_fired->raise(); |
2255 | - }); |
2256 | - |
2257 | - UnblockMainLoop unblocker(ml); |
2258 | - |
2259 | - clock->advance_by(delay, ml); |
2260 | - ASSERT_TRUE(alarm_fired->wait_for(std::chrono::milliseconds{100})); |
2261 | - |
2262 | - EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2263 | -} |
2264 | - |
2265 | -TEST_F(AsioMainLoopAlarmTest, cancelled_alarm_doesnt_fire) |
2266 | -{ |
2267 | - UnblockMainLoop unblocker(ml); |
2268 | - auto alarm = ml.notify_in(std::chrono::milliseconds{100}, |
2269 | - [](){ FAIL() << "Alarm handler of canceld alarm called";}); |
2270 | - |
2271 | - EXPECT_TRUE(alarm->cancel()); |
2272 | - |
2273 | - EXPECT_EQ(mir::time::Alarm::cancelled, alarm->state()); |
2274 | - |
2275 | - clock->advance_by(std::chrono::milliseconds{100}, ml); |
2276 | - |
2277 | - EXPECT_EQ(mir::time::Alarm::cancelled, alarm->state()); |
2278 | -} |
2279 | - |
2280 | -TEST_F(AsioMainLoopAlarmTest, destroyed_alarm_doesnt_fire) |
2281 | -{ |
2282 | - auto alarm = ml.notify_in(std::chrono::milliseconds{200}, |
2283 | - [](){ FAIL() << "Alarm handler of destroyed alarm called"; }); |
2284 | - |
2285 | - UnblockMainLoop unblocker(ml); |
2286 | - |
2287 | - alarm.reset(nullptr); |
2288 | - clock->advance_by(std::chrono::milliseconds{200}, ml); |
2289 | -} |
2290 | - |
2291 | -TEST_F(AsioMainLoopAlarmTest, rescheduled_alarm_fires_again) |
2292 | -{ |
2293 | - std::atomic<int> call_count{0}; |
2294 | - |
2295 | - auto alarm = ml.notify_in(std::chrono::milliseconds{0}, [&call_count]() |
2296 | - { |
2297 | - if (call_count++ > 1) |
2298 | - FAIL() << "Alarm called too many times"; |
2299 | - }); |
2300 | - |
2301 | - UnblockMainLoop unblocker(ml); |
2302 | - |
2303 | - clock->advance_by(std::chrono::milliseconds{0}, ml); |
2304 | - ASSERT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2305 | - |
2306 | - alarm->reschedule_in(std::chrono::milliseconds{100}); |
2307 | - EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2308 | - |
2309 | - clock->advance_by(std::chrono::milliseconds{100}, ml); |
2310 | - EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2311 | -} |
2312 | - |
2313 | -TEST_F(AsioMainLoopAlarmTest, rescheduled_alarm_cancels_previous_scheduling) |
2314 | -{ |
2315 | - std::atomic<int> call_count{0}; |
2316 | - |
2317 | - auto alarm = ml.notify_in(std::chrono::milliseconds{100}, [&call_count]() |
2318 | - { |
2319 | - call_count++; |
2320 | - }); |
2321 | - |
2322 | - UnblockMainLoop unblocker(ml); |
2323 | - clock->advance_by(std::chrono::milliseconds{90}, ml); |
2324 | - |
2325 | - EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2326 | - EXPECT_EQ(0, call_count); |
2327 | - EXPECT_TRUE(alarm->reschedule_in(std::chrono::milliseconds{100})); |
2328 | - EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2329 | - |
2330 | - clock->advance_by(std::chrono::milliseconds{110}, ml); |
2331 | - |
2332 | - EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2333 | - EXPECT_EQ(1, call_count); |
2334 | -} |
2335 | - |
2336 | -TEST_F(AsioMainLoopAlarmTest, alarm_fires_at_correct_time_point) |
2337 | -{ |
2338 | - mir::time::Timestamp real_soon = clock->sample() + std::chrono::milliseconds{120}; |
2339 | - |
2340 | - auto alarm = ml.notify_at(real_soon, []{}); |
2341 | - |
2342 | - UnblockMainLoop unblocker(ml); |
2343 | - |
2344 | - clock->advance_by(std::chrono::milliseconds{119}, ml); |
2345 | - EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2346 | - |
2347 | - clock->advance_by(std::chrono::milliseconds{1}, ml); |
2348 | - EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2349 | +TEST_F(AsioMainLoopTest, unregister_prevents_callback_and_does_not_harm_other_callbacks) |
2350 | +{ |
2351 | + mt::Pipe p1, p2; |
2352 | + char const data_to_write{'a'}; |
2353 | + int p1_handler_never_triggers{-1}; |
2354 | + int p2_handler_executes{-1}; |
2355 | + char data_read{0}; |
2356 | + |
2357 | + ml.register_fd_handler( |
2358 | + {p1.read_fd()}, |
2359 | + this, |
2360 | + [&p1_handler_never_triggers](int fd) |
2361 | + { |
2362 | + p1_handler_never_triggers = fd; |
2363 | + }); |
2364 | + |
2365 | + ml.register_fd_handler( |
2366 | + {p2.read_fd()}, |
2367 | + this+2, |
2368 | + [&p2_handler_executes,&data_read,this](int fd) |
2369 | + { |
2370 | + p2_handler_executes = fd; |
2371 | + EXPECT_EQ(1, read(fd, &data_read, 1)); |
2372 | + ml.stop(); |
2373 | + }); |
2374 | + |
2375 | + ml.unregister_fd_handler(this); |
2376 | + |
2377 | + EXPECT_EQ(-1, send(p1.write_fd(), &data_to_write, 1, MSG_NOSIGNAL)); |
2378 | + EXPECT_EQ(1, write(p2.write_fd(), &data_to_write, 1)); |
2379 | + |
2380 | + ml.run(); |
2381 | + |
2382 | + EXPECT_EQ(data_to_write, data_read); |
2383 | + EXPECT_EQ(-1, p1_handler_never_triggers); |
2384 | + EXPECT_EQ(p2.read_fd(), p2_handler_executes); |
2385 | } |
2386 | |
2387 | TEST_F(AsioMainLoopTest, dispatches_action) |
2388 | @@ -718,7 +519,7 @@ |
2389 | { |
2390 | int const id = 0; |
2391 | actions.push_back(id); |
2392 | - |
2393 | + |
2394 | for (int i = 1; i < num_actions; ++i) |
2395 | { |
2396 | ml.enqueue( |
2397 | |
2398 | === added file 'tests/unit-tests/test_asio_server_action_queue.cpp' |
2399 | --- tests/unit-tests/test_asio_server_action_queue.cpp 1970-01-01 00:00:00 +0000 |
2400 | +++ tests/unit-tests/test_asio_server_action_queue.cpp 2014-06-16 22:41:10 +0000 |
2401 | @@ -0,0 +1,219 @@ |
2402 | +/* |
2403 | + * Copyright © 2013 Canonical Ltd. |
2404 | + * |
2405 | + * This program is free software: you can redistribute it and/or modify |
2406 | + * it under the terms of the GNU General Public License version 3 as |
2407 | + * published by the Free Software Foundation. |
2408 | + * |
2409 | + * This program is distributed in the hope that it will be useful, |
2410 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2411 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2412 | + * GNU General Public License for more details. |
2413 | + * |
2414 | + * You should have received a copy of the GNU General Public License |
2415 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2416 | + * |
2417 | + * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
2418 | + */ |
2419 | + |
2420 | +#include "src/server/asio_server_action_queue.h" |
2421 | + |
2422 | +#include "mir_test/pipe.h" |
2423 | +#include "mir_test/auto_unblock_thread.h" |
2424 | +#include "mir_test/wait_object.h" |
2425 | + |
2426 | +#include <gtest/gtest.h> |
2427 | +#include <gmock/gmock.h> |
2428 | + |
2429 | +#include <thread> |
2430 | +#include <atomic> |
2431 | +#include <functional> |
2432 | +#include <mutex> |
2433 | +#include <condition_variable> |
2434 | +#include <array> |
2435 | +#include <boost/throw_exception.hpp> |
2436 | + |
2437 | +#include <sys/types.h> |
2438 | +#include <unistd.h> |
2439 | + |
2440 | +namespace mt = mir::test; |
2441 | + |
2442 | +namespace |
2443 | +{ |
2444 | + |
2445 | +class AsioServerActionQueueTest : public ::testing::Test |
2446 | +{ |
2447 | +public: |
2448 | + boost::asio::io_service service; |
2449 | + mir::AsioServerActionQueue queue{service}; |
2450 | +}; |
2451 | + |
2452 | +} |
2453 | + |
2454 | +TEST_F(AsioServerActionQueueTest, dispatches_action) |
2455 | +{ |
2456 | + using namespace testing; |
2457 | + |
2458 | + int num_actions{0}; |
2459 | + int const owner{0}; |
2460 | + |
2461 | + queue.enqueue( |
2462 | + &owner, |
2463 | + [&] |
2464 | + { |
2465 | + ++num_actions; |
2466 | + service.stop(); |
2467 | + }); |
2468 | + |
2469 | + service.run(); |
2470 | + |
2471 | + EXPECT_THAT(num_actions, Eq(1)); |
2472 | +} |
2473 | + |
2474 | +TEST_F(AsioServerActionQueueTest, dispatches_multiple_actions_in_order) |
2475 | +{ |
2476 | + using namespace testing; |
2477 | + |
2478 | + int const num_actions{5}; |
2479 | + std::vector<int> actions; |
2480 | + int const owner{0}; |
2481 | + |
2482 | + for (int i = 0; i < num_actions; ++i) |
2483 | + { |
2484 | + queue.enqueue( |
2485 | + &owner, |
2486 | + [&,i] |
2487 | + { |
2488 | + actions.push_back(i); |
2489 | + if (i == num_actions - 1) |
2490 | + service.stop(); |
2491 | + }); |
2492 | + } |
2493 | + |
2494 | + service.run(); |
2495 | + |
2496 | + ASSERT_THAT(actions.size(), Eq(num_actions)); |
2497 | + for (int i = 0; i < num_actions; ++i) |
2498 | + EXPECT_THAT(actions[i], Eq(i)) << "i = " << i; |
2499 | +} |
2500 | + |
2501 | +TEST_F(AsioServerActionQueueTest, does_not_dispatch_paused_actions) |
2502 | +{ |
2503 | + using namespace testing; |
2504 | + |
2505 | + std::vector<int> actions; |
2506 | + int const owner1{0}; |
2507 | + int const owner2{0}; |
2508 | + |
2509 | + queue.enqueue( |
2510 | + &owner1, |
2511 | + |
2512 | + [&] |
2513 | + { |
2514 | + int const id = 0; |
2515 | + actions.push_back(id); |
2516 | + }); |
2517 | + |
2518 | + queue.enqueue( |
2519 | + &owner2, |
2520 | + [&] |
2521 | + { |
2522 | + int const id = 1; |
2523 | + actions.push_back(id); |
2524 | + }); |
2525 | + |
2526 | + queue.enqueue( |
2527 | + &owner1, |
2528 | + [&] |
2529 | + { |
2530 | + int const id = 2; |
2531 | + actions.push_back(id); |
2532 | + }); |
2533 | + |
2534 | + queue.enqueue( |
2535 | + &owner2, |
2536 | + [&] |
2537 | + { |
2538 | + int const id = 3; |
2539 | + actions.push_back(id); |
2540 | + service.stop(); |
2541 | + }); |
2542 | + |
2543 | + queue.pause_processing_for(&owner1); |
2544 | + |
2545 | + service.run(); |
2546 | + |
2547 | + ASSERT_THAT(actions.size(), Eq(2)); |
2548 | + EXPECT_THAT(actions[0], Eq(1)); |
2549 | + EXPECT_THAT(actions[1], Eq(3)); |
2550 | +} |
2551 | + |
2552 | +TEST_F(AsioServerActionQueueTest, dispatches_resumed_actions) |
2553 | +{ |
2554 | + using namespace testing; |
2555 | + |
2556 | + std::vector<int> actions; |
2557 | + void const* const owner1_ptr{&actions}; |
2558 | + int const owner2{0}; |
2559 | + |
2560 | + queue.enqueue( |
2561 | + owner1_ptr, |
2562 | + [&] |
2563 | + { |
2564 | + int const id = 0; |
2565 | + actions.push_back(id); |
2566 | + service.stop(); |
2567 | + }); |
2568 | + |
2569 | + queue.enqueue( |
2570 | + &owner2, |
2571 | + [&] |
2572 | + { |
2573 | + int const id = 1; |
2574 | + actions.push_back(id); |
2575 | + queue.resume_processing_for(owner1_ptr); |
2576 | + }); |
2577 | + |
2578 | + queue.pause_processing_for(owner1_ptr); |
2579 | + |
2580 | + service.run(); |
2581 | + |
2582 | + ASSERT_THAT(actions.size(), Eq(2)); |
2583 | + EXPECT_THAT(actions[0], Eq(1)); |
2584 | + EXPECT_THAT(actions[1], Eq(0)); |
2585 | +} |
2586 | + |
2587 | +TEST_F(AsioServerActionQueueTest, handles_enqueue_from_within_action) |
2588 | +{ |
2589 | + using namespace testing; |
2590 | + |
2591 | + std::vector<int> actions; |
2592 | + int const num_actions{10}; |
2593 | + void const* const owner{&num_actions}; |
2594 | + |
2595 | + queue.enqueue( |
2596 | + owner, |
2597 | + [&] |
2598 | + { |
2599 | + int const id = 0; |
2600 | + actions.push_back(id); |
2601 | + |
2602 | + for (int i = 1; i < num_actions; ++i) |
2603 | + { |
2604 | + queue.enqueue( |
2605 | + owner, |
2606 | + [&,i] |
2607 | + { |
2608 | + actions.push_back(i); |
2609 | + if (i == num_actions - 1) |
2610 | + service.stop(); |
2611 | + }); |
2612 | + } |
2613 | + }); |
2614 | + |
2615 | + service.run(); |
2616 | + |
2617 | + ASSERT_THAT(actions.size(), Eq(num_actions)); |
2618 | + for (int i = 0; i < num_actions; ++i) |
2619 | + EXPECT_THAT(actions[i], Eq(i)) << "i = " << i; |
2620 | +} |
2621 | |
2622 | === added file 'tests/unit-tests/test_asio_timer_service.cpp' |
2623 | --- tests/unit-tests/test_asio_timer_service.cpp 1970-01-01 00:00:00 +0000 |
2624 | +++ tests/unit-tests/test_asio_timer_service.cpp 2014-06-16 22:41:10 +0000 |
2625 | @@ -0,0 +1,312 @@ |
2626 | +/* |
2627 | + * Copyright © 2014 Canonical Ltd. |
2628 | + * |
2629 | + * This program is free software: you can redistribute it and/or modify |
2630 | + * it under the terms of the GNU General Public License version 3 as |
2631 | + * published by the Free Software Foundation. |
2632 | + * |
2633 | + * This program is distributed in the hope that it will be useful, |
2634 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2635 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2636 | + * GNU General Public License for more details. |
2637 | + * |
2638 | + * You should have received a copy of the GNU General Public License |
2639 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2640 | + * |
2641 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
2642 | + */ |
2643 | + |
2644 | +#include "src/server/asio_timer_service.h" |
2645 | + |
2646 | +#include "mir/time/high_resolution_clock.h" |
2647 | +#include "mir_test/auto_unblock_thread.h" |
2648 | +#include "mir_test/wait_object.h" |
2649 | + |
2650 | +#include <gtest/gtest.h> |
2651 | +#include <gmock/gmock.h> |
2652 | + |
2653 | +#include <thread> |
2654 | +#include <atomic> |
2655 | +#include <functional> |
2656 | +#include <mutex> |
2657 | +#include <condition_variable> |
2658 | +#include <array> |
2659 | +#include <boost/throw_exception.hpp> |
2660 | + |
2661 | +#include <sys/types.h> |
2662 | +#include <unistd.h> |
2663 | + |
2664 | +namespace mt = mir::test; |
2665 | + |
2666 | +namespace |
2667 | +{ |
2668 | + |
2669 | +class AdvanceableClock : public mir::time::Clock |
2670 | +{ |
2671 | +public: |
2672 | + mir::time::Timestamp sample() const override |
2673 | + { |
2674 | + std::lock_guard<std::mutex> lock(time_mutex); |
2675 | + return current_time; |
2676 | + } |
2677 | + void advance_by(std::chrono::milliseconds const step, mir::AsioTimerService & timer_service) |
2678 | + { |
2679 | + { |
2680 | + std::lock_guard<std::mutex> lock(time_mutex); |
2681 | + current_time += step; |
2682 | + } |
2683 | + |
2684 | + bool done = false; |
2685 | + auto evaluate_clock_alarm = timer_service.notify_in( |
2686 | + std::chrono::milliseconds{0}, |
2687 | + [&done, this] |
2688 | + { |
2689 | + std::unique_lock<std::mutex> lock(update_time_checkpoint_mutex); |
2690 | + done = true; |
2691 | + update_time_checkpoint.notify_one(); |
2692 | + }); |
2693 | + |
2694 | + std::unique_lock<std::mutex> lock(update_time_checkpoint_mutex); |
2695 | + while(!done) update_time_checkpoint.wait(lock); |
2696 | + } |
2697 | + |
2698 | +private: |
2699 | + mutable std::mutex time_mutex; |
2700 | + |
2701 | + std::mutex update_time_checkpoint_mutex; |
2702 | + std::condition_variable update_time_checkpoint; |
2703 | + |
2704 | + |
2705 | + mir::time::Timestamp current_time{ |
2706 | + [] |
2707 | + { |
2708 | + mir::time::HighResolutionClock clock; |
2709 | + return clock.sample(); |
2710 | + }() |
2711 | + }; |
2712 | +}; |
2713 | + |
2714 | + |
2715 | +class AsioTimerServiceTest : public ::testing::Test |
2716 | +{ |
2717 | +public: |
2718 | + std::shared_ptr<AdvanceableClock> clock = std::make_shared<AdvanceableClock>(); |
2719 | + mir::AsioTimerService timer_service{clock}; |
2720 | + int call_count{0}; |
2721 | + mt::WaitObject wait; |
2722 | + std::chrono::milliseconds delay{50}; |
2723 | + |
2724 | + struct UnblockTimerService : mt::AutoUnblockThread |
2725 | + { |
2726 | + UnblockTimerService(mir::AsioTimerService & timer_service) |
2727 | + : mt::AutoUnblockThread([&timer_service]() {timer_service.stop();}, |
2728 | + [&timer_service]() {timer_service.run();}) |
2729 | + {} |
2730 | + }; |
2731 | +}; |
2732 | + |
2733 | +class Counter |
2734 | +{ |
2735 | +public: |
2736 | + int operator++() |
2737 | + { |
2738 | + std::lock_guard<decltype(mutex)> lock(mutex); |
2739 | + cv.notify_one(); |
2740 | + return ++counter; |
2741 | + } |
2742 | + |
2743 | + bool wait_for(std::chrono::milliseconds const& delay, int expected) |
2744 | + { |
2745 | + std::unique_lock<decltype(mutex)> lock(mutex); |
2746 | + return cv.wait_for(lock, delay, [&]{ return counter == expected;}); |
2747 | + } |
2748 | + |
2749 | + operator int() const |
2750 | + { |
2751 | + std::lock_guard<decltype(mutex)> lock(mutex); |
2752 | + return counter; |
2753 | + } |
2754 | + |
2755 | +private: |
2756 | + std::mutex mutable mutex; |
2757 | + std::condition_variable cv; |
2758 | + int counter{0}; |
2759 | +}; |
2760 | + |
2761 | +} |
2762 | + |
2763 | +TEST_F(AsioTimerServiceTest, runs_until_stopped) |
2764 | +{ |
2765 | + std::mutex checkpoint_mutex; |
2766 | + std::condition_variable checkpoint; |
2767 | + bool hit_checkpoint{false}; |
2768 | + |
2769 | + auto fire_on_timer_service_start = timer_service.notify_in( |
2770 | + std::chrono::milliseconds{0}, |
2771 | + [&checkpoint_mutex, &checkpoint, &hit_checkpoint]() |
2772 | + { |
2773 | + std::unique_lock<decltype(checkpoint_mutex)> lock(checkpoint_mutex); |
2774 | + hit_checkpoint = true; |
2775 | + checkpoint.notify_all(); |
2776 | + }); |
2777 | + |
2778 | + UnblockTimerService unblocker(timer_service); |
2779 | + |
2780 | + // TODO time dependency: |
2781 | + { |
2782 | + std::unique_lock<decltype(checkpoint_mutex)> lock(checkpoint_mutex); |
2783 | + ASSERT_TRUE(checkpoint.wait_for(lock, std::chrono::milliseconds{500}, [&hit_checkpoint]() { return hit_checkpoint; })); |
2784 | + } |
2785 | + |
2786 | + auto alarm = timer_service.notify_in(std::chrono::milliseconds{10}, [this] |
2787 | + { |
2788 | + wait.notify_ready(); |
2789 | + }); |
2790 | + |
2791 | + clock->advance_by(std::chrono::milliseconds{10}, timer_service); |
2792 | + EXPECT_NO_THROW(wait.wait_until_ready(std::chrono::milliseconds{500})); |
2793 | + |
2794 | + timer_service.stop(); |
2795 | + // Timer Service should be stopped now |
2796 | + |
2797 | + hit_checkpoint = false; |
2798 | + auto should_not_fire = timer_service.notify_in(std::chrono::milliseconds{0}, |
2799 | + [&checkpoint_mutex, &checkpoint, &hit_checkpoint]() |
2800 | + { |
2801 | + std::unique_lock<decltype(checkpoint_mutex)> lock(checkpoint_mutex); |
2802 | + hit_checkpoint = true; |
2803 | + checkpoint.notify_all(); |
2804 | + }); |
2805 | + |
2806 | + std::unique_lock<decltype(checkpoint_mutex)> lock(checkpoint_mutex); |
2807 | + EXPECT_FALSE(checkpoint.wait_for(lock, std::chrono::milliseconds{50}, [&hit_checkpoint]() { return hit_checkpoint; })); |
2808 | +} |
2809 | + |
2810 | +TEST_F(AsioTimerServiceTest, alarm_starts_in_pending_state) |
2811 | +{ |
2812 | + auto alarm = timer_service.notify_in(delay, [this]() {}); |
2813 | + |
2814 | + UnblockTimerService unblocker(timer_service); |
2815 | + |
2816 | + EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2817 | +} |
2818 | + |
2819 | +TEST_F(AsioTimerServiceTest, alarm_fires_with_correct_delay) |
2820 | +{ |
2821 | + auto alarm = timer_service.notify_in(delay, [](){}); |
2822 | + |
2823 | + UnblockTimerService unblocker(timer_service); |
2824 | + |
2825 | + clock->advance_by(delay - std::chrono::milliseconds{1}, timer_service); |
2826 | + EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2827 | + |
2828 | + clock->advance_by(delay, timer_service); |
2829 | + EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2830 | +} |
2831 | + |
2832 | +TEST_F(AsioTimerServiceTest, multiple_alarms_fire) |
2833 | +{ |
2834 | + using namespace testing; |
2835 | + |
2836 | + int const alarm_count{10}; |
2837 | + Counter call_count; |
2838 | + std::array<std::unique_ptr<mir::time::Alarm>, alarm_count> alarms; |
2839 | + |
2840 | + for (auto& alarm : alarms) |
2841 | + alarm = timer_service.notify_in(delay, [&call_count](){++call_count;}); |
2842 | + |
2843 | + UnblockTimerService unblocker(timer_service); |
2844 | + clock->advance_by(delay, timer_service); |
2845 | + |
2846 | + call_count.wait_for(delay, alarm_count); |
2847 | + EXPECT_THAT(call_count, Eq(alarm_count)); |
2848 | + |
2849 | + for (auto const& alarm : alarms) |
2850 | + EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2851 | +} |
2852 | + |
2853 | +TEST_F(AsioTimerServiceTest, cancelled_alarm_doesnt_fire) |
2854 | +{ |
2855 | + UnblockTimerService unblocker(timer_service); |
2856 | + auto alarm = timer_service.notify_in(std::chrono::milliseconds{100}, |
2857 | + [](){ FAIL() << "Alarm handler of canceld alarm called";}); |
2858 | + |
2859 | + EXPECT_TRUE(alarm->cancel()); |
2860 | + |
2861 | + EXPECT_EQ(mir::time::Alarm::cancelled, alarm->state()); |
2862 | + |
2863 | + clock->advance_by(std::chrono::milliseconds{100}, timer_service); |
2864 | + |
2865 | + EXPECT_EQ(mir::time::Alarm::cancelled, alarm->state()); |
2866 | +} |
2867 | + |
2868 | +TEST_F(AsioTimerServiceTest, destroyed_alarm_doesnt_fire) |
2869 | +{ |
2870 | + auto alarm = timer_service.notify_in(std::chrono::milliseconds{200}, |
2871 | + [](){ FAIL() << "Alarm handler of destroyed alarm called"; }); |
2872 | + |
2873 | + UnblockTimerService unblocker(timer_service); |
2874 | + |
2875 | + alarm.reset(nullptr); |
2876 | + clock->advance_by(std::chrono::milliseconds{200}, timer_service); |
2877 | +} |
2878 | + |
2879 | +TEST_F(AsioTimerServiceTest, rescheduled_alarm_fires_again) |
2880 | +{ |
2881 | + std::atomic<int> call_count{0}; |
2882 | + |
2883 | + auto alarm = timer_service.notify_in(std::chrono::milliseconds{0}, [&call_count]() |
2884 | + { |
2885 | + if (call_count++ > 1) |
2886 | + FAIL() << "Alarm called too many times"; |
2887 | + }); |
2888 | + |
2889 | + UnblockTimerService unblocker(timer_service); |
2890 | + |
2891 | + clock->advance_by(std::chrono::milliseconds{0}, timer_service); |
2892 | + ASSERT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2893 | + |
2894 | + alarm->reschedule_in(std::chrono::milliseconds{100}); |
2895 | + EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2896 | + |
2897 | + clock->advance_by(std::chrono::milliseconds{100}, timer_service); |
2898 | + EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2899 | +} |
2900 | + |
2901 | +TEST_F(AsioTimerServiceTest, rescheduled_alarm_cancels_previous_scheduling) |
2902 | +{ |
2903 | + std::atomic<int> call_count{0}; |
2904 | + |
2905 | + auto alarm = timer_service.notify_in(std::chrono::milliseconds{100}, [&call_count]() |
2906 | + { |
2907 | + call_count++; |
2908 | + }); |
2909 | + |
2910 | + UnblockTimerService unblocker(timer_service); |
2911 | + clock->advance_by(std::chrono::milliseconds{90}, timer_service); |
2912 | + |
2913 | + EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2914 | + EXPECT_EQ(0, call_count); |
2915 | + EXPECT_TRUE(alarm->reschedule_in(std::chrono::milliseconds{100})); |
2916 | + EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2917 | + |
2918 | + clock->advance_by(std::chrono::milliseconds{110}, timer_service); |
2919 | + |
2920 | + EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2921 | + EXPECT_EQ(1, call_count); |
2922 | +} |
2923 | + |
2924 | +TEST_F(AsioTimerServiceTest, alarm_fires_at_correct_time_point) |
2925 | +{ |
2926 | + mir::time::Timestamp real_soon = clock->sample() + std::chrono::milliseconds{120}; |
2927 | + |
2928 | + auto alarm = timer_service.notify_at(real_soon, []{}); |
2929 | + |
2930 | + UnblockTimerService unblocker(timer_service); |
2931 | + |
2932 | + clock->advance_by(std::chrono::milliseconds{119}, timer_service); |
2933 | + EXPECT_EQ(mir::time::Alarm::pending, alarm->state()); |
2934 | + |
2935 | + clock->advance_by(std::chrono::milliseconds{1}, timer_service); |
2936 | + EXPECT_EQ(mir::time::Alarm::triggered, alarm->state()); |
2937 | +} |
2938 | |
2939 | === added file 'tests/unit-tests/test_synchronous_server_action.cpp' |
2940 | --- tests/unit-tests/test_synchronous_server_action.cpp 1970-01-01 00:00:00 +0000 |
2941 | +++ tests/unit-tests/test_synchronous_server_action.cpp 2014-06-16 22:41:10 +0000 |
2942 | @@ -0,0 +1,78 @@ |
2943 | +/* |
2944 | + * Copyright © 2014 Canonical Ltd. |
2945 | + * |
2946 | + * This program is free software: you can redistribute it and/or modify it |
2947 | + * under the terms of the GNU General Public License version 3, |
2948 | + * as published by the Free Software Foundation. |
2949 | + * |
2950 | + * This program is distributed in the hope that it will be useful, |
2951 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2952 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2953 | + * GNU General Public License for more details. |
2954 | + * |
2955 | + * You should have received a copy of the GNU General Public License |
2956 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2957 | + * |
2958 | + * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com> |
2959 | + */ |
2960 | + |
2961 | +#include "src/server/synchronous_server_action.h" |
2962 | +#include "mir_test_doubles/mock_server_action_queue.h" |
2963 | + |
2964 | +#include <thread> |
2965 | + |
2966 | +namespace mtd = mir::test::doubles; |
2967 | + |
2968 | +TEST(SynchronousServerActionTest, just_executes_action_if_queue_is_not_running) |
2969 | +{ |
2970 | + using namespace ::testing; |
2971 | + |
2972 | + NiceMock<mtd::MockServerActionQueue> mock_queue; |
2973 | + EXPECT_CALL(mock_queue, enqueue(_,_)).Times(0); |
2974 | + int val = 0; |
2975 | + boost::optional<std::thread::id> empty_queue_thread_id; |
2976 | + { |
2977 | + mir::SynchronousServerAction action(mock_queue, empty_queue_thread_id, [&val]{val=1;}); |
2978 | + } |
2979 | + EXPECT_EQ(val, 1); |
2980 | +} |
2981 | + |
2982 | +TEST(SynchronousServerActionTest, just_executes_action_if_thread_identical) |
2983 | +{ |
2984 | + using namespace ::testing; |
2985 | + |
2986 | + NiceMock<mtd::MockServerActionQueue> mock_queue; |
2987 | + EXPECT_CALL(mock_queue, enqueue(_,_)).Times(0); |
2988 | + int val = 0; |
2989 | + boost::optional<std::thread::id> this_one{std::this_thread::get_id()}; |
2990 | + { |
2991 | + mir::SynchronousServerAction action(mock_queue, this_one, [&val]{val=1;}); |
2992 | + } |
2993 | + EXPECT_EQ(val, 1); |
2994 | +} |
2995 | + |
2996 | +TEST(SynchronousServerActionTest, enqueues_action_if_thread_differs) |
2997 | +{ |
2998 | + using namespace ::testing; |
2999 | + |
3000 | + class DirectlyExecutingServerActionQueue : public StrictMock<mtd::MockServerActionQueue> |
3001 | + { |
3002 | + void enqueue(void const* ptr, mir::ServerAction const& action) override |
3003 | + { |
3004 | + action(); |
3005 | + StrictMock<mtd::MockServerActionQueue>::enqueue(ptr, action); |
3006 | + } |
3007 | + }; |
3008 | + |
3009 | + DirectlyExecutingServerActionQueue execute_it; |
3010 | + EXPECT_CALL(execute_it, enqueue(_, _)); |
3011 | + |
3012 | + int val = 0; |
3013 | + std::thread::id arbitrary_thread; |
3014 | + boost::optional<std::thread::id> not_this_one{arbitrary_thread}; |
3015 | + |
3016 | + { |
3017 | + mir::SynchronousServerAction action(execute_it, not_this_one, [&val]{val=1;}); |
3018 | + } |
3019 | + EXPECT_EQ(1,val); |
3020 | +} |
PASSED: Continuous integration, rev:1643 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- ci/1704/ jenkins. qa.ubuntu. com/job/ mir-android- utopic- i386-build/ 296 jenkins. qa.ubuntu. com/job/ mir-clang- utopic- amd64-build/ 297 jenkins. qa.ubuntu. com/job/ mir-mediumtests -utopic- touch/296 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- amd64-ci/ 223 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- amd64-ci/ 223/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- armhf-ci/ 222 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- utopic- armhf-ci/ 222/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/780 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- utopic- armhf/780/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/1543 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 7499
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- team-mir- development- branch- ci/1704/ rebuild
http://