Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(122)

Side by Side Diff: mojo/system/local_data_pipe.cc

Issue 129163003: Mojo: DataPipe: Implement "may discard" for two-phase writes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: oops Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « mojo/system/local_data_pipe.h ('k') | mojo/system/local_data_pipe_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 // TODO(vtl): I currently potentially overflow in doing index calculations. 5 // TODO(vtl): I currently potentially overflow in doing index calculations.
6 // E.g., |start_index_| and |current_num_bytes_| fit into a |uint32_t|, but 6 // E.g., |start_index_| and |current_num_bytes_| fit into a |uint32_t|, but
7 // their sum may not. This is bad and poses a security risk. (We're currently 7 // their sum may not. This is bad and poses a security risk. (We're currently
8 // saved by the limit on capacity -- the maximum size of the buffer, checked in 8 // saved by the limit on capacity -- the maximum size of the buffer, checked in
9 // |DataPipe::ValidateOptions()|, is currently sufficiently small. 9 // |DataPipe::ValidateOptions()|, is currently sufficiently small.
10 10
(...skipping 24 matching lines...) Expand all
35 // If the consumer is still open and we still have data, we have to keep the 35 // If the consumer is still open and we still have data, we have to keep the
36 // buffer around. Currently, we won't free it even if it empties later. (We 36 // buffer around. Currently, we won't free it even if it empties later. (We
37 // could do this -- requiring a check on every read -- but that seems to be 37 // could do this -- requiring a check on every read -- but that seems to be
38 // optimizing for the uncommon case.) 38 // optimizing for the uncommon case.)
39 if (!consumer_open_no_lock() || !current_num_bytes_) { 39 if (!consumer_open_no_lock() || !current_num_bytes_) {
40 // Note: There can only be a two-phase *read* (by the consumer) if we still 40 // Note: There can only be a two-phase *read* (by the consumer) if we still
41 // have data. 41 // have data.
42 DCHECK(!consumer_in_two_phase_read_no_lock()); 42 DCHECK(!consumer_in_two_phase_read_no_lock());
43 DestroyBufferNoLock(); 43 DestroyBufferNoLock();
44 } 44 }
45 AwakeConsumerWaitersForStateChangeNoLock();
46 } 45 }
47 46
48 MojoResult LocalDataPipe::ProducerWriteDataImplNoLock(const void* elements, 47 MojoResult LocalDataPipe::ProducerWriteDataImplNoLock(const void* elements,
49 uint32_t* num_bytes, 48 uint32_t* num_bytes,
50 bool all_or_none) { 49 bool all_or_none) {
51 DCHECK_EQ(*num_bytes % element_num_bytes(), 0u); 50 DCHECK_EQ(*num_bytes % element_num_bytes(), 0u);
52 DCHECK_GT(*num_bytes, 0u); 51 DCHECK_GT(*num_bytes, 0u);
53 DCHECK(consumer_open_no_lock()); 52 DCHECK(consumer_open_no_lock());
54 53
55 size_t num_bytes_to_write = 0; 54 size_t num_bytes_to_write = 0;
56 if (may_discard()) { 55 if (may_discard()) {
57 if (all_or_none && *num_bytes > capacity_num_bytes()) 56 if (all_or_none && *num_bytes > capacity_num_bytes())
58 return MOJO_RESULT_OUT_OF_RANGE; 57 return MOJO_RESULT_OUT_OF_RANGE;
59 58
60 num_bytes_to_write = std::min(static_cast<size_t>(*num_bytes), 59 num_bytes_to_write = std::min(static_cast<size_t>(*num_bytes),
61 capacity_num_bytes()); 60 capacity_num_bytes());
62 if (num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) { 61 if (num_bytes_to_write > capacity_num_bytes() - current_num_bytes_) {
63 // Discard as much as needed (discard oldest first). 62 // Discard as much as needed (discard oldest first).
64 size_t num_bytes_to_discard = 63 MarkDataAsConsumedNoLock(
65 num_bytes_to_write - (capacity_num_bytes() - current_num_bytes_); 64 num_bytes_to_write - (capacity_num_bytes() - current_num_bytes_));
66 start_index_ += num_bytes_to_discard; 65 // No need to wake up write waiters, since we're definitely going to leave
67 start_index_ %= capacity_num_bytes(); 66 // the buffer full.
68 current_num_bytes_ -= num_bytes_to_discard;
69 } 67 }
70 } else { 68 } else {
71 if (all_or_none && *num_bytes > capacity_num_bytes() - current_num_bytes_) { 69 if (all_or_none && *num_bytes > capacity_num_bytes() - current_num_bytes_) {
72 // Don't return "should wait" since you can't wait for a specified amount 70 // Don't return "should wait" since you can't wait for a specified amount
73 // of data. 71 // of data.
74 return MOJO_RESULT_OUT_OF_RANGE; 72 return MOJO_RESULT_OUT_OF_RANGE;
75 } 73 }
76 74
77 num_bytes_to_write = std::min(static_cast<size_t>(*num_bytes), 75 num_bytes_to_write = std::min(static_cast<size_t>(*num_bytes),
78 capacity_num_bytes() - current_num_bytes_); 76 capacity_num_bytes() - current_num_bytes_);
(...skipping 10 matching lines...) Expand all
89 EnsureBufferNoLock(); 87 EnsureBufferNoLock();
90 memcpy(buffer_.get() + first_write_index, elements, num_bytes_to_write_first); 88 memcpy(buffer_.get() + first_write_index, elements, num_bytes_to_write_first);
91 89
92 if (num_bytes_to_write_first < num_bytes_to_write) { 90 if (num_bytes_to_write_first < num_bytes_to_write) {
93 // The "second write index" is zero. 91 // The "second write index" is zero.
94 memcpy(buffer_.get(), 92 memcpy(buffer_.get(),
95 static_cast<const char*>(elements) + num_bytes_to_write_first, 93 static_cast<const char*>(elements) + num_bytes_to_write_first,
96 num_bytes_to_write - num_bytes_to_write_first); 94 num_bytes_to_write - num_bytes_to_write_first);
97 } 95 }
98 96
99 bool was_empty = (current_num_bytes_ == 0);
100
101 current_num_bytes_ += num_bytes_to_write; 97 current_num_bytes_ += num_bytes_to_write;
102 DCHECK_LE(current_num_bytes_, capacity_num_bytes()); 98 DCHECK_LE(current_num_bytes_, capacity_num_bytes());
103
104 if (was_empty && num_bytes_to_write > 0)
105 AwakeConsumerWaitersForStateChangeNoLock();
106
107 *num_bytes = static_cast<uint32_t>(num_bytes_to_write); 99 *num_bytes = static_cast<uint32_t>(num_bytes_to_write);
108 return MOJO_RESULT_OK; 100 return MOJO_RESULT_OK;
109 } 101 }
110 102
111 MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock( 103 MojoResult LocalDataPipe::ProducerBeginWriteDataImplNoLock(
112 void** buffer, 104 void** buffer,
113 uint32_t* buffer_num_bytes, 105 uint32_t* buffer_num_bytes,
114 bool all_or_none) { 106 bool all_or_none) {
115 DCHECK(consumer_open_no_lock()); 107 DCHECK(consumer_open_no_lock());
116 108
109 // The index we need to start writing at.
110 size_t write_index =
111 (start_index_ + current_num_bytes_) % capacity_num_bytes();
112
117 size_t max_num_bytes_to_write = GetMaxNumBytesToWriteNoLock(); 113 size_t max_num_bytes_to_write = GetMaxNumBytesToWriteNoLock();
118 if (all_or_none && *buffer_num_bytes > max_num_bytes_to_write) { 114 if (all_or_none && *buffer_num_bytes > max_num_bytes_to_write) {
119 // Don't return "should wait" since you can't wait for a specified amount of 115 // In "may discard" mode, we can always write from the write index to the
120 // data. 116 // end of the buffer.
121 return MOJO_RESULT_OUT_OF_RANGE; 117 if (may_discard() &&
118 *buffer_num_bytes <= capacity_num_bytes() - write_index) {
119 // To do so, we need to discard an appropriate amount of data.
120 // We should only reach here if the start index is after the write index!
121 DCHECK_GE(start_index_, write_index);
122 DCHECK_GT(*buffer_num_bytes - max_num_bytes_to_write, 0u);
123 MarkDataAsConsumedNoLock(*buffer_num_bytes - max_num_bytes_to_write);
124 max_num_bytes_to_write = *buffer_num_bytes;
125 } else {
126 // Don't return "should wait" since you can't wait for a specified amount
127 // of data.
128 return MOJO_RESULT_OUT_OF_RANGE;
129 }
122 } 130 }
123 131
124 // Don't go into a two-phase write if there's no room. 132 // Don't go into a two-phase write if there's no room.
125 if (max_num_bytes_to_write == 0) 133 if (max_num_bytes_to_write == 0)
126 return MOJO_RESULT_SHOULD_WAIT; 134 return MOJO_RESULT_SHOULD_WAIT;
127 135
128 size_t write_index =
129 (start_index_ + current_num_bytes_) % capacity_num_bytes();
130 EnsureBufferNoLock(); 136 EnsureBufferNoLock();
131 *buffer = buffer_.get() + write_index; 137 *buffer = buffer_.get() + write_index;
132 *buffer_num_bytes = static_cast<uint32_t>(max_num_bytes_to_write); 138 *buffer_num_bytes = static_cast<uint32_t>(max_num_bytes_to_write);
133 set_producer_two_phase_max_num_bytes_written_no_lock( 139 set_producer_two_phase_max_num_bytes_written_no_lock(
134 static_cast<uint32_t>(max_num_bytes_to_write)); 140 static_cast<uint32_t>(max_num_bytes_to_write));
135 return MOJO_RESULT_OK; 141 return MOJO_RESULT_OK;
136 } 142 }
137 143
138 MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock( 144 MojoResult LocalDataPipe::ProducerEndWriteDataImplNoLock(
139 uint32_t num_bytes_written) { 145 uint32_t num_bytes_written) {
140 if (num_bytes_written > producer_two_phase_max_num_bytes_written_no_lock()) { 146 if (num_bytes_written > producer_two_phase_max_num_bytes_written_no_lock()) {
141 // Note: The two-phase write ends here even on failure. 147 // Note: The two-phase write ends here even on failure.
142 set_producer_two_phase_max_num_bytes_written_no_lock(0); 148 set_producer_two_phase_max_num_bytes_written_no_lock(0);
143 return MOJO_RESULT_INVALID_ARGUMENT; 149 return MOJO_RESULT_INVALID_ARGUMENT;
144 } 150 }
145 151
146 bool was_empty = (current_num_bytes_ == 0);
147
148 current_num_bytes_ += num_bytes_written; 152 current_num_bytes_ += num_bytes_written;
149 DCHECK_LE(current_num_bytes_, capacity_num_bytes()); 153 DCHECK_LE(current_num_bytes_, capacity_num_bytes());
150 set_producer_two_phase_max_num_bytes_written_no_lock(0); 154 set_producer_two_phase_max_num_bytes_written_no_lock(0);
151
152 if (was_empty && num_bytes_written > 0)
153 AwakeConsumerWaitersForStateChangeNoLock();
154
155 return MOJO_RESULT_OK; 155 return MOJO_RESULT_OK;
156 } 156 }
157 157
158 MojoWaitFlags LocalDataPipe::ProducerSatisfiedFlagsNoLock() { 158 MojoWaitFlags LocalDataPipe::ProducerSatisfiedFlagsNoLock() {
159 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE; 159 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
160 if (consumer_open_no_lock() && current_num_bytes_ < capacity_num_bytes()) 160 if (consumer_open_no_lock() &&
161 (may_discard() || current_num_bytes_ < capacity_num_bytes()) &&
162 !producer_in_two_phase_write_no_lock())
161 rv |= MOJO_WAIT_FLAG_WRITABLE; 163 rv |= MOJO_WAIT_FLAG_WRITABLE;
162 return rv; 164 return rv;
163 } 165 }
164 166
165 MojoWaitFlags LocalDataPipe::ProducerSatisfiableFlagsNoLock() { 167 MojoWaitFlags LocalDataPipe::ProducerSatisfiableFlagsNoLock() {
166 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE; 168 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
167 if (consumer_open_no_lock()) 169 if (consumer_open_no_lock())
168 rv |= MOJO_WAIT_FLAG_WRITABLE; 170 rv |= MOJO_WAIT_FLAG_WRITABLE;
169 return rv; 171 return rv;
170 } 172 }
171 173
172 void LocalDataPipe::ConsumerCloseImplNoLock() { 174 void LocalDataPipe::ConsumerCloseImplNoLock() {
173 // If the producer is around and in a two-phase write, we have to keep the 175 // If the producer is around and in a two-phase write, we have to keep the
174 // buffer around. (We then don't free it until the producer is closed. This 176 // buffer around. (We then don't free it until the producer is closed. This
175 // could be rectified, but again seems like optimizing for the uncommon case.) 177 // could be rectified, but again seems like optimizing for the uncommon case.)
176 if (!producer_open_no_lock() || !producer_in_two_phase_write_no_lock()) 178 if (!producer_open_no_lock() || !producer_in_two_phase_write_no_lock())
177 DestroyBufferNoLock(); 179 DestroyBufferNoLock();
178 current_num_bytes_ = 0; 180 current_num_bytes_ = 0;
179 AwakeProducerWaitersForStateChangeNoLock();
180 } 181 }
181 182
182 MojoResult LocalDataPipe::ConsumerReadDataImplNoLock(void* elements, 183 MojoResult LocalDataPipe::ConsumerReadDataImplNoLock(void* elements,
183 uint32_t* num_bytes, 184 uint32_t* num_bytes,
184 bool all_or_none) { 185 bool all_or_none) {
185 DCHECK_EQ(*num_bytes % element_num_bytes(), 0u); 186 DCHECK_EQ(*num_bytes % element_num_bytes(), 0u);
186 DCHECK_GT(*num_bytes, 0u); 187 DCHECK_GT(*num_bytes, 0u);
187 188
188 if (all_or_none && *num_bytes > current_num_bytes_) { 189 if (all_or_none && *num_bytes > current_num_bytes_) {
189 // Don't return "should wait" since you can't wait for a specified amount of 190 // Don't return "should wait" since you can't wait for a specified amount of
(...skipping 14 matching lines...) Expand all
204 std::min(num_bytes_to_read, GetMaxNumBytesToReadNoLock()); 205 std::min(num_bytes_to_read, GetMaxNumBytesToReadNoLock());
205 memcpy(elements, buffer_.get() + start_index_, num_bytes_to_read_first); 206 memcpy(elements, buffer_.get() + start_index_, num_bytes_to_read_first);
206 207
207 if (num_bytes_to_read_first < num_bytes_to_read) { 208 if (num_bytes_to_read_first < num_bytes_to_read) {
208 // The "second read index" is zero. 209 // The "second read index" is zero.
209 memcpy(static_cast<char*>(elements) + num_bytes_to_read_first, 210 memcpy(static_cast<char*>(elements) + num_bytes_to_read_first,
210 buffer_.get(), 211 buffer_.get(),
211 num_bytes_to_read - num_bytes_to_read_first); 212 num_bytes_to_read - num_bytes_to_read_first);
212 } 213 }
213 214
214 bool was_full = (current_num_bytes_ == capacity_num_bytes()); 215 MarkDataAsConsumedNoLock(num_bytes_to_read);
215
216 start_index_ += num_bytes_to_read;
217 start_index_ %= capacity_num_bytes();
218 current_num_bytes_ -= num_bytes_to_read;
219
220 if (was_full && num_bytes_to_read > 0)
221 AwakeProducerWaitersForStateChangeNoLock();
222
223 *num_bytes = static_cast<uint32_t>(num_bytes_to_read); 216 *num_bytes = static_cast<uint32_t>(num_bytes_to_read);
224 return MOJO_RESULT_OK; 217 return MOJO_RESULT_OK;
225 } 218 }
226 219
227 MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock(uint32_t* num_bytes, 220 MojoResult LocalDataPipe::ConsumerDiscardDataImplNoLock(uint32_t* num_bytes,
228 bool all_or_none) { 221 bool all_or_none) {
229 DCHECK_EQ(*num_bytes % element_num_bytes(), 0u); 222 DCHECK_EQ(*num_bytes % element_num_bytes(), 0u);
230 DCHECK_GT(*num_bytes, 0u); 223 DCHECK_GT(*num_bytes, 0u);
231 224
232 if (all_or_none && *num_bytes > current_num_bytes_) { 225 if (all_or_none && *num_bytes > current_num_bytes_) {
233 // Don't return "should wait" since you can't wait for a specified amount of 226 // Don't return "should wait" since you can't wait for a specified amount of
234 // data. 227 // data.
235 return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE : 228 return producer_open_no_lock() ? MOJO_RESULT_OUT_OF_RANGE :
236 MOJO_RESULT_FAILED_PRECONDITION; 229 MOJO_RESULT_FAILED_PRECONDITION;
237 } 230 }
238 231
239 // Be consistent with other operations; error if no data available. 232 // Be consistent with other operations; error if no data available.
240 if (current_num_bytes_ == 0) { 233 if (current_num_bytes_ == 0) {
241 return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT : 234 return producer_open_no_lock() ? MOJO_RESULT_SHOULD_WAIT :
242 MOJO_RESULT_FAILED_PRECONDITION; 235 MOJO_RESULT_FAILED_PRECONDITION;
243 } 236 }
244 237
245 bool was_full = (current_num_bytes_ == capacity_num_bytes());
246
247 size_t num_bytes_to_discard = 238 size_t num_bytes_to_discard =
248 std::min(static_cast<size_t>(*num_bytes), current_num_bytes_); 239 std::min(static_cast<size_t>(*num_bytes), current_num_bytes_);
249 start_index_ = (start_index_ + num_bytes_to_discard) % capacity_num_bytes(); 240 MarkDataAsConsumedNoLock(num_bytes_to_discard);
250 current_num_bytes_ -= num_bytes_to_discard;
251
252 if (was_full && num_bytes_to_discard > 0)
253 AwakeProducerWaitersForStateChangeNoLock();
254
255 *num_bytes = static_cast<uint32_t>(num_bytes_to_discard); 241 *num_bytes = static_cast<uint32_t>(num_bytes_to_discard);
256 return MOJO_RESULT_OK; 242 return MOJO_RESULT_OK;
257 } 243 }
258 244
259 MojoResult LocalDataPipe::ConsumerQueryDataImplNoLock(uint32_t* num_bytes) { 245 MojoResult LocalDataPipe::ConsumerQueryDataImplNoLock(uint32_t* num_bytes) {
260 // Note: This cast is safe, since the capacity fits into a |uint32_t|. 246 // Note: This cast is safe, since the capacity fits into a |uint32_t|.
261 *num_bytes = static_cast<uint32_t>(current_num_bytes_); 247 *num_bytes = static_cast<uint32_t>(current_num_bytes_);
262 return MOJO_RESULT_OK; 248 return MOJO_RESULT_OK;
263 } 249 }
264 250
(...skipping 23 matching lines...) Expand all
288 } 274 }
289 275
290 MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock( 276 MojoResult LocalDataPipe::ConsumerEndReadDataImplNoLock(
291 uint32_t num_bytes_read) { 277 uint32_t num_bytes_read) {
292 if (num_bytes_read > consumer_two_phase_max_num_bytes_read_no_lock()) { 278 if (num_bytes_read > consumer_two_phase_max_num_bytes_read_no_lock()) {
293 // Note: The two-phase read ends here even on failure. 279 // Note: The two-phase read ends here even on failure.
294 set_consumer_two_phase_max_num_bytes_read_no_lock(0); 280 set_consumer_two_phase_max_num_bytes_read_no_lock(0);
295 return MOJO_RESULT_INVALID_ARGUMENT; 281 return MOJO_RESULT_INVALID_ARGUMENT;
296 } 282 }
297 283
298 bool was_full = (current_num_bytes_ == capacity_num_bytes()); 284 DCHECK_LE(start_index_ + num_bytes_read, capacity_num_bytes());
299 285 MarkDataAsConsumedNoLock(num_bytes_read);
300 start_index_ += num_bytes_read;
301 DCHECK_LE(start_index_, capacity_num_bytes());
302 start_index_ %= capacity_num_bytes();
303 DCHECK_LE(num_bytes_read, current_num_bytes_);
304 current_num_bytes_ -= num_bytes_read;
305 set_consumer_two_phase_max_num_bytes_read_no_lock(0); 286 set_consumer_two_phase_max_num_bytes_read_no_lock(0);
306
307 if (was_full && num_bytes_read > 0)
308 AwakeProducerWaitersForStateChangeNoLock();
309
310 return MOJO_RESULT_OK; 287 return MOJO_RESULT_OK;
311 } 288 }
312 289
313 MojoWaitFlags LocalDataPipe::ConsumerSatisfiedFlagsNoLock() { 290 MojoWaitFlags LocalDataPipe::ConsumerSatisfiedFlagsNoLock() {
314 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE; 291 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
315 if (current_num_bytes_ > 0) 292 if (current_num_bytes_ > 0 && !consumer_in_two_phase_read_no_lock())
316 rv |= MOJO_WAIT_FLAG_READABLE; 293 rv |= MOJO_WAIT_FLAG_READABLE;
317 return rv; 294 return rv;
318 } 295 }
319 296
320 MojoWaitFlags LocalDataPipe::ConsumerSatisfiableFlagsNoLock() { 297 MojoWaitFlags LocalDataPipe::ConsumerSatisfiableFlagsNoLock() {
321 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE; 298 MojoWaitFlags rv = MOJO_WAIT_FLAG_NONE;
322 if (current_num_bytes_ > 0 || producer_open_no_lock()) 299 if (current_num_bytes_ > 0 || producer_open_no_lock())
323 rv |= MOJO_WAIT_FLAG_READABLE; 300 rv |= MOJO_WAIT_FLAG_READABLE;
324 return rv; 301 return rv;
325 } 302 }
(...skipping 27 matching lines...) Expand all
353 } 330 }
354 return capacity_num_bytes() - next_index; 331 return capacity_num_bytes() - next_index;
355 } 332 }
356 333
357 size_t LocalDataPipe::GetMaxNumBytesToReadNoLock() { 334 size_t LocalDataPipe::GetMaxNumBytesToReadNoLock() {
358 if (start_index_ + current_num_bytes_ > capacity_num_bytes()) 335 if (start_index_ + current_num_bytes_ > capacity_num_bytes())
359 return capacity_num_bytes() - start_index_; 336 return capacity_num_bytes() - start_index_;
360 return current_num_bytes_; 337 return current_num_bytes_;
361 } 338 }
362 339
340 void LocalDataPipe::MarkDataAsConsumedNoLock(size_t num_bytes) {
341 DCHECK_LE(num_bytes, current_num_bytes_);
342 start_index_ += num_bytes;
343 start_index_ %= capacity_num_bytes();
344 current_num_bytes_ -= num_bytes;
345 }
346
363 } // namespace system 347 } // namespace system
364 } // namespace mojo 348 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/system/local_data_pipe.h ('k') | mojo/system/local_data_pipe_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698