File: channel_endpoint.h

package info (click to toggle)
chromium-browser 41.0.2272.118-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 2,189,132 kB
  • sloc: cpp: 9,691,462; ansic: 3,341,451; python: 712,689; asm: 518,779; xml: 208,926; java: 169,820; sh: 119,353; perl: 68,907; makefile: 28,311; yacc: 13,305; objc: 11,385; tcl: 3,186; cs: 2,225; sql: 2,217; lex: 2,215; lisp: 1,349; pascal: 1,256; awk: 407; ruby: 155; sed: 53; php: 14; exp: 11
file content (210 lines) | stat: -rw-r--r-- 10,742 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_
#define MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "mojo/edk/system/channel_endpoint_id.h"
#include "mojo/edk/system/message_in_transit_queue.h"
#include "mojo/edk/system/system_impl_export.h"

namespace mojo {
namespace system {

class Channel;
class ChannelEndpointClient;
class MessageInTransit;

// TODO(vtl): The plan:
//   - (Done.) Move |Channel::Endpoint| to |ChannelEndpoint|. Make it
//     refcounted, and not copyable. Make |Channel| a friend. Make things work.
//   - (Done.) Give |ChannelEndpoint| a lock. The lock order (in order of
//     allowable acquisition) is: |MessagePipe|, |ChannelEndpoint|, |Channel|.
//   - (Done) Stop having |Channel| as a friend.
//   - (Done) Move logic from |ProxyMessagePipeEndpoint| into |ChannelEndpoint|.
//     Right now, we have to go through lots of contortions to manipulate state
//     owned by |ProxyMessagePipeEndpoint| (in particular, |Channel::Endpoint|
//     doesn't know about the remote ID; the local ID is duplicated in two
//     places). Hollow out |ProxyMessagePipeEndpoint|, and have it just own a
//     reference to |ChannelEndpoint| (hence the refcounting).
//   - In essence, |ChannelEndpoint| becomes the thing that knows about
//     channel-specific aspects of an endpoint (notably local and remote IDs,
//     and knowledge about handshaking), and mediates between the |Channel| and
//     the |MessagePipe|.
//   - In the end state, |Channel| should no longer need to know about
//     |MessagePipe| and ports (but only |ChannelEndpoint|) and
//     |ProxyMessagePipeEndpoint| should no longer need to know about |Channel|
//     (ditto).
//
// Things as they are now, before I change everything (TODO(vtl): update this
// comment appropriately):
//
// Terminology:
//   - "Message pipe endpoint": In the implementation, a |MessagePipe| owns
//     two |MessagePipeEndpoint| objects, one for each port. The
//     |MessagePipeEndpoint| objects are only accessed via the |MessagePipe|
//     (which has the lock), with the additional information of the port
//     number. So as far as the channel is concerned, a message pipe endpoint
//     is a pointer to a |MessagePipe| together with the port number.
//       - The value of |port| in |EndpointInfo| refers to the
//         |ProxyMessagePipeEndpoint| (i.e., the endpoint that is logically on
//         the other side). Messages received by a channel for a message pipe
//         are thus written to the *peer* of this port.
//   - "Attached"/"detached": A message pipe endpoint is attached to a channel
//     if it has a pointer to it. It must be detached before the channel gives
//     up its pointer to it in order to break a reference cycle. (This cycle
//     is needed to allow a channel to be shut down cleanly, without shutting
//     down everything else first.)
//   - "Running" (message pipe endpoint): A message pipe endpoint is running
//     if messages written to it (via some |MessagePipeDispatcher|, to which
//     some |MojoHandle| is assigned) are being transmitted through the
//     channel.
//       - Before a message pipe endpoint is run, it will queue messages.
//       - When a message pipe endpoint is detached from a channel, it is also
//         taken out of the running state. After that point, messages should
//         no longer be written to it.
//   - "Normal" message pipe endpoint (state): The channel itself does not
//     have knowledge of whether a message pipe endpoint has started running
//     yet. It will *receive* messages for a message pipe in either state (but
//     the message pipe endpoint won't *send* messages to the channel if it
//     has not started running).
//   - "Zombie" message pipe endpoint (state): A message pipe endpoint is a
//     zombie if it is still in |local_id_to_endpoint_info_map_|, but the
//     channel is no longer forwarding messages to it (even if it may still be
//     receiving messages for it).
//       - There are various types of zombies, depending on the reason the
//         message pipe endpoint cannot yet be removed.
//       - If the remote side is closed, it will send a "remove" control
//         message. After the channel receives that message (to which it
//         responds with a "remove ack" control message), it knows that it
//         shouldn't receive any more messages for that message pipe endpoint
//         (local ID), but it must wait for the endpoint to detach. (It can't
//         do so without a race, since it can't call into the message pipe
//         under |lock_|.) [TODO(vtl): When I add remotely-allocated IDs,
//         we'll have to remove the |EndpointInfo| from
//         |local_id_to_endpoint_info_map_| -- i.e., remove the local ID,
//         since it's no longer valid and may be reused by the remote side --
//         and keep the |EndpointInfo| alive in some other way.]
//       - If the local side is closed and the message pipe endpoint was
//         already running (so there are no queued messages left to send), it
//         will detach the endpoint, and send a "remove" control message.
//         However, the channel may still receive messages for that endpoint
//         until it receives a "remove ack" control message.
//       - If the local side is closed but the message pipe endpoint was not
//         yet running , the detaching is delayed until after it is run and
//         all the queued messages are sent to the channel. On being detached,
//         things proceed as in one of the above cases. The endpoint is *not*
//         a zombie until it is detached (or a "remove" message is received).
//         [TODO(vtl): Maybe we can get rid of this case? It'd only not yet be
//         running since under the current scheme it wouldn't have a remote ID
//         yet.]
//       - Note that even if the local side is closed, it may still receive a
//         "remove" message from the other side (if the other side is closed
//         simultaneously, and both sides send "remove" messages). In that
//         case, it must still remain alive until it receives the "remove
//         ack" (and it must ack the "remove" message that it received).
class MOJO_SYSTEM_IMPL_EXPORT ChannelEndpoint
    : public base::RefCountedThreadSafe<ChannelEndpoint> {
 public:
  // Constructor for a |ChannelEndpoint| with the given client (specified by
  // |client| and |client_port|). Optionally takes messages from
  // |*message_queue| if |message_queue| is non-null.
  //
  // |client| may be null if this endpoint will never need to receive messages,
  // in which case |message_queue| should not be null. In that case, this
  // endpoint will simply send queued messages upon being attached to a
  // |Channel| and immediately detach itself.
  ChannelEndpoint(ChannelEndpointClient* client,
                  unsigned client_port,
                  MessageInTransitQueue* message_queue = nullptr);

  // Methods called by |ChannelEndpointClient|:

  // Called to enqueue an outbound message. (If |AttachAndRun()| has not yet
  // been called, the message will be enqueued and sent when |AttachAndRun()| is
  // called.)
  bool EnqueueMessage(scoped_ptr<MessageInTransit> message);

  // Called to *replace* current client with a new client (which must differ
  // from the existing client). This must not be called after
  // |DetachFromClient()| has been called.
  //
  // This returns true in the typical case, and false if this endpoint has been
  // detached from the channel, in which case the caller should probably call
  // its (new) client's |OnDetachFromChannel()|.
  bool ReplaceClient(ChannelEndpointClient* client, unsigned client_port);

  // Called before the |ChannelEndpointClient| gives up its reference to this
  // object.
  void DetachFromClient();

  // Methods called by |Channel|:

  // Called when the |Channel| takes a reference to this object. This will send
  // all queue messages (in |channel_message_queue_|).
  // TODO(vtl): Maybe rename this "OnAttach"?
  void AttachAndRun(Channel* channel,
                    ChannelEndpointId local_id,
                    ChannelEndpointId remote_id);

  // Called when the |Channel| receives a message for the |ChannelEndpoint|.
  void OnReadMessage(scoped_ptr<MessageInTransit> message);

  // Called before the |Channel| gives up its reference to this object.
  void DetachFromChannel();

 private:
  friend class base::RefCountedThreadSafe<ChannelEndpoint>;
  ~ChannelEndpoint();

  // Must be called with |lock_| held.
  bool WriteMessageNoLock(scoped_ptr<MessageInTransit> message);

  // Resets |channel_| to null (and sets |is_detached_from_channel_|). This may
  // only be called if |channel_| is non-null. Must be called with |lock_| held.
  void ResetChannelNoLock();

  // Protects the members below.
  base::Lock lock_;

  // |client_| must be valid whenever it is non-null. Before |*client_| gives up
  // its reference to this object, it must call |DetachFromClient()|.
  // NOTE: This is a |scoped_refptr<>|, rather than a raw pointer, since the
  // |Channel| needs to keep the |MessagePipe| alive for the "proxy-proxy" case.
  // Possibly we'll be able to eliminate that case when we have full
  // multiprocess support.
  // WARNING: |ChannelEndpointClient| methods must not be called under |lock_|.
  // Thus to make such a call, a reference must first be taken under |lock_| and
  // the lock released.
  // WARNING: Beware of interactions with |ReplaceClient()|. By the time the
  // call is made, the client may have changed. This must be detected and dealt
  // with.
  scoped_refptr<ChannelEndpointClient> client_;
  unsigned client_port_;

  // |channel_| must be valid whenever it is non-null. Before |*channel_| gives
  // up its reference to this object, it must call |DetachFromChannel()|.
  // |local_id_| and |remote_id_| are valid if and only |channel_| is non-null.
  Channel* channel_;
  ChannelEndpointId local_id_;
  ChannelEndpointId remote_id_;
  // This distinguishes the two cases of |channel| being null: not yet attached
  // versus detached.
  bool is_detached_from_channel_;

  // This queue is used before we're running on a channel and ready to send
  // messages to the channel.
  MessageInTransitQueue channel_message_queue_;

  DISALLOW_COPY_AND_ASSIGN(ChannelEndpoint);
};

}  // namespace system
}  // namespace mojo

#endif  // MOJO_EDK_SYSTEM_CHANNEL_ENDPOINT_H_