Mir

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

Proposed by Alberto Aguirre
Status: Rejected
Rejected by: Alberto Aguirre
Proposed branch: lp:~albaguirre/mir/fix-1522105
Merge into: lp:mir
Diff against target: 253 lines (+87/-24)
3 files modified
src/server/scene/surface_stack.cpp (+15/-9)
src/server/scene/surface_stack.h (+5/-4)
tests/unit-tests/scene/test_surface_stack.cpp (+67/-11)
To merge this branch: bzr merge lp:~albaguirre/mir/fix-1522105
Reviewer Review Type Date Requested Status
Alan Griffiths Abstain
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+279393@code.launchpad.net

Commit message

Fix SurfaceStack::add_observer iterating over surface list while dropping/reacquiring its mutex. (LP: #1522105)

Another thread could call SurfaceStack::surface_removed while the lock is dropped, invalidating the iterator, leading to runtime errors.

A secondary lock is used to ensure surface_removed and add_observer are mutually exclusive thus ensuring the order of calls to Observer::surface_exists and Observer::surface_removed is as expected and so that Observer::surface_exists is never called with a stale surface pointer.

Description of the change

A bit ugly but fixes the bug. Note that a surface_exists handler which triggers another thread to remove a surface from the scene could potentially deadlock - but I fail to a reason we should support that.

To post a comment you must log in.
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Clearly the "before" is worse than the proposal - it holds (and uses) a reference to an element that needs to be locked to avoid invalidation.

While there are no obvious problems with the proposed approach it isn't obvious that there are no problems. Maybe this is a case for RecursiveReadWriteMutex?

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I've put up an alternative using RecursiveReadWriteMutex:

    lp:~alan-griffiths/mir/fix-1522105/+merge/279418

I'll let the voters choose.

review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/server/scene/surface_stack.cpp'
--- src/server/scene/surface_stack.cpp 2015-11-05 11:51:04 +0000
+++ src/server/scene/surface_stack.cpp 2015-12-03 07:23:10 +0000
@@ -221,7 +221,7 @@
221 }221 }
222 overlays.erase(p);222 overlays.erase(p);
223 }223 }
224 224
225 emit_scene_changed();225 emit_scene_changed();
226}226}
227227
@@ -252,7 +252,7 @@
252void ms::SurfaceStack::remove_surface(std::weak_ptr<Surface> const& surface)252void ms::SurfaceStack::remove_surface(std::weak_ptr<Surface> const& surface)
253{253{
254 auto const keep_alive = surface.lock();254 auto const keep_alive = surface.lock();
255255 std::lock_guard<decltype(remove_guard)> lg{remove_guard};
256 bool found_surface = false;256 bool found_surface = false;
257 {257 {
258 std::lock_guard<decltype(guard)> lg(guard);258 std::lock_guard<decltype(guard)> lg(guard);
@@ -378,13 +378,19 @@
378{378{
379 observers.add(observer);379 observers.add(observer);
380380
381 // Notify observer of existing surfaces381 // NOTE: Guarantee that surface_exists will not be called with stale surfaces
382 // by preventing surface_removed from removing surfaces before we are done here
383 std::lock_guard<decltype(remove_guard)> rlk{remove_guard};
384
385 // A copy of the surfaces is taken, as raise and add_surface can still invalidate
386 // the iterators and surface_exists should not be called with the surface list lock held.
382 std::unique_lock<decltype(guard)> lk(guard);387 std::unique_lock<decltype(guard)> lk(guard);
383 for (auto &surface : surfaces)388 auto const surfaces_copy = surfaces;
389 lk.unlock();
390
391 for (auto &surface : surfaces_copy)
384 {392 {
385 lk.unlock();
386 observer->surface_exists(surface.get());393 observer->surface_exists(surface.get());
387 lk.lock();
388 }394 }
389}395}
390396
@@ -393,13 +399,13 @@
393 auto o = observer.lock();399 auto o = observer.lock();
394 if (!o)400 if (!o)
395 BOOST_THROW_EXCEPTION(std::logic_error("Invalid observer (destroyed)"));401 BOOST_THROW_EXCEPTION(std::logic_error("Invalid observer (destroyed)"));
396 402
397 o->end_observation();403 o->end_observation();
398 404
399 observers.remove(o);405 observers.remove(o);
400}406}
401407
402void ms::Observers::surface_added(ms::Surface* surface) 408void ms::Observers::surface_added(ms::Surface* surface)
403{409{
404 for_each([&](std::shared_ptr<Observer> const& observer)410 for_each([&](std::shared_ptr<Observer> const& observer)
405 { observer->surface_added(surface); });411 { observer->surface_added(surface); });
406412
=== modified file 'src/server/scene/surface_stack.h'
--- src/server/scene/surface_stack.h 2015-11-25 16:32:07 +0000
+++ src/server/scene/surface_stack.h 2015-12-03 07:23:10 +0000
@@ -88,16 +88,16 @@
88 void add_surface(88 void add_surface(
89 std::shared_ptr<Surface> const& surface,89 std::shared_ptr<Surface> const& surface,
90 input::InputReceptionMode input_mode) override;90 input::InputReceptionMode input_mode) override;
91 91
92 auto surface_at(geometry::Point) const -> std::shared_ptr<Surface> override;92 auto surface_at(geometry::Point) const -> std::shared_ptr<Surface> override;
9393
94 void add_observer(std::shared_ptr<Observer> const& observer) override;94 void add_observer(std::shared_ptr<Observer> const& observer) override;
95 void remove_observer(std::weak_ptr<Observer> const& observer) override;95 void remove_observer(std::weak_ptr<Observer> const& observer) override;
96 96
97 // Intended for input overlays, as described in mir::input::Scene documentation.97 // Intended for input overlays, as described in mir::input::Scene documentation.
98 void add_input_visualization(std::shared_ptr<graphics::Renderable> const& overlay) override;98 void add_input_visualization(std::shared_ptr<graphics::Renderable> const& overlay) override;
99 void remove_input_visualization(std::weak_ptr<graphics::Renderable> const& overlay) override;99 void remove_input_visualization(std::weak_ptr<graphics::Renderable> const& overlay) override;
100 100
101 void emit_scene_changed() override;101 void emit_scene_changed() override;
102102
103private:103private:
@@ -107,6 +107,7 @@
107 void update_rendering_tracker_compositors();107 void update_rendering_tracker_compositors();
108108
109 std::mutex mutable guard;109 std::mutex mutable guard;
110 std::recursive_mutex mutable remove_guard;
110111
111 std::shared_ptr<InputRegistrar> const input_registrar;112 std::shared_ptr<InputRegistrar> const input_registrar;
112 std::shared_ptr<SceneReport> const report;113 std::shared_ptr<SceneReport> const report;
@@ -114,7 +115,7 @@
114 std::vector<std::shared_ptr<Surface>> surfaces;115 std::vector<std::shared_ptr<Surface>> surfaces;
115 std::map<Surface*,std::shared_ptr<RenderingTracker>> rendering_trackers;116 std::map<Surface*,std::shared_ptr<RenderingTracker>> rendering_trackers;
116 std::set<compositor::CompositorID> registered_compositors;117 std::set<compositor::CompositorID> registered_compositors;
117 118
118 std::vector<std::shared_ptr<graphics::Renderable>> overlays;119 std::vector<std::shared_ptr<graphics::Renderable>> overlays;
119120
120 Observers observers;121 Observers observers;
121122
=== modified file 'tests/unit-tests/scene/test_surface_stack.cpp'
--- tests/unit-tests/scene/test_surface_stack.cpp 2015-11-05 11:51:04 +0000
+++ tests/unit-tests/scene/test_surface_stack.cpp 2015-12-03 07:23:10 +0000
@@ -349,7 +349,7 @@
349 .WillByDefault(InvokeWithoutArgs([&]{ready++;}));349 .WillByDefault(InvokeWithoutArgs([&]{ready++;}));
350 ON_CALL(*mock_queue, compositor_acquire(_))350 ON_CALL(*mock_queue, compositor_acquire(_))
351 .WillByDefault(InvokeWithoutArgs([&]{ready--; return mt::fake_shared(stub_buffer); }));351 .WillByDefault(InvokeWithoutArgs([&]{ready--; return mt::fake_shared(stub_buffer); }));
352 352
353 auto surface = std::make_shared<ms::BasicSurface>(353 auto surface = std::make_shared<ms::BasicSurface>(
354 std::string("stub"),354 std::string("stub"),
355 geom::Rectangle{{},{}},355 geom::Rectangle{{},{}},
@@ -620,20 +620,76 @@
620{620{
621 using namespace ::testing;621 using namespace ::testing;
622622
623 using namespace ::testing;
624
625 MockSceneObserver observer;623 MockSceneObserver observer;
626624
627 InSequence seq;625 InSequence seq;
628 EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1);626 EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1);
629 EXPECT_CALL(observer, surface_exists(stub_surface2.get())).Times(1);627 EXPECT_CALL(observer, surface_exists(stub_surface2.get())).Times(1);
630 628
631 stack.add_surface(stub_surface1, default_params.input_mode);629 stack.add_surface(stub_surface1, default_params.input_mode);
632 stack.add_surface(stub_surface2, default_params.input_mode);630 stack.add_surface(stub_surface2, default_params.input_mode);
633631
634 stack.add_observer(mt::fake_shared(observer));632 stack.add_observer(mt::fake_shared(observer));
635}633}
636634
635TEST_F(SurfaceStack, scene_observer_can_query_scene_within_surface_exists_notification)
636{
637 using namespace ::testing;
638
639 MockSceneObserver observer;
640
641 auto const scene_query = [&]{
642 stack.for_each([&](std::shared_ptr<mi::Surface> const& surface){
643 EXPECT_THAT(surface.get(), Eq(stub_surface1.get()));
644 });
645 };
646 EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1)
647 .WillOnce(InvokeWithoutArgs(scene_query));
648
649 stack.add_surface(stub_surface1, default_params.input_mode);
650 stack.add_observer(mt::fake_shared(observer));
651}
652
653TEST_F(SurfaceStack, scene_observer_can_async_query_scene_within_surface_exists_notification)
654{
655 using namespace ::testing;
656
657 MockSceneObserver observer;
658
659 auto const scene_query = [&]{
660 stack.for_each([&](std::shared_ptr<mi::Surface> const& surface){
661 EXPECT_THAT(surface.get(), Eq(stub_surface1.get()));
662 });
663 };
664
665 auto const async_scene_query = [&]{
666 std::async(std::launch::async, scene_query);
667 };
668
669 EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1)
670 .WillOnce(InvokeWithoutArgs(async_scene_query));
671
672 stack.add_surface(stub_surface1, default_params.input_mode);
673 stack.add_observer(mt::fake_shared(observer));
674}
675
676
677TEST_F(SurfaceStack, scene_observer_can_remove_surface_from_scene_within_surface_exists_notification)
678{
679 using namespace ::testing;
680
681 MockSceneObserver observer;
682
683 auto const surface_removal = [&]{
684 stack.remove_surface(stub_surface1);
685 };
686 EXPECT_CALL(observer, surface_exists(stub_surface1.get())).Times(1)
687 .WillOnce(InvokeWithoutArgs(surface_removal));
688
689 stack.add_surface(stub_surface1, default_params.input_mode);
690 stack.add_observer(mt::fake_shared(observer));
691}
692
637TEST_F(SurfaceStack, surfaces_reordered)693TEST_F(SurfaceStack, surfaces_reordered)
638{694{
639 using namespace ::testing;695 using namespace ::testing;
@@ -923,7 +979,7 @@
923TEST_F(SurfaceStack, overlays_do_not_appear_in_input_enumeration)979TEST_F(SurfaceStack, overlays_do_not_appear_in_input_enumeration)
924{980{
925 mtd::StubRenderable r;981 mtd::StubRenderable r;
926 982
927 stack.add_surface(stub_surface1, default_params.input_mode);983 stack.add_surface(stub_surface1, default_params.input_mode);
928 stack.add_surface(stub_surface2, default_params.input_mode);984 stack.add_surface(stub_surface2, default_params.input_mode);
929985
@@ -946,7 +1002,7 @@
946 using namespace ::testing;1002 using namespace ::testing;
9471003
948 mtd::StubRenderable r;1004 mtd::StubRenderable r;
949 1005
950 stack.add_surface(stub_surface1, default_params.input_mode);1006 stack.add_surface(stub_surface1, default_params.input_mode);
951 stack.add_input_visualization(mt::fake_shared(r));1007 stack.add_input_visualization(mt::fake_shared(r));
952 stack.add_surface(stub_surface2, default_params.input_mode);1008 stack.add_surface(stub_surface2, default_params.input_mode);
@@ -956,7 +1012,7 @@
956 ElementsAre(1012 ElementsAre(
957 SceneElementForSurface(stub_surface1),1013 SceneElementForSurface(stub_surface1),
958 SceneElementForSurface(stub_surface2),1014 SceneElementForSurface(stub_surface2),
959 SceneElementForStream(mt::fake_shared(r)))); 1015 SceneElementForStream(mt::fake_shared(r))));
960}1016}
9611017
962TEST_F(SurfaceStack, removed_overlays_are_removed)1018TEST_F(SurfaceStack, removed_overlays_are_removed)
@@ -964,7 +1020,7 @@
964 using namespace ::testing;1020 using namespace ::testing;
9651021
966 mtd::StubRenderable r;1022 mtd::StubRenderable r;
967 1023
968 stack.add_surface(stub_surface1, default_params.input_mode);1024 stack.add_surface(stub_surface1, default_params.input_mode);
969 stack.add_input_visualization(mt::fake_shared(r));1025 stack.add_input_visualization(mt::fake_shared(r));
970 stack.add_surface(stub_surface2, default_params.input_mode);1026 stack.add_surface(stub_surface2, default_params.input_mode);
@@ -975,7 +1031,7 @@
975 SceneElementForSurface(stub_surface1),1031 SceneElementForSurface(stub_surface1),
976 SceneElementForSurface(stub_surface2),1032 SceneElementForSurface(stub_surface2),
977 SceneElementForStream(mt::fake_shared(r))));1033 SceneElementForStream(mt::fake_shared(r))));
978 1034
979 stack.remove_input_visualization(mt::fake_shared(r));1035 stack.remove_input_visualization(mt::fake_shared(r));
9801036
981 EXPECT_THAT(1037 EXPECT_THAT(
@@ -991,10 +1047,10 @@
9911047
992 EXPECT_CALL(o1, scene_changed()).Times(1);1048 EXPECT_CALL(o1, scene_changed()).Times(1);
993 EXPECT_CALL(o2, scene_changed()).Times(1);1049 EXPECT_CALL(o2, scene_changed()).Times(1);
994 1050
995 stack.add_observer(mt::fake_shared(o1));1051 stack.add_observer(mt::fake_shared(o1));
996 stack.add_observer(mt::fake_shared(o2));1052 stack.add_observer(mt::fake_shared(o2));
997 1053
998 stack.emit_scene_changed();1054 stack.emit_scene_changed();
999}1055}
10001056

Subscribers

People subscribed via source and target branches