Mir

Merge lp:~albaguirre/mir/fix-1427976 into lp:mir

Proposed by Alberto Aguirre
Status: Rejected
Rejected by: Alberto Aguirre
Proposed branch: lp:~albaguirre/mir/fix-1427976
Merge into: lp:mir
Diff against target: 363 lines (+162/-23)
5 files modified
src/include/common/mir/semaphore_lock.h (+81/-0)
src/server/frontend/session_mediator.cpp (+22/-22)
src/server/frontend/session_mediator.h (+2/-1)
tests/unit-tests/CMakeLists.txt (+1/-0)
tests/unit-tests/test_semaphore_lock.cpp (+56/-0)
To merge this branch: bzr merge lp:~albaguirre/mir/fix-1427976
Reviewer Review Type Date Requested Status
Alberto Aguirre (community) Disapprove
Alan Griffiths Needs Fixing
Alexandros Frantzis (community) Needs Fixing
Daniel van Vugt Needs Fixing
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+251817@code.launchpad.net

Commit message

Avoid unlocking sesssion mutex in a different thread.

In some cases, SessionMediator needs to lock the session
while it waits for Surface::swap_buffers which may complete
asynchronously on a different thread. Since a std::mutex
cannot be used for such purposes a SemaphoreLock type is introduced.

Description of the change

Avoid unlocking sesssion mutex in a different thread.

In some cases, SessionMediator needs to lock the session
while it waits for Surface::swap_buffers which may complete
asynchronously on a different thread. Since a std::mutex
cannot be used for such purposes a SemaphoreLock type is introduced.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I'm not yet sure what you're trying to express, but Semaphore(Lock) seems like the wrong name.

I recall semaphore as a type of lock with a counter. The counter is the defining feature of a semaphore. And a quick search seems to support this: http://en.wikipedia.org/wiki/Semaphore_(programming)

So I think "Semaphore" is an incorrect name.

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

I am OK with the name, perhaps I would slightly prefer BinarySemaphore.

103 - auto const lock = std::make_shared<std::unique_lock<std::mutex>>(session_mutex);
104 + SemaphoreUnlockIfUnwinding lock{session_lock};1

147 - auto const lock = std::make_shared<std::unique_lock<std::mutex>>(session_mutex);
148 + SemaphoreUnlockIfUnwinding lock{session_lock};

The current locking scheme (using shared<unique_lock>) is safer. With the new scheme we may never release the lock if something goes wrong with the caller of the functor or the functor itself, since we have dropped RAII.

> I recall semaphore as a type of lock with a counter. The counter is the defining feature
> of a semaphore. And a quick search seems to support this:
> http://en.wikipedia.org/wiki/Semaphore_(programming)

From the bottom of the page:

A mutex is essentially the same thing as a binary semaphore and sometimes uses the
same basic implementation ... Mutexes have a concept of an owner. Only the process
that locked the mutex is supposed to unlock it. If the owner is stored by the mutex
this can be verified at runtime.

"Needs fixing" for the less safe locking scheme, looks good otherwise.

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

*Needs Discussion*

I wonder whether the better fix is to ensure there is only a single IPC thread running and dispatch all activity to that thread.

review: Needs Information
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

> *Needs Discussion*
>
> I wonder whether the better fix is to ensure there is only a single IPC thread
> running and dispatch all activity to that thread.

By default the IPC thread pool is set to 1 but it's configurable option (--ipc-thread-pool)

The other thread involved is the compositor thread, because of the completion given to surface.swap_buffers in SessionMediator::exchange_buffer (which we could make synchronous).

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

So we have three options:

1. Use a BinarySemaphore as proposed here
2. Make SessionMediator::exchange_buffer (and others which advance the buffer) wait for the completion and execute done->Run synchronously, in which case we can use a std::mutex
3. Remove the session_mutex all together by removing the --ipc-thread-pool option and doing 2 above (so only a single IPC thread is active at any time)

Revision history for this message
Alberto Aguirre (albaguirre) wrote :

Well option 2,3 would block the IPC thread, so not good.

Option 4. Schedule IPC responses to the IPC thread that originated them (if we still want an IPC thread pool)

I think we can land this for now (pending addressing Alexandros comments) and then do some refactoring to allow option 4 in a separate MP.

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

OK.

Let's go with a variant of this scheme.

Can we avoid the dubious release semantics of SemaphoreUnlockIfUnwinding by using move capture? (Thereby proving it was right to change to C++14!)

review: Needs Fixing
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

Well after looking at bit more at the code, it seems like the real reason for the session_mutex unlock in the completion is to avoid holding a lock when invoking IPC callbacks.

Since Surface::swap_buffers may execute the completion within, we need to detect such condition and only unlock the session_mutex then instead of using a binary semaphore.

review: Disapprove

Unmerged revisions

2361. By Alberto Aguirre

Avoid unlocking sesssion mutex in a different thread.

In some cases, SessionMediator needs to lock the session
while it waits for Surface::swap_buffers which may complete asynchronously on a different thread.
Since a std::mutex cannot be used for such purposes a SemaphoreLock type is introduced.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'src/include/common/mir/semaphore_lock.h'
--- src/include/common/mir/semaphore_lock.h 1970-01-01 00:00:00 +0000
+++ src/include/common/mir/semaphore_lock.h 2015-03-04 20:00:48 +0000
@@ -0,0 +1,81 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as 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 Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored By: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
18
19#ifndef MIR_SEMAPHORE_LOCK_H_
20#define MIR_SEMAPHORE_LOCK_H_
21
22#include <boost/throw_exception.hpp>
23
24#include <exception>
25#include <mutex>
26#include <condition_variable>
27
28namespace mir
29{
30
31class SemaphoreLock
32{
33public:
34 // Make it a BasicLockable so it can be used with
35 // std::lock_guard or std::unique_lock
36 void lock()
37 {
38 std::unique_lock<decltype(mutex)> lock{mutex};
39 cv.wait(lock, [this]{ return !locked;});
40 locked = true;
41 }
42
43 void unlock()
44 {
45 std::lock_guard<decltype(mutex)> lock{mutex};
46
47 if (!locked)
48 BOOST_THROW_EXCEPTION(std::logic_error("Already unlocked"));
49
50 locked = false;
51 cv.notify_one();
52 }
53
54private:
55 std::mutex mutex;
56 std::condition_variable cv;
57 bool locked = false;
58};
59
60class SemaphoreUnlockIfUnwinding
61{
62public:
63 explicit SemaphoreUnlockIfUnwinding(SemaphoreLock& guard)
64 : guard(guard)
65 {
66 guard.lock();
67 }
68
69 ~SemaphoreUnlockIfUnwinding()
70 {
71 if (std::uncaught_exception())
72 guard.unlock();
73 }
74
75private:
76 SemaphoreLock& guard;
77};
78
79}
80
81#endif /* MIR_SEMAPHORE_LOCK_H_ */
082
=== modified file 'src/server/frontend/session_mediator.cpp'
--- src/server/frontend/session_mediator.cpp 2015-02-13 06:12:34 +0000
+++ src/server/frontend/session_mediator.cpp 2015-03-04 20:00:48 +0000
@@ -122,7 +122,7 @@
122122
123 auto const session = shell->open_session(client_pid_, request->application_name(), event_sink);123 auto const session = shell->open_session(client_pid_, request->application_name(), event_sink);
124 {124 {
125 std::lock_guard<std::mutex> lock(session_mutex);125 std::lock_guard<decltype(session_lock)> lock{session_lock};
126 weak_session = session;126 weak_session = session;
127 }127 }
128 connection_context.handle_client_connect(session);128 connection_context.handle_client_connect(session);
@@ -172,7 +172,7 @@
172 google::protobuf::Closure* done)172 google::protobuf::Closure* done)
173{173{
174174
175 auto const lock = std::make_shared<std::unique_lock<std::mutex>>(session_mutex);175 SemaphoreUnlockIfUnwinding lock{session_lock};
176176
177 auto const session = weak_session.lock();177 auto const session = weak_session.lock();
178178
@@ -243,10 +243,10 @@
243 }243 }
244244
245 advance_buffer(surf_id, *surface,245 advance_buffer(surf_id, *surface,
246 [lock, this, &surf_id, response, done, session]246 [this, &surf_id, response, done, session]
247 (graphics::Buffer* client_buffer, graphics::BufferIpcMsgType msg_type)247 (graphics::Buffer* client_buffer, graphics::BufferIpcMsgType msg_type)
248 {248 {
249 lock->unlock();249 session_lock.unlock();
250250
251 response->mutable_buffer_stream()->mutable_id()->set_value(251 response->mutable_buffer_stream()->mutable_id()->set_value(
252 surf_id.as_value());252 surf_id.as_value());
@@ -271,7 +271,7 @@
271{271{
272 SurfaceId const surf_id{request->value()};272 SurfaceId const surf_id{request->value()};
273273
274 auto const lock = std::make_shared<std::unique_lock<std::mutex>>(session_mutex);274 SemaphoreUnlockIfUnwinding lock{session_lock};
275275
276 auto const session = weak_session.lock();276 auto const session = weak_session.lock();
277277
@@ -283,10 +283,10 @@
283 auto surface = session->get_surface(surf_id);283 auto surface = session->get_surface(surf_id);
284284
285 advance_buffer(surf_id, *surface,285 advance_buffer(surf_id, *surface,
286 [lock, this, response, done, session]286 [this, response, done, session]
287 (graphics::Buffer* client_buffer, graphics::BufferIpcMsgType msg_type)287 (graphics::Buffer* client_buffer, graphics::BufferIpcMsgType msg_type)
288 {288 {
289 lock->unlock();289 session_lock.unlock();
290290
291 pack_protobuf_buffer(*response, client_buffer, msg_type);291 pack_protobuf_buffer(*response, client_buffer, msg_type);
292292
@@ -306,7 +306,7 @@
306 mfd::ProtobufBufferPacker request_msg{const_cast<mir::protobuf::Buffer*>(&request->buffer())};306 mfd::ProtobufBufferPacker request_msg{const_cast<mir::protobuf::Buffer*>(&request->buffer())};
307 ipc_operations->unpack_buffer(request_msg, *surface_tracker.last_buffer(surface_id));307 ipc_operations->unpack_buffer(request_msg, *surface_tracker.last_buffer(surface_id));
308308
309 auto const lock = std::make_shared<std::unique_lock<std::mutex>>(session_mutex);309 SemaphoreUnlockIfUnwinding lock{session_lock};
310 auto const session = weak_session.lock();310 auto const session = weak_session.lock();
311 if (!session)311 if (!session)
312 BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session"));312 BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session"));
@@ -316,15 +316,15 @@
316 auto const& surface = session->get_surface(surface_id);316 auto const& surface = session->get_surface(surface_id);
317 surface->swap_buffers(317 surface->swap_buffers(
318 surface_tracker.buffer_from(buffer_id),318 surface_tracker.buffer_from(buffer_id),
319 [this, surface_id, lock, response, done](mg::Buffer* new_buffer)319 [this, surface_id, response, done](mg::Buffer* new_buffer)
320 {320 {
321 lock->unlock();
322
323 if (surface_tracker.track_buffer(surface_id, new_buffer))321 if (surface_tracker.track_buffer(surface_id, new_buffer))
324 pack_protobuf_buffer(*response, new_buffer, mg::BufferIpcMsgType::update_msg);322 pack_protobuf_buffer(*response, new_buffer, mg::BufferIpcMsgType::update_msg);
325 else323 else
326 pack_protobuf_buffer(*response, new_buffer, mg::BufferIpcMsgType::full_msg);324 pack_protobuf_buffer(*response, new_buffer, mg::BufferIpcMsgType::full_msg);
327325
326 // Unlock until now as surface_tracker may be modified above.
327 session_lock.unlock();
328 done->Run();328 done->Run();
329 });329 });
330}330}
@@ -336,7 +336,7 @@
336 google::protobuf::Closure* done)336 google::protobuf::Closure* done)
337{337{
338 {338 {
339 std::unique_lock<std::mutex> lock(session_mutex);339 std::lock_guard<decltype(session_lock)> lock{session_lock};
340340
341 auto session = weak_session.lock();341 auto session = weak_session.lock();
342342
@@ -362,7 +362,7 @@
362 google::protobuf::Closure* done)362 google::protobuf::Closure* done)
363{363{
364 {364 {
365 std::unique_lock<std::mutex> lock(session_mutex);365 std::lock_guard<decltype(session_lock)> lock{session_lock};
366366
367 auto session = weak_session.lock();367 auto session = weak_session.lock();
368368
@@ -391,7 +391,7 @@
391 response->set_attrib(attrib);391 response->set_attrib(attrib);
392392
393 {393 {
394 std::unique_lock<std::mutex> lock(session_mutex);394 std::lock_guard<decltype(session_lock)> lock{session_lock};
395395
396 auto session = weak_session.lock();396 auto session = weak_session.lock();
397397
@@ -417,7 +417,7 @@
417 ::google::protobuf::Closure* done)417 ::google::protobuf::Closure* done)
418{418{
419 {419 {
420 std::unique_lock<std::mutex> lock(session_mutex);420 std::lock_guard<decltype(session_lock)> lock{session_lock};
421 auto session = weak_session.lock();421 auto session = weak_session.lock();
422422
423 if (session.get() == nullptr)423 if (session.get() == nullptr)
@@ -536,7 +536,7 @@
536 google::protobuf::Closure* done)536 google::protobuf::Closure* done)
537{537{
538 {538 {
539 std::unique_lock<std::mutex> lock(session_mutex);539 std::lock_guard<decltype(session_lock)> lock{session_lock};
540540
541 auto session = weak_session.lock();541 auto session = weak_session.lock();
542542
@@ -568,7 +568,7 @@
568 ::google::protobuf::Closure* done)568 ::google::protobuf::Closure* done)
569{569{
570 {570 {
571 std::unique_lock<std::mutex> lock(session_mutex);571 std::lock_guard<decltype(session_lock)> lock{session_lock};
572 auto session = weak_session.lock();572 auto session = weak_session.lock();
573573
574 if (session.get() == nullptr)574 if (session.get() == nullptr)
@@ -600,7 +600,7 @@
600 ::google::protobuf::Closure *done)600 ::google::protobuf::Closure *done)
601{601{
602 {602 {
603 std::unique_lock<std::mutex> lock(session_mutex);603 std::lock_guard<decltype(session_lock)> lock{session_lock};
604604
605 auto session = weak_session.lock();605 auto session = weak_session.lock();
606606
@@ -626,7 +626,7 @@
626 google::protobuf::Closure* done)626 google::protobuf::Closure* done)
627{627{
628 {628 {
629 std::unique_lock<std::mutex> lock(session_mutex);629 std::lock_guard<decltype(session_lock)> lock{session_lock};
630 auto session = weak_session.lock();630 auto session = weak_session.lock();
631631
632 if (session.get() == nullptr)632 if (session.get() == nullptr)
@@ -660,7 +660,7 @@
660 google::protobuf::Closure* done)660 google::protobuf::Closure* done)
661{661{
662 {662 {
663 std::unique_lock<std::mutex> lock(session_mutex);663 std::lock_guard<decltype(session_lock)> lock{session_lock};
664 auto session = weak_session.lock();664 auto session = weak_session.lock();
665665
666 if (session.get() == nullptr)666 if (session.get() == nullptr)
@@ -695,7 +695,7 @@
695 ::google::protobuf::Closure* done)695 ::google::protobuf::Closure* done)
696{696{
697 {697 {
698 std::unique_lock<std::mutex> lock(session_mutex);698 std::lock_guard<decltype(session_lock)> lock{session_lock};
699 auto const session = weak_session.lock();699 auto const session = weak_session.lock();
700700
701 if (!session)701 if (!session)
@@ -721,7 +721,7 @@
721 ::google::protobuf::Closure* done)721 ::google::protobuf::Closure* done)
722{722{
723 {723 {
724 std::unique_lock<std::mutex> lock(session_mutex);724 std::lock_guard<decltype(session_lock)> lock{session_lock};
725 auto const session = weak_session.lock();725 auto const session = weak_session.lock();
726726
727 if (!session)727 if (!session)
728728
=== modified file 'src/server/frontend/session_mediator.h'
--- src/server/frontend/session_mediator.h 2015-01-21 07:34:50 +0000
+++ src/server/frontend/session_mediator.h 2015-03-04 20:00:48 +0000
@@ -20,6 +20,7 @@
20#define MIR_FRONTEND_SESSION_MEDIATOR_H_20#define MIR_FRONTEND_SESSION_MEDIATOR_H_
2121
22#include "display_server.h"22#include "display_server.h"
23#include "mir/semaphore_lock.h"
23#include "mir/frontend/connection_context.h"24#include "mir/frontend/connection_context.h"
24#include "mir/frontend/surface_id.h"25#include "mir/frontend/surface_id.h"
25#include "mir/graphics/platform_ipc_operations.h"26#include "mir/graphics/platform_ipc_operations.h"
@@ -213,7 +214,7 @@
213214
214 SurfaceTracker surface_tracker;215 SurfaceTracker surface_tracker;
215216
216 std::mutex session_mutex;217 SemaphoreLock session_lock;
217 std::weak_ptr<Session> weak_session;218 std::weak_ptr<Session> weak_session;
218 std::weak_ptr<PromptSession> weak_prompt_session;219 std::weak_ptr<PromptSession> weak_prompt_session;
219};220};
220221
=== modified file 'tests/unit-tests/CMakeLists.txt'
--- tests/unit-tests/CMakeLists.txt 2015-03-03 06:56:37 +0000
+++ tests/unit-tests/CMakeLists.txt 2015-03-04 20:00:48 +0000
@@ -60,6 +60,7 @@
60 test_shared_library_prober.cpp60 test_shared_library_prober.cpp
61 test_lockable_callback.cpp61 test_lockable_callback.cpp
62 test_module_deleter.cpp62 test_module_deleter.cpp
63 test_semaphore_lock.cpp
63)64)
6465
65add_subdirectory(options/)66add_subdirectory(options/)
6667
=== added file 'tests/unit-tests/test_semaphore_lock.cpp'
--- tests/unit-tests/test_semaphore_lock.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/test_semaphore_lock.cpp 2015-03-04 20:00:48 +0000
@@ -0,0 +1,56 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,
6 * as 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: Alberto Aguirre <alberto.aguirre@canonical.com>
17 */
18
19#include "mir/semaphore_lock.h"
20
21#include <gtest/gtest.h>
22
23#include <exception>
24
25using namespace testing;
26
27namespace
28{
29void lock_and_throw(mir::SemaphoreLock& guard)
30{
31 mir::SemaphoreUnlockIfUnwinding lock{guard};
32 throw std::runtime_error("");
33}
34}
35
36TEST(SemaphoreLock, throws_on_unlock_if_already_unlocked)
37{
38 mir::SemaphoreLock guard;
39 EXPECT_THROW(guard.unlock(), std::logic_error);
40}
41
42TEST(SemaphoreUnlockIfUnwinding, keeps_lock_if_no_exception)
43{
44 mir::SemaphoreLock guard;
45 {
46 mir::SemaphoreUnlockIfUnwinding lock{guard};
47 }
48 EXPECT_NO_THROW(guard.unlock());
49}
50
51TEST(SemaphoreUnlockIfUnwinding, unlocks_after_exception)
52{
53 mir::SemaphoreLock guard;
54 EXPECT_THROW(lock_and_throw(guard), std::runtime_error);
55 EXPECT_THROW(guard.unlock(), std::logic_error);
56}

Subscribers

People subscribed via source and target branches