Merge lp:~andreas-pokorny/mir/add-dispatchable-alarm-factory into lp:mir
- add-dispatchable-alarm-factory
- Merge into development-branch
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 |
Related bugs: |
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:
Implementation of mir::time:
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 MultiplexingDis
Mir CI Bot (mir-ci-bot) wrote : | # |
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3415
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
ABORTED: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3415
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
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.
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
Mir CI Bot (mir-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:3417
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
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 DispatchableAla
Why not use composition?
class AlarmFactory : public time::AlarmFactory
{
...
ReadableFd fd;
};
auto AlarmFactory:
-> bool { return fd.dispatch(
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
> DispatchableAla
> 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:
> -> bool { return fd.dispatch(
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_
class AlarmFactory : public time::AlarmFactory, public dispatch:
{
...
std:
};
Andreas Pokorny (andreas-pokorny) wrote : | # |
oops I meant:
class AlarmFactory : public time::AlarmFactory, public dispatch:
{
...
ReadableFd const timer_fd;
};
Alan Griffiths (alan-griffiths) wrote : | # |
> oops I meant:
> class AlarmFactory : public time::AlarmFactory, public dispatch:
> {
> ...
> ReadableFd const timer_fd;
> };
Hmm. If that's the intention I prefer:
class AlarmFactory : public time::AlarmFactory, public dispatch:
private ReadableFd
{
...
};
But for that dispatch:
- 3418. By Andreas Pokorny
-
Remove ReadableFd
Andreas Pokorny (andreas-pokorny) wrote : | # |
> > oops I meant:
> > class AlarmFactory : public time::AlarmFactory, public
> dispatch:
> > {
> > ...
> > ReadableFd const timer_fd;
> > };
>
> Hmm. If that's the intention I prefer:
>
> class AlarmFactory : public time::AlarmFactory, public dispatch:
> private ReadableFd
> {
> ...
> };
>
> But for that dispatch:
> 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
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:
The downside is that tests of dispatchable:
Did you try this in an earlier iteration and it turned out to be difficult?
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:
> it to an internal MultiplexingDis
> Dispatchable) or took a reference to a MultiplexingDis
> constructor and stopped implementing Dispatchable.
>
> The downside is that tests of dispatchable:
> 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 MultiplexingDis
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 MultiplexingDis
I'm OK with it as it is, but still have the gut feeling that it could be significantly simpler.
Chris Halse Rogers (raof) wrote : | # |
Bah, I'm wavering. Maybe convince me a bit more that this isn't needlessly complex :)
Mir CI Bot (mir-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:3420
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Chris Halse Rogers (raof) wrote : | # |
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:
AlarmFactor
std:
std:
Fd watch_fd() const override;
bool dispatch(FdEvents events) override;
FdEvents relevant_events() const override;
private:
std:
Multiplexin
};
class Alarm : public mt::Alarm
{
public:
Alarm(
: timer{std:
{
}}
{
auto internal_callback = [this, weak_cb]()
{
if (auto cb = weak_cb.lock())
{
}
}
}
~Alarm()
{
{
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.
}
}
cancel();
// We need to be triggered once more to remove us from the MultiplexingDis
// Might be better to take shared ownership of the MultiplexingDis
// manually remove_watch() us. Would let us not have to wait for a dispatch() iteration.
if (!canary.expired())
{
}
}
bool cancel() override
{
...
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
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 |
FAILED: Continuous integration, rev:3415 /mir-jenkins. ubuntu. com/job/ mir-ci/ 643/ /mir-jenkins. ubuntu. com/job/ build-mir/ 579/console /mir-jenkins. ubuntu. com/job/ build-0- fetch/615 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 607 /mir-jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial/ 607 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= vivid+overlay/ 589 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= clang,platform= mesa,release= vivid+overlay/ 589/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 589 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= amd64,compiler= gcc,platform= mesa,release= xenial/ 589/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= cross-armhf, compiler= gcc,platform= android, release= vivid+overlay/ 589/console /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 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 589 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= android, release= vivid+overlay/ 589/artifact/ output/ *zip*/output. zip /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 589 /mir-jenkins. ubuntu. com/job/ build-2- binpkg- mir/arch= i386,compiler= gcc,platform= mesa,release= xenial/ 589/artifact/ output/ *zip*/output. zip
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild: /mir-jenkins. ubuntu. com/job/ mir-ci/ 643/rebuild
https:/