Mir

Merge lp:~andreas-pokorny/mir/add-dispatchable-alarm-factory into lp:mir

Proposed by Andreas Pokorny
Status: Work in progress
Proposed branch: lp:~andreas-pokorny/mir/add-dispatchable-alarm-factory
Merge into: lp:mir
Diff against target: 1309 lines (+1145/-26)
15 files modified
include/common/mir/dispatch/alarm_factory.h (+57/-0)
include/common/mir/lockable_callback.h (+3/-3)
include/common/mir/time/alarm.h (+3/-3)
include/common/mir/time/alarm_factory.h (+4/-4)
include/common/mir/time/steady_timer_fd.h (+43/-0)
include/common/mir/time/timer_fd.h (+68/-0)
include/test/mir/test/doubles/mock_alarm_factory.h (+49/-0)
src/common/dispatch/CMakeLists.txt (+1/-0)
src/common/dispatch/alarm_factory.cpp (+400/-0)
src/common/symbols.map (+25/-15)
src/common/time/CMakeLists.txt (+1/-0)
src/common/time/steady_timer_fd.cpp (+51/-0)
tests/unit-tests/dispatch/CMakeLists.txt (+1/-0)
tests/unit-tests/dispatch/test_dispatchable_alarm_factory.cpp (+438/-0)
tests/unit-tests/test_glib_main_loop.cpp (+1/-1)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/add-dispatchable-alarm-factory
Reviewer Review Type Date Requested Status
Chris Halse Rogers Needs Fixing
Mir CI Bot continuous-integration Needs Fixing
Alan Griffiths Abstain
Review via email: mp+289959@code.launchpad.net

Commit message

Add mir::dispatch::AlarmFactory

Implementation of mir::time::AlarmFactory that works with mir::dispatch::Dispatchable and supports all the state transitions required by the test suite accumulated in test_glib_main_loop.cpp.

With this change several GPL interfaces defined inside the mirserver API have been moved to mircommon and LGPL.

Description of the change

Adds an AlarmFactory that works inside a MultiplexingDispatchable and is hence not connected to a MainLoop. The implementation was driven by the test cases for the GLibMainLoop.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3415
https://mir-jenkins.ubuntu.com/job/mir-ci/643/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/579/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/615
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/607
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/607
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/589
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/589/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/589
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/589/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/589/console
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/589/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/589
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/589/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/589
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/589/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/643/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3415
https://mir-jenkins.ubuntu.com/job/mir-ci/650/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/591/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/627
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/619
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/619
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/601
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/601/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/601
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/601/artifact/output/*zip*/output.zip
    ABORTED: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/601/console
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/601/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/601
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/601/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/601
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/601/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/650/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3415
https://mir-jenkins.ubuntu.com/job/mir-ci/664/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/610/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/647
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/639
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/639
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/620
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/620/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/620/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/620
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/620/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/620
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/620/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/620/console

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/664/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

Overall: what is the purpose of this alarm factory? I presume it's to get key-repeat timers that are dispatched appropriately?

Nit:
+ struct Alarm;
+ struct AlarmData;

Both of these have significant interfaces and significant internal state; is there any reason why they're not declared as classes? Relatedly: C++ *really* should have not introduced the “class” keyword.

The code looks fine, although you should add documentation for the new public interfaces.

review: Needs Fixing
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> Overall: what is the purpose of this alarm factory? I presume it's to get key-
> repeat timers that are dispatched appropriately?

Yes. Not sending repeat events on a different thread than all other input events. Not having to pass *the* mainloop instance to the platform, where device tracking and handling repeat is even simpler. Oh and yes .. creating multiple mainloops also has fascinating effects that I want to avoid.

>
> Nit:
> + struct Alarm;
> + struct AlarmData;
>
> Both of these have significant interfaces and significant internal state; is
> there any reason why they're not declared as classes? Relatedly: C++ *really*
> should have not introduced the “class” keyword.

Of course they started out as simple structs before I remembered all the gory behavior details of Alarms..

> The code looks fine, although you should add documentation for the new public
> interfaces.

sure

3416. By Andreas Pokorny

Cleanups..
 - add doxygen for new headers
 - struct/class changes
 - consume expired timer counts on dispatch

3417. By Andreas Pokorny

some more doxygen

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3417
https://mir-jenkins.ubuntu.com/job/mir-ci/689/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/641
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/678
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/670
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/670
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/651
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/651/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/651
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/651/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/651
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/651/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/651
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/651/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/651
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/651/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/689/rebuild

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

+class AlarmFactory : public ReadableFd, public time::AlarmFactory

ReadableFd is not designed to be derived from. Nor does "AlarmFactory is a ReadableFd" make sense to me. Even "AlarmFactory is a Dispatchable" seems dubious.

I see no use of relevant_events(), watch_fd() is used internally and by DispatchableAlarmFactory.yields_timer_fd - but the latter doesn't really seem like a required behaviour, just (possibly unnecessary) testing of implementation detail.

Why not use composition?

class AlarmFactory : public time::AlarmFactory
{
...
   ReadableFd fd;
};

auto AlarmFactory::dispatch(FdEvents events)
-> bool { return fd.dispatch(events); }

review: Needs Fixing
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> +class AlarmFactory : public ReadableFd, public time::AlarmFactory
>
> ReadableFd is not designed to be derived from. Nor does "AlarmFactory is a
> ReadableFd" make sense to me. Even "AlarmFactory is a Dispatchable" seems
> dubious.
>
> I see no use of relevant_events(), watch_fd() is used internally and by
> DispatchableAlarmFactory.yields_timer_fd - but the latter doesn't really seem
> like a required behaviour, just (possibly unnecessary) testing of
> implementation detail.

The use of those methods is not visible because I split the implementation and its uses in separate MPs.

> Why not use composition?
>
> class AlarmFactory : public time::AlarmFactory
> {
> ...
> ReadableFd fd;
> };
>
> auto AlarmFactory::dispatch(FdEvents events)
> -> bool { return fd.dispatch(events); }

Why would there ever be a dispatch function when that implementation is not deriving md::Dispatchable? I could do that with composition and have a shared_ptr<Dispatchable> member, and provide access to that - to allow the caller to register the AlarmFactory to the MultiplexingDispatcher. But that would only add complexity to the life time dependencies between AlarmFactory and the dispatcher instance.. If you still think composition is better in this case I would go with:

class AlarmFactory : public time::AlarmFactory, public dispatch::Dispatchable
{
   ...
   std::shared_ptr<Dispatchable> const timer_fd;
};

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

oops I meant:
class AlarmFactory : public time::AlarmFactory, public dispatch::Dispatchable
{
   ...
   ReadableFd const timer_fd;
};

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> oops I meant:
> class AlarmFactory : public time::AlarmFactory, public dispatch::Dispatchable
> {
> ...
> ReadableFd const timer_fd;
> };

Hmm. If that's the intention I prefer:

class AlarmFactory : public time::AlarmFactory, public dispatch::Dispatchable,
    private ReadableFd
{
   ...
};

But for that dispatch::Dispatchable needs to be a virtual base class of ReadableFd.

review: Abstain
3418. By Andreas Pokorny

Remove ReadableFd

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> > oops I meant:
> > class AlarmFactory : public time::AlarmFactory, public
> dispatch::Dispatchable
> > {
> > ...
> > ReadableFd const timer_fd;
> > };
>
> Hmm. If that's the intention I prefer:
>
> class AlarmFactory : public time::AlarmFactory, public dispatch::Dispatchable,
> private ReadableFd
> {
> ...
> };
>
> But for that dispatch::Dispatchable needs to be a virtual base class of
> ReadableFd.

I guess reusing ReadableFd is not really worth the trouble.. got rid of it in the latest change.

3419. By Andreas Pokorny

merge lp:mir

3420. By Andreas Pokorny

resolve merge conflict

Revision history for this message
Chris Halse Rogers (raof) wrote :

Hm. Now that I read the docs, isn't TimerFd very nearly a time::Alarm implementation already?

It seems that this would be substantially simpler if you dispatchable::AlarmFactory just created a TimerFd per alarm and either added it to an internal MultiplexingDispatchable (and remained an implementation of Dispatchable) or took a reference to a MultiplexingDispatchable in the constructor and stopped implementing Dispatchable.

The downside is that tests of dispatchable::AlarmFactory become anchored to real time, but I'm not a big fan of significantly increasing the complexity of code in order to make tests run faster.

Did you try this in an earlier iteration and it turned out to be difficult?

review: Needs Information
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

> Hm. Now that I read the docs, isn't TimerFd very nearly a time::Alarm
> implementation already?
>
> It seems that this would be substantially simpler if you
> dispatchable::AlarmFactory just created a TimerFd per alarm and either added
> it to an internal MultiplexingDispatchable (and remained an implementation of
> Dispatchable) or took a reference to a MultiplexingDispatchable in the
> constructor and stopped implementing Dispatchable.
>
> The downside is that tests of dispatchable::AlarmFactory become anchored to
> real time, but I'm not a big fan of significantly increasing the complexity of
> code in order to make tests run faster.
>
> Did you try this in an earlier iteration and it turned out to be difficult?

I considered the idea of having one fd per alarm. Given the way I wanted to use the Alarm* interfaces (by creating and destroying alarms on key presses), my gut feeling made me go with a single fd solution. The fact that I factored out real time with the TimerFd interface from the beginning and having the test cases already available, made driving the implementation simple.

A one fd per alarm solution rule out the complexity to exclude parallel access in the pending and canceled timer containers because the containers would not be needed. Also AlarmData would not be needed, but that did not really contribute to complexity. I think one would still need the other parts - tracking the executed state, and having a separate Alarm::Internal which plays the Dispatchable, and is decoupled in its life time, because the caller owns the Alarm via a unique ptr, while the MultiplexingDispacher just shares ownership of the Dispatchable. So roughly one out of three reasons for complexity would be gone..

Revision history for this message
Chris Halse Rogers (raof) wrote :

I don't think you need a separate Alarm::Internal, because you can just register the relevant mir::Fd + std::function<> pair with the MultiplexingDispatchable.

I'm OK with it as it is, but still have the gut feeling that it could be significantly simpler.

review: Approve
Revision history for this message
Chris Halse Rogers (raof) wrote :

Bah, I'm wavering. Maybe convince me a bit more that this isn't needlessly complex :)

review: Needs Information
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

FAILED: Continuous integration, rev:3420
https://mir-jenkins.ubuntu.com/job/mir-ci/710/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/673/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/710
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/701
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/701
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/682/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/682
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/682/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/682
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/682/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/682
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/682/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/682
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/682/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/710/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :
Download full text (3.8 KiB)

Ok.

I've got one guaranteed needs-fixing, which is that the TimerFd interface is incorrect - it combines both a factory for TimerFds and the TimerFd interface itself, with the result that there's nothing stopping you allocating a mir::Fd from one TimerFd implementation and using it on another.

It should be split into TimerFdProvider and TimerFd interfaces.

Secondarily, I'm pretty sure the simplification would work - here's mostly complete code:

class AlarmFactory : public Dispatchable, public time::AlarmFactory
{
public:
    AlarmFactory(std::shared_ptr<time::TimerFdProvider> const& timer_source);
    std::unique_ptr<time::Alarm> create_alarm(std::function<void()> const& callback) override;
    std::unique_ptr<time::Alarm> create_alarm(std::unique_ptr<LockableCallback>&& callback) override;

    Fd watch_fd() const override;
    bool dispatch(FdEvents events) override;
    FdEvents relevant_events() const override;
private:
    std::shared_ptr<time::TimerFdProvider> const timer_source;
    MultiplexingDispatchable multiplexer;
};

class Alarm : public mt::Alarm
{
public:
    Alarm(
        std::unique_ptr<mt::TimerFd>&& timer,
        std::unique_ptr<mir::LockableCallback>&& callback,
        md::MultiplexingDispatchable& multiplexer)
        : timer{std::move(timer)},
          callback{callback.release(),
              [this](auto* victim)
              {
                  destroyed.notify_all();
                  delete victim;
              }}
    {
        std::weak_ptr<mir::LockableCallback> weak_cb = this->callback;
        auto internal_callback = [this, weak_cb]()
            {
                if (auto cb = weak_cb.lock())
                {
                    std::lock_guard<decltype(state_mutex)> lock{state_mutex};

                    if (state_holder == mt::Alarm::State::pending)
                    {
                        state_holder = mt::Alarm::State::triggered;
                        (*cb)();
                    }
                }
                return !weak_cb.expired();
            }
        multiplexer.add_watch(this->timer->fd(), internal_callback);
    }

    ~Alarm()
    {
        {
            std::lock_guard<decltype(state_mutex)> lock(state_mutex);

            if (state_holder == triggered)
            {
                // Either there are no pending calls or we're currently in the callback.
                // In either case, we don't have to wait.
                return;
            }
        }

        cancel();
        std::weak_ptr<mir::LockableCallback> canary = callback;
        callback.reset();

        // We need to be triggered once more to remove us from the MultiplexingDispatchable.
        reschedule_in(0ms);

        // Might be better to take shared ownership of the MultiplexingDispatchable and
        // manually remove_watch() us. Would let us not have to wait for a dispatch() iteration.
        if (!canary.expired())
        {
            std::unique_lock<decltype(state_mutex)> lock(state_mutex);

            destroyed.wait(lock, [this]() { canary.expired(); });
        }

    }

    bool cancel() override
    {
        std::unique_lock<decltype(state_mutex)> lock(state_mutex);

        ...

Read more...

review: Needs Fixing

Unmerged revisions

3420. By Andreas Pokorny

resolve merge conflict

3419. By Andreas Pokorny

merge lp:mir

3418. By Andreas Pokorny

Remove ReadableFd

3417. By Andreas Pokorny

some more doxygen

3416. By Andreas Pokorny

Cleanups..
 - add doxygen for new headers
 - struct/class changes
 - consume expired timer counts on dispatch

3415. By Andreas Pokorny

fix init of itimerspec

3414. By Andreas Pokorny

Add mir::dispatch::AlarmFactory

Implementation of mir::time::AlarmFactory that works with mir::dispatch::Dispatchable and supports all the state transitions required by the test suite accumulated in test_glib_main_loop.cpp.

With this change several GPL interfaces defined inside the mirserver API have been moved to mircommon and LGPL.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'include/common/mir/dispatch/alarm_factory.h'
2--- include/common/mir/dispatch/alarm_factory.h 1970-01-01 00:00:00 +0000
3+++ include/common/mir/dispatch/alarm_factory.h 2016-03-30 14:25:37 +0000
4@@ -0,0 +1,57 @@
5+/*
6+ * Copyright © 2016 Canonical Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU Lesser General Public License version 3,
10+ * as published by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ *
20+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
21+ */
22+
23+#ifndef MIR_DISPATCH_ALARM_FACTORY_H
24+#define MIR_DISPATCH_ALARM_FACTORY_H
25+
26+#include "mir/time/alarm_factory.h"
27+#include "mir/dispatch/dispatchable.h"
28+#include "mir/fd.h"
29+
30+namespace mir
31+{
32+namespace time
33+{
34+class Clock;
35+class TimerFd;
36+}
37+namespace dispatch
38+{
39+
40+class AlarmFactory : public Dispatchable, public time::AlarmFactory
41+{
42+public:
43+ AlarmFactory(std::shared_ptr<time::Clock> const& clock, std::shared_ptr<time::TimerFd> const& fd_source);
44+ std::unique_ptr<time::Alarm> create_alarm(std::function<void()> const& callback) override;
45+ std::unique_ptr<time::Alarm> create_alarm(std::shared_ptr<LockableCallback> const& callback) override;
46+
47+ Fd watch_fd() const override;
48+ bool dispatch(FdEvents events) override;
49+ FdEvents relevant_events() const override;
50+private:
51+ class Alarm;
52+ class AlarmData;
53+
54+ friend class Alarm;
55+ mir::Fd const timer_fd;
56+ std::shared_ptr<AlarmData> const data;
57+};
58+
59+}
60+}
61+#endif
62
63=== renamed file 'include/server/mir/lockable_callback.h' => 'include/common/mir/lockable_callback.h'
64--- include/server/mir/lockable_callback.h 2015-02-24 23:45:39 +0000
65+++ include/common/mir/lockable_callback.h 2016-03-30 14:25:37 +0000
66@@ -2,15 +2,15 @@
67 * Copyright © 2015 Canonical Ltd.
68 *
69 * This program is free software: you can redistribute it and/or modify it
70- * under the terms of the GNU General Public License version 3,
71+ * under the terms of the GNU Lesser General Public License version 3,
72 * as published by the Free Software Foundation.
73 *
74 * This program is distributed in the hope that it will be useful,
75 * but WITHOUT ANY WARRANTY; without even the implied warranty of
76 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
77- * GNU General Public License for more details.
78+ * GNU Lesser General Public License for more details.
79 *
80- * You should have received a copy of the GNU General Public License
81+ * You should have received a copy of the GNU Lesser General Public License
82 * along with this program. If not, see <http://www.gnu.org/licenses/>.
83 *
84 * Authored by: Alberto Aguirre <alberto.aguirre@canonical.com>
85
86=== renamed file 'include/server/mir/time/alarm.h' => 'include/common/mir/time/alarm.h'
87--- include/server/mir/time/alarm.h 2016-01-29 08:18:22 +0000
88+++ include/common/mir/time/alarm.h 2016-03-30 14:25:37 +0000
89@@ -2,15 +2,15 @@
90 * Copyright © 2014 Canonical Ltd.
91 *
92 * This program is free software: you can redistribute it and/or modify it
93- * under the terms of the GNU General Public License version 3,
94+ * under the terms of the GNU Lesser General Public License version 3,
95 * as published by the Free Software Foundation.
96 *
97 * This program is distributed in the hope that it will be useful,
98 * but WITHOUT ANY WARRANTY; without even the implied warranty of
99 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
100- * GNU General Public License for more details.
101+ * GNU Lesser General Public License for more details.
102 *
103- * You should have received a copy of the GNU General Public License
104+ * You should have received a copy of the GNU Lesser General Public License
105 * along with this program. If not, see <http://www.gnu.org/licenses/>.
106 *
107 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
108
109=== renamed file 'include/server/mir/time/alarm_factory.h' => 'include/common/mir/time/alarm_factory.h'
110--- include/server/mir/time/alarm_factory.h 2015-06-17 05:20:42 +0000
111+++ include/common/mir/time/alarm_factory.h 2016-03-30 14:25:37 +0000
112@@ -1,16 +1,16 @@
113 /*
114- * Copyright © 2014-2015 Canonical Ltd.
115+ * Copyright © 2014-2016 Canonical Ltd.
116 *
117 * This program is free software: you can redistribute it and/or modify it
118- * under the terms of the GNU General Public License version 3,
119+ * under the terms of the GNU Lesser General Public License version 3,
120 * as published by the Free Software Foundation.
121 *
122 * This program is distributed in the hope that it will be useful,
123 * but WITHOUT ANY WARRANTY; without even the implied warranty of
124 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
125- * GNU General Public License for more details.
126+ * GNU Lesser General Public License for more details.
127 *
128- * You should have received a copy of the GNU General Public License
129+ * You should have received a copy of the GNU Lesser General Public License
130 * along with this program. If not, see <http://www.gnu.org/licenses/>.
131 *
132 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
133
134=== added file 'include/common/mir/time/steady_timer_fd.h'
135--- include/common/mir/time/steady_timer_fd.h 1970-01-01 00:00:00 +0000
136+++ include/common/mir/time/steady_timer_fd.h 2016-03-30 14:25:37 +0000
137@@ -0,0 +1,43 @@
138+/*
139+ * Copyright © 2016 Canonical Ltd.
140+ *
141+ * This program is free software: you can redistribute it and/or modify it
142+ * under the terms of the GNU Lesser General Public License version 3,
143+ * as published by the Free Software Foundation.
144+ *
145+ * This program is distributed in the hope that it will be useful,
146+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
147+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
148+ * GNU Lesser General Public License for more details.
149+ *
150+ * You should have received a copy of the GNU Lesser General Public License
151+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
152+ *
153+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
154+ */
155+
156+#ifndef MIR_TIME_STEADY_TIMER_FD_
157+#define MIR_TIME_STEADY_TIMER_FD_
158+
159+#include "mir/time/timer_fd.h"
160+
161+namespace mir
162+{
163+namespace time
164+{
165+
166+/**
167+ * SteadyTimerFd creates timer file descriptors using CLOCK_MONOTONIC.
168+ */
169+class SteadyTimerFd : public TimerFd
170+{
171+public:
172+ SteadyTimerFd();
173+ Fd create_timer() override;
174+ void schedule_for(Fd& fd, Timestamp timeout) override;
175+ void cancel(Fd& fd) override;
176+};
177+
178+}
179+}
180+#endif
181
182=== added file 'include/common/mir/time/timer_fd.h'
183--- include/common/mir/time/timer_fd.h 1970-01-01 00:00:00 +0000
184+++ include/common/mir/time/timer_fd.h 2016-03-30 14:25:37 +0000
185@@ -0,0 +1,68 @@
186+/*
187+ * Copyright © 2016 Canonical Ltd.
188+ *
189+ * This program is free software: you can redistribute it and/or modify it
190+ * under the terms of the GNU Lesser General Public License version 3,
191+ * as published by the Free Software Foundation.
192+ *
193+ * This program is distributed in the hope that it will be useful,
194+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
195+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
196+ * GNU Lesser General Public License for more details.
197+ *
198+ * You should have received a copy of the GNU Lesser General Public License
199+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
200+ *
201+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
202+ */
203+
204+#ifndef MIR_TIME_TIMER_FD_H_
205+#define MIR_TIME_TIMER_FD_H_
206+
207+#include "mir/fd.h"
208+#include "mir/time/types.h"
209+
210+#include <chrono>
211+
212+namespace mir
213+{
214+namespace time
215+{
216+
217+/**
218+ * TimerFd provides timers that notify via file descriptors.
219+ */
220+class TimerFd
221+{
222+public:
223+ TimerFd() = default;
224+ virtual ~TimerFd() = default;
225+ /**
226+ * Create an unconfigured timer and return a file descriptor to monitor it.
227+ *
228+ * \returns a file descriptor to monitor the timer
229+ */
230+ virtual mir::Fd create_timer() = 0;
231+ /**
232+ * Configure the timer monitored by the file descriptor to expire at the given \a timestamp.
233+ *
234+ * \param[in] timestamp the timestamp
235+ * \param[in] fd file descriptor of the timer
236+ */
237+ virtual void schedule_for(mir::Fd &fd, mir::time::Timestamp timestamp) = 0;
238+ /**
239+ * Cancel the timer.
240+ *
241+ * After that call the timer can be configured again using schedule_for.
242+ *
243+ * \param[in] fd file descriptor of the timer
244+ */
245+ virtual void cancel(mir::Fd& fd) = 0;
246+protected:
247+ TimerFd(TimerFd const&) = delete;
248+ TimerFd& operator=(TimerFd const&) = delete;
249+};
250+
251+}
252+}
253+#endif
254
255=== added file 'include/test/mir/test/doubles/mock_alarm_factory.h'
256--- include/test/mir/test/doubles/mock_alarm_factory.h 1970-01-01 00:00:00 +0000
257+++ include/test/mir/test/doubles/mock_alarm_factory.h 2016-03-30 14:25:37 +0000
258@@ -0,0 +1,49 @@
259+/*
260+ * Copyright © 2016 Canonical Ltd.
261+ *
262+ * This program is free software: you can redistribute it and/or modify
263+ * it under the terms of the GNU General Public License version 3 as
264+ * published by the Free Software Foundation.
265+ *
266+ * This program is distributed in the hope that it will be useful,
267+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
268+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
269+ * GNU General Public License for more details.
270+ *
271+ * You should have received a copy of the GNU General Public License
272+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
273+ *
274+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
275+ */
276+
277+#ifndef MIR_TEST_DOUBLES_MOCK_ALARM_FACTORY_H
278+#define MIR_TEST_DOUBLES_MOCK_ALARM_FACTORY_H
279+
280+#include "mir/time/alarm_factory.h"
281+
282+namespace mir
283+{
284+namespace test
285+{
286+namespace doubles
287+{
288+
289+struct MockAlarmFactory : public mir::time::AlarmFactory
290+{
291+ MOCK_METHOD1(create_alarm_adapter, mir::time::Alarm*(std::function<void()> const&));
292+ MOCK_METHOD1(create_lockable_alarm_adapter, mir::time::Alarm*(std::shared_ptr<mir::LockableCallback> const&));
293+ std::unique_ptr<mir::time::Alarm> create_alarm(std::function<void()> const& cb)
294+ {
295+ return std::unique_ptr<mir::time::Alarm>(create_alarm_adapter(cb));
296+ }
297+
298+ std::unique_ptr<mir::time::Alarm> create_alarm(std::shared_ptr<mir::LockableCallback> const& lcb)
299+ {
300+ return std::unique_ptr<mir::time::Alarm>(create_lockable_alarm_adapter(lcb));
301+ }
302+};
303+
304+}
305+}
306+}
307+#endif
308
309=== modified file 'src/common/dispatch/CMakeLists.txt'
310--- src/common/dispatch/CMakeLists.txt 2016-01-29 08:18:22 +0000
311+++ src/common/dispatch/CMakeLists.txt 2016-03-30 14:25:37 +0000
312@@ -17,6 +17,7 @@
313 list(
314 APPEND MIR_COMMON_SOURCES
315 ${CMAKE_CURRENT_SOURCE_DIR}/action_queue.cpp
316+ ${CMAKE_CURRENT_SOURCE_DIR}/alarm_factory.cpp
317 ${CMAKE_CURRENT_SOURCE_DIR}/multiplexing_dispatchable.cpp
318 ${CMAKE_CURRENT_SOURCE_DIR}/readable_fd.cpp
319 ${CMAKE_CURRENT_SOURCE_DIR}/threaded_dispatcher.cpp
320
321=== added file 'src/common/dispatch/alarm_factory.cpp'
322--- src/common/dispatch/alarm_factory.cpp 1970-01-01 00:00:00 +0000
323+++ src/common/dispatch/alarm_factory.cpp 2016-03-30 14:25:37 +0000
324@@ -0,0 +1,400 @@
325+/*
326+ * Copyright © 2016 Canonical Ltd.
327+ *
328+ * This program is free software: you can redistribute it and/or modify it
329+ * under the terms of the GNU Lesser General Public License version 3,
330+ * as published by the Free Software Foundation.
331+ *
332+ * This program is distributed in the hope that it will be useful,
333+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
334+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
335+ * GNU Lesser General Public License for more details.
336+ *
337+ * You should have received a copy of the GNU Lesser General Public License
338+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
339+ *
340+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
341+ */
342+
343+#include "mir/dispatch/alarm_factory.h"
344+#include "mir/time/timer_fd.h"
345+#include "mir/time/clock.h"
346+#include "mir/lockable_callback.h"
347+#include "mir/optional_value.h"
348+
349+#include <map>
350+#include <vector>
351+#include <unordered_set>
352+#include <mutex>
353+#include <chrono>
354+#include <algorithm>
355+#include <atomic>
356+#include <condition_variable>
357+#include <thread>
358+#include <unistd.h>
359+
360+namespace md = mir::dispatch;
361+namespace mt = mir::time;
362+
363+namespace
364+{
365+
366+struct Callback : mir::LockableCallback
367+{
368+ Callback(std::function<void()> const& callback) : callback{callback} {}
369+
370+ void operator()() override
371+ {
372+ callback();
373+ }
374+ void lock() override
375+ {
376+ }
377+ void unlock() override
378+ {
379+ }
380+ std::function<void()> callback;
381+};
382+
383+}
384+
385+
386+class md::AlarmFactory::AlarmData
387+{
388+public:
389+ AlarmData(mir::Fd fd, std::shared_ptr<mt::Clock> const& clock, std::shared_ptr<mt::TimerFd> const& fd_source);
390+
391+ void handle_timer();
392+ void deactivate_timer(Alarm* alarm);
393+ void activate_timer(mt::Timestamp timeout, Alarm* alarm);
394+ void activate_timer(std::chrono::milliseconds delay, Alarm* alarm);
395+
396+private:
397+ void erase_alarm_locked(Alarm* alarm);
398+ void clear_cancelled_timers();
399+ mir::Fd timer_fd;
400+ std::shared_ptr<time::Clock> const clock;
401+ std::shared_ptr<time::TimerFd> const fd_source;
402+
403+ std::mutex timer_queue_mutex;
404+ std::multimap<mt::Timestamp, Alarm*> timer_queue;
405+
406+ std::mutex cancelled_timers_mutex;
407+ std::unordered_set<Alarm*> cancelled_timers;
408+};
409+
410+class md::AlarmFactory::Alarm : public mt::Alarm
411+{
412+public:
413+ enum class AlarmState
414+ {
415+ cancelled,
416+ pending,
417+ executing,
418+ triggered
419+ };
420+
421+ Alarm(std::shared_ptr<mir::LockableCallback> const& callback,
422+ std::shared_ptr<md::AlarmFactory::AlarmData> const& data)
423+ : internal{std::make_shared<Internal>(this, callback, data)}
424+ {
425+ }
426+
427+ ~Alarm()
428+ {
429+ internal->cancel();
430+ }
431+
432+ bool cancel() override
433+ {
434+ return internal->cancel();
435+ }
436+
437+ State state() const override
438+ {
439+ return internal->state();
440+ }
441+
442+ bool reschedule_in(std::chrono::milliseconds delay) override
443+ {
444+ return internal->reschedule_in(delay);
445+ }
446+
447+ bool reschedule_for(mt::Timestamp timeout) override
448+ {
449+ return internal->reschedule_for(timeout);
450+ }
451+
452+ struct Internal
453+ {
454+ Internal(Alarm* owner,
455+ std::shared_ptr<mir::LockableCallback> const& callback,
456+ std::shared_ptr<md::AlarmFactory::AlarmData> const& data)
457+ : owner{owner}, wrapped_callback{callback}, alarm_data{data}
458+ {
459+ }
460+ bool cancel();
461+ State state() const;
462+ bool reschedule_for(mt::Timestamp timeout);
463+ bool reschedule_in(std::chrono::milliseconds delay);
464+ void execute();
465+ void pending_locked();
466+ void cancelled_locked();
467+
468+ private:
469+ Alarm* const owner;
470+ std::shared_ptr<mir::LockableCallback> const wrapped_callback;
471+ std::shared_ptr<md::AlarmFactory::AlarmData> const alarm_data;
472+
473+ mutable std::mutex alarm_mutex;
474+ mir::optional_value<std::thread::id> executing_thread;
475+ std::condition_variable callback_done;
476+ AlarmState alarm_state{AlarmState::cancelled};
477+ };
478+
479+ std::shared_ptr<Internal> const internal;
480+};
481+
482+bool md::AlarmFactory::Alarm::Internal::reschedule_in(std::chrono::milliseconds delay)
483+{
484+ std::lock_guard<std::mutex> lock(alarm_mutex);
485+ bool previously_pending = alarm_state == AlarmState::pending;
486+ alarm_data->activate_timer(delay, owner);
487+ return previously_pending;
488+}
489+
490+bool md::AlarmFactory::Alarm::Internal::reschedule_for(mt::Timestamp timeout)
491+{
492+ std::lock_guard<std::mutex> lock(alarm_mutex);
493+ bool previously_pending = alarm_state == AlarmState::pending;
494+ alarm_data->activate_timer(timeout, owner);
495+ return previously_pending;
496+}
497+
498+bool md::AlarmFactory::Alarm::Internal::cancel()
499+{
500+ std::unique_lock<std::mutex> lock(alarm_mutex);
501+ switch(alarm_state)
502+ {
503+ case AlarmState::pending:
504+ alarm_data->deactivate_timer(owner);
505+ return true;
506+ case AlarmState::executing:
507+ if (executing_thread.value() != std::this_thread::get_id())
508+ callback_done.wait(lock);
509+ default:
510+ case AlarmState::cancelled:
511+ case AlarmState::triggered:
512+ return false;
513+ }
514+}
515+
516+mt::Alarm::State md::AlarmFactory::Alarm::Internal::state() const
517+{
518+ std::lock_guard<std::mutex> lock(alarm_mutex);
519+ switch(alarm_state)
520+ {
521+ case AlarmState::pending:
522+ return pending;
523+ case AlarmState::cancelled:
524+ return cancelled;
525+ case AlarmState::executing:
526+ case AlarmState::triggered:
527+ default:
528+ return triggered;
529+ }
530+}
531+
532+void md::AlarmFactory::Alarm::Internal::execute()
533+{
534+ std::lock_guard<LockableCallback> callback_lock{*wrapped_callback};
535+ std::unique_lock<std::mutex> alarm_lock(alarm_mutex);
536+ alarm_state = Alarm::AlarmState::executing;
537+ executing_thread = std::this_thread::get_id();
538+ alarm_lock.unlock();
539+
540+ (*wrapped_callback)();
541+
542+ alarm_lock.lock();
543+ if (alarm_state == Alarm::AlarmState::executing)
544+ alarm_state = Alarm::AlarmState::triggered;
545+
546+ executing_thread.consume();
547+ callback_done.notify_all();
548+ alarm_lock.unlock();
549+}
550+
551+void md::AlarmFactory::Alarm::Internal::cancelled_locked()
552+{
553+ alarm_state = Alarm::AlarmState::cancelled;
554+}
555+
556+void md::AlarmFactory::Alarm::Internal::pending_locked()
557+{
558+ alarm_state = Alarm::AlarmState::pending;
559+}
560+
561+md::AlarmFactory::AlarmData::AlarmData(mir::Fd fd,
562+ std::shared_ptr<time::Clock> const& clock,
563+ std::shared_ptr<time::TimerFd> const& fd_source)
564+ : timer_fd{fd}, clock{clock}, fd_source{fd_source}
565+{
566+}
567+
568+void md::AlarmFactory::AlarmData::handle_timer()
569+{
570+ auto now = clock->now();
571+ std::vector<Alarm*> expired_timers;
572+
573+ {
574+ std::lock_guard<std::mutex> lock(timer_queue_mutex);
575+ auto alarm_upper_bound = timer_queue.upper_bound(now);
576+ std::transform(
577+ timer_queue.begin(),
578+ alarm_upper_bound,
579+ std::inserter(expired_timers, expired_timers.begin()),
580+ [](auto const& entry)
581+ {
582+ return entry.second;
583+ });
584+ timer_queue.erase(timer_queue.begin(), alarm_upper_bound);
585+ }
586+
587+ for (auto const& timer : expired_timers)
588+ {
589+ std::lock_guard<std::mutex> alarm_data_lock(cancelled_timers_mutex);
590+ if (cancelled_timers.find(timer) != cancelled_timers.end())
591+ continue;
592+
593+ auto timer_data = timer->internal;
594+ timer_data->execute();
595+ }
596+
597+ clear_cancelled_timers();
598+
599+ {
600+ std::lock_guard<std::mutex> lock(timer_queue_mutex);
601+ if (timer_queue.empty())
602+ fd_source->cancel(timer_fd);
603+ else fd_source->schedule_for(timer_fd, timer_queue.begin()->first);
604+ }
605+}
606+
607+void md::AlarmFactory::AlarmData::activate_timer(std::chrono::milliseconds delay, md::AlarmFactory::Alarm* alarm)
608+{
609+ activate_timer(clock->now() + delay, alarm);
610+}
611+
612+void md::AlarmFactory::AlarmData::activate_timer(mt::Timestamp timeout, md::AlarmFactory::Alarm* alarm)
613+{
614+ bool optional_previous_timeout = false;
615+ mt::Timestamp previous_timeout;
616+ std::unique_lock<std::mutex> lock(timer_queue_mutex);
617+
618+ if (!timer_queue.empty())
619+ {
620+ previous_timeout = timer_queue.begin()->first;
621+ optional_previous_timeout = true;
622+
623+ erase_alarm_locked(alarm);
624+ }
625+
626+ timer_queue.insert(std::make_pair(timeout, alarm));
627+
628+ if ((optional_previous_timeout && timeout < previous_timeout) || !optional_previous_timeout)
629+ fd_source->schedule_for(timer_fd, timeout);
630+
631+ alarm->internal->pending_locked();
632+}
633+
634+void md::AlarmFactory::AlarmData::deactivate_timer(md::AlarmFactory::Alarm* alarm)
635+{
636+ bool removed = false;
637+ mt::Timestamp previous_timeout;
638+
639+ {
640+ std::unique_lock<std::mutex> lock(timer_queue_mutex);
641+ if (!timer_queue.empty())
642+ {
643+ previous_timeout = timer_queue.begin()->first;
644+
645+ erase_alarm_locked(alarm);
646+
647+ removed = true;
648+ alarm->internal->cancelled_locked();
649+
650+ if (timer_queue.empty())
651+ fd_source->cancel(timer_fd);
652+ else if (previous_timeout > timer_queue.begin()->first)
653+ fd_source->schedule_for(timer_fd, timer_queue.begin()->first);
654+ }
655+ }
656+
657+ if (!removed)
658+ {
659+ std::unique_lock<std::mutex> lock(cancelled_timers_mutex);
660+ cancelled_timers.insert(alarm);
661+ alarm->internal->cancelled_locked();
662+ }
663+}
664+
665+md::AlarmFactory::AlarmFactory(std::shared_ptr<mt::Clock> const& clock, std::shared_ptr<mt::TimerFd> const& fd_source)
666+ : timer_fd{fd_source->create_timer()},
667+ data{std::make_shared<AlarmData>(watch_fd(), clock, fd_source)}
668+{
669+}
670+
671+std::unique_ptr<mt::Alarm> md::AlarmFactory::create_alarm(std::function<void()> const& callback)
672+{
673+ return std::make_unique<Alarm>(std::make_shared<Callback>(callback), data);
674+}
675+
676+std::unique_ptr<mt::Alarm> md::AlarmFactory::create_alarm(std::shared_ptr<LockableCallback> const& callback)
677+{
678+ return std::make_unique<Alarm>(callback, data);
679+}
680+
681+void md::AlarmFactory::AlarmData::erase_alarm_locked(Alarm* alarm)
682+{
683+ auto it = find_if(
684+ begin(timer_queue),
685+ end(timer_queue),
686+ [&](auto const& item)
687+ {
688+ return item.second == alarm;
689+ });
690+
691+ if (it != timer_queue.end())
692+ timer_queue.erase(it);
693+}
694+
695+void md::AlarmFactory::AlarmData::clear_cancelled_timers()
696+{
697+ std::lock_guard<std::mutex> lock(cancelled_timers_mutex);
698+ cancelled_timers.clear();
699+}
700+
701+mir::Fd md::AlarmFactory::watch_fd() const
702+{
703+ return timer_fd;
704+}
705+
706+bool md::AlarmFactory::dispatch(FdEvents events)
707+{
708+ if (events & FdEvent::error)
709+ return false;
710+
711+ if (events & FdEvent::readable)
712+ {
713+ uint64_t dummy;
714+ read(watch_fd(), &dummy, sizeof(dummy));
715+ data->handle_timer();
716+ }
717+
718+ return true;
719+}
720+
721+md::FdEvents md::AlarmFactory::relevant_events() const
722+{
723+ return FdEvent::readable;
724+}
725
726=== modified file 'src/common/symbols.map'
727--- src/common/symbols.map 2016-03-29 23:37:32 +0000
728+++ src/common/symbols.map 2016-03-30 14:25:37 +0000
729@@ -219,22 +219,32 @@
730 };
731 } MIR_COMMON_5v19; # <- Note Mir 0.19.0 used the wrong syntax
732
733-MIR_COMMON_unreleased {
734+MIR_COMMON_unreleased { # New functions in Mir 0.22.0?
735 global:
736 extern "C++" {
737- MirEvent::to_surface*;
738- MirEvent::to_resize*;
739- MirEvent::to_orientation*;
740- MirEvent::to_close_surface*;
741- MirEvent::to_keymap*;
742- MirEvent::to_input*;
743- MirEvent::to_prompt_session*;
744- MirEvent::serialize*;
745- MirEvent::deserialize*;
746- MirEvent::clone*;
747- MirInputEvent::to_keyboard*;
748- MirInputEvent::to_motion*;
749- MirMotionEvent::to_touch*;
750- MirMotionEvent::to_pointer*;
751+ typeinfo?for?mir::time::SteadyTimerFd;
752+ typeinfo?for?mir::dispatch::AlarmFactory;
753+ vtable?for?mir::time::SteadyTimerFd;
754+ vtable?for?mir::dispatch::AlarmFactory;
755+ mir::time::SteadyTimerFd::SteadyTimerFd*;
756+ mir::time::SteadyTimerFd::create_timer*;
757+ mir::time::SteadyTimerFd::schedue_for*;
758+ mir::time::SteadyTimerFd::cancel*;
759+ mir::dispatch::AlarmFactory::AlarmFactory*;
760+ mir::dispatch::AlarmFactory::create_alarm*;
761+ MirEvent::to_surface*;
762+ MirEvent::to_resize*;
763+ MirEvent::to_orientation*;
764+ MirEvent::to_close_surface*;
765+ MirEvent::to_keymap*;
766+ MirEvent::to_input*;
767+ MirEvent::to_prompt_session*;
768+ MirEvent::serialize*;
769+ MirEvent::deserialize*;
770+ MirEvent::clone*;
771+ MirInputEvent::to_keyboard*;
772+ MirInputEvent::to_motion*;
773+ MirMotionEvent::to_touch*;
774+ MirMotionEvent::to_pointer*;
775 };
776 } MIR_COMMON_0.19.1;
777
778=== modified file 'src/common/time/CMakeLists.txt'
779--- src/common/time/CMakeLists.txt 2015-02-22 07:46:25 +0000
780+++ src/common/time/CMakeLists.txt 2016-03-30 14:25:37 +0000
781@@ -2,4 +2,5 @@
782 mirtime OBJECT
783
784 steady_clock.cpp
785+ steady_timer_fd.cpp
786 )
787
788=== added file 'src/common/time/steady_timer_fd.cpp'
789--- src/common/time/steady_timer_fd.cpp 1970-01-01 00:00:00 +0000
790+++ src/common/time/steady_timer_fd.cpp 2016-03-30 14:25:37 +0000
791@@ -0,0 +1,51 @@
792+/*
793+ * Copyright © 2016 Canonical Ltd.
794+ *
795+ * This program is free software: you can redistribute it and/or modify it
796+ * under the terms of the GNU Lesser General Public License version 3,
797+ * as published by the Free Software Foundation.
798+ *
799+ * This program is distributed in the hope that it will be useful,
800+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
801+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
802+ * GNU Lesser General Public License for more details.
803+ *
804+ * You should have received a copy of the GNU Lesser General Public License
805+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
806+ *
807+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
808+ */
809+
810+#include "mir/time/steady_timer_fd.h"
811+#include <sys/timerfd.h>
812+#include <chrono>
813+
814+namespace mt = mir::time;
815+
816+mt::SteadyTimerFd::SteadyTimerFd() = default;
817+
818+mir::Fd mt::SteadyTimerFd::create_timer()
819+{
820+ return mir::Fd{timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)};
821+}
822+
823+void mt::SteadyTimerFd::schedule_for(mir::Fd& fd, mir::time::Timestamp timeout)
824+{
825+ using namespace std::chrono;
826+ auto const in_seconds = duration_cast<seconds>(timeout.time_since_epoch());
827+ itimerspec delay_spec;
828+
829+ delay_spec.it_value.tv_sec = in_seconds.count();
830+ delay_spec.it_value.tv_nsec = nanoseconds(timeout.time_since_epoch() - in_seconds).count();
831+ delay_spec.it_interval.tv_sec = 0;
832+ delay_spec.it_interval.tv_nsec = 0;
833+
834+ const int absolute_timeout = TFD_TIMER_ABSTIME;
835+ timerfd_settime(fd, absolute_timeout, &delay_spec, nullptr);
836+}
837+
838+void mt::SteadyTimerFd::cancel(mir::Fd& fd)
839+{
840+ const itimerspec cancel_timer = {{0,0},{0,0}};
841+ timerfd_settime(fd, 0, &cancel_timer, nullptr);
842+}
843
844=== modified file 'tests/unit-tests/dispatch/CMakeLists.txt'
845--- tests/unit-tests/dispatch/CMakeLists.txt 2016-01-29 08:18:22 +0000
846+++ tests/unit-tests/dispatch/CMakeLists.txt 2016-03-30 14:25:37 +0000
847@@ -1,6 +1,7 @@
848 list(APPEND UNIT_TEST_SOURCES
849 ${CMAKE_CURRENT_SOURCE_DIR}/test_action_queue.cpp
850 ${CMAKE_CURRENT_SOURCE_DIR}/test_dispatch_utils.cpp
851+ ${CMAKE_CURRENT_SOURCE_DIR}/test_dispatchable_alarm_factory.cpp
852 ${CMAKE_CURRENT_SOURCE_DIR}/test_multiplexing_dispatchable.cpp
853 ${CMAKE_CURRENT_SOURCE_DIR}/test_readable_fd.cpp
854 ${CMAKE_CURRENT_SOURCE_DIR}/test_threaded_dispatcher.cpp
855
856=== added file 'tests/unit-tests/dispatch/test_dispatchable_alarm_factory.cpp'
857--- tests/unit-tests/dispatch/test_dispatchable_alarm_factory.cpp 1970-01-01 00:00:00 +0000
858+++ tests/unit-tests/dispatch/test_dispatchable_alarm_factory.cpp 2016-03-30 14:25:37 +0000
859@@ -0,0 +1,438 @@
860+/*
861+ * Copyright © 2016 Canonical Ltd.
862+ *
863+ * This program is free software: you can redistribute it and/or modify
864+ * it under the terms of the GNU General Public License version 3 as
865+ * published by the Free Software Foundation.
866+ *
867+ * This program is distributed in the hope that it will be useful,
868+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
869+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
870+ * GNU General Public License for more details.
871+ *
872+ * You should have received a copy of the GNU General Public License
873+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
874+ *
875+ * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
876+ */
877+
878+#include "mir/dispatch/alarm_factory.h"
879+#include "mir/time/timer_fd.h"
880+#include "mir/time/clock.h"
881+
882+#include "mir/test/fake_shared.h"
883+#include "mir/test/signal.h"
884+#include "mir/test/barrier.h"
885+#include "mir/test/auto_unblock_thread.h"
886+#include "mir/test/doubles/advanceable_clock.h"
887+#include "mir/test/doubles/mock_lockable_callback.h"
888+#include <gtest/gtest.h>
889+#include <gmock/gmock.h>
890+
891+using namespace testing;
892+using namespace std::chrono_literals;
893+
894+namespace md = mir::dispatch;
895+namespace mt = mir::test;
896+namespace mtd = mt::doubles;
897+
898+namespace
899+{
900+
901+struct MockTimerFd : mir::time::TimerFd
902+{
903+
904+ mir::Fd timer_fd{mir::IntOwnedFd{3}};
905+ MockTimerFd()
906+ {
907+ ON_CALL(*this, create_timer()).WillByDefault(Return(timer_fd));
908+ }
909+ MOCK_METHOD0(create_timer, mir::Fd());
910+ MOCK_METHOD2(schedule_for, void(mir::Fd&, mir::time::Timestamp));
911+ MOCK_METHOD1(cancel, void(mir::Fd&));
912+};
913+
914+struct DispatchableAlarmFactory : public Test
915+{
916+ std::shared_ptr<NiceMock<MockTimerFd>> mock_timer_fd = std::make_shared<NiceMock<MockTimerFd>>();
917+ std::shared_ptr<mtd::AdvanceableClock> fake_clock = std::make_shared<mtd::AdvanceableClock>();
918+ md::AlarmFactory fac{fake_clock, mock_timer_fd};
919+};
920+
921+}
922+
923+TEST_F(DispatchableAlarmFactory, alarm_starts_in_cancelled_state)
924+{
925+ auto alarm = fac.create_alarm([](){});
926+ EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::cancelled));
927+}
928+
929+TEST_F(DispatchableAlarmFactory, yields_timer_fd)
930+{
931+ auto alarm = fac.create_alarm([](){});
932+ EXPECT_THAT(fac.watch_fd(), Eq(mock_timer_fd->timer_fd));
933+}
934+
935+TEST_F(DispatchableAlarmFactory, alarm_scheduling_updates_timer_fd)
936+{
937+ auto alarm = fac.create_alarm([]{});
938+
939+ const auto scheduled_time = fake_clock->now() + 2s;
940+
941+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, scheduled_time));
942+
943+ alarm->reschedule_for(scheduled_time);
944+}
945+
946+TEST_F(DispatchableAlarmFactory, timeout_after_scheduled_alarm_leaves_timer_fd_untouched)
947+{
948+ auto alarm = fac.create_alarm([](){});
949+ auto second_alarm = fac.create_alarm([&](){});
950+
951+ const auto scheduled_time = fake_clock->now() + 2s;
952+ const auto time_after_alarm = scheduled_time + 1s;
953+
954+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, scheduled_time)).Times(1);
955+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, time_after_alarm)).Times(0);
956+
957+ alarm->reschedule_for(scheduled_time);
958+ second_alarm->reschedule_for(time_after_alarm);
959+}
960+
961+TEST_F(DispatchableAlarmFactory, timeout_before_scheduled_alarm_updates_timer_fd)
962+{
963+ auto alarm = fac.create_alarm([&](){});
964+ auto second_alarm = fac.create_alarm([&](){});
965+
966+ const mir::time::Timestamp scheduled_time = fake_clock->now() + 4s;
967+ const mir::time::Timestamp time_before_alarm = scheduled_time - 2s;
968+
969+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, scheduled_time)).Times(1);
970+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, time_before_alarm)).Times(1);
971+
972+ alarm->reschedule_for(scheduled_time);
973+ second_alarm->reschedule_for(time_before_alarm);
974+}
975+
976+TEST_F(DispatchableAlarmFactory, reschedule_of_first_alarm_updates_timer_fd)
977+{
978+ auto alarm = fac.create_alarm([&](){});
979+ auto second_alarm = fac.create_alarm([&](){});
980+
981+ const auto scheduled_time = fake_clock->now() + 4s;
982+ const auto time_before_alarm = scheduled_time - 2s;
983+
984+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, scheduled_time)).Times(1);
985+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, time_before_alarm)).Times(1);
986+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, time_before_alarm - 1s)).Times(1);
987+
988+ alarm->reschedule_for(scheduled_time);
989+ second_alarm->reschedule_for(time_before_alarm);
990+ alarm->reschedule_for(time_before_alarm - 1s);
991+}
992+
993+TEST_F(DispatchableAlarmFactory, cancelling_sole_alarm_cancels_timer_fd)
994+{
995+ auto alarm = fac.create_alarm([&](){});
996+
997+ const auto sometime_later = fake_clock->now() + 5s;
998+ alarm->reschedule_for(sometime_later);
999+
1000+ EXPECT_CALL(*mock_timer_fd, cancel(_)).Times(1);
1001+ alarm->cancel();
1002+ Mock::VerifyAndClearExpectations(mock_timer_fd.get());
1003+}
1004+
1005+TEST_F(DispatchableAlarmFactory, cancelling_last_alarm_cancels_timer_fd)
1006+{
1007+ auto alarm = fac.create_alarm([&](){});
1008+ auto second_alarm = fac.create_alarm([&](){});
1009+
1010+ const auto sometime_later = fake_clock->now() + 5s;
1011+
1012+ EXPECT_CALL(*mock_timer_fd, schedule_for(_, sometime_later)).Times(1);
1013+ EXPECT_CALL(*mock_timer_fd, cancel(_)).Times(1);
1014+
1015+ alarm->reschedule_for(sometime_later);
1016+ second_alarm->reschedule_for(sometime_later);
1017+ alarm->cancel();
1018+ second_alarm->cancel();
1019+
1020+ Mock::VerifyAndClearExpectations(mock_timer_fd.get());
1021+}
1022+
1023+TEST_F(DispatchableAlarmFactory, alarm_fires_with_correct_delay)
1024+{
1025+ int times_called = 0;
1026+ auto alarm = fac.create_alarm([&](){++times_called;});
1027+
1028+ const auto sometime_later = fake_clock->now() + 5s;
1029+ alarm->reschedule_for(sometime_later);
1030+
1031+ EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::pending));
1032+
1033+ fake_clock->advance_by(5s);
1034+ fac.dispatch(md::FdEvent::readable);
1035+
1036+ EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::triggered));
1037+ EXPECT_THAT(times_called, Eq(1));
1038+}
1039+
1040+TEST_F(DispatchableAlarmFactory, no_alarm_fires_before_timepoint)
1041+{
1042+ int times_called = 0;
1043+ auto alarm = fac.create_alarm([&](){++times_called;});
1044+
1045+ const auto sometime_later = fake_clock->now() + 3s;
1046+ alarm->reschedule_for(sometime_later);
1047+ EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::pending));
1048+ fake_clock->advance_by(2s);
1049+ fac.dispatch(md::FdEvent::readable);
1050+ EXPECT_THAT(times_called, Eq(0));
1051+}
1052+
1053+TEST_F(DispatchableAlarmFactory, cancelled_alarms_do_not_fire)
1054+{
1055+ int times_called = 0;
1056+ auto alarm = fac.create_alarm([&](){++times_called;});
1057+
1058+ const auto sometime_later = fake_clock->now() + 2s;
1059+ alarm->reschedule_for(sometime_later);
1060+ alarm->cancel();
1061+ fake_clock->advance_by(5s);
1062+ fac.dispatch(md::FdEvent::readable);
1063+ EXPECT_THAT(times_called, Eq(0));
1064+}
1065+
1066+TEST_F(DispatchableAlarmFactory, alarm_falling_out_scope_cancels)
1067+{
1068+ int times_called = 0;
1069+
1070+ {
1071+ auto alarm = fac.create_alarm([&](){++times_called;});
1072+ const auto sometime_later = fake_clock->now() + 2s;
1073+ alarm->reschedule_for(sometime_later);
1074+ }
1075+
1076+ fake_clock->advance_by(5s);
1077+ fac.dispatch(md::FdEvent::readable);
1078+ EXPECT_THAT(times_called, Eq(0));
1079+}
1080+
1081+
1082+TEST_F(DispatchableAlarmFactory, alarms_can_be_rescheduled_after_firing)
1083+{
1084+ int times_called = 0;
1085+ auto alarm = fac.create_alarm([&](){++times_called;});
1086+ const auto sometime_later = fake_clock->now() + 2s;
1087+ alarm->reschedule_for(sometime_later);
1088+ fake_clock->advance_by(2s);
1089+ fac.dispatch(md::FdEvent::readable);
1090+
1091+ const auto even_later = fake_clock->now() + 4s;
1092+ alarm->reschedule_for(even_later);
1093+ fake_clock->advance_by(4s);
1094+ fac.dispatch(md::FdEvent::readable);
1095+ EXPECT_THAT(times_called, Eq(2));
1096+}
1097+
1098+TEST_F(DispatchableAlarmFactory, alarms_can_be_rescheduled_after_cancelling)
1099+{
1100+ int times_called = 0;
1101+ auto alarm = fac.create_alarm([&](){++times_called;});
1102+ const auto sometime_later = fake_clock->now() + 2s;
1103+ alarm->reschedule_for(sometime_later);
1104+ alarm->cancel();
1105+ fake_clock->advance_by(2s);
1106+ fac.dispatch(md::FdEvent::readable);
1107+
1108+ const auto even_later = fake_clock->now() + 4s;
1109+ alarm->reschedule_for(even_later);
1110+ fake_clock->advance_by(4s);
1111+ fac.dispatch(md::FdEvent::readable);
1112+ EXPECT_THAT(times_called, Eq(1));
1113+}
1114+
1115+
1116+TEST_F(DispatchableAlarmFactory, can_reschedule_alarm_from_within_alarm_callback)
1117+{
1118+ int num_triggers = 0;
1119+ int const expected_triggers = 3;
1120+
1121+ std::shared_ptr<mir::time::Alarm> alarm = fac.create_alarm(
1122+ [&]
1123+ {
1124+ if (++num_triggers != expected_triggers)
1125+ alarm->reschedule_in(0ms);
1126+ });
1127+
1128+ alarm->reschedule_in(0ms);
1129+
1130+ fac.dispatch(md::FdEvent::readable);
1131+ fac.dispatch(md::FdEvent::readable);
1132+ fac.dispatch(md::FdEvent::readable);
1133+
1134+ EXPECT_THAT(num_triggers, Eq(expected_triggers));
1135+}
1136+
1137+TEST_F(DispatchableAlarmFactory, alarm_callback_preserves_lock_ordering)
1138+{
1139+ mtd::MockLockableCallback handler;
1140+ {
1141+ InSequence s;
1142+ EXPECT_CALL(handler, lock());
1143+ EXPECT_CALL(handler, functor());
1144+ EXPECT_CALL(handler, unlock());
1145+ }
1146+
1147+ auto alarm = fac.create_alarm(mt::fake_shared(handler));
1148+
1149+ alarm->reschedule_in(10ms);
1150+ fake_clock->advance_by(11ms);
1151+ fac.dispatch(md::FdEvent::readable);
1152+}
1153+
1154+TEST_F(DispatchableAlarmFactory, rescheduling_alarm_from_within_alarm_callback_doesnt_deadlock_with_external_reschedule)
1155+{
1156+ mt::Signal in_alarm;
1157+ mt::Signal alarm_rescheduled;
1158+
1159+ std::shared_ptr<mir::time::Alarm> alarm = fac.create_alarm(
1160+ [&]
1161+ {
1162+ // Ensure that the external thread reschedules us while we're
1163+ // in the callback.
1164+ in_alarm.raise();
1165+ ASSERT_TRUE(alarm_rescheduled.wait_for(5s));
1166+
1167+ alarm->reschedule_in(0ms);
1168+ });
1169+
1170+ alarm->reschedule_in(0ms);
1171+
1172+ mt::AutoJoinThread rescheduler{
1173+ [alarm, &in_alarm, &alarm_rescheduled]()
1174+ {
1175+ ASSERT_TRUE(in_alarm.wait_for(5s));
1176+ alarm->reschedule_in(0ms);
1177+ alarm_rescheduled.raise();
1178+ }};
1179+
1180+ fac.dispatch(md::FdEvent::readable);
1181+}
1182+
1183+TEST_F(DispatchableAlarmFactory, cancel_blocks_until_definitely_cancelled)
1184+{
1185+ auto waiting_in_lock = std::make_shared<mt::Barrier>(2);
1186+ auto has_been_called = std::make_shared<mt::Signal>();
1187+
1188+ std::shared_ptr<mir::time::Alarm> alarm = fac.create_alarm(
1189+ [waiting_in_lock, has_been_called]()
1190+ {
1191+ waiting_in_lock->ready();
1192+ std::this_thread::sleep_for(500ms);
1193+ has_been_called->raise();
1194+ });
1195+
1196+ alarm->reschedule_in(0ms);
1197+
1198+ mt::AutoJoinThread canceller{
1199+ [waiting_in_lock, has_been_called, alarm, this]()
1200+ {
1201+ waiting_in_lock->ready();
1202+ alarm->cancel();
1203+ EXPECT_TRUE(has_been_called->raised());
1204+ }
1205+ };
1206+
1207+ fac.dispatch(md::FdEvent::readable);
1208+}
1209+
1210+TEST_F(DispatchableAlarmFactory, can_cancel_from_callback)
1211+{
1212+ mir::time::Alarm* raw_alarm;
1213+ auto cancel_didnt_deadlock = std::make_shared<mt::Signal>();
1214+ auto alarm = fac.create_alarm(
1215+ [&raw_alarm, cancel_didnt_deadlock]()
1216+ {
1217+ raw_alarm->cancel();
1218+ cancel_didnt_deadlock->raise();
1219+ });
1220+
1221+ raw_alarm = alarm.get();
1222+
1223+ alarm->reschedule_in(0ms);
1224+ mt::AutoJoinThread(
1225+ [&]()
1226+ {
1227+ fac.dispatch(md::FdEvent::readable);
1228+ });
1229+
1230+ EXPECT_TRUE(cancel_didnt_deadlock->wait_for(10s));
1231+ if (!cancel_didnt_deadlock->raised())
1232+ {
1233+ // Deadlocking is no fun. There's nothing we can sensibly do,
1234+ // so die rather than wait for the build to timeout.
1235+ std::terminate();
1236+ }
1237+}
1238+
1239+TEST_F(DispatchableAlarmFactory, can_destroy_alarm_from_callback)
1240+{
1241+ mir::time::Alarm* raw_alarm;
1242+ auto cancel_didnt_deadlock = std::make_shared<mt::Signal>();
1243+ auto alarm = fac.create_alarm(
1244+ [&raw_alarm, cancel_didnt_deadlock]()
1245+ {
1246+ delete raw_alarm;
1247+ cancel_didnt_deadlock->raise();
1248+ });
1249+
1250+ alarm->reschedule_in(0ms);
1251+ raw_alarm = alarm.release();
1252+
1253+ mt::AutoJoinThread(
1254+ [&]()
1255+ {
1256+ fac.dispatch(md::FdEvent::readable);
1257+ });
1258+
1259+ EXPECT_TRUE(cancel_didnt_deadlock->wait_for(10s));
1260+ if (!cancel_didnt_deadlock->raised())
1261+ {
1262+ // Deadlocking is no fun. There's nothing we can sensibly do,
1263+ // so die rather than wait for the build to timeout.
1264+ std::terminate();
1265+ }
1266+}
1267+
1268+TEST_F(DispatchableAlarmFactory, cancelling_a_triggered_alarm_has_no_effect)
1269+{
1270+ auto alarm = fac.create_alarm([](){});
1271+
1272+ alarm->reschedule_in(0ms);
1273+
1274+ fac.dispatch(md::FdEvent::readable);
1275+
1276+ EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::State::triggered));
1277+ EXPECT_FALSE(alarm->cancel());
1278+ EXPECT_THAT(alarm->state(), Eq(mir::time::Alarm::State::triggered));
1279+}
1280+
1281+TEST_F(DispatchableAlarmFactory, reschedule_returns_true_when_it_resets_a_previous_schedule)
1282+{
1283+ auto alarm = fac.create_alarm([](){});
1284+
1285+ alarm->reschedule_in(10min);
1286+ EXPECT_TRUE(alarm->reschedule_in(5s));
1287+}
1288+
1289+TEST_F(DispatchableAlarmFactory, reschedule_returns_false_when_it_didnt_reset_a_previous_schedule)
1290+{
1291+ auto alarm = fac.create_alarm([](){});
1292+
1293+ alarm->reschedule_in(1min);
1294+ fake_clock->advance_by(2min);
1295+ fac.dispatch(md::FdEvent::readable);
1296+ EXPECT_FALSE(alarm->reschedule_in(10s));
1297+}
1298
1299=== modified file 'tests/unit-tests/test_glib_main_loop.cpp'
1300--- tests/unit-tests/test_glib_main_loop.cpp 2016-01-29 08:18:22 +0000
1301+++ tests/unit-tests/test_glib_main_loop.cpp 2016-03-30 14:25:37 +0000
1302@@ -928,7 +928,7 @@
1303 EXPECT_FALSE(timer_fired->wait_for(std::chrono::milliseconds{10}));
1304 }
1305
1306-TEST_F(GLibMainLoopAlarmTest, alarm_starts_in_pending_state)
1307+TEST_F(GLibMainLoopAlarmTest, alarm_starts_in_cancelled_state)
1308 {
1309 auto alarm = ml.create_alarm([]{});
1310

Subscribers

People subscribed via source and target branches