1 /*
2 * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2002, Marcus Overhagen. All Rights Reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8 /*! Used for BBufferGroup and BBuffer management across teams.
9 Created in the media server, cloned into each BBufferGroup (visible in
10 all address spaces).
11 */
12
13 // TODO: don't use a simple list!
14
15
16 #include <SharedBufferList.h>
17
18 #include <string.h>
19
20 #include <Autolock.h>
21 #include <Buffer.h>
22 #include <Locker.h>
23
24 #include <MediaDebug.h>
25 #include <DataExchange.h>
26
27
28 static BPrivate::SharedBufferList* sList = NULL;
29 static area_id sArea = -1;
30 static int32 sRefCount = 0;
31 static BLocker sLocker("shared buffer list");
32
33
34 namespace BPrivate {
35
36
37 /*static*/ area_id
Create(SharedBufferList ** _list)38 SharedBufferList::Create(SharedBufferList** _list)
39 {
40 CALLED();
41
42 size_t size = (sizeof(SharedBufferList) + (B_PAGE_SIZE - 1))
43 & ~(B_PAGE_SIZE - 1);
44 SharedBufferList* list;
45
46 area_id area = create_area("shared buffer list", (void**)&list,
47 B_ANY_ADDRESS, size, B_LAZY_LOCK,
48 B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
49 if (area < 0)
50 return area;
51
52 status_t status = list->_Init();
53 if (status != B_OK) {
54 delete_area(area);
55 return status;
56 }
57
58 return area;
59 }
60
61
62 /*static*/ SharedBufferList*
Get()63 SharedBufferList::Get()
64 {
65 CALLED();
66
67 BAutolock _(sLocker);
68
69 if (atomic_add(&sRefCount, 1) > 0 && sList != NULL)
70 return sList;
71
72 // ask media_server to get the area_id of the shared buffer list
73 server_get_shared_buffer_area_request areaRequest;
74 server_get_shared_buffer_area_reply areaReply;
75 if (QueryServer(SERVER_GET_SHARED_BUFFER_AREA, &areaRequest,
76 sizeof(areaRequest), &areaReply, sizeof(areaReply)) != B_OK) {
77 ERROR("SharedBufferList::Get() SERVER_GET_SHARED_BUFFER_AREA failed\n");
78 return NULL;
79 }
80
81 sArea = clone_area("shared buffer list clone", (void**)&sList,
82 B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, areaReply.area);
83 if (sArea < 0) {
84 ERROR("SharedBufferList::Get() clone area %" B_PRId32 ": %s\n",
85 areaReply.area, strerror(sArea));
86 return NULL;
87 }
88
89 return sList;
90 }
91
92
93 /*static*/ void
Invalidate()94 SharedBufferList::Invalidate()
95 {
96 delete_area(sArea);
97 sList = NULL;
98 }
99
100
101 void
Put()102 SharedBufferList::Put()
103 {
104 CALLED();
105 BAutolock _(sLocker);
106
107 if (atomic_add(&sRefCount, -1) == 1)
108 Invalidate();
109 }
110
111
112 /*! Deletes all BBuffers of the group specified by \a groupReclaimSem, then
113 unmaps the list from memory.
114 */
115 void
DeleteGroupAndPut(sem_id groupReclaimSem)116 SharedBufferList::DeleteGroupAndPut(sem_id groupReclaimSem)
117 {
118 CALLED();
119
120 if (Lock() == B_OK) {
121 for (int32 i = 0; i < fCount; i++) {
122 if (fInfos[i].reclaim_sem == groupReclaimSem) {
123 // delete the associated buffer
124 delete fInfos[i].buffer;
125
126 // Decrement buffer count by one, and fill the gap
127 // in the list with its last entry
128 fCount--;
129 if (fCount > 0)
130 fInfos[i--] = fInfos[fCount];
131 }
132 }
133
134 Unlock();
135 }
136
137 Put();
138 }
139
140
141 status_t
Lock()142 SharedBufferList::Lock()
143 {
144 if (atomic_add(&fAtom, 1) > 0) {
145 status_t status;
146 do {
147 status = acquire_sem(fSemaphore);
148 } while (status == B_INTERRUPTED);
149
150 return status;
151 }
152 return B_OK;
153 }
154
155
156 status_t
Unlock()157 SharedBufferList::Unlock()
158 {
159 if (atomic_add(&fAtom, -1) > 1)
160 return release_sem(fSemaphore);
161
162 return B_OK;
163 }
164
165
166 status_t
AddBuffer(sem_id groupReclaimSem,const buffer_clone_info & info,BBuffer ** _buffer)167 SharedBufferList::AddBuffer(sem_id groupReclaimSem,
168 const buffer_clone_info& info, BBuffer** _buffer)
169 {
170 status_t status = Lock();
171 if (status != B_OK)
172 return status;
173
174 // Check if the id exists
175 status = CheckID(groupReclaimSem, info.buffer);
176 if (status != B_OK) {
177 Unlock();
178 return status;
179 }
180 BBuffer* buffer = new(std::nothrow) BBuffer(info);
181 if (buffer == NULL) {
182 Unlock();
183 return B_NO_MEMORY;
184 }
185
186 if (buffer->Data() == NULL) {
187 // BBuffer::Data() will return NULL if an error occured
188 ERROR("BBufferGroup: error while creating buffer\n");
189 delete buffer;
190 Unlock();
191 return B_ERROR;
192 }
193
194 status = AddBuffer(groupReclaimSem, buffer);
195 if (status != B_OK) {
196 delete buffer;
197 Unlock();
198 return status;
199 } else if (_buffer != NULL)
200 *_buffer = buffer;
201
202 return Unlock();
203 }
204
205
206 status_t
AddBuffer(sem_id groupReclaimSem,BBuffer * buffer)207 SharedBufferList::AddBuffer(sem_id groupReclaimSem, BBuffer* buffer)
208 {
209 CALLED();
210
211 if (buffer == NULL)
212 return B_BAD_VALUE;
213
214 if (fCount == kMaxBuffers) {
215 return B_MEDIA_TOO_MANY_BUFFERS;
216 }
217
218 fInfos[fCount].id = buffer->ID();
219 fInfos[fCount].buffer = buffer;
220 fInfos[fCount].reclaim_sem = groupReclaimSem;
221 fInfos[fCount].reclaimed = true;
222 fCount++;
223
224 return release_sem_etc(groupReclaimSem, 1, B_DO_NOT_RESCHEDULE);
225 }
226
227
228 status_t
CheckID(sem_id groupSem,media_buffer_id id) const229 SharedBufferList::CheckID(sem_id groupSem, media_buffer_id id) const
230 {
231 CALLED();
232
233 if (id == 0)
234 return B_OK;
235 if (id < 0)
236 return B_BAD_VALUE;
237
238 for (int32 i = 0; i < fCount; i++) {
239 if (fInfos[i].id == id
240 && fInfos[i].reclaim_sem == groupSem) {
241 return B_ERROR;
242 }
243 }
244 return B_OK;
245 }
246
247
248 status_t
RequestBuffer(sem_id groupReclaimSem,int32 buffersInGroup,size_t size,media_buffer_id wantID,BBuffer ** _buffer,bigtime_t timeout)249 SharedBufferList::RequestBuffer(sem_id groupReclaimSem, int32 buffersInGroup,
250 size_t size, media_buffer_id wantID, BBuffer** _buffer, bigtime_t timeout)
251 {
252 CALLED();
253 // We always search for a buffer from the group indicated by groupReclaimSem
254 // first.
255 // If "size" != 0, we search for a buffer that is "size" bytes or larger.
256 // If "wantID" != 0, we search for a buffer with this ID.
257 // If "*_buffer" != NULL, we search for a buffer at this address.
258 //
259 // If we found a buffer, we also need to mark it in all other groups as
260 // requested and also once need to acquire the reclaim_sem of the other
261 // groups
262
263 uint32 acquireFlags;
264
265 if (timeout <= 0) {
266 timeout = 0;
267 acquireFlags = B_RELATIVE_TIMEOUT;
268 } else if (timeout == B_INFINITE_TIMEOUT) {
269 acquireFlags = B_RELATIVE_TIMEOUT;
270 } else {
271 timeout += system_time();
272 acquireFlags = B_ABSOLUTE_TIMEOUT;
273 }
274
275 // With each itaration we request one more buffer, since we need to skip
276 // the buffers that don't fit the request
277 int32 count = 1;
278
279 do {
280 status_t status;
281 do {
282 status = acquire_sem_etc(groupReclaimSem, count, acquireFlags,
283 timeout);
284 } while (status == B_INTERRUPTED);
285
286 if (status != B_OK)
287 return status;
288
289 // try to exit savely if the lock fails
290 status = Lock();
291 if (status != B_OK) {
292 ERROR("SharedBufferList:: RequestBuffer: Lock failed: %s\n",
293 strerror(status));
294 release_sem_etc(groupReclaimSem, count, 0);
295 return B_ERROR;
296 }
297
298 for (int32 i = 0; i < fCount; i++) {
299 // We need a BBuffer from the group, and it must be marked as
300 // reclaimed
301 if (fInfos[i].reclaim_sem == groupReclaimSem
302 && fInfos[i].reclaimed) {
303 if ((size != 0 && size <= fInfos[i].buffer->SizeAvailable())
304 || (*_buffer != 0 && fInfos[i].buffer == *_buffer)
305 || (wantID != 0 && fInfos[i].id == wantID)) {
306 // we found a buffer
307 fInfos[i].reclaimed = false;
308 *_buffer = fInfos[i].buffer;
309
310 // if we requested more than one buffer, release the rest
311 if (count > 1) {
312 release_sem_etc(groupReclaimSem, count - 1,
313 B_DO_NOT_RESCHEDULE);
314 }
315
316 // And mark all buffers with the same ID as requested in
317 // all other buffer groups
318 _RequestBufferInOtherGroups(groupReclaimSem,
319 fInfos[i].buffer->ID());
320
321 Unlock();
322 return B_OK;
323 }
324 }
325 }
326
327 release_sem_etc(groupReclaimSem, count, B_DO_NOT_RESCHEDULE);
328 if (Unlock() != B_OK) {
329 ERROR("SharedBufferList:: RequestBuffer: unlock failed\n");
330 return B_ERROR;
331 }
332 // prepare to request one more buffer next time
333 count++;
334 } while (count <= buffersInGroup);
335
336 ERROR("SharedBufferList:: RequestBuffer: no buffer found\n");
337 return B_ERROR;
338 }
339
340
341 status_t
RecycleBuffer(BBuffer * buffer)342 SharedBufferList::RecycleBuffer(BBuffer* buffer)
343 {
344 CALLED();
345
346 media_buffer_id id = buffer->ID();
347
348 if (Lock() != B_OK)
349 return B_ERROR;
350
351 int32 reclaimedCount = 0;
352
353 for (int32 i = 0; i < fCount; i++) {
354 // find the buffer id, and reclaim it in all groups it belongs to
355 if (fInfos[i].id == id) {
356 reclaimedCount++;
357 if (fInfos[i].reclaimed) {
358 ERROR("SharedBufferList::RecycleBuffer, BBuffer %p, id = %"
359 B_PRId32 " already reclaimed\n", buffer, id);
360 DEBUG_ONLY(debugger("buffer already reclaimed"));
361 continue;
362 }
363 fInfos[i].reclaimed = true;
364 release_sem_etc(fInfos[i].reclaim_sem, 1, B_DO_NOT_RESCHEDULE);
365 }
366 }
367
368 if (Unlock() != B_OK)
369 return B_ERROR;
370
371 if (reclaimedCount == 0) {
372 ERROR("shared_buffer_list::RecycleBuffer, BBuffer %p, id = %" B_PRId32
373 " NOT reclaimed\n", buffer, id);
374 return B_ERROR;
375 }
376
377 return B_OK;
378 }
379
380
381 status_t
RemoveBuffer(BBuffer * buffer)382 SharedBufferList::RemoveBuffer(BBuffer* buffer)
383 {
384 CALLED();
385
386 media_buffer_id id = buffer->ID();
387
388 if (Lock() != B_OK)
389 return B_ERROR;
390
391 int32 notRemovedCount = 0;
392
393 for (int32 i = 0; i < fCount; i++) {
394 // find the buffer id, and remove it in all groups it belongs to
395 if (fInfos[i].id == id) {
396 if (!fInfos[i].reclaimed) {
397 notRemovedCount++;
398 ERROR("SharedBufferList::RequestBuffer, BBuffer %p, id = %"
399 B_PRId32 " not reclaimed\n", buffer, id);
400 DEBUG_ONLY(debugger("buffer not reclaimed"));
401 continue;
402 }
403 fInfos[i].buffer = NULL;
404 fInfos[i].id = -1;
405 fInfos[i].reclaim_sem = -1;
406 }
407 }
408
409 if (Unlock() != B_OK)
410 return B_ERROR;
411
412 if (notRemovedCount != 0) {
413 ERROR("SharedBufferList::RemoveBuffer, BBuffer %p, id = %" B_PRId32
414 " not reclaimed\n", buffer, id);
415 return B_ERROR;
416 }
417
418 return B_OK;
419 }
420
421
422
423 /*! Returns exactly \a bufferCount buffers from the group specified via its
424 \a groupReclaimSem if successful.
425 */
426 status_t
GetBufferList(sem_id groupReclaimSem,int32 bufferCount,BBuffer ** buffers)427 SharedBufferList::GetBufferList(sem_id groupReclaimSem, int32 bufferCount,
428 BBuffer** buffers)
429 {
430 CALLED();
431
432 if (Lock() != B_OK)
433 return B_ERROR;
434
435 int32 found = 0;
436
437 for (int32 i = 0; i < fCount; i++)
438 if (fInfos[i].reclaim_sem == groupReclaimSem) {
439 buffers[found++] = fInfos[i].buffer;
440 if (found == bufferCount)
441 break;
442 }
443
444 if (Unlock() != B_OK)
445 return B_ERROR;
446
447 return found == bufferCount ? B_OK : B_ERROR;
448 }
449
450
451 status_t
_Init()452 SharedBufferList::_Init()
453 {
454 CALLED();
455
456 fSemaphore = create_sem(0, "shared buffer list lock");
457 if (fSemaphore < 0)
458 return fSemaphore;
459
460 fAtom = 0;
461
462 for (int32 i = 0; i < kMaxBuffers; i++) {
463 fInfos[i].id = -1;
464 }
465 fCount = 0;
466
467 return B_OK;
468 }
469
470
471 /*! Used by RequestBuffer, call this one with the list locked!
472 */
473 void
_RequestBufferInOtherGroups(sem_id groupReclaimSem,media_buffer_id id)474 SharedBufferList::_RequestBufferInOtherGroups(sem_id groupReclaimSem,
475 media_buffer_id id)
476 {
477 for (int32 i = 0; i < fCount; i++) {
478 // find buffers with same id, but belonging to other groups
479 if (fInfos[i].id == id && fInfos[i].reclaim_sem != groupReclaimSem) {
480 // and mark them as requested
481 // TODO: this can deadlock if BBuffers with same media_buffer_id
482 // exist in more than one BBufferGroup, and RequestBuffer()
483 // is called on both groups (which should not be done).
484 status_t status;
485 do {
486 status = acquire_sem(fInfos[i].reclaim_sem);
487 } while (status == B_INTERRUPTED);
488
489 // try to skip entries that belong to crashed teams
490 if (status != B_OK)
491 continue;
492
493 if (fInfos[i].reclaimed == false) {
494 ERROR("SharedBufferList:: RequestBufferInOtherGroups BBuffer "
495 "%p, id = %" B_PRId32 " not reclaimed while requesting\n",
496 fInfos[i].buffer, id);
497 continue;
498 }
499
500 fInfos[i].reclaimed = false;
501 }
502 }
503 }
504
505
506 } // namespace BPrivate
507