Mir

Merge lp:~afrantzis/mir/fix-1340669-deadlock into lp:mir

Proposed by Alexandros Frantzis
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 1768
Proposed branch: lp:~afrantzis/mir/fix-1340669-deadlock
Merge into: lp:mir
Diff against target: 259 lines (+205/-3)
5 files modified
include/shared/mir/basic_observers.h (+7/-3)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_client_with_custom_display_config_deadlock.cpp (+72/-0)
tests/unit-tests/CMakeLists.txt (+1/-0)
tests/unit-tests/test_basic_observers.cpp (+124/-0)
To merge this branch: bzr merge lp:~afrantzis/mir/fix-1340669-deadlock
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Approve
Review via email: mp+226467@code.launchpad.net

Commit message

server: Fix deadlock when client with custom configuration disconnects

Description of the change

server: Fix deadlock when client with custom configuration disconnects

To post a comment you must log in.
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Oops pushed non-refactored version of BasicObservers tests, will push update (doesn't change test logic).

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

Agreed

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

> Oops pushed non-refactored version of BasicObservers tests, will push update
> (doesn't change test logic).

Pushed.

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

> > Oops pushed non-refactored version of BasicObservers tests, will push update
> > (doesn't change test logic).
>
> Pushed.

Better still

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

26 + while (current_item->next && (current_item = current_item->next));

could be

    while (current_item = current_item->next);

Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

> 26 + while (current_item->next && (current_item = current_item->next));
>
> could be
>
> while (current_item = current_item->next);

Fixed (with extra parentheses to suppress warning/error).

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/shared/mir/basic_observers.h'
2--- include/shared/mir/basic_observers.h 2014-07-10 13:46:41 +0000
3+++ include/shared/mir/basic_observers.h 2014-07-11 15:00:30 +0000
4@@ -104,8 +104,13 @@
5 {
6 ListItem* current_item = &head;
7
8- while (current_item)
9+ do
10 {
11+ {
12+ RecursiveReadLock lock{current_item->mutex};
13+ if (current_item->observer != observer) continue;
14+ }
15+
16 RecursiveWriteLock lock{current_item->mutex};
17
18 if (current_item->observer == observer)
19@@ -113,9 +118,8 @@
20 current_item->observer.reset();
21 return;
22 }
23-
24- current_item = current_item->next;
25 }
26+ while ((current_item = current_item->next));
27 }
28 }
29
30
31=== modified file 'tests/acceptance-tests/CMakeLists.txt'
32--- tests/acceptance-tests/CMakeLists.txt 2014-06-23 17:19:22 +0000
33+++ tests/acceptance-tests/CMakeLists.txt 2014-07-11 15:00:30 +0000
34@@ -38,6 +38,7 @@
35 test_client_cursor_api.cpp
36 test_large_messages.cpp
37 test_client_surface_visibility.cpp
38+ test_client_with_custom_display_config_deadlock.cpp
39 ${GENERATED_PROTOBUF_SRCS}
40 ${GENERATED_PROTOBUF_HDRS}
41 )
42
43=== added file 'tests/acceptance-tests/test_client_with_custom_display_config_deadlock.cpp'
44--- tests/acceptance-tests/test_client_with_custom_display_config_deadlock.cpp 1970-01-01 00:00:00 +0000
45+++ tests/acceptance-tests/test_client_with_custom_display_config_deadlock.cpp 2014-07-11 15:00:30 +0000
46@@ -0,0 +1,72 @@
47+/*
48+ * Copyright © 2014 Canonical Ltd.
49+ *
50+ * This program is free software: you can redistribute it and/or modify it
51+ * under the terms of the GNU General Public License version 3,
52+ * as published by the Free Software Foundation.
53+ *
54+ * This program is distributed in the hope that it will be useful,
55+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
56+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
57+ * GNU General Public License for more details.
58+ *
59+ * You should have received a copy of the GNU General Public License
60+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
61+ *
62+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
63+ */
64+
65+#include "mir_toolkit/mir_client_library.h"
66+
67+#include "mir_test_framework/stubbed_server_configuration.h"
68+#include "mir_test_framework/basic_client_server_fixture.h"
69+
70+#include <gtest/gtest.h>
71+
72+namespace mtf = mir_test_framework;
73+
74+namespace
75+{
76+
77+struct StubServerConfig : mtf::StubbedServerConfiguration
78+{
79+ std::shared_ptr<mir::input::InputSender> the_input_sender()
80+ {
81+ return DefaultServerConfiguration::the_input_sender();
82+ }
83+};
84+
85+using ClientWithCustomDisplayConfiguration = mtf::BasicClientServerFixture<StubServerConfig>;
86+
87+}
88+
89+// Regression test for LP:#1340669
90+// Test is not deterministic since we are testing a race, but failure can be
91+// reproduced easily with repeated runs.
92+TEST_F(ClientWithCustomDisplayConfiguration,
93+ does_not_deadlock_server_with_existing_client_when_disconnecting)
94+{
95+ auto second_connection = mir_connect_sync(new_connection().c_str(), __PRETTY_FUNCTION__);
96+ MirSurfaceParameters const request_params =
97+ {
98+ __PRETTY_FUNCTION__,
99+ 640, 480,
100+ mir_pixel_format_abgr_8888,
101+ mir_buffer_usage_hardware,
102+ mir_display_output_id_invalid
103+ };
104+
105+ auto second_surface = mir_connection_create_surface_sync(second_connection, &request_params);
106+ ASSERT_TRUE(mir_surface_is_valid(second_surface));
107+
108+ auto configuration = mir_connection_create_display_config(connection);
109+ mir_wait_for(mir_connection_apply_display_config(connection, configuration));
110+ EXPECT_STREQ("", mir_connection_get_error_message(connection));
111+ mir_display_config_destroy(configuration);
112+
113+ mir_connection_release(second_connection);
114+
115+ // Server (and therefore the test) will deadlock and won't be able to
116+ // shut down without the fix. It's not ideal to deadlock on test failure,
117+ // but it's the best check we have at the moment.
118+}
119
120=== modified file 'tests/unit-tests/CMakeLists.txt'
121--- tests/unit-tests/CMakeLists.txt 2014-07-11 03:33:03 +0000
122+++ tests/unit-tests/CMakeLists.txt 2014-07-11 15:00:30 +0000
123@@ -19,6 +19,7 @@
124 test_variable_length_array.cpp
125 test_thread_name.cpp
126 test_default_emergency_cleanup.cpp
127+ test_basic_observers.cpp
128 )
129
130 add_subdirectory(options/)
131
132=== added file 'tests/unit-tests/test_basic_observers.cpp'
133--- tests/unit-tests/test_basic_observers.cpp 1970-01-01 00:00:00 +0000
134+++ tests/unit-tests/test_basic_observers.cpp 2014-07-11 15:00:30 +0000
135@@ -0,0 +1,124 @@
136+/*
137+ * Copyright © 2014 Canonical Ltd.
138+ *
139+ * This program is free software: you can redistribute it and/or modify it
140+ * under the terms of the GNU General Public License version 3,
141+ * as published by the Free Software Foundation.
142+ *
143+ * This program is distributed in the hope that it will be useful,
144+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
145+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
146+ * GNU General Public License for more details.
147+ *
148+ * You should have received a copy of the GNU General Public License
149+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
150+ *
151+ * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
152+ */
153+
154+#include "mir/basic_observers.h"
155+#include "mir_test/wait_condition.h"
156+
157+#include <gtest/gtest.h>
158+#include <gmock/gmock.h>
159+
160+namespace
161+{
162+
163+struct DummyObserver
164+{
165+};
166+
167+struct DummyObservers : mir::BasicObservers<DummyObserver>
168+{
169+ using mir::BasicObservers<DummyObserver>::add;
170+ using mir::BasicObservers<DummyObserver>::remove;
171+ using mir::BasicObservers<DummyObserver>::for_each;
172+};
173+
174+struct BasicObserversTest : testing::Test
175+{
176+ DummyObservers observers;
177+
178+ std::shared_ptr<DummyObserver> const observer1 = std::make_shared<DummyObserver>();
179+ std::shared_ptr<DummyObserver> const observer2 = std::make_shared<DummyObserver>();
180+};
181+
182+}
183+
184+TEST_F(BasicObserversTest, can_remove_observer_from_within_same_observer)
185+{
186+ using namespace testing;
187+
188+ observers.add(observer1);
189+
190+ observers.for_each(
191+ [&] (std::shared_ptr<DummyObserver> const& observer)
192+ {
193+ observers.remove(observer);
194+ });
195+
196+ int observers_seen = 0;
197+
198+ observers.for_each(
199+ [&] (std::shared_ptr<DummyObserver> const&)
200+ {
201+ ++observers_seen;
202+ });
203+
204+ EXPECT_THAT(observers_seen, Eq(0));
205+}
206+
207+TEST_F(BasicObserversTest, can_remove_unused_observer_from_within_different_observer)
208+{
209+ using namespace testing;
210+
211+ observers.add(observer1);
212+ observers.add(observer2);
213+
214+ int observers_seen = 0;
215+
216+ observers.for_each(
217+ [&] (std::shared_ptr<DummyObserver> const&)
218+ {
219+ observers.remove(observer2);
220+ ++observers_seen;
221+ });
222+
223+ EXPECT_THAT(observers_seen, Eq(1));
224+}
225+
226+TEST_F(BasicObserversTest,
227+ can_remove_unused_observer_while_different_observer_is_used_in_different_thread)
228+{
229+ using namespace testing;
230+
231+ observers.add(observer1);
232+ observers.add(observer2);
233+
234+ mir::test::WaitCondition first_observer_in_use;
235+ mir::test::WaitCondition second_observer_removed;
236+
237+ int observers_seen = 0;
238+
239+ std::thread t{
240+ [&]
241+ {
242+ observers.for_each(
243+ [&] (std::shared_ptr<DummyObserver> const&)
244+ {
245+ first_observer_in_use.wake_up_everyone();
246+ second_observer_removed.wait_for_at_most_seconds(3);
247+ EXPECT_TRUE(second_observer_removed.woken());
248+ ++observers_seen;
249+ });
250+ }};
251+
252+ first_observer_in_use.wait_for_at_most_seconds(3);
253+ observers.remove(observer2);
254+ second_observer_removed.wake_up_everyone();
255+
256+ t.join();
257+
258+ EXPECT_THAT(observers_seen, Eq(1));
259+}

Subscribers

People subscribed via source and target branches