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
40BBufferGroup::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
94BBufferGroup::BBufferGroup()
95{
96	CALLED();
97	if (_Init() != B_OK)
98		return;
99
100	// this one simply creates an empty BBufferGroup
101}
102
103
104BBufferGroup::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
128BBufferGroup::~BBufferGroup()
129{
130	CALLED();
131	if (fBufferList != NULL)
132		fBufferList->DeleteGroupAndPut(fReclaimSem);
133
134	delete_sem(fReclaimSem);
135}
136
137
138status_t
139BBufferGroup::InitCheck()
140{
141	CALLED();
142	return fInitError;
143}
144
145
146status_t
147BBufferGroup::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
183BBuffer*
184BBufferGroup::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
205status_t
206BBufferGroup::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
224status_t
225BBufferGroup::RequestError()
226{
227	CALLED();
228	if (fInitError != B_OK)
229		return B_NO_INIT;
230
231	return fRequestError;
232}
233
234
235status_t
236BBufferGroup::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
247status_t
248BBufferGroup::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
261status_t
262BBufferGroup::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
293status_t
294BBufferGroup::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
331status_t
332BBufferGroup::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
363end:
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
377status_t
378BBufferGroup::_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