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