1 /* 2 * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <new> 7 8 #include <AutoDeleter.h> 9 10 #include "AreaSupport.h" 11 #include "Compatibility.h" 12 #include "Port.h" 13 14 15 using std::nothrow; 16 17 // minimal and maximal port size 18 static const int32 kMinPortSize = 1024; // 1 kB 19 static const int32 kMaxPortSize = 64 * 1024; // 64 kB 20 21 22 // constructor 23 Port::Port(int32 size) 24 : 25 fBuffer(NULL), 26 fCapacity(0), 27 fReservedSize(0), 28 fInitStatus(B_NO_INIT), 29 fOwner(true) 30 { 31 // adjust size to be within the sane bounds 32 if (size < kMinPortSize) 33 size = kMinPortSize; 34 else if (size > kMaxPortSize) 35 size = kMaxPortSize; 36 // allocate the buffer 37 fBuffer = new(nothrow) uint8[size]; 38 if (!fBuffer) { 39 fInitStatus = B_NO_MEMORY; 40 return; 41 } 42 // create the owner port 43 fInfo.owner_port = create_port(1, "port owner port"); 44 if (fInfo.owner_port < 0) { 45 fInitStatus = fInfo.owner_port; 46 return; 47 } 48 // create the client port 49 fInfo.client_port = create_port(1, "port client port"); 50 if (fInfo.client_port < 0) { 51 fInitStatus = fInfo.client_port; 52 return; 53 } 54 fInfo.size = size; 55 fCapacity = size; 56 fInitStatus = B_OK; 57 } 58 59 60 // constructor 61 Port::Port(const Info* info) 62 : 63 fBuffer(NULL), 64 fCapacity(0), 65 fReservedSize(0), 66 fInitStatus(B_NO_INIT), 67 fOwner(false) 68 { 69 // check parameters 70 if (!info || info->owner_port < 0 || info->client_port < 0 71 || info->size < kMinPortSize || info->size > kMaxPortSize) { 72 return; 73 } 74 // allocate the buffer 75 fBuffer = new(nothrow) uint8[info->size]; 76 if (!fBuffer) { 77 fInitStatus = B_NO_MEMORY; 78 return; 79 } 80 // init the info 81 fInfo.owner_port = info->owner_port; 82 fInfo.client_port = info->client_port; 83 fInfo.size = info->size; 84 // init the other members 85 fCapacity = info->size; 86 fInitStatus = B_OK; 87 } 88 89 90 // destructor 91 Port::~Port() 92 { 93 Close(); 94 delete[] fBuffer; 95 } 96 97 98 // Close 99 void 100 Port::Close() 101 { 102 if (fInitStatus != B_OK) 103 return; 104 fInitStatus = B_NO_INIT; 105 // delete the ports only if we are the owner 106 if (fOwner) { 107 if (fInfo.owner_port >= 0) 108 delete_port(fInfo.owner_port); 109 if (fInfo.client_port >= 0) 110 delete_port(fInfo.client_port); 111 } 112 fInfo.owner_port = -1; 113 fInfo.client_port = -1; 114 } 115 116 117 // InitCheck 118 status_t 119 Port::InitCheck() const 120 { 121 return fInitStatus; 122 } 123 124 125 // GetInfo 126 const Port::Info* 127 Port::GetInfo() const 128 { 129 return &fInfo; 130 } 131 132 133 // Reserve 134 void 135 Port::Reserve(int32 endOffset) 136 { 137 if (endOffset > fReservedSize) 138 fReservedSize = endOffset; 139 } 140 141 142 // Unreserve 143 void 144 Port::Unreserve(int32 endOffset) 145 { 146 if (endOffset < fReservedSize) 147 fReservedSize = endOffset; 148 } 149 150 151 // Send 152 status_t 153 Port::Send(const void* message, int32 size) 154 { 155 if (fInitStatus != B_OK) 156 return fInitStatus; 157 if (size <= 0) 158 return B_BAD_VALUE; 159 160 port_id port = (fOwner ? fInfo.client_port : fInfo.owner_port); 161 status_t error; 162 do { 163 error = write_port(port, 0, message, size); 164 } while (error == B_INTERRUPTED); 165 166 return (fInitStatus = error); 167 } 168 169 170 // Receive 171 status_t 172 Port::Receive(void** _message, size_t* _size, bigtime_t timeout) 173 { 174 if (fInitStatus != B_OK) 175 return fInitStatus; 176 177 // convert to timeout to flags + timeout we can use in the loop 178 uint32 timeoutFlags = 0; 179 if (timeout < 0) { 180 timeout = 0; 181 } else if (timeout == 0) { 182 timeoutFlags = B_RELATIVE_TIMEOUT; 183 } else if (timeout >= 0) { 184 timeout += system_time(); 185 timeoutFlags = B_ABSOLUTE_TIMEOUT; 186 } 187 188 port_id port = (fOwner ? fInfo.owner_port : fInfo.client_port); 189 190 // wait for the next message 191 status_t error = B_OK; 192 ssize_t bufferSize; 193 do { 194 // TODO: When compiling for userland, we might want to save this syscall 195 // by using read_port_etc() directly, using a sufficiently large 196 // on-stack buffer and copying onto the heap. 197 bufferSize = port_buffer_size_etc(port, timeoutFlags, timeout); 198 if (bufferSize < 0) 199 error = bufferSize; 200 } while (error == B_INTERRUPTED); 201 202 if (error == B_TIMED_OUT || error == B_WOULD_BLOCK) 203 return error; 204 if (error != B_OK) 205 return (fInitStatus = error); 206 207 // allocate memory for the message 208 void* message = malloc(bufferSize); 209 if (message == NULL) 210 return (fInitStatus = B_NO_MEMORY); 211 MemoryDeleter messageDeleter(message); 212 213 // read the message 214 int32 code; 215 ssize_t bytesRead = read_port_etc(port, &code, message, bufferSize, 216 B_RELATIVE_TIMEOUT, 0); 217 if (bytesRead < 0) 218 return fInitStatus = bytesRead; 219 if (bytesRead != bufferSize) 220 return fInitStatus = B_BAD_DATA; 221 222 messageDeleter.Detach(); 223 *_message = message; 224 *_size = bytesRead; 225 226 return B_OK; 227 } 228