xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/private/Port.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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