~andreas-pokorny/mir/activate-pointer-acceleration

« back to all changes in this revision

Viewing changes to tests/unit-tests/dispatch/test_multiplexing_dispatchable.cpp

  • Committer: Tarmac
  • Author(s): Christopher James Halse Rogers, Christopher James Halse Rogers
  • Date: 2015-02-18 04:25:26 UTC
  • mfrom: (2239.7.33 add-multiplexing-dispatchable)
  • Revision ID: tarmac-20150218042526-j1ei9ni2k4tua4e9
Add MultiplexingDispatchable.

This does exactly what it says; multiplexes multiple Dispatchables into a single Dispatchable. Each dispatch() of the MultiplexingDispatchable dispatches to a child Dispatchable with active events.

Approved by Alexandros Frantzis, PS Jenkins bot, Robert Carr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2015 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License version 3 as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
 
17
 */
 
18
 
 
19
#include "mir/dispatch/multiplexing_dispatchable.h"
 
20
#include "mir/dispatch/simple_dispatch_thread.h"
 
21
#include "mir/fd.h"
 
22
#include "mir_test/pipe.h"
 
23
#include "mir_test/signal.h"
 
24
#include "mir_test/fd_utils.h"
 
25
#include "mir_test/test_dispatchable.h"
 
26
#include "mir_test/auto_unblock_thread.h"
 
27
 
 
28
#include <fcntl.h>
 
29
 
 
30
#include <atomic>
 
31
#include <thread>
 
32
 
 
33
#include <gtest/gtest.h>
 
34
#include <gmock/gmock.h>
 
35
 
 
36
namespace md = mir::dispatch;
 
37
namespace mt = mir::test;
 
38
 
 
39
TEST(MultiplexingDispatchableTest, dispatches_dispatchee_when_appropriate)
 
40
{
 
41
    bool dispatched{false};
 
42
    auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched]() { dispatched = true; });
 
43
    md::MultiplexingDispatchable dispatcher{dispatchee};
 
44
 
 
45
    dispatchee->trigger();
 
46
 
 
47
    ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd()));
 
48
    dispatcher.dispatch(md::FdEvent::readable);
 
49
 
 
50
    EXPECT_TRUE(dispatched);
 
51
}
 
52
 
 
53
TEST(MultiplexingDispatchableTest, calls_correct_dispatchee_when_fd_becomes_readable)
 
54
{
 
55
    bool a_dispatched{false};
 
56
    auto dispatchee_a = std::make_shared<mt::TestDispatchable>([&a_dispatched]() { a_dispatched = true; });
 
57
 
 
58
    bool b_dispatched{false};
 
59
    auto dispatchee_b = std::make_shared<mt::TestDispatchable>([&b_dispatched]() { b_dispatched = true; });
 
60
 
 
61
    md::MultiplexingDispatchable dispatcher{dispatchee_a, dispatchee_b};
 
62
 
 
63
    dispatchee_a->trigger();
 
64
 
 
65
    ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd()));
 
66
    dispatcher.dispatch(md::FdEvent::readable);
 
67
 
 
68
    EXPECT_TRUE(a_dispatched);
 
69
    EXPECT_FALSE(b_dispatched);
 
70
 
 
71
    ASSERT_FALSE(mt::fd_is_readable(dispatcher.watch_fd()));
 
72
    a_dispatched = false;
 
73
 
 
74
    dispatchee_b->trigger();
 
75
 
 
76
    ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd()));
 
77
    dispatcher.dispatch(md::FdEvent::readable);
 
78
 
 
79
    EXPECT_FALSE(a_dispatched);
 
80
    EXPECT_TRUE(b_dispatched);
 
81
}
 
82
 
 
83
TEST(MultiplexingDispatchableTest, keeps_dispatching_until_fd_is_unreadable)
 
84
{
 
85
    bool dispatched{false};
 
86
    auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched]() { dispatched = true; });
 
87
    md::MultiplexingDispatchable dispatcher{dispatchee};
 
88
 
 
89
    int const trigger_count{10};
 
90
 
 
91
    for (int i = 0; i < trigger_count; ++i)
 
92
    {
 
93
        dispatchee->trigger();
 
94
    }
 
95
 
 
96
    for (int i = 0; i < trigger_count; ++i)
 
97
    {
 
98
        ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd()));
 
99
        dispatcher.dispatch(md::FdEvent::readable);
 
100
 
 
101
        EXPECT_TRUE(dispatched);
 
102
        dispatched = false;
 
103
    }
 
104
 
 
105
    EXPECT_FALSE(mt::fd_is_readable(dispatcher.watch_fd()));
 
106
}
 
107
 
 
108
TEST(MultiplexingDispatchableTest, dispatching_without_pending_event_is_harmless)
 
109
{
 
110
    bool dispatched{false};
 
111
    auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched]() { dispatched = true; });
 
112
    md::MultiplexingDispatchable dispatcher{dispatchee};
 
113
 
 
114
    dispatcher.dispatch(md::FdEvent::readable);
 
115
 
 
116
    EXPECT_FALSE(dispatched);
 
117
}
 
118
 
 
119
TEST(MultiplexingDispatchableTest, keeps_dispatchees_alive)
 
120
{
 
121
    bool dispatched{false};
 
122
    auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched]() { dispatched = true; });
 
123
    dispatchee->trigger();
 
124
 
 
125
    md::MultiplexingDispatchable dispatcher;
 
126
    dispatcher.add_watch(dispatchee);
 
127
    dispatchee.reset();
 
128
 
 
129
    ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd()));
 
130
    dispatcher.dispatch(md::FdEvent::readable);
 
131
 
 
132
    EXPECT_TRUE(dispatched);
 
133
}
 
134
 
 
135
TEST(MultiplexingDispatchableTest, removed_dispatchables_are_no_longer_dispatched)
 
136
{
 
137
    using namespace testing;
 
138
 
 
139
    mt::Pipe pipe;
 
140
 
 
141
    bool dispatched{false};
 
142
    auto dispatchable = std::make_shared<mt::TestDispatchable>([&dispatched]() { dispatched = true; });
 
143
 
 
144
    md::MultiplexingDispatchable dispatcher;
 
145
    dispatcher.add_watch(dispatchable);
 
146
    dispatcher.remove_watch(dispatchable);
 
147
 
 
148
    while (mt::fd_is_readable(dispatcher.watch_fd()))
 
149
    {
 
150
        dispatcher.dispatch(md::FdEvent::readable);
 
151
    }
 
152
 
 
153
    dispatchable->trigger();
 
154
 
 
155
    EXPECT_FALSE(mt::fd_is_readable(dispatcher.watch_fd()));
 
156
    dispatcher.dispatch(md::FdEvent::readable);
 
157
 
 
158
    EXPECT_FALSE(dispatched);
 
159
}
 
160
 
 
161
TEST(MultiplexingDispatchableTest, adding_same_fd_twice_is_an_error)
 
162
{
 
163
    using namespace testing;
 
164
 
 
165
    auto dispatchable = std::make_shared<mt::TestDispatchable>([](){});
 
166
 
 
167
    md::MultiplexingDispatchable dispatcher;
 
168
    dispatcher.add_watch(dispatchable);
 
169
 
 
170
    EXPECT_THROW(dispatcher.add_watch(dispatchable),
 
171
                 std::logic_error);
 
172
}
 
173
 
 
174
TEST(MultiplexingDispatchableTest, dispatcher_does_not_hold_reference_after_failing_to_add_dispatchee)
 
175
{
 
176
    using namespace testing;
 
177
 
 
178
    auto dispatchable = std::make_shared<mt::TestDispatchable>([](){});
 
179
 
 
180
    md::MultiplexingDispatchable dispatcher;
 
181
    dispatcher.add_watch(dispatchable);
 
182
 
 
183
    auto const dispatchable_refcount = dispatchable.use_count();
 
184
 
 
185
    // This should not increase refcount
 
186
    EXPECT_THROW(dispatcher.add_watch(dispatchable),
 
187
                 std::logic_error);
 
188
    EXPECT_THAT(dispatchable.use_count(), Eq(dispatchable_refcount));
 
189
}
 
190
 
 
191
TEST(MultiplexingDispatchableTest, individual_dispatchee_is_not_concurrent)
 
192
{
 
193
    using namespace testing;
 
194
 
 
195
    auto second_dispatch = std::make_shared<mt::Signal>();
 
196
    auto dispatchee = std::make_shared<mt::TestDispatchable>([second_dispatch]()
 
197
    {
 
198
        static std::atomic<int> canary{0};
 
199
        static std::atomic<int> total_count{0};
 
200
 
 
201
        ++canary;
 
202
        EXPECT_THAT(canary, Eq(1));
 
203
        if (++total_count == 2)
 
204
        {
 
205
            second_dispatch->raise();
 
206
        }
 
207
        else
 
208
        {
 
209
            std::this_thread::sleep_for(std::chrono::seconds{1});
 
210
        }
 
211
        --canary;
 
212
    });
 
213
 
 
214
    dispatchee->trigger();
 
215
    dispatchee->trigger();
 
216
 
 
217
    auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
 
218
    dispatcher->add_watch(dispatchee);
 
219
 
 
220
    md::SimpleDispatchThread first_loop{dispatcher};
 
221
    md::SimpleDispatchThread second_loop{dispatcher};
 
222
 
 
223
    EXPECT_TRUE(second_dispatch->wait_for(std::chrono::seconds{5}));
 
224
}
 
225
 
 
226
TEST(MultiplexingDispatchableTest, reentrant_dispatchee_is_dispatched_concurrently)
 
227
{
 
228
    using namespace testing;
 
229
 
 
230
    std::atomic<int> count{0};
 
231
 
 
232
    auto dispatchee = std::make_shared<mt::TestDispatchable>([&count]()
 
233
    {
 
234
        ++count;
 
235
        std::this_thread::sleep_for(std::chrono::seconds{1});
 
236
        EXPECT_THAT(count, Gt(1));
 
237
    });
 
238
 
 
239
    dispatchee->trigger();
 
240
    dispatchee->trigger();
 
241
 
 
242
    md::MultiplexingDispatchable dispatcher;
 
243
    dispatcher.add_watch(dispatchee, md::DispatchReentrancy::reentrant);
 
244
 
 
245
    std::thread first{[&dispatcher]() { dispatcher.dispatch(md::FdEvent::readable); }};
 
246
    std::thread second{[&dispatcher]() { dispatcher.dispatch(md::FdEvent::readable); }};
 
247
 
 
248
    first.join();
 
249
    second.join();
 
250
}
 
251
 
 
252
TEST(MultiplexingDispatchableTest, raw_callback_is_dispatched)
 
253
{
 
254
    using namespace testing;
 
255
 
 
256
    bool dispatched{false};
 
257
    auto dispatchee = [&dispatched]() { dispatched = true; };
 
258
    mt::Pipe fd_source;
 
259
 
 
260
    md::MultiplexingDispatchable dispatcher;
 
261
    dispatcher.add_watch(fd_source.read_fd(), dispatchee);
 
262
 
 
263
    char buffer{0};
 
264
    ASSERT_THAT(::write(fd_source.write_fd(), &buffer, sizeof(buffer)), Eq(sizeof(buffer)));
 
265
 
 
266
    EXPECT_TRUE(mt::fd_is_readable(dispatcher.watch_fd()));
 
267
    dispatcher.dispatch(md::FdEvent::readable);
 
268
 
 
269
    EXPECT_TRUE(dispatched);
 
270
}
 
271
 
 
272
TEST(MultiplexingDispatchableTest, raw_callback_can_be_removed)
 
273
{
 
274
    using namespace testing;
 
275
 
 
276
    bool dispatched{false};
 
277
    auto dispatchee = [&dispatched]() { dispatched = true; };
 
278
    mt::Pipe fd_source;
 
279
 
 
280
    md::MultiplexingDispatchable dispatcher;
 
281
    dispatcher.add_watch(fd_source.read_fd(), dispatchee);
 
282
    dispatcher.remove_watch(fd_source.read_fd());
 
283
 
 
284
    while (mt::fd_is_readable(dispatcher.watch_fd()))
 
285
    {
 
286
        dispatcher.dispatch(md::FdEvent::readable);
 
287
    }
 
288
 
 
289
    char buffer{0};
 
290
    ASSERT_THAT(::write(fd_source.write_fd(), &buffer, sizeof(buffer)), Eq(sizeof(buffer)));
 
291
 
 
292
    EXPECT_FALSE(mt::fd_is_readable(dispatcher.watch_fd()));
 
293
    dispatcher.dispatch(md::FdEvent::readable);
 
294
 
 
295
    EXPECT_FALSE(dispatched);
 
296
}
 
297
 
 
298
TEST(MultiplexingDispatchableTest, removal_is_threadsafe)
 
299
{
 
300
    using namespace testing;
 
301
 
 
302
    auto canary_killed = std::make_shared<mt::Signal>();
 
303
    auto canary = std::shared_ptr<int>(new int, [canary_killed](int* victim) { delete victim; canary_killed->raise(); });
 
304
    auto in_dispatch = std::make_shared<mt::Signal>();
 
305
 
 
306
    auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
 
307
 
 
308
    auto dispatchee = std::make_shared<mt::TestDispatchable>([canary, in_dispatch]()
 
309
    {
 
310
        in_dispatch->raise();
 
311
        std::this_thread::sleep_for(std::chrono::seconds{1});
 
312
        EXPECT_THAT(canary.use_count(), Gt(0));
 
313
    });
 
314
    dispatcher->add_watch(dispatchee);
 
315
 
 
316
    dispatchee->trigger();
 
317
 
 
318
    md::SimpleDispatchThread eventloop{dispatcher};
 
319
 
 
320
    EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));
 
321
 
 
322
    dispatcher->remove_watch(dispatchee);
 
323
    dispatchee.reset();
 
324
    canary.reset();
 
325
 
 
326
    EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));
 
327
}
 
328
 
 
329
TEST(MultiplexingDispatchableTest, destruction_is_threadsafe)
 
330
{
 
331
    using namespace testing;
 
332
 
 
333
    auto canary_killed = std::make_shared<mt::Signal>();
 
334
    auto canary = std::shared_ptr<int>(new int, [canary_killed](int* victim) { delete victim; canary_killed->raise(); });
 
335
    auto in_dispatch = std::make_shared<mt::Signal>();
 
336
 
 
337
    auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
 
338
 
 
339
    auto dispatchee = std::make_shared<mt::TestDispatchable>([canary, in_dispatch]()
 
340
    {
 
341
        in_dispatch->raise();
 
342
        std::this_thread::sleep_for(std::chrono::seconds{1});
 
343
        EXPECT_THAT(canary.use_count(), Gt(0));
 
344
    });
 
345
    dispatcher->add_watch(dispatchee);
 
346
 
 
347
    dispatchee->trigger();
 
348
 
 
349
    mt::AutoJoinThread dispatch_thread{[dispatcher]() { dispatcher->dispatch(md::FdEvent::readable); }};
 
350
 
 
351
    EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));
 
352
 
 
353
    dispatcher->remove_watch(dispatchee);
 
354
    dispatcher.reset();
 
355
    dispatchee.reset();
 
356
    canary.reset();
 
357
 
 
358
    EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));
 
359
}
 
360
 
 
361
TEST(MultiplexingDispatchableTest, stress_test_threading)
 
362
{
 
363
    using namespace testing;
 
364
 
 
365
    int const dispatchee_count{20};
 
366
 
 
367
    auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
 
368
 
 
369
    std::vector<std::shared_ptr<md::SimpleDispatchThread>> eventloops;
 
370
    for (int i = 0 ; i < dispatchee_count + 5 ; ++i)
 
371
    {
 
372
        eventloops.push_back(std::make_shared<md::SimpleDispatchThread>(dispatcher));
 
373
    }
 
374
 
 
375
    std::vector<std::shared_ptr<mt::Signal>> canary_tomb;
 
376
    std::vector<std::shared_ptr<mt::TestDispatchable>> dispatchees;
 
377
    for (int i = 0 ; i < dispatchee_count ; ++i)
 
378
    {
 
379
        canary_tomb.push_back(std::make_shared<mt::Signal>());
 
380
        auto current_canary = canary_tomb.back();
 
381
        auto canary = std::shared_ptr<int>(new int, [current_canary](int* victim) { delete victim; current_canary->raise(); });
 
382
        auto dispatchee = std::make_shared<mt::TestDispatchable>([canary]()
 
383
        {
 
384
            std::this_thread::sleep_for(std::chrono::seconds{1});
 
385
            EXPECT_THAT(canary.use_count(), Gt(0));
 
386
        });
 
387
        dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant);
 
388
 
 
389
        dispatchee->trigger();
 
390
    }
 
391
 
 
392
    for (auto& dispatchee : dispatchees)
 
393
    {
 
394
        dispatchee->trigger();
 
395
        dispatcher->remove_watch(dispatchee);
 
396
    }
 
397
 
 
398
    dispatchees.clear();
 
399
    dispatcher.reset();
 
400
    eventloops.clear();
 
401
 
 
402
    for (auto headstone : canary_tomb)
 
403
    {
 
404
        // Use assert so as to not block for *ages* on failure
 
405
        ASSERT_TRUE(headstone->wait_for(std::chrono::seconds{2}));
 
406
    }
 
407
}
 
408
 
 
409
TEST(MultiplexingDispatchableTest, removes_dispatchable_that_returns_false_from_dispatch)
 
410
{
 
411
    bool dispatched{false};
 
412
    auto dispatchee = std::make_shared<mt::TestDispatchable>([&dispatched](md::FdEvents)
 
413
    {
 
414
        dispatched = true;
 
415
        return false;
 
416
    });
 
417
    md::MultiplexingDispatchable dispatcher{dispatchee};
 
418
 
 
419
    dispatchee->trigger();
 
420
    dispatchee->trigger();
 
421
 
 
422
    ASSERT_TRUE(mt::fd_is_readable(dispatcher.watch_fd()));
 
423
    dispatcher.dispatch(md::FdEvent::readable);
 
424
 
 
425
    EXPECT_TRUE(dispatched);
 
426
 
 
427
    dispatched = false;
 
428
    while (mt::fd_is_readable(dispatcher.watch_fd()))
 
429
    {
 
430
        dispatcher.dispatch(md::FdEvent::readable);
 
431
    }
 
432
 
 
433
    EXPECT_FALSE(dispatched);
 
434
}
 
435
 
 
436
TEST(MultiplexingDispatchableTest, multiple_removals_are_threadsafe)
 
437
{
 
438
    using namespace testing;
 
439
 
 
440
    auto canary_killed = std::make_shared<mt::Signal>();
 
441
    auto canary = std::shared_ptr<int>(new int, [canary_killed](int* victim) { delete victim; canary_killed->raise(); });
 
442
    auto in_dispatch = std::make_shared<mt::Signal>();
 
443
    auto unblock_dispatchee = std::make_shared<mt::Signal>();
 
444
 
 
445
    auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
 
446
 
 
447
    auto first_dispatchee = std::make_shared<mt::TestDispatchable>([canary, in_dispatch, unblock_dispatchee]()
 
448
    {
 
449
        in_dispatch->raise();
 
450
        EXPECT_TRUE(unblock_dispatchee->wait_for(std::chrono::seconds{5}));
 
451
        EXPECT_THAT(canary.use_count(), Gt(0));
 
452
    });
 
453
    auto dummy_dispatchee = std::make_shared<mt::TestDispatchable>([](){});
 
454
    dispatcher->add_watch(first_dispatchee);
 
455
    dispatcher->add_watch(dummy_dispatchee);
 
456
 
 
457
    first_dispatchee->trigger();
 
458
 
 
459
    md::SimpleDispatchThread eventloop_one{dispatcher};
 
460
    md::SimpleDispatchThread eventloop_two{dispatcher};
 
461
 
 
462
    EXPECT_TRUE(in_dispatch->wait_for(std::chrono::seconds{1}));
 
463
 
 
464
    dispatcher->remove_watch(dummy_dispatchee);
 
465
    dispatcher->remove_watch(first_dispatchee);
 
466
    dispatcher.reset();
 
467
    first_dispatchee.reset();
 
468
    dummy_dispatchee.reset();
 
469
    canary.reset();
 
470
 
 
471
    unblock_dispatchee->raise();
 
472
 
 
473
    EXPECT_TRUE(canary_killed->wait_for(std::chrono::seconds{2}));
 
474
}
 
475
 
 
476
TEST(MultiplexingDispatchableTest, automatic_removals_are_threadsafe)
 
477
{
 
478
    auto dispatcher = std::make_shared<md::MultiplexingDispatchable>();
 
479
 
 
480
    auto dispatchee = std::make_shared<mt::TestDispatchable>([](md::FdEvents) { return false; });
 
481
 
 
482
    dispatcher->add_watch(dispatchee, md::DispatchReentrancy::reentrant);
 
483
 
 
484
    md::SimpleDispatchThread one{dispatcher}, two{dispatcher}, three{dispatcher}, four{dispatcher};
 
485
 
 
486
    dispatchee->trigger();
 
487
}