remove redundant DCHECK that a size_t variable >= 0
[chromium-blink-merge.git] / base / mach_ipc_mac.mm
blob6f2575eda03a418598853bf169f499a40f693875
1 // Copyright (c) 2012 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 "base/mach_ipc_mac.h"
7 #import <Foundation/Foundation.h>
8 #include <mach/vm_map.h>
10 #include <stdio.h>
11 #include "base/logging.h"
13 namespace base {
15 // static
16 const size_t MachMessage::kEmptyMessageSize = sizeof(mach_msg_header_t) +
17     sizeof(mach_msg_body_t) + sizeof(MessageDataPacket);
19 //==============================================================================
20 MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() {
21   Initialize(message_id);
24 MachSendMessage::MachSendMessage(void *storage, size_t storage_length,
25                                  int32_t message_id)
26     : MachMessage(storage, storage_length) {
27   Initialize(message_id);
30 void MachSendMessage::Initialize(int32_t message_id) {
31   Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
33   // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage()
34   Head()->msgh_local_port = MACH_PORT_NULL;
35   Head()->msgh_reserved = 0;
36   Head()->msgh_id = 0;
38   SetDescriptorCount(0);  // start out with no descriptors
40   SetMessageID(message_id);
41   SetData(NULL, 0);       // client may add data later
44 //==============================================================================
45 MachMessage::MachMessage()
46     : storage_(new MachMessageData),  // Allocate storage_ ourselves
47       storage_length_bytes_(sizeof(MachMessageData)),
48       own_storage_(true) {
49   memset(storage_, 0, storage_length_bytes_);
52 //==============================================================================
53 MachMessage::MachMessage(void *storage, size_t storage_length)
54     : storage_(static_cast<MachMessageData*>(storage)),
55       storage_length_bytes_(storage_length),
56       own_storage_(false) {
57   DCHECK(storage);
58   DCHECK_GE(storage_length, kEmptyMessageSize);
61 //==============================================================================
62 MachMessage::~MachMessage() {
63   if (own_storage_) {
64     delete storage_;
65     storage_ = NULL;
66   }
69 //==============================================================================
70 // returns true if successful
71 bool MachMessage::SetData(const void* data,
72                           int32_t data_length) {
73   // Enforce the fact that it's only safe to call this method once on a
74   // message.
75   DCHECK(GetDataPacket()->data_length == 0);
77   // first check to make sure we have enough space
78   int size = CalculateSize();
79   int new_size = size + data_length;
81   if ((unsigned)new_size > storage_length_bytes_) {
82     return false;  // not enough space
83   }
85   GetDataPacket()->data_length = EndianU32_NtoL(data_length);
86   if (data) memcpy(GetDataPacket()->data, data, data_length);
88   // Update the Mach header with the new aligned size of the message.
89   CalculateSize();
91   return true;
94 //==============================================================================
95 // calculates and returns the total size of the message
96 // Currently, the entire message MUST fit inside of the MachMessage
97 //    messsage size <= EmptyMessageSize()
98 int MachMessage::CalculateSize() {
99   int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t);
101   // add space for MessageDataPacket
102   int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3;
103   size += 2*sizeof(int32_t) + alignedDataLength;
105   // add space for descriptors
106   size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor);
108   Head()->msgh_size = size;
110   return size;
113 //==============================================================================
114 MachMessage::MessageDataPacket *MachMessage::GetDataPacket() const {
115   int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount();
116   MessageDataPacket *packet =
117     reinterpret_cast<MessageDataPacket*>(storage_->padding + desc_size);
119   return packet;
122 //==============================================================================
123 void MachMessage::SetDescriptor(int n,
124                                 const MachMsgPortDescriptor &desc) {
125   MachMsgPortDescriptor *desc_array =
126     reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
127   desc_array[n] = desc;
130 //==============================================================================
131 // returns true if successful otherwise there was not enough space
132 bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) {
133   // first check to make sure we have enough space
134   int size = CalculateSize();
135   int new_size = size + sizeof(MachMsgPortDescriptor);
137   if ((unsigned)new_size > storage_length_bytes_) {
138     return false;  // not enough space
139   }
141   // unfortunately, we need to move the data to allow space for the
142   // new descriptor
143   u_int8_t *p = reinterpret_cast<u_int8_t*>(GetDataPacket());
144   bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t));
146   SetDescriptor(GetDescriptorCount(), desc);
147   SetDescriptorCount(GetDescriptorCount() + 1);
149   CalculateSize();
151   return true;
154 //==============================================================================
155 void MachMessage::SetDescriptorCount(int n) {
156   storage_->body.msgh_descriptor_count = n;
158   if (n > 0) {
159     Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX;
160   } else {
161     Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
162   }
165 //==============================================================================
166 MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) const {
167   if (n < GetDescriptorCount()) {
168     MachMsgPortDescriptor *desc =
169         reinterpret_cast<MachMsgPortDescriptor*>(storage_->padding);
170     return desc + n;
171   }
173   return nil;
176 //==============================================================================
177 mach_port_t MachMessage::GetTranslatedPort(int n) const {
178   if (n < GetDescriptorCount()) {
179     return GetDescriptor(n)->GetMachPort();
180   }
181   return MACH_PORT_NULL;
184 #pragma mark -
186 //==============================================================================
187 // create a new mach port for receiving messages and register a name for it
188 ReceivePort::ReceivePort(const char *receive_port_name) {
189   mach_port_t current_task = mach_task_self();
191   init_result_ = mach_port_allocate(current_task,
192                                     MACH_PORT_RIGHT_RECEIVE,
193                                     &port_);
195   if (init_result_ != KERN_SUCCESS)
196     return;
198   init_result_ = mach_port_insert_right(current_task,
199                                         port_,
200                                         port_,
201                                         MACH_MSG_TYPE_MAKE_SEND);
203   if (init_result_ != KERN_SUCCESS)
204     return;
206   // Without |NSMachPortDeallocateNone|, the NSMachPort seems to deallocate
207   // receive rights on port when it is eventually released.  It is not necessary
208   // to deallocate any rights here as |port_| is fully deallocated in the
209   // ReceivePort destructor.
210   NSPort *ns_port = [NSMachPort portWithMachPort:port_
211                                          options:NSMachPortDeallocateNone];
212   NSString *port_name = [NSString stringWithUTF8String:receive_port_name];
213   [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name];
216 //==============================================================================
217 // create a new mach port for receiving messages
218 ReceivePort::ReceivePort() {
219   mach_port_t current_task = mach_task_self();
221   init_result_ = mach_port_allocate(current_task,
222                                     MACH_PORT_RIGHT_RECEIVE,
223                                     &port_);
225   if (init_result_ != KERN_SUCCESS)
226     return;
228   init_result_ = mach_port_insert_right(current_task,
229                                         port_,
230                                         port_,
231                                         MACH_MSG_TYPE_MAKE_SEND);
234 //==============================================================================
235 // Given an already existing mach port, use it.  We take ownership of the
236 // port and deallocate it in our destructor.
237 ReceivePort::ReceivePort(mach_port_t receive_port)
238   : port_(receive_port),
239     init_result_(KERN_SUCCESS) {
242 //==============================================================================
243 ReceivePort::~ReceivePort() {
244   if (init_result_ == KERN_SUCCESS)
245     mach_port_deallocate(mach_task_self(), port_);
248 //==============================================================================
249 kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message,
250                                           mach_msg_timeout_t timeout) {
251   if (!out_message) {
252     return KERN_INVALID_ARGUMENT;
253   }
255   // return any error condition encountered in constructor
256   if (init_result_ != KERN_SUCCESS)
257     return init_result_;
259   out_message->Head()->msgh_bits = 0;
260   out_message->Head()->msgh_local_port = port_;
261   out_message->Head()->msgh_remote_port = MACH_PORT_NULL;
262   out_message->Head()->msgh_reserved = 0;
263   out_message->Head()->msgh_id = 0;
265   mach_msg_option_t rcv_options = MACH_RCV_MSG;
266   if (timeout != MACH_MSG_TIMEOUT_NONE)
267     rcv_options |= MACH_RCV_TIMEOUT;
269   kern_return_t result = mach_msg(out_message->Head(),
270                                   rcv_options,
271                                   0,
272                                   out_message->MaxSize(),
273                                   port_,
274                                   timeout,              // timeout in ms
275                                   MACH_PORT_NULL);
277   return result;
280 #pragma mark -
282 //==============================================================================
283 // get a port with send rights corresponding to a named registered service
284 MachPortSender::MachPortSender(const char *receive_port_name) {
285   mach_port_t bootstrap_port = 0;
286   init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
288   if (init_result_ != KERN_SUCCESS)
289     return;
291   init_result_ = bootstrap_look_up(bootstrap_port,
292                     const_cast<char*>(receive_port_name),
293                     &send_port_);
296 //==============================================================================
297 MachPortSender::MachPortSender(mach_port_t send_port)
298   : send_port_(send_port),
299     init_result_(KERN_SUCCESS) {
302 //==============================================================================
303 kern_return_t MachPortSender::SendMessage(const MachSendMessage& message,
304                                           mach_msg_timeout_t timeout) {
305   if (message.Head()->msgh_size == 0) {
306     NOTREACHED();
307     return KERN_INVALID_VALUE;    // just for safety -- never should occur
308   };
310   if (init_result_ != KERN_SUCCESS)
311     return init_result_;
313   message.Head()->msgh_remote_port = send_port_;
315   kern_return_t result = mach_msg(message.Head(),
316                                   MACH_SEND_MSG | MACH_SEND_TIMEOUT,
317                                   message.Head()->msgh_size,
318                                   0,
319                                   MACH_PORT_NULL,
320                                   timeout,              // timeout in ms
321                                   MACH_PORT_NULL);
323   return result;
326 //==============================================================================
328 namespace mac {
330 kern_return_t GetNumberOfMachPorts(mach_port_t task_port, int* num_ports) {
331   mach_port_name_array_t names;
332   mach_msg_type_number_t names_count;
333   mach_port_type_array_t types;
334   mach_msg_type_number_t types_count;
336   // A friendlier interface would allow NULL buffers to only get the counts.
337   kern_return_t kr = mach_port_names(task_port, &names, &names_count,
338                                      &types, &types_count);
339   if (kr != KERN_SUCCESS)
340     return kr;
342   // The documentation states this is an invariant.
343   DCHECK_EQ(names_count, types_count);
344   *num_ports = names_count;
346   kr = vm_deallocate(mach_task_self(),
347       reinterpret_cast<vm_address_t>(names),
348       names_count * sizeof(mach_port_name_array_t));
349   kr = vm_deallocate(mach_task_self(),
350       reinterpret_cast<vm_address_t>(types),
351       types_count * sizeof(mach_port_type_array_t));
353   return kr;
356 }  // namespace mac
358 }  // namespace base