xref: /haiku/src/kits/media/BufferGroup.cpp (revision b0944c78b074a8110bd98e060415d0e8f38a7f65)
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 
31 #include <BufferGroup.h>
32 
33 #include <Buffer.h>
34 
35 #include "debug.h"
36 #include "DataExchange.h"
37 #include "SharedBufferList.h"
38 
39 
40 BBufferGroup::BBufferGroup(size_t size, int32 count, uint32 placement,
41 	uint32 lock)
42 {
43 	CALLED();
44 	if (_Init() != B_OK)
45 		return;
46 
47 	// This one is easy. We need to create "count" BBuffers,
48 	// each one "size" bytes large. They all go into one
49 	// area, with "placement" and "lock" attributes.
50 	// The BBuffers created will clone the area, and
51 	// then we delete our area. This way BBuffers are
52 	// independent from the BBufferGroup
53 
54 	// don't allow all placement parameter values
55 	if (placement != B_ANY_ADDRESS && placement != B_ANY_KERNEL_ADDRESS) {
56 		ERROR("BBufferGroup: placement != B_ANY_ADDRESS "
57 			"&& placement != B_ANY_KERNEL_ADDRESS (0x%#" B_PRIx32 ")\n",
58 			placement);
59 		placement = B_ANY_ADDRESS;
60 	}
61 
62 	// first we roundup for a better placement in memory
63 	size_t allocSize = (size + 63) & ~63;
64 
65 	// now we create the area
66 	size_t areaSize
67 		= ((allocSize * count) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
68 
69 	void* startAddress;
70 	area_id bufferArea = create_area("some buffers area", &startAddress,
71 		placement, areaSize, lock, B_READ_AREA | B_WRITE_AREA);
72 	if (bufferArea < 0) {
73 		ERROR("BBufferGroup: failed to allocate %ld bytes area\n", areaSize);
74 		fInitError = (status_t)bufferArea;
75 		return;
76 	}
77 
78 	buffer_clone_info info;
79 
80 	for (int32 i = 0; i < count; i++) {
81 		info.area = bufferArea;
82 		info.offset = i * allocSize;
83 		info.size = size;
84 
85 		fInitError = AddBuffer(info);
86 		if (fInitError != B_OK)
87 			break;
88 	}
89 
90 	delete_area(bufferArea);
91 }
92 
93 
94 BBufferGroup::BBufferGroup()
95 {
96 	CALLED();
97 	if (_Init() != B_OK)
98 		return;
99 
100 	// this one simply creates an empty BBufferGroup
101 }
102 
103 
104 BBufferGroup::BBufferGroup(int32 count, const media_buffer_id* buffers)
105 {
106 	CALLED();
107 	if (_Init() != B_OK)
108 		return;
109 
110 	// TODO: we need to make sure that a media_buffer_id is only added
111 	// once to each group
112 
113 	// this one creates "BBuffer"s from "media_buffer_id"s passed
114 	// by the application.
115 
116 	buffer_clone_info info;
117 
118 	for (int32 i = 0; i < count; i++) {
119 		info.buffer = buffers[i];
120 
121 		fInitError = AddBuffer(info);
122 		if (fInitError != B_OK)
123 			break;
124 	}
125 }
126 
127 
128 BBufferGroup::~BBufferGroup()
129 {
130 	CALLED();
131 	if (fBufferList != NULL)
132 		fBufferList->DeleteGroupAndPut(fReclaimSem);
133 
134 	delete_sem(fReclaimSem);
135 }
136 
137 
138 status_t
139 BBufferGroup::InitCheck()
140 {
141 	CALLED();
142 	return fInitError;
143 }
144 
145 
146 status_t
147 BBufferGroup::AddBuffer(const buffer_clone_info& info, BBuffer** _buffer)
148 {
149 	CALLED();
150 	if (fInitError != B_OK)
151 		return B_NO_INIT;
152 
153 	// TODO: we need to make sure that a media_buffer_id is only added
154 	// once to each group
155 
156 	BBuffer* buffer = new(std::nothrow) BBuffer(info);
157 	if (buffer == NULL)
158 		return B_NO_MEMORY;
159 
160 	if (buffer->Data() == NULL) {
161 		// BBuffer::Data() will return NULL if an error occured
162 		ERROR("BBufferGroup: error while creating buffer\n");
163 		delete buffer;
164 		return B_ERROR;
165 	}
166 
167 	status_t status = fBufferList->AddBuffer(fReclaimSem, buffer);
168 	if (status != B_OK) {
169 		ERROR("BBufferGroup: error when adding buffer\n");
170 		delete buffer;
171 		return status;
172 	}
173 
174 	atomic_add(&fBufferCount, 1);
175 
176 	if (_buffer != NULL)
177 		*_buffer = buffer;
178 
179 	return B_OK;
180 }
181 
182 
183 BBuffer*
184 BBufferGroup::RequestBuffer(size_t size, bigtime_t timeout)
185 {
186 	CALLED();
187 	if (fInitError != B_OK)
188 		return NULL;
189 
190 	if (size <= 0)
191 		return NULL;
192 
193 	BBuffer *buffer;
194 	status_t status;
195 
196 	buffer = NULL;
197 	status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, size, 0,
198 		&buffer, timeout);
199 	fRequestError = status;
200 
201 	return status == B_OK ? buffer : NULL;
202 }
203 
204 
205 status_t
206 BBufferGroup::RequestBuffer(BBuffer* buffer, bigtime_t timeout)
207 {
208 	CALLED();
209 	if (fInitError != B_OK)
210 		return B_NO_INIT;
211 
212 	if (buffer == NULL)
213 		return B_BAD_VALUE;
214 
215 	status_t status;
216 	status = fBufferList->RequestBuffer(fReclaimSem, fBufferCount, 0, 0,
217 		&buffer, timeout);
218 	fRequestError = status;
219 
220 	return status;
221 }
222 
223 
224 status_t
225 BBufferGroup::RequestError()
226 {
227 	CALLED();
228 	if (fInitError != B_OK)
229 		return B_NO_INIT;
230 
231 	return fRequestError;
232 }
233 
234 
235 status_t
236 BBufferGroup::CountBuffers(int32* _count)
237 {
238 	CALLED();
239 	if (fInitError != B_OK)
240 		return B_NO_INIT;
241 
242 	*_count = fBufferCount;
243 	return B_OK;
244 }
245 
246 
247 status_t
248 BBufferGroup::GetBufferList(int32 bufferCount, BBuffer** _buffers)
249 {
250 	CALLED();
251 	if (fInitError != B_OK)
252 		return B_NO_INIT;
253 
254 	if (bufferCount <= 0 || bufferCount > fBufferCount)
255 		return B_BAD_VALUE;
256 
257 	return fBufferList->GetBufferList(fReclaimSem, bufferCount, _buffers);
258 }
259 
260 
261 status_t
262 BBufferGroup::WaitForBuffers()
263 {
264 	CALLED();
265 	if (fInitError != B_OK)
266 		return B_NO_INIT;
267 
268 	// TODO: this function is not really useful anyway, and will
269 	// not work exactly as documented, but it is close enough
270 
271 	if (fBufferCount < 0)
272 		return B_BAD_VALUE;
273 	if (fBufferCount == 0)
274 		return B_OK;
275 
276 	// We need to wait until at least one buffer belonging to this group is
277 	// reclaimed.
278 	// This has happened when can aquire "fReclaimSem"
279 
280 	status_t status;
281 	while ((status = acquire_sem(fReclaimSem)) == B_INTERRUPTED)
282 		;
283 	if (status != B_OK)
284 		return status;
285 
286 	// we need to release the "fReclaimSem" now, else we would block
287 	// requesting of new buffers
288 
289 	return release_sem(fReclaimSem);
290 }
291 
292 
293 status_t
294 BBufferGroup::ReclaimAllBuffers()
295 {
296 	CALLED();
297 	if (fInitError != B_OK)
298 		return B_NO_INIT;
299 
300 	// because additional BBuffers might get added to this group betweeen
301 	// acquire and release
302 	int32 count = fBufferCount;
303 
304 	if (count < 0)
305 		return B_BAD_VALUE;
306 	if (count == 0)
307 		return B_OK;
308 
309 	// we need to wait until all BBuffers belonging to this group are reclaimed.
310 	// this has happened when the "fReclaimSem" can be aquired "fBufferCount"
311 	// times
312 
313 	status_t status;
314 	do {
315 		status = acquire_sem_etc(fReclaimSem, count, 0, 0);
316 	} while (status == B_INTERRUPTED);
317 
318 	if (status != B_OK)
319 		return status;
320 
321 	// we need to release the "fReclaimSem" now, else we would block
322 	// requesting of new buffers
323 
324 	return release_sem_etc(fReclaimSem, count, 0);
325 }
326 
327 
328 //	#pragma mark - deprecated BeOS R4 API
329 
330 
331 status_t
332 BBufferGroup::AddBuffersTo(BMessage* message, const char* name, bool needLock)
333 {
334 	CALLED();
335 	if (fInitError != B_OK)
336 		return B_NO_INIT;
337 
338 	// BeOS R4 legacy API. Implemented as a wrapper around GetBufferList
339 	// "needLock" is ignored, GetBufferList will do locking
340 
341 	if (message == NULL)
342 		return B_BAD_VALUE;
343 
344 	if (name == NULL || strlen(name) == 0)
345 		return B_BAD_VALUE;
346 
347 	BBuffer** buffers;
348 	int32 count;
349 
350 	count = fBufferCount;
351 	buffers = new BBuffer * [count];
352 
353 	status_t status = GetBufferList(count, buffers);
354 	if (status != B_OK)
355 		goto end;
356 
357 	for (int32 i = 0; i < count; i++) {
358 		status = message->AddInt32(name, int32(buffers[i]->ID()));
359 		if (status != B_OK)
360 			goto end;
361 	}
362 
363 end:
364 	delete [] buffers;
365 	return status;
366 }
367 
368 
369 //	#pragma mark - private methods
370 
371 
372 /* not implemented */
373 //BBufferGroup::BBufferGroup(const BBufferGroup &)
374 //BBufferGroup & BBufferGroup::operator=(const BBufferGroup &)
375 
376 
377 status_t
378 BBufferGroup::_Init()
379 {
380 	CALLED();
381 
382 	// some defaults in case we drop out early
383 	fBufferList = 0;
384 	fInitError = B_ERROR;
385 	fRequestError = B_ERROR;
386 	fBufferCount = 0;
387 
388 	// Create the reclaim semaphore
389 	// This is also used as a system wide unique identifier for this group
390 	fReclaimSem = create_sem(0, "buffer reclaim sem");
391 	if (fReclaimSem < B_OK) {
392 		ERROR("BBufferGroup::InitBufferGroup: couldn't create fReclaimSem\n");
393 		fInitError = (status_t)fReclaimSem;
394 		return fInitError;
395 	}
396 
397 	fBufferList = BPrivate::SharedBufferList::Get();
398 	if (fBufferList == NULL) {
399 		ERROR("BBufferGroup::InitBufferGroup: SharedBufferList::Get() "
400 			"failed\n");
401 		fInitError = B_ERROR;
402 		return fInitError;
403 	}
404 
405 	fInitError = B_OK;
406 	return fInitError;
407 }
408 
409