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
Port(int32 size)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
Port(const Info * info)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
~Port()91 Port::~Port()
92 {
93 Close();
94 delete[] fBuffer;
95 }
96
97
98 // Close
99 void
Close()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
InitCheck() const119 Port::InitCheck() const
120 {
121 return fInitStatus;
122 }
123
124
125 // GetInfo
126 const Port::Info*
GetInfo() const127 Port::GetInfo() const
128 {
129 return &fInfo;
130 }
131
132
133 // Reserve
134 void
Reserve(int32 endOffset)135 Port::Reserve(int32 endOffset)
136 {
137 if (endOffset > fReservedSize)
138 fReservedSize = endOffset;
139 }
140
141
142 // Unreserve
143 void
Unreserve(int32 endOffset)144 Port::Unreserve(int32 endOffset)
145 {
146 if (endOffset < fReservedSize)
147 fReservedSize = endOffset;
148 }
149
150
151 // Send
152 status_t
Send(const void * message,int32 size)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
Receive(void ** _message,size_t * _size,bigtime_t timeout)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