michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #ifndef BASE_MACH_IPC_MAC_H_ michael@0: #define BASE_MACH_IPC_MAC_H_ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: michael@0: //============================================================================== michael@0: // DISCUSSION: michael@0: // michael@0: // The three main classes of interest are michael@0: // michael@0: // MachMessage: a wrapper for a mach message of the following form michael@0: // mach_msg_header_t michael@0: // mach_msg_body_t michael@0: // optional descriptors michael@0: // optional extra message data michael@0: // michael@0: // MachReceiveMessage and MachSendMessage subclass MachMessage michael@0: // and are used instead of MachMessage which is an abstract base class michael@0: // michael@0: // ReceivePort: michael@0: // Represents a mach port for which we have receive rights michael@0: // michael@0: // MachPortSender: michael@0: // Represents a mach port for which we have send rights michael@0: // michael@0: // Here's an example to receive a message on a server port: michael@0: // michael@0: // // This creates our named server port michael@0: // ReceivePort receivePort("com.Google.MyService"); michael@0: // michael@0: // MachReceiveMessage message; michael@0: // kern_return_t result = receivePort.WaitForMessage(&message, 0); michael@0: // michael@0: // if (result == KERN_SUCCESS && message.GetMessageID() == 57) { michael@0: // mach_port_t task = message.GetTranslatedPort(0); michael@0: // mach_port_t thread = message.GetTranslatedPort(1); michael@0: // michael@0: // char *messageString = message.GetData(); michael@0: // michael@0: // printf("message string = %s\n", messageString); michael@0: // } michael@0: // michael@0: // Here is an example of using these classes to send a message to this port: michael@0: // michael@0: // // send to already named port michael@0: // MachPortSender sender("com.Google.MyService"); michael@0: // MachSendMessage message(57); // our message ID is 57 michael@0: // michael@0: // // add some ports to be translated for us michael@0: // message.AddDescriptor(mach_task_self()); // our task michael@0: // message.AddDescriptor(mach_thread_self()); // this thread michael@0: // michael@0: // char messageString[] = "Hello server!\n"; michael@0: // message.SetData(messageString, strlen(messageString)+1); michael@0: // // timeout 1000ms michael@0: // kern_return_t result = sender.SendMessage(message, 1000); michael@0: // michael@0: michael@0: #define PRINT_MACH_RESULT(result_, message_) \ michael@0: printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); michael@0: michael@0: //============================================================================== michael@0: // A wrapper class for mach_msg_port_descriptor_t (with same memory layout) michael@0: // with convenient constructors and accessors michael@0: class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { michael@0: public: michael@0: // General-purpose constructor michael@0: MachMsgPortDescriptor(mach_port_t in_name, michael@0: mach_msg_type_name_t in_disposition) { michael@0: name = in_name; michael@0: pad1 = 0; michael@0: pad2 = 0; michael@0: disposition = in_disposition; michael@0: type = MACH_MSG_PORT_DESCRIPTOR; michael@0: } michael@0: michael@0: // For passing send rights to a port michael@0: MachMsgPortDescriptor(mach_port_t in_name) { michael@0: name = in_name; michael@0: pad1 = 0; michael@0: pad2 = 0; michael@0: disposition = MACH_MSG_TYPE_PORT_SEND; michael@0: type = MACH_MSG_PORT_DESCRIPTOR; michael@0: } michael@0: michael@0: // Copy constructor michael@0: MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { michael@0: name = desc.name; michael@0: pad1 = desc.pad1; michael@0: pad2 = desc.pad2; michael@0: disposition = desc.disposition; michael@0: type = desc.type; michael@0: } michael@0: michael@0: mach_port_t GetMachPort() const { michael@0: return name; michael@0: } michael@0: michael@0: mach_msg_type_name_t GetDisposition() const { michael@0: return disposition; michael@0: } michael@0: michael@0: // For convenience michael@0: operator mach_port_t() const { michael@0: return GetMachPort(); michael@0: } michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // MachMessage: a wrapper for a mach message michael@0: // (mach_msg_header_t, mach_msg_body_t, extra data) michael@0: // michael@0: // This considerably simplifies the construction of a message for sending michael@0: // and the getting at relevant data and descriptors for the receiver. michael@0: // michael@0: // This class can be initialized using external storage of an arbitrary size michael@0: // or it can manage storage internally. michael@0: // 1. If storage is allocated internally, the combined size of the descriptors michael@0: // plus data must be less than 1024. But as a benefit no memory allocation is michael@0: // necessary. michael@0: // 2. For external storage, a buffer of at least EmptyMessageSize() must be michael@0: // provided. michael@0: // michael@0: // A MachMessage object is used by ReceivePort::WaitForMessage michael@0: // and MachPortSender::SendMessage michael@0: // michael@0: class MachMessage { michael@0: public: michael@0: michael@0: virtual ~MachMessage(); michael@0: michael@0: // The receiver of the message can retrieve the raw data this way michael@0: u_int8_t *GetData() { michael@0: return GetDataLength() > 0 ? GetDataPacket()->data : NULL; michael@0: } michael@0: michael@0: u_int32_t GetDataLength() { michael@0: return EndianU32_LtoN(GetDataPacket()->data_length); michael@0: } michael@0: michael@0: // The message ID may be used as a code identifying the type of message michael@0: void SetMessageID(int32_t message_id) { michael@0: GetDataPacket()->id = EndianU32_NtoL(message_id); michael@0: } michael@0: michael@0: int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } michael@0: michael@0: // Adds a descriptor (typically a mach port) to be translated michael@0: // returns true if successful, otherwise not enough space michael@0: bool AddDescriptor(const MachMsgPortDescriptor &desc); michael@0: michael@0: int GetDescriptorCount() const { michael@0: return storage_->body.msgh_descriptor_count; michael@0: } michael@0: MachMsgPortDescriptor *GetDescriptor(int n); michael@0: michael@0: // Convenience method which gets the mach port described by the descriptor michael@0: mach_port_t GetTranslatedPort(int n); michael@0: michael@0: // A simple message is one with no descriptors michael@0: bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } michael@0: michael@0: // Sets raw data for the message (returns false if not enough space) michael@0: bool SetData(const void* data, int32_t data_length); michael@0: michael@0: protected: michael@0: // Consider this an abstract base class - must create an actual instance michael@0: // of MachReceiveMessage or MachSendMessage michael@0: MachMessage(); michael@0: michael@0: // Constructor for use with preallocate storage. michael@0: // storage_length must be >= EmptyMessageSize() michael@0: MachMessage(void *storage, size_t storage_length); michael@0: michael@0: friend class ReceivePort; michael@0: friend class MachPortSender; michael@0: michael@0: // Represents raw data in our message michael@0: struct MessageDataPacket { michael@0: int32_t id; // little-endian michael@0: int32_t data_length; // little-endian michael@0: u_int8_t data[1]; // actual size limited by storage_length_bytes_ michael@0: }; michael@0: michael@0: MessageDataPacket* GetDataPacket(); michael@0: michael@0: void SetDescriptorCount(int n); michael@0: void SetDescriptor(int n, const MachMsgPortDescriptor &desc); michael@0: michael@0: // Returns total message size setting msgh_size in the header to this value michael@0: int CalculateSize(); michael@0: michael@0: // Returns total storage size that this object can grow to, this is inclusive michael@0: // of the mach header. michael@0: size_t MaxSize() const { return storage_length_bytes_; } michael@0: michael@0: protected: michael@0: mach_msg_header_t *Head() { return &(storage_->head); } michael@0: michael@0: private: michael@0: struct MachMessageData { michael@0: mach_msg_header_t head; michael@0: mach_msg_body_t body; michael@0: // descriptors and data may be embedded here. michael@0: u_int8_t padding[1024]; michael@0: }; michael@0: michael@0: // kEmptyMessageSize needs to have the definition of MachMessageData before michael@0: // it. michael@0: public: michael@0: // The size of an empty message with no data. michael@0: static const size_t kEmptyMessageSize = sizeof(mach_msg_header_t) + michael@0: sizeof(mach_msg_body_t) + michael@0: sizeof(MessageDataPacket); michael@0: michael@0: private: michael@0: MachMessageData *storage_; michael@0: size_t storage_length_bytes_; michael@0: bool own_storage_; // Is storage owned by this object? michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // MachReceiveMessage and MachSendMessage are useful to separate the idea michael@0: // of a mach message being sent and being received, and adds increased type michael@0: // safety: michael@0: // ReceivePort::WaitForMessage() only accepts a MachReceiveMessage michael@0: // MachPortSender::SendMessage() only accepts a MachSendMessage michael@0: michael@0: //============================================================================== michael@0: class MachReceiveMessage : public MachMessage { michael@0: public: michael@0: MachReceiveMessage() : MachMessage() {} michael@0: MachReceiveMessage(void *storage, size_t storage_length) michael@0: : MachMessage(storage, storage_length) {} michael@0: michael@0: private: michael@0: DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage); michael@0: }; michael@0: michael@0: //============================================================================== michael@0: class MachSendMessage : public MachMessage { michael@0: public: michael@0: MachSendMessage(int32_t message_id); michael@0: MachSendMessage(void *storage, size_t storage_length, int32_t message_id); michael@0: michael@0: private: michael@0: void Initialize(int32_t message_id); michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(MachSendMessage); michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // Represents a mach port for which we have receive rights michael@0: class ReceivePort { michael@0: public: michael@0: // Creates a new mach port for receiving messages and registers a name for it michael@0: ReceivePort(const char *receive_port_name); michael@0: michael@0: // Given an already existing mach port, use it. We take ownership of the michael@0: // port and deallocate it in our destructor. michael@0: ReceivePort(mach_port_t receive_port); michael@0: michael@0: // Create a new mach port for receiving messages michael@0: ReceivePort(); michael@0: michael@0: ~ReceivePort(); michael@0: michael@0: // Waits on the mach port until message received or timeout michael@0: kern_return_t WaitForMessage(MachReceiveMessage *out_message, michael@0: mach_msg_timeout_t timeout); michael@0: michael@0: // The underlying mach port that we wrap michael@0: mach_port_t GetPort() const { return port_; } michael@0: michael@0: private: michael@0: mach_port_t port_; michael@0: kern_return_t init_result_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(ReceivePort); michael@0: }; michael@0: michael@0: //============================================================================== michael@0: // Represents a mach port for which we have send rights michael@0: class MachPortSender { michael@0: public: michael@0: // get a port with send rights corresponding to a named registered service michael@0: MachPortSender(const char *receive_port_name); michael@0: michael@0: michael@0: // Given an already existing mach port, use it. michael@0: MachPortSender(mach_port_t send_port); michael@0: michael@0: kern_return_t SendMessage(MachSendMessage &message, michael@0: mach_msg_timeout_t timeout); michael@0: michael@0: private: michael@0: mach_port_t send_port_; michael@0: kern_return_t init_result_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(MachPortSender); michael@0: }; michael@0: michael@0: #endif // BASE_MACH_IPC_MAC_H_