Mir

Merge lp:~kdub/mir/client-resize-logic into lp:mir

Proposed by Kevin DuBois
Status: Merged
Approved by: Kevin DuBois
Approved revision: no longer in the source branch.
Merged at revision: 2878
Proposed branch: lp:~kdub/mir/client-resize-logic
Merge into: lp:mir
Diff against target: 251 lines (+113/-14)
3 files modified
src/client/buffer_vault.cpp (+26/-6)
src/client/buffer_vault.h (+3/-1)
tests/unit-tests/client/test_buffer_vault.cpp (+84/-7)
To merge this branch: bzr merge lp:~kdub/mir/client-resize-logic
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Alexandros Frantzis (community) Approve
Chris Halse Rogers Approve
Review via email: mp+268269@code.launchpad.net

Commit message

client: add logic to free and allocate buffers of the proper size after a buffer size change.

Description of the change

client: add logic to free and allocate buffers of the proper size after a buffer size change.

There's a bit of plumbing to pass the resize messages to the stream that goes along with this; but its dependent on other yet-unlanded branches.

I tried a few places in the buffer lifecycle to free and allocate. Between the compositor release and the client usage is where we currently do this for exchange-semantics, and this place is the analogous place on the client side. Its a good place to avoid spamming the server with allocation and free requests when rapid resizing occurs.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

«Insert obligatory comment about how I'm pretty sure this all goes away after transition ☺»

186 +TEST_F(StartedBufferVault, frees_incoming_buffers_of_incorrect_size_with_immediate_response)
207 +TEST_F(StartedBufferVault, frees_incoming_buffers_of_incorrect_size_with_delayed_response)

There doesn't seem to be any reason why these use InSequence tests. The implementation happens to free before allocating, but would be perfectly correct to allocate before free; the InSequence isn't protecting a useful invariant of the code.

207 +TEST_F(StartedBufferVault, frees_incoming_buffers_of_incorrect_size_with_delayed_response)

There doesn't seem to be any reason to expect an allocation here? We're testing that the buffer is freed, right? We have
226 +TEST_F(StartedBufferVault, withdraw_gives_only_newly_sized_buffers_after_resize)
to test that we actually get correctly-sized buffers.

The code itself looks sane ;).

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

@InSequence
Removed the requirement, it could free or allocate in either order.
@allocation expectation
It is useful to test where the allocation happens. Another (bad) approach could be to allocate a new buffer on the resize() and this would create some buffers that may not get used if rapidly resizing. Opted to change the test name instead.

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

Fair chop.

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

Looks good overall.

55 + lk.unlock();
56 + server_requests->free_buffer(it->first);
57 + server_requests->allocate_buffer(size, format, usage);
58 + lk.lock();

I am bit wary of unlocking to perform the reallocation, as, in theory at least, it could allow other threads to come and mess up the state. Not sure if this could happen in practice in this case, but it would be good to know.

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

Looks good.

review: Approve
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 'src/client/buffer_vault.cpp'
2--- src/client/buffer_vault.cpp 2015-08-10 17:04:35 +0000
3+++ src/client/buffer_vault.cpp 2015-08-25 13:27:45 +0000
4@@ -41,7 +41,9 @@
5 geom::Size size, MirPixelFormat format, int usage, unsigned int initial_nbuffers) :
6 factory(client_buffer_factory),
7 server_requests(server_requests),
8- format(format)
9+ format(format),
10+ usage(usage),
11+ size(size)
12 {
13 for (auto i = 0u; i < initial_nbuffers; i++)
14 server_requests->allocate_buffer(size, format, usage);
15@@ -58,7 +60,8 @@
16 std::lock_guard<std::mutex> lk(mutex);
17 std::promise<std::shared_ptr<mcl::ClientBuffer>> promise;
18 auto it = std::find_if(buffers.begin(), buffers.end(),
19- [](std::pair<int, BufferEntry> const& entry) { return entry.second.owner == Owner::Self; });
20+ [this](std::pair<int, BufferEntry> const& entry) {
21+ return ((entry.second.owner == Owner::Self) && (size == entry.second.buffer->size())); });
22
23 auto future = promise.get_future();
24 if (it != buffers.end())
25@@ -68,7 +71,6 @@
26 }
27 else
28 {
29- //TODO: We'll eventually overallocate a limited number of buffers here instead of promising.
30 promises.emplace_back(std::move(promise));
31 }
32 return future;
33@@ -113,7 +115,7 @@
34 package->width = protobuf_buffer.width();
35 package->height = protobuf_buffer.height();
36
37- std::lock_guard<std::mutex> lk(mutex);
38+ std::unique_lock<std::mutex> lk(mutex);
39 auto it = buffers.find(protobuf_buffer.buffer_id());
40 if (it == buffers.end())
41 {
42@@ -122,8 +124,20 @@
43 }
44 else
45 {
46- it->second.owner = Owner::Self;
47- it->second.buffer->update_from(*package);
48+ if (size == it->second.buffer->size())
49+ {
50+ it->second.owner = Owner::Self;
51+ it->second.buffer->update_from(*package);
52+ }
53+ else
54+ {
55+ int id = it->first;
56+ buffers.erase(it);
57+ lk.unlock();
58+ server_requests->free_buffer(id);
59+ server_requests->allocate_buffer(size, format, usage);
60+ return;
61+ }
62 }
63
64 if (!promises.empty())
65@@ -133,3 +147,9 @@
66 promises.pop_front();
67 }
68 }
69+
70+void mcl::BufferVault::set_size(geom::Size sz)
71+{
72+ std::lock_guard<std::mutex> lk(mutex);
73+ size = sz;
74+}
75
76=== modified file 'src/client/buffer_vault.h'
77--- src/client/buffer_vault.h 2015-07-30 17:06:34 +0000
78+++ src/client/buffer_vault.h 2015-08-25 13:27:45 +0000
79@@ -61,12 +61,13 @@
80 void deposit(std::shared_ptr<ClientBuffer> const& buffer);
81 void wire_transfer_inbound(protobuf::Buffer const&);
82 void wire_transfer_outbound(std::shared_ptr<ClientBuffer> const& buffer);
83- //TODO: class will handle allocation, transition, and destruction around resize notification
84+ void set_size(geometry::Size);
85
86 private:
87 std::shared_ptr<ClientBufferFactory> const factory;
88 std::shared_ptr<ServerBufferRequests> const server_requests;
89 MirPixelFormat const format;
90+ int const usage;
91
92 enum class Owner;
93 struct BufferEntry
94@@ -78,6 +79,7 @@
95 std::mutex mutex;
96 std::map<int, BufferEntry> buffers;
97 std::deque<std::promise<std::shared_ptr<ClientBuffer>>> promises;
98+ geometry::Size size;
99 };
100 }
101 }
102
103=== modified file 'tests/unit-tests/client/test_buffer_vault.cpp'
104--- tests/unit-tests/client/test_buffer_vault.cpp 2015-08-10 17:04:35 +0000
105+++ tests/unit-tests/client/test_buffer_vault.cpp 2015-08-25 13:27:45 +0000
106@@ -42,8 +42,10 @@
107 {
108 struct MockBuffer : public mcl::AgingBuffer
109 {
110- MockBuffer()
111+ MockBuffer(geom::Size sz)
112 {
113+ ON_CALL(*this, size())
114+ .WillByDefault(Return(sz));
115 ON_CALL(*this, mark_as_submitted())
116 .WillByDefault(Invoke([this](){this->AgingBuffer::mark_as_submitted();}));
117 }
118@@ -65,9 +67,12 @@
119 MockClientBufferFactory()
120 {
121 ON_CALL(*this, create_buffer(_,_,_))
122- .WillByDefault(InvokeWithoutArgs([] { return std::make_shared<NiceMock<MockBuffer>>(); }));
123+ .WillByDefault(Invoke([](
124+ std::shared_ptr<MirBufferPackage> const&, geom::Size size, MirPixelFormat)
125+ {
126+ return std::make_shared<NiceMock<MockBuffer>>(size);
127+ }));
128 }
129-
130 MOCK_METHOD3(create_buffer, std::shared_ptr<mcl::ClientBuffer>(
131 std::shared_ptr<MirBufferPackage> const&, geom::Size, MirPixelFormat));
132 };
133@@ -85,6 +90,10 @@
134 {
135 package.set_width(size.width.as_int());
136 package.set_height(size.height.as_int());
137+ package2.set_width(size.width.as_int());
138+ package2.set_height(size.height.as_int());
139+ package3.set_width(size.width.as_int());
140+ package3.set_height(size.height.as_int());
141
142 package.set_buffer_id(1);
143 package2.set_buffer_id(2);
144@@ -145,7 +154,7 @@
145
146 TEST_F(BufferVault, updates_buffer_on_subsequent_insertions)
147 {
148- auto mock_buffer = std::make_shared<NiceMock<MockBuffer>>();
149+ auto mock_buffer = std::make_shared<NiceMock<MockBuffer>>(size);
150 EXPECT_CALL(*mock_buffer, update_from(_));
151 ON_CALL(mock_factory, create_buffer(_,_,_))
152 .WillByDefault(Return(mock_buffer));
153@@ -195,7 +204,7 @@
154
155 TEST_F(StartedBufferVault, depositing_external_buffer_throws)
156 {
157- auto buffer = std::make_shared<MockBuffer>();
158+ auto buffer = std::make_shared<MockBuffer>(size);
159 EXPECT_THROW({
160 vault.deposit(buffer);
161 }, std::logic_error);
162@@ -265,7 +274,7 @@
163
164 TEST_F(BufferVault, ages_buffer_on_deposit)
165 {
166- auto mock_buffer = std::make_shared<NiceMock<MockBuffer>>();
167+ auto mock_buffer = std::make_shared<NiceMock<MockBuffer>>(size);
168 EXPECT_CALL(*mock_buffer, increment_age());
169 ON_CALL(mock_factory, create_buffer(_,_,_))
170 .WillByDefault(Return(mock_buffer));
171@@ -278,7 +287,7 @@
172
173 TEST_F(BufferVault, marks_as_submitted_on_transfer)
174 {
175- auto mock_buffer = std::make_shared<NiceMock<MockBuffer>>();
176+ auto mock_buffer = std::make_shared<NiceMock<MockBuffer>>(size);
177 EXPECT_CALL(*mock_buffer, mark_as_submitted());
178 ON_CALL(mock_factory, create_buffer(_,_,_))
179 .WillByDefault(Return(mock_buffer));
180@@ -304,3 +313,71 @@
181 }
182 EXPECT_THAT(buffers, Each(buffers[0]));
183 }
184+
185+
186+TEST_F(StartedBufferVault, reallocates_incoming_buffers_of_incorrect_size_with_immediate_response)
187+{
188+ mp::Buffer package4;
189+ geom::Size new_size{80, 100};
190+ EXPECT_CALL(mock_requests, free_buffer(package.buffer_id()));
191+ EXPECT_CALL(mock_requests, allocate_buffer(new_size,_,_))
192+ .WillOnce(Invoke(
193+ [&, this](geom::Size sz, MirPixelFormat, int)
194+ {
195+ package4.set_width(sz.width.as_int());
196+ package4.set_height(sz.height.as_int());
197+ package4.set_buffer_id(4);
198+ vault.wire_transfer_inbound(package4);
199+ }));
200+
201+ vault.set_size(new_size);
202+ vault.wire_transfer_inbound(package);
203+ Mock::VerifyAndClearExpectations(&mock_requests);
204+}
205+
206+TEST_F(StartedBufferVault, reallocates_incoming_buffers_of_incorrect_size_with_delayed_response)
207+{
208+ geom::Size new_size{80, 100};
209+ mp::Buffer package4;
210+ package4.set_width(new_size.width.as_int());
211+ package4.set_height(new_size.height.as_int());
212+ package4.set_buffer_id(4);
213+
214+ EXPECT_CALL(mock_requests, free_buffer(package.buffer_id()));
215+ EXPECT_CALL(mock_requests, allocate_buffer(new_size,_,_));
216+
217+ vault.set_size(new_size);
218+ vault.wire_transfer_inbound(package);
219+ vault.wire_transfer_inbound(package4);
220+ EXPECT_THAT(vault.withdraw().get()->size(), Eq(new_size));
221+ Mock::VerifyAndClearExpectations(&mock_requests);
222+}
223+
224+TEST_F(StartedBufferVault, withdraw_gives_only_newly_sized_buffers_after_resize)
225+{
226+ mp::Buffer package4;
227+ geom::Size new_size{80, 100};
228+ package4.set_width(new_size.width.as_int());
229+ package4.set_height(new_size.height.as_int());
230+ package4.set_buffer_id(4);
231+
232+ vault.set_size(new_size);
233+ vault.wire_transfer_inbound(package4);
234+
235+ EXPECT_THAT(vault.withdraw().get()->size(), Eq(new_size));
236+ Mock::VerifyAndClearExpectations(&mock_requests);
237+}
238+
239+TEST_F(StartedBufferVault, simply_setting_size_triggers_no_server_interations)
240+{
241+ EXPECT_CALL(mock_requests, free_buffer(_)).Times(0);
242+ EXPECT_CALL(mock_requests, allocate_buffer(_,_,_)).Times(0);
243+ auto const cycles = 30u;
244+ geom::Size new_size(80, 100);
245+ for(auto i = 0u; i < cycles; i++)
246+ {
247+ new_size = geom::Size(geom::Width(i), new_size.height);
248+ vault.set_size(new_size);
249+ }
250+ Mock::VerifyAndClearExpectations(&mock_requests);
251+}

Subscribers

People subscribed via source and target branches