message_in_transit.cc revision 010d83a9304c5a91596085d917d248abff47903a
14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file.
44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "mojo/system/message_in_transit.h"
64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include <string.h>
84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/compiler_specific.h"
104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/logging.h"
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "mojo/system/constants.h"
12010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)#include "mojo/system/transport_data.h"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace mojo {
154e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace system {
164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kTypeMessagePipeEndpoint;
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kTypeMessagePipe;
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Type
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kTypeChannel;
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kSubtypeMessagePipeEndpointData;
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kSubtypeChannelRunMessagePipeEndpoint;
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpoint;
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::Subtype
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kSubtypeChannelRemoveMessagePipeEndpointAck;
315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    MessageInTransit::kInvalidEndpointId;
335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSTATIC_CONST_MEMBER_DEFINITION const size_t MessageInTransit::kMessageAlignment;
345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)struct MessageInTransit::PrivateStructForCompileAsserts {
36c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  // The size of |Header| must be a multiple of the alignment.
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  COMPILE_ASSERT(sizeof(Header) % kMessageAlignment == 0,
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 sizeof_MessageInTransit_Header_invalid);
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Avoid dangerous situations, but making sure that the size of the "header" +
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // the size of the data fits into a 31-bit number.
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  COMPILE_ASSERT(static_cast<uint64_t>(sizeof(Header)) + kMaxMessageNumBytes <=
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                     0x7fffffffULL,
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                 kMaxMessageNumBytes_too_big);
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
45effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // We assume (to avoid extra rounding code) that the maximum message (data)
46effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // size is a multiple of the alignment.
47effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  COMPILE_ASSERT(kMaxMessageNumBytes % kMessageAlignment == 0,
48effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                 kMessageAlignment_not_a_multiple_of_alignment);
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)};
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)MessageInTransit::View::View(size_t message_size, const void* buffer)
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : buffer_(buffer) {
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  size_t next_message_size = 0;
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(MessageInTransit::GetNextMessageSize(buffer_, message_size,
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                              &next_message_size));
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(message_size, next_message_size);
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // This should be equivalent.
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(message_size, total_size());
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
61effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool MessageInTransit::View::IsValid(const char** error_message) const {
62effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Note: This also implies a check on the |main_buffer_size()|, which is just
63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // |RoundUpMessageAlignment(sizeof(Header) + num_bytes())|.
64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (num_bytes() > kMaxMessageNumBytes) {
65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    *error_message = "Message data payload too large";
66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return false;
67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
68effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
69010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (transport_data_buffer_size() > 0) {
70010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    const char* e = TransportData::ValidateBuffer(transport_data_buffer(),
71010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                                  transport_data_buffer_size());
72010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    if (e) {
73010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      *error_message = e;
74010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      return false;
75010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
76effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  }
77effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
78effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return true;
79effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)MessageInTransit::MessageInTransit(Type type,
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                   Subtype subtype,
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                   uint32_t num_bytes,
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                   const void* bytes)
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : main_buffer_size_(RoundUpMessageAlignment(sizeof(Header) + num_bytes)),
86010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_,
87010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                                         kMessageAlignment))) {
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_LE(num_bytes, kMaxMessageNumBytes);
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // |total_size| is updated below, from the other values.
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  header()->type = type;
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  header()->subtype = subtype;
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  header()->source_id = kInvalidEndpointId;
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  header()->destination_id = kInvalidEndpointId;
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  header()->num_bytes = num_bytes;
96010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  header()->unused = 0;
97010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Note: If dispatchers are subsequently attached, then |total_size| will have
98010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // to be adjusted.
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UpdateTotalSize();
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (bytes) {
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    memcpy(MessageInTransit::bytes(), bytes, num_bytes);
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    memset(static_cast<char*>(MessageInTransit::bytes()) + num_bytes, 0,
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)           main_buffer_size_ - sizeof(Header) - num_bytes);
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  } else {
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    memset(MessageInTransit::bytes(), 0, main_buffer_size_ - sizeof(Header));
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)MessageInTransit::MessageInTransit(const View& message_view)
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    : main_buffer_size_(message_view.main_buffer_size()),
112010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)      main_buffer_(static_cast<char*>(base::AlignedAlloc(main_buffer_size_,
113010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                                         kMessageAlignment))) {
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_GE(main_buffer_size_, sizeof(Header));
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
117010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  memcpy(main_buffer_.get(), message_view.main_buffer(), main_buffer_size_);
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(main_buffer_size_,
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            RoundUpMessageAlignment(sizeof(Header) + num_bytes()));
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)MessageInTransit::~MessageInTransit() {
123c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  if (dispatchers_) {
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for (size_t i = 0; i < dispatchers_->size(); i++) {
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if (!(*dispatchers_)[i])
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        continue;
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      DCHECK((*dispatchers_)[i]->HasOneRef());
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      (*dispatchers_)[i]->Close();
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool MessageInTransit::GetNextMessageSize(const void* buffer,
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                          size_t buffer_size,
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                          size_t* next_message_size) {
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(next_message_size);
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!buffer_size)
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(buffer);
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(reinterpret_cast<uintptr_t>(buffer) %
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                MessageInTransit::kMessageAlignment, 0u);
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
145a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (buffer_size < sizeof(Header))
146a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
147a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
148a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const Header* header = static_cast<const Header*>(buffer);
149a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  *next_message_size = header->total_size;
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(*next_message_size % kMessageAlignment, 0u);
151a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return true;
152a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
153a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
154a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void MessageInTransit::SetDispatchers(
155010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    scoped_ptr<DispatcherVector> dispatchers) {
156c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DCHECK(dispatchers);
157c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  DCHECK(!dispatchers_);
158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  dispatchers_ = dispatchers.Pass();
160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#ifndef NDEBUG
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (size_t i = 0; i < dispatchers_->size(); i++)
162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    DCHECK(!(*dispatchers_)[i] || (*dispatchers_)[i]->HasOneRef());
163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#endif
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void MessageInTransit::SerializeAndCloseDispatchers(Channel* channel) {
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK(channel);
168010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  DCHECK(!transport_data_);
169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
170010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (!dispatchers_ || !dispatchers_->size())
171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
173010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  transport_data_.reset(new TransportData(dispatchers_.Pass(), channel));
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
175010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  // Update the sizes in the message header.
176a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  UpdateTotalSize();
177a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
178a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
179a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void MessageInTransit::UpdateTotalSize() {
180a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  DCHECK_EQ(main_buffer_size_ % kMessageAlignment, 0u);
181010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  header()->total_size = static_cast<uint32_t>(main_buffer_size_);
182010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  if (transport_data_) {
183010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    header()->total_size +=
184010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        static_cast<uint32_t>(transport_data_->buffer_size());
185010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)  }
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace system
1894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace mojo
190