xref: /haiku/src/kits/media/BufferGroup.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 /*
2  * Copyright (c) 2002, 2003 Marcus Overhagen <Marcus@Overhagen.de>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files or portions
6  * thereof (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject
10  * to the following conditions:
11  *
12  *  * Redistributions of source code must retain the above copyright notice,
13  *    this list of conditions and the following disclaimer.
14  *
15  *  * Redistributions in binary form must reproduce the above copyright notice
16  *    in the  binary, as well as this list of conditions and the following
17  *    disclaimer in the documentation and/or other materials provided with
18  *    the distribution.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  *
28  */
29 
30 #include <BufferGroup.h>
31 #include <Buffer.h>
32 #include "debug.h"
33 #include "SharedBufferList.h"
34 #include "DataExchange.h"
35 
36 /*************************************************************
37  * private BBufferGroup
38  *************************************************************/
39 
40 status_t
41 BBufferGroup::InitBufferGroup()
42 {
43 	CALLED();
44 
45 	// some defaults
46 	fBufferList = 0;
47 	fReclaimSem = B_ERROR;
48 	fInitError = B_ERROR;
49 	fRequestError = B_ERROR;
50 	fBufferCount = 0;
51 
52 	// create the reclaim semaphore
53 	fReclaimSem = create_sem(0,"buffer reclaim sem");
54 	if (fReclaimSem < B_OK) {
55 		ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n");
56 		fInitError = (status_t)fReclaimSem;
57 		return fInitError;
58 	}
59 
60 	// ask media_server to get the area_id of the shared buffer list
61 	server_get_shared_buffer_area_request area_request;
62 	server_get_shared_buffer_area_reply area_reply;
63 	if (QueryServer(SERVER_GET_SHARED_BUFFER_AREA, &area_request, sizeof(area_request), &area_reply, sizeof(area_reply)) != B_OK) {
64 		ERROR("BBufferGroup::InitBufferGroup: SERVER_GET_SHARED_BUFFER_AREA failed\n");
65 		fInitError = B_ERROR;
66 		return fInitError;
67 	}
68 	ASSERT(area_reply.area > 0);
69 
70 	fBufferList = _shared_buffer_list::Clone(area_reply.area);
71 	if (fBufferList == NULL) {
72 		ERROR("BBufferGroup::InitBufferGroup: _shared_buffer_list::Clone failed\n");
73 		fInitError = B_ERROR;
74 		return fInitError;
75 	}
76 
77 	fInitError = B_OK;
78 	return fInitError;
79 }
80 
81 
82 /*************************************************************
83  * public BBufferGroup
84  *************************************************************/
85 
86 BBufferGroup::BBufferGroup(size_t size,
87 						   int32 count,
88 						   uint32 placement,
89 						   uint32 lock)
90 {
91 	CALLED();
92 	if (InitBufferGroup() != B_OK)
93 		return;
94 
95 	// This one is easy. We need to create "count" BBuffers,
96 	// each one "size" bytes large. They all go into one
97 	// area, with "placement" and "lock" attributes.
98 	// The BBuffers created will clone the area, and
99 	// then we delete our area. This way BBuffers are
100 	// independent from the BBufferGroup
101 
102 	void *start_addr;
103 	area_id buffer_area;
104 	size_t area_size;
105 	buffer_clone_info bci;
106 	BBuffer *buffer;
107 
108 	// don't allow all placement parameter values
109 	if (placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS) {
110 		ERROR("BBufferGroup: placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS (0x%08lx)\n",placement);
111 		placement = B_ANY_ADDRESS;
112 	}
113 
114 	// first we roundup for a better placement in memory
115 	size = (size + 63) & ~63;
116 
117 	// now we create the area
118 	area_size = ((size * count) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1);
119 
120 	buffer_area = create_area("some buffers area", &start_addr,placement,area_size,lock,B_READ_AREA | B_WRITE_AREA);
121 	if (buffer_area < B_OK) {
122 		ERROR("BBufferGroup: failed to allocate %ld bytes area\n",area_size);
123 		fInitError = (status_t)buffer_area;
124 		return;
125 	}
126 
127 	fBufferCount = count;
128 
129 	for (int32 i = 0; i < count; i++) {
130 		bci.area = buffer_area;
131 		bci.offset = i * size;
132 		bci.size = size;
133 		buffer = new BBuffer(bci);
134 		if (0 == buffer->Data()) {
135 			// BBuffer::Data() will return 0 if an error occured
136 			ERROR("BBufferGroup: error while creating buffer\n");
137 			delete buffer;
138 			fInitError = B_ERROR;
139 			break;
140 		}
141 		if (B_OK != fBufferList->AddBuffer(fReclaimSem,buffer)) {
142 			ERROR("BBufferGroup: error when adding buffer\n");
143 			delete buffer;
144 			fInitError = B_ERROR;
145 			break;
146 		}
147 	}
148 
149 	delete_area(buffer_area);
150 }
151 
152 /* explicit */
153 BBufferGroup::BBufferGroup()
154 {
155 	CALLED();
156 	if (InitBufferGroup() != B_OK)
157 		return;
158 
159 	// this one simply creates an empty BBufferGroup
160 }
161 
162 
163 BBufferGroup::BBufferGroup(int32 count,
164 						   const media_buffer_id *buffers)
165 {
166 	CALLED();
167 	if (InitBufferGroup() != B_OK)
168 		return;
169 
170 	// XXX we need to make sure that a media_buffer_id is only added once to each group
171 
172 	// this one creates "BBuffer"s from "media_buffer_id"s passed
173 	// by the application.
174 
175 	fBufferCount = count;
176 
177 	buffer_clone_info bci;
178 	BBuffer *buffer;
179 	for (int32 i = 0; i < count; i++) {
180 		bci.buffer = buffers[i];
181 		buffer = new BBuffer(bci);
182 		if (0 == buffer->Data()) {
183 			// BBuffer::Data() will return 0 if an error occured
184 			ERROR("BBufferGroup(2): error while creating buffer\n");
185 			delete buffer;
186 			fInitError = B_ERROR;
187 			break;
188 		}
189 		if (B_OK != fBufferList->AddBuffer(fReclaimSem,buffer)) {
190 			ERROR("BBufferGroup(2): error when adding buffer\n");
191 			delete buffer;
192 			fInitError = B_ERROR;
193 			break;
194 		}
195 	}
196 }
197 
198 
199 BBufferGroup::~BBufferGroup()
200 {
201 	CALLED();
202 	if (fBufferList)
203 		fBufferList->Terminate(fReclaimSem);
204 	if (fReclaimSem >= B_OK)
205 		delete_sem(fReclaimSem);
206 }
207 
208 
209 status_t
210 BBufferGroup::InitCheck()
211 {
212 	CALLED();
213 	return fInitError;
214 }
215 
216 
217 status_t
218 BBufferGroup::AddBuffer(const buffer_clone_info &info,
219 						BBuffer **out_buffer)
220 {
221 	CALLED();
222 	if (fInitError != B_OK)
223 		return B_NO_INIT;
224 
225 	// XXX we need to make sure that a media_buffer_id is only added once to each group
226 
227 	BBuffer *buffer;
228 	buffer = new BBuffer(info);
229 	if (0 == buffer->Data()) {
230 		// BBuffer::Data() will return 0 if an error occured
231 		ERROR("BBufferGroup::AddBuffer: error while creating buffer\n");
232 		delete buffer;
233 		return B_ERROR;
234 	}
235 	if (B_OK != fBufferList->AddBuffer(fReclaimSem,buffer)) {
236 		ERROR("BBufferGroup::AddBuffer: error when adding buffer\n");
237 		delete buffer;
238 		fInitError = B_ERROR;
239 		return B_ERROR;
240 	}
241 	atomic_add(&fBufferCount,1);
242 
243 	if (out_buffer != 0)
244 		*out_buffer = buffer;
245 
246 	return B_OK;
247 }
248 
249 
250 BBuffer *
251 BBufferGroup::RequestBuffer(size_t size,
252 							bigtime_t timeout)
253 {
254 	CALLED();
255 	if (fInitError != B_OK)
256 		return NULL;
257 
258 	if (size <= 0)
259 		return NULL;
260 
261 	BBuffer *buffer;
262 	status_t status;
263 
264 	buffer = NULL;
265 	status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, size, 0, &buffer, timeout);
266 	fRequestError = status;
267 
268 	return (status == B_OK) ? buffer : NULL;
269 }
270 
271 
272 status_t
273 BBufferGroup::RequestBuffer(BBuffer *buffer,
274 							bigtime_t timeout)
275 {
276 	CALLED();
277 	if (fInitError != B_OK)
278 		return B_NO_INIT;
279 
280 	if (buffer == NULL)
281 		return B_BAD_VALUE;
282 
283 	status_t status;
284 	status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0, &buffer, timeout);
285 	fRequestError = status;
286 
287 	return status;
288 }
289 
290 
291 status_t
292 BBufferGroup::RequestError()
293 {
294 	CALLED();
295 	if (fInitError != B_OK)
296 		return B_NO_INIT;
297 
298 	return fRequestError;
299 }
300 
301 
302 status_t
303 BBufferGroup::CountBuffers(int32 *out_count)
304 {
305 	CALLED();
306 	if (fInitError != B_OK)
307 		return B_NO_INIT;
308 
309 	*out_count = fBufferCount;
310 	return B_OK;
311 }
312 
313 
314 status_t
315 BBufferGroup::GetBufferList(int32 buf_count,
316 							BBuffer **out_buffers)
317 {
318 	CALLED();
319 	if (fInitError != B_OK)
320 		return B_NO_INIT;
321 
322 	if (buf_count <= 0 || buf_count > fBufferCount)
323 		return B_BAD_VALUE;
324 
325 	return fBufferList->GetBufferList(fReclaimSem,buf_count,out_buffers);
326 }
327 
328 
329 status_t
330 BBufferGroup::WaitForBuffers()
331 {
332 	CALLED();
333 	if (fInitError != B_OK)
334 		return B_NO_INIT;
335 
336 	// XXX this function is not really useful anyway, and will
337 	// XXX not work exactly as documented, but it is close enough
338 
339 	if (fBufferCount < 0)
340 		return B_BAD_VALUE;
341 	if (fBufferCount == 0)
342 		return B_OK;
343 
344 	// we need to wait until at least one buffer belonging to this group is reclaimed.
345 	// this has happened when can aquire "fReclaimSem"
346 
347 	status_t status;
348 	while (B_INTERRUPTED == (status = acquire_sem(fReclaimSem)))
349 		;
350 
351 	if (status != B_OK) // some error happened
352 		return status;
353 
354 	// we need to release the "fReclaimSem" now, else we would block requesting of new buffers
355 
356 	return release_sem(fReclaimSem);
357 }
358 
359 
360 status_t
361 BBufferGroup::ReclaimAllBuffers()
362 {
363 	CALLED();
364 	if (fInitError != B_OK)
365 		return B_NO_INIT;
366 
367 	// because additional BBuffers might get added to this group betweeen acquire and release
368 	int32 count = fBufferCount;
369 
370 	if (count < 0)
371 		return B_BAD_VALUE;
372 	if (count == 0)
373 		return B_OK;
374 
375 	// we need to wait until all BBuffers belonging to this group are reclaimed.
376 	// this has happened when the "fReclaimSem" can be aquired "fBufferCount" times
377 
378 	status_t status;
379 	while (B_INTERRUPTED == (status = acquire_sem_etc(fReclaimSem, count, 0, 0)))
380 		;
381 
382 	if (status != B_OK) // some error happened
383 		return status;
384 
385 	// we need to release the "fReclaimSem" now, else we would block requesting of new buffers
386 
387 	return release_sem_etc(fReclaimSem, count, 0);
388 }
389 
390 /*************************************************************
391  * private BBufferGroup
392  *************************************************************/
393 
394 /* not implemented */
395 /*
396 BBufferGroup::BBufferGroup(const BBufferGroup &)
397 BBufferGroup & BBufferGroup::operator=(const BBufferGroup &)
398 */
399 
400 status_t
401 BBufferGroup::AddBuffersTo(BMessage * message, const char * name, bool needLock)
402 {
403 	CALLED();
404 	if (fInitError != B_OK)
405 		return B_NO_INIT;
406 
407 	// BeOS R4 legacy API. Implemented as a wrapper around GetBufferList
408 	// "needLock" is ignored, GetBufferList will do locking
409 
410 	if (message == NULL)
411 		return B_BAD_VALUE;
412 
413 	if (name == NULL || strlen(name) == 0)
414 		return B_BAD_VALUE;
415 
416 	BBuffer ** buffers;
417 	status_t status;
418 	int32 count;
419 
420 	count = fBufferCount;
421 	buffers = new BBuffer * [count];
422 
423 	if (B_OK != (status = GetBufferList(count,buffers)))
424 		goto end;
425 
426 	for (int32 i = 0; i < count; i++)
427 		if (B_OK != (status = message->AddInt32(name,int32(buffers[i]->ID()))))
428 			goto end;
429 
430 end:
431 	delete [] buffers;
432 	return status;
433 }
434 
435