Mir

Merge lp:~andreas-pokorny/mir/fix-1672955 into lp:mir

Proposed by Andreas Pokorny
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 4114
Proposed branch: lp:~andreas-pokorny/mir/fix-1672955
Merge into: lp:mir
Prerequisite: lp:~andreas-pokorny/mir/store-device-config
Diff against target: 308 lines (+99/-33)
6 files modified
src/server/input/default_device.cpp (+30/-15)
src/server/input/default_device.h (+2/-1)
src/server/input/default_input_device_hub.cpp (+19/-12)
src/server/input/default_input_device_hub.h (+6/-5)
tests/unit-tests/input/test_default_device.cpp (+23/-0)
tests/unit-tests/input/test_default_input_device_hub.cpp (+19/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/mir/fix-1672955
Reviewer Review Type Date Requested Status
Kevin DuBois (community) Approve
Alan Griffiths Approve
Alexandros Frantzis (community) Approve
Mir CI Bot continuous-integration Approve
Review via email: mp+320066@code.launchpad.net

Commit message

Ensure that no action is still queued that may access the mir::input::InputDevice (LP: #1672955)

Shared pointers to mir::input::Device are handed out to other threads and may still be used after the mir::input::InputDevice was removed. To avoid this problem the ActionQueues for manipulating input devices are now per device and will be removed with the InputDevice

Description of the change

The attached bug was caused by actions in the queue that try to apply configuration while the device was already removed. mir::input::Device does not to keep the platform devices alive. Changing that to shared_ptr does not really improve the situation as we would keep the device alive until the queue is processed. Instead this change will remove the queue instead such that outstanding actions are canceled. All that happens inside the input thread..

Proposing that on top of lp:~andreas-pokorny/mir/store-device-config since it would conflict otherwise.

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

PASSED: Continuous integration, rev:4074
https://mir-jenkins.ubuntu.com/job/mir-ci/3180/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4273
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4360
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4350
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4350
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4350
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4300
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4300/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4300
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4300/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4300
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4300/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/4300
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4300/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/4300
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4300/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4300
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4300/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

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

LGTM

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

lgtm too, would be nice to note bug in code comments somewhere

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/server/input/default_device.cpp'
2--- src/server/input/default_device.cpp 2017-03-16 15:10:51 +0000
3+++ src/server/input/default_device.cpp 2017-03-16 15:10:52 +0000
4@@ -150,12 +150,13 @@
5 {
6 std::lock_guard<std::mutex> lock(config_mutex);
7 pointer = settings;
8+ if (!actions) // device is disabled
9+ return;
10+ actions->enqueue([settings = std::move(settings), dev=&device]
11+ {
12+ dev->apply_settings(settings);
13+ });
14 }
15-
16- actions->enqueue([settings = std::move(settings), dev=&device]
17- {
18- dev->apply_settings(settings);
19- });
20 }
21
22 void mi::DefaultDevice::apply_touchpad_configuration(MirTouchpadConfig const& conf)
23@@ -188,12 +189,13 @@
24 {
25 std::lock_guard<std::mutex> lock(config_mutex);
26 touchpad = settings;
27+ if (!actions) // device is disabled
28+ return;
29+ actions->enqueue([settings = std::move(settings), dev=&device]
30+ {
31+ dev->apply_settings(settings);
32+ });
33 }
34-
35- actions->enqueue([settings = std::move(settings), dev=&device]
36- {
37- dev->apply_settings(settings);
38- });
39 }
40
41 mir::optional_value<MirKeyboardConfig> mi::DefaultDevice::keyboard_configuration() const
42@@ -214,6 +216,8 @@
43 void mi::DefaultDevice::set_keyboard_configuration(MirKeyboardConfig const& conf)
44 {
45 std::lock_guard<std::mutex> lock(config_mutex);
46+ if (!actions) // device is disabled
47+ return;
48 if (keyboard.value().device_keymap() != conf.device_keymap())
49 keyboard = conf;
50 else
51@@ -246,12 +250,17 @@
52 settings.output_id = config.output_id();
53 settings.mapping_mode = config.mapping_mode();
54
55- touchscreen = settings;
56+ {
57+ std::lock_guard<std::mutex> lock(config_mutex);
58+ touchscreen = settings;
59
60- actions->enqueue([settings = std::move(settings), dev=&device]
61- {
62- dev->apply_settings(settings);
63- });
64+ if (!actions) // device is disabled
65+ return;
66+ actions->enqueue([settings = std::move(settings), dev=&device]
67+ {
68+ dev->apply_settings(settings);
69+ });
70+ }
71 }
72
73 MirInputDevice mi::DefaultDevice::config() const
74@@ -276,3 +285,9 @@
75
76 return stored_dev;
77 }
78+
79+void mi::DefaultDevice::disable_queue()
80+{
81+ std::lock_guard<std::mutex> lock(config_mutex);
82+ actions.reset();
83+}
84
85=== modified file 'src/server/input/default_device.h'
86--- src/server/input/default_device.h 2017-03-16 15:10:51 +0000
87+++ src/server/input/default_device.h 2017-03-16 15:10:52 +0000
88@@ -78,6 +78,7 @@
89 void apply_touchscreen_configuration(MirTouchscreenConfig const&) override;
90
91 MirInputDevice config() const;
92+ void disable_queue();
93 private:
94 void set_pointer_configuration(MirPointerConfig const&);
95 void set_touchpad_configuration(MirTouchpadConfig const&);
96@@ -92,7 +93,7 @@
97 optional_value<TouchpadSettings> touchpad;
98 optional_value<MirKeyboardConfig> keyboard;
99 optional_value<TouchscreenSettings> touchscreen;
100- std::shared_ptr<dispatch::ActionQueue> const actions;
101+ std::shared_ptr<dispatch::ActionQueue> actions;
102 std::shared_ptr<KeyMapper> const key_mapper;
103 std::function<void(Device*)> device_changed_callback;
104 std::mutex mutable config_mutex;
105
106=== modified file 'src/server/input/default_input_device_hub.cpp'
107--- src/server/input/default_input_device_hub.cpp 2017-03-16 15:10:51 +0000
108+++ src/server/input/default_input_device_hub.cpp 2017-03-16 15:10:52 +0000
109@@ -187,16 +187,17 @@
110
111 if (it == end(devices))
112 {
113- auto handle = restore_or_create_device(*device);
114+ auto queue = std::make_shared<dispatch::ActionQueue>();
115+ auto handle = restore_or_create_device(*device, queue);
116 // send input device info to observer loop..
117 devices.push_back(std::make_unique<RegisteredDevice>(
118- device, handle->id(), input_dispatchable, cookie_authority, handle));
119+ device, handle->id(), queue, cookie_authority, handle));
120
121 auto const& dev = devices.back();
122 add_device_handle(handle);
123
124 seat->add_device(*handle);
125- dev->start(seat);
126+ dev->start(seat, input_dispatchable);
127 }
128 else
129 {
130@@ -222,7 +223,7 @@
131 if (seat)
132 {
133 seat->remove_device(*item->handle);
134- item->stop();
135+ item->stop(input_dispatchable);
136 }
137 remove_device_handle(item->id());
138
139@@ -242,14 +243,14 @@
140 mi::DefaultInputDeviceHub::RegisteredDevice::RegisteredDevice(
141 std::shared_ptr<InputDevice> const& dev,
142 MirInputDeviceId device_id,
143- std::shared_ptr<dispatch::MultiplexingDispatchable> const& multiplexer,
144+ std::shared_ptr<dispatch::ActionQueue> const& queue,
145 std::shared_ptr<mir::cookie::Authority> const& cookie_authority,
146 std::shared_ptr<mi::DefaultDevice> const& handle)
147 : handle(handle),
148 device_id(device_id),
149 cookie_authority(cookie_authority),
150 device(dev),
151- multiplexer(multiplexer)
152+ queue(queue)
153 {
154 }
155
156@@ -282,17 +283,23 @@
157 return dev == device;
158 }
159
160-void mi::DefaultInputDeviceHub::RegisteredDevice::start(std::shared_ptr<Seat> const& seat)
161+void mi::DefaultInputDeviceHub::RegisteredDevice::start(std::shared_ptr<Seat> const& seat, std::shared_ptr<dispatch::MultiplexingDispatchable> const& multiplexer)
162 {
163+ multiplexer->add_watch(queue);
164+
165 this->seat = seat;
166 builder = std::make_unique<DefaultEventBuilder>(device_id, cookie_authority, seat);
167 device->start(this, builder.get());
168 }
169
170-void mi::DefaultInputDeviceHub::RegisteredDevice::stop()
171+void mi::DefaultInputDeviceHub::RegisteredDevice::stop(std::shared_ptr<dispatch::MultiplexingDispatchable> const& multiplexer)
172 {
173+ multiplexer->remove_watch(queue);
174+ handle->disable_queue();
175+
176 device->stop();
177 seat = nullptr;
178+ queue.reset();
179 builder.reset();
180 }
181
182@@ -516,22 +523,22 @@
183 return optional_config;
184 }
185
186-std::shared_ptr<mi::DefaultDevice>
187-mi::DefaultInputDeviceHub::restore_or_create_device(mi::InputDevice& device)
188+std::shared_ptr<mi::DefaultDevice> mi::DefaultInputDeviceHub::restore_or_create_device(
189+ mi::InputDevice& device, std::shared_ptr<mir::dispatch::ActionQueue> const& queue)
190 {
191 auto device_config = get_stored_device_config(device.get_device_info().unique_id);
192
193 if (device_config.is_set())
194 return std::make_shared<DefaultDevice>(
195 device_config.value(),
196- device_queue,
197+ queue,
198 device,
199 key_mapper,
200 [this](Device *d){device_changed(d);});
201 else
202 return std::make_shared<DefaultDevice>(
203 create_new_device_id(),
204- device_queue,
205+ queue,
206 device,
207 key_mapper,
208 [this](Device *d){device_changed(d);});
209
210=== modified file 'src/server/input/default_input_device_hub.h'
211--- src/server/input/default_input_device_hub.h 2017-03-16 15:10:51 +0000
212+++ src/server/input/default_input_device_hub.h 2017-03-16 15:10:52 +0000
213@@ -105,7 +105,8 @@
214 void emit_changed_devices();
215 MirInputDeviceId create_new_device_id();
216 void store_device_config(DefaultDevice const& dev);
217- std::shared_ptr<DefaultDevice> restore_or_create_device(InputDevice& dev);
218+ std::shared_ptr<DefaultDevice> restore_or_create_device(InputDevice& dev,
219+ std::shared_ptr<dispatch::ActionQueue> const& queue);
220 mir::optional_value<MirInputDevice> get_stored_device_config(std::string const& id);
221
222 std::shared_ptr<Seat> const seat;
223@@ -121,15 +122,15 @@
224 public:
225 RegisteredDevice(std::shared_ptr<InputDevice> const& dev,
226 MirInputDeviceId dev_id,
227- std::shared_ptr<dispatch::MultiplexingDispatchable> const& multiplexer,
228+ std::shared_ptr<dispatch::ActionQueue> const& multiplexer,
229 std::shared_ptr<cookie::Authority> const& cookie_authority,
230 std::shared_ptr<DefaultDevice> const& handle);
231 void handle_input(MirEvent& event) override;
232 geometry::Rectangle bounding_rectangle() const override;
233 input::OutputInfo output_info(uint32_t output_id) const override;
234 bool device_matches(std::shared_ptr<InputDevice> const& dev) const;
235- void start(std::shared_ptr<Seat> const& seat);
236- void stop();
237+ void start(std::shared_ptr<Seat> const& seat, std::shared_ptr<dispatch::MultiplexingDispatchable> const& dispatchable);
238+ void stop(std::shared_ptr<dispatch::MultiplexingDispatchable> const& dispatchable);
239 MirInputDeviceId id();
240 std::shared_ptr<Seat> seat;
241 const std::shared_ptr<DefaultDevice> handle;
242@@ -141,7 +142,7 @@
243 std::unique_ptr<DefaultEventBuilder> builder;
244 std::shared_ptr<cookie::Authority> cookie_authority;
245 std::shared_ptr<InputDevice> const device;
246- std::shared_ptr<dispatch::MultiplexingDispatchable> const multiplexer;
247+ std::shared_ptr<dispatch::ActionQueue> queue;
248 };
249
250 std::vector<std::shared_ptr<Device>> handles;
251
252=== modified file 'tests/unit-tests/input/test_default_device.cpp'
253--- tests/unit-tests/input/test_default_device.cpp 2017-03-16 15:10:51 +0000
254+++ tests/unit-tests/input/test_default_device.cpp 2017-03-16 15:10:52 +0000
255@@ -308,3 +308,26 @@
256
257 EXPECT_EQ(ts_config, dev.touchscreen_configuration().value());
258 }
259+
260+TEST_F(DefaultDevice, disable_queue_ends_config_processing)
261+{
262+ mi::DefaultDevice dev(MirInputDeviceId{17}, queue, touchpad, mt::fake_shared(key_mapper));
263+
264+ dev.disable_queue();
265+ MirPointerConfig pointer_conf;
266+ pointer_conf.cursor_acceleration_bias(1.0);
267+
268+ EXPECT_CALL(touchpad, apply_settings(Matcher<mi::TouchpadSettings const&>(_))).Times(0);
269+ dev.apply_pointer_configuration(pointer_conf);
270+}
271+
272+TEST_F(DefaultDevice, disable_queue_removes_reference_to_queue)
273+{
274+ std::weak_ptr<md::ActionQueue> ref = queue;
275+ mi::DefaultDevice dev(MirInputDeviceId{17}, queue, touchpad, mt::fake_shared(key_mapper));
276+ queue.reset();
277+
278+ EXPECT_THAT(ref.lock(), Ne(nullptr));
279+ dev.disable_queue();
280+ EXPECT_THAT(ref.lock(), Eq(nullptr));
281+}
282
283=== modified file 'tests/unit-tests/input/test_default_input_device_hub.cpp'
284--- tests/unit-tests/input/test_default_input_device_hub.cpp 2017-03-16 15:10:51 +0000
285+++ tests/unit-tests/input/test_default_input_device_hub.cpp 2017-03-16 15:10:52 +0000
286@@ -340,3 +340,22 @@
287
288 EXPECT_THAT(dev_ptr->pointer_configuration().value(), Eq(ptr_config));
289 }
290+
291+TEST_F(InputDeviceHubTest, no_device_config_action_after_device_removal)
292+{
293+ std::shared_ptr<mi::Device> dev_ptr;
294+ MirPointerConfig ptr_config;
295+ ptr_config.cursor_acceleration_bias(0.5);
296+
297+ ON_CALL(mock_observer, device_added(WithName("mouse"))).WillByDefault(SaveArg<0>(&dev_ptr));
298+
299+ hub.add_device(mt::fake_shared(mouse));
300+ hub.add_observer(mt::fake_shared(mock_observer));
301+ expect_and_execute_multiplexer();
302+
303+ EXPECT_CALL(mouse, apply_settings(Matcher<mi::PointerSettings const&>(_))).Times(0);
304+
305+ dev_ptr->apply_pointer_configuration(ptr_config);
306+ hub.remove_device(mt::fake_shared(mouse));
307+ expect_and_execute_multiplexer();
308+}

Subscribers

People subscribed via source and target branches