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
3 // found in the LICENSE file.
4
5 #include "mojo/system/channel.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/compiler_specific.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/strings/stringprintf.h"
13
14 namespace mojo {
15 namespace system {
16
17 COMPILE_ASSERT(Channel::kBootstrapEndpointId !=
18 MessageInTransit::kInvalidEndpointId,
19 kBootstrapEndpointId_is_invalid);
20
21 STATIC_CONST_MEMBER_DEFINITION const MessageInTransit::EndpointId
22 Channel::kBootstrapEndpointId;
23
EndpointInfo()24 Channel::EndpointInfo::EndpointInfo() {
25 }
26
EndpointInfo(scoped_refptr<MessagePipe> message_pipe,unsigned port)27 Channel::EndpointInfo::EndpointInfo(scoped_refptr<MessagePipe> message_pipe,
28 unsigned port)
29 : message_pipe(message_pipe),
30 port(port) {
31 }
32
~EndpointInfo()33 Channel::EndpointInfo::~EndpointInfo() {
34 }
35
Channel()36 Channel::Channel()
37 : next_local_id_(kBootstrapEndpointId) {
38 }
39
Init(const PlatformChannelHandle & handle)40 bool Channel::Init(const PlatformChannelHandle& handle) {
41 DCHECK(creation_thread_checker_.CalledOnValidThread());
42
43 // No need to take |lock_|, since this must be called before this object
44 // becomes thread-safe.
45 DCHECK(!raw_channel_.get());
46
47 raw_channel_.reset(
48 RawChannel::Create(handle, this, base::MessageLoop::current()));
49 if (!raw_channel_->Init()) {
50 raw_channel_.reset();
51 return false;
52 }
53
54 return true;
55 }
56
Shutdown()57 void Channel::Shutdown() {
58 DCHECK(creation_thread_checker_.CalledOnValidThread());
59
60 base::AutoLock locker(lock_);
61 DCHECK(raw_channel_.get());
62 raw_channel_->Shutdown();
63 raw_channel_.reset();
64
65 // TODO(vtl): Should I clear |local_id_to_endpoint_info_map_|? Or assert that
66 // it's empty?
67 }
68
AttachMessagePipeEndpoint(scoped_refptr<MessagePipe> message_pipe,unsigned port)69 MessageInTransit::EndpointId Channel::AttachMessagePipeEndpoint(
70 scoped_refptr<MessagePipe> message_pipe, unsigned port) {
71 MessageInTransit::EndpointId local_id;
72 {
73 base::AutoLock locker(lock_);
74
75 while (next_local_id_ == MessageInTransit::kInvalidEndpointId ||
76 local_id_to_endpoint_info_map_.find(next_local_id_) !=
77 local_id_to_endpoint_info_map_.end())
78 next_local_id_++;
79
80 local_id = next_local_id_;
81 next_local_id_++;
82
83 // TODO(vtl): Use emplace when we move to C++11 unordered_maps. (It'll avoid
84 // some expensive reference count increment/decrements.) Once this is done,
85 // we should be able to delete |EndpointInfo|'s default constructor.
86 local_id_to_endpoint_info_map_[local_id] = EndpointInfo(message_pipe, port);
87 }
88
89 message_pipe->Attach(port, scoped_refptr<Channel>(this), local_id);
90 return local_id;
91 }
92
RunMessagePipeEndpoint(MessageInTransit::EndpointId local_id,MessageInTransit::EndpointId remote_id)93 void Channel::RunMessagePipeEndpoint(MessageInTransit::EndpointId local_id,
94 MessageInTransit::EndpointId remote_id) {
95 EndpointInfo endpoint_info;
96 {
97 base::AutoLock locker(lock_);
98
99 IdToEndpointInfoMap::const_iterator it =
100 local_id_to_endpoint_info_map_.find(local_id);
101 CHECK(it != local_id_to_endpoint_info_map_.end());
102 endpoint_info = it->second;
103 }
104
105 endpoint_info.message_pipe->Run(endpoint_info.port, remote_id);
106 }
107
WriteMessage(MessageInTransit * message)108 bool Channel::WriteMessage(MessageInTransit* message) {
109 base::AutoLock locker(lock_);
110 if (!raw_channel_.get()) {
111 // TODO(vtl): I think this is probably not an error condition, but I should
112 // think about it (and the shutdown sequence) more carefully.
113 LOG(INFO) << "WriteMessage() after shutdown";
114 return false;
115 }
116
117 return raw_channel_->WriteMessage(message);
118 }
119
DetachMessagePipeEndpoint(MessageInTransit::EndpointId local_id)120 void Channel::DetachMessagePipeEndpoint(MessageInTransit::EndpointId local_id) {
121 DCHECK_NE(local_id, MessageInTransit::kInvalidEndpointId);
122
123 base::AutoLock locker_(lock_);
124 local_id_to_endpoint_info_map_.erase(local_id);
125 }
126
~Channel()127 Channel::~Channel() {
128 // The channel should have been shut down first.
129 DCHECK(!raw_channel_.get());
130 }
131
OnReadMessage(const MessageInTransit & message)132 void Channel::OnReadMessage(const MessageInTransit& message) {
133 switch (message.type()) {
134 case MessageInTransit::kTypeMessagePipeEndpoint:
135 case MessageInTransit::kTypeMessagePipe:
136 OnReadMessageForDownstream(message);
137 break;
138 case MessageInTransit::TYPE_CHANNEL:
139 OnReadMessageForChannel(message);
140 break;
141 default:
142 HandleRemoteError(base::StringPrintf(
143 "Received message of invalid type %u",
144 static_cast<unsigned>(message.type())));
145 break;
146 }
147 }
148
OnFatalError(FatalError fatal_error)149 void Channel::OnFatalError(FatalError fatal_error) {
150 // TODO(vtl): IMPORTANT. Notify all our endpoints that they're dead.
151 NOTIMPLEMENTED();
152 }
153
OnReadMessageForDownstream(const MessageInTransit & message)154 void Channel::OnReadMessageForDownstream(const MessageInTransit& message) {
155 DCHECK(message.type() == MessageInTransit::kTypeMessagePipeEndpoint ||
156 message.type() == MessageInTransit::kTypeMessagePipe);
157
158 MessageInTransit::EndpointId local_id = message.destination_id();
159 if (local_id == MessageInTransit::kInvalidEndpointId) {
160 HandleRemoteError("Received message with no destination ID");
161 return;
162 }
163
164 EndpointInfo endpoint_info;
165 {
166 base::AutoLock locker(lock_);
167
168 // Since we own |raw_channel_|, and this method and |Shutdown()| should only
169 // be called from the creation thread, |raw_channel_| should never be null
170 // here.
171 DCHECK(raw_channel_.get());
172
173 IdToEndpointInfoMap::const_iterator it =
174 local_id_to_endpoint_info_map_.find(local_id);
175 if (it == local_id_to_endpoint_info_map_.end()) {
176 HandleRemoteError(base::StringPrintf(
177 "Received a message for nonexistent local destination ID %u",
178 static_cast<unsigned>(local_id)));
179 return;
180 }
181 endpoint_info = it->second;
182 }
183
184 // We need to duplicate the message, because |EnqueueMessage()| will take
185 // ownership of it.
186 MessageInTransit* own_message = MessageInTransit::Create(
187 message.type(), message.subtype(), message.data(), message.data_size());
188 if (endpoint_info.message_pipe->EnqueueMessage(
189 MessagePipe::GetPeerPort(endpoint_info.port), own_message, NULL) !=
190 MOJO_RESULT_OK) {
191 HandleLocalError(base::StringPrintf(
192 "Failed to enqueue message to local destination ID %u",
193 static_cast<unsigned>(local_id)));
194 return;
195 }
196 }
197
OnReadMessageForChannel(const MessageInTransit & message)198 void Channel::OnReadMessageForChannel(const MessageInTransit& message) {
199 // TODO(vtl): Currently no channel-only messages yet.
200 HandleRemoteError("Received invalid channel message");
201 NOTREACHED();
202 }
203
HandleRemoteError(const base::StringPiece & error_message)204 void Channel::HandleRemoteError(const base::StringPiece& error_message) {
205 // TODO(vtl): Is this how we really want to handle this?
206 LOG(INFO) << error_message;
207 }
208
HandleLocalError(const base::StringPiece & error_message)209 void Channel::HandleLocalError(const base::StringPiece& error_message) {
210 // TODO(vtl): Is this how we really want to handle this?
211 LOG(FATAL) << error_message;
212 }
213
214 } // namespace system
215 } // namespace mojo
216