Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / sandbox / win / src / sharedmem_ipc_client.cc
blob8f79109d1e8e6a92ab4ef4556758758b13dc21ef
1 // Copyright (c) 2006-2008 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.
5 #include <string.h>
6 #include "sandbox/win/src/sharedmem_ipc_client.h"
7 #include "sandbox/win/src/sandbox.h"
8 #include "sandbox/win/src/crosscall_client.h"
9 #include "sandbox/win/src/crosscall_params.h"
10 #include "base/logging.h"
12 namespace sandbox {
14 // Get the base of the data buffer of the channel; this is where the input
15 // parameters get serialized. Since they get serialized directly into the
16 // channel we avoid one copy.
17 void* SharedMemIPCClient::GetBuffer() {
18 bool failure = false;
19 size_t ix = LockFreeChannel(&failure);
20 if (failure) {
21 return NULL;
23 return reinterpret_cast<char*>(control_) +
24 control_->channels[ix].channel_base;
27 // If we need to cancel an IPC before issuing DoCall
28 // our client should call FreeBuffer with the same pointer
29 // returned by GetBuffer.
30 void SharedMemIPCClient::FreeBuffer(void* buffer) {
31 size_t num = ChannelIndexFromBuffer(buffer);
32 ChannelControl* channel = control_->channels;
33 LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
34 DCHECK_NE(kFreeChannel, static_cast<ChannelState>(result));
37 // The constructor simply casts the shared memory to the internal
38 // structures. This is a cheap step that is why this IPC object can
39 // and should be constructed per call.
40 SharedMemIPCClient::SharedMemIPCClient(void* shared_mem)
41 : control_(reinterpret_cast<IPCControl*>(shared_mem)) {
42 first_base_ = reinterpret_cast<char*>(shared_mem) +
43 control_->channels[0].channel_base;
44 // There must be at least one channel.
45 DCHECK(0 != control_->channels_count);
48 // Do the IPC. At this point the channel should have already been
49 // filled with the serialized input parameters.
50 // We follow the pattern explained in the header file.
51 ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params,
52 CrossCallReturn* answer) {
53 if (!control_->server_alive)
54 return SBOX_ERROR_CHANNEL_ERROR;
56 size_t num = ChannelIndexFromBuffer(params->GetBuffer());
57 ChannelControl* channel = control_->channels;
58 // Note that the IPC tag goes outside the buffer as well inside
59 // the buffer. This should enable the server to prioritize based on
60 // IPC tags without having to de-serialize the entire message.
61 channel[num].ipc_tag = params->GetTag();
63 // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
64 // we check if the server_alive mutex was abandoned which will indicate
65 // that the server has died.
67 // While the atomic signaling and waiting is not a requirement, it
68 // is nice because we save a trip to kernel.
69 DWORD wait = ::SignalObjectAndWait(channel[num].ping_event,
70 channel[num].pong_event,
71 kIPCWaitTimeOut1, FALSE);
72 if (WAIT_TIMEOUT == wait) {
73 // The server is taking too long. Enter a loop were we check if the
74 // server_alive mutex has been abandoned which would signal a server crash
75 // or else we keep waiting for a response.
76 while (true) {
77 wait = ::WaitForSingleObject(control_->server_alive, 0);
78 if (WAIT_TIMEOUT == wait) {
79 // Server seems still alive. We already signaled so here we just wait.
80 wait = ::WaitForSingleObject(channel[num].pong_event, kIPCWaitTimeOut1);
81 if (WAIT_OBJECT_0 == wait) {
82 // The server took a long time but responded.
83 break;
84 } else if (WAIT_TIMEOUT == wait) {
85 continue;
86 } else {
87 return SBOX_ERROR_CHANNEL_ERROR;
89 } else {
90 // The server has crashed and windows has signaled the mutex as
91 // abandoned.
92 ::InterlockedExchange(&channel[num].state, kAbandonedChannel);
93 control_->server_alive = 0;
94 return SBOX_ERROR_CHANNEL_ERROR;
97 } else if (WAIT_OBJECT_0 != wait) {
98 // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
99 return SBOX_ERROR_CHANNEL_ERROR;
102 // The server has returned an answer, copy it and free the channel.
103 memcpy(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
105 // Return the IPC state It can indicate that while the IPC has
106 // completed some error in the Broker has caused to not return valid
107 // results.
108 return answer->call_outcome;
111 // Locking a channel is a simple as looping over all the channels
112 // looking for one that is has state = kFreeChannel and atomically
113 // swapping it to kBusyChannel.
114 // If there is no free channel, then we must back off so some other
115 // thread makes progress and frees a channel. To back off we sleep.
116 size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
117 if (0 == control_->channels_count) {
118 *severe_failure = true;
119 return 0;
121 ChannelControl* channel = control_->channels;
122 do {
123 for (size_t ix = 0; ix != control_->channels_count; ++ix) {
124 if (kFreeChannel == ::InterlockedCompareExchange(&channel[ix].state,
125 kBusyChannel,
126 kFreeChannel)) {
127 *severe_failure = false;
128 return ix;
131 // We did not find any available channel, maybe the server is dead.
132 DWORD wait = ::WaitForSingleObject(control_->server_alive,
133 kIPCWaitTimeOut2);
134 if (WAIT_TIMEOUT != wait) {
135 // The server is dead and we outlive it enough to get in trouble.
136 *severe_failure = true;
137 return 0;
140 while (true);
143 // Find out which channel we are from the pointer returned by GetBuffer.
144 size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) {
145 ptrdiff_t d = reinterpret_cast<const char*>(buffer) - first_base_;
146 size_t num = d/kIPCChannelSize;
147 DCHECK_LT(num, control_->channels_count);
148 return (num);
151 } // namespace sandbox