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
15using std::nothrow;
16
17// minimal and maximal port size
18static const int32 kMinPortSize = 1024;			// 1 kB
19static const int32 kMaxPortSize = 64 * 1024;	// 64 kB
20
21
22// constructor
23Port::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
61Port::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
91Port::~Port()
92{
93	Close();
94	delete[] fBuffer;
95}
96
97
98// Close
99void
100Port::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
118status_t
119Port::InitCheck() const
120{
121	return fInitStatus;
122}
123
124
125// GetInfo
126const Port::Info*
127Port::GetInfo() const
128{
129	return &fInfo;
130}
131
132
133// Reserve
134void
135Port::Reserve(int32 endOffset)
136{
137	if (endOffset > fReservedSize)
138		fReservedSize = endOffset;
139}
140
141
142// Unreserve
143void
144Port::Unreserve(int32 endOffset)
145{
146	if (endOffset < fReservedSize)
147		fReservedSize = endOffset;
148}
149
150
151// Send
152status_t
153Port::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
171status_t
172Port::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