xref: /haiku/src/kits/media/SharedBufferList.cpp (revision 2807c36668a1730dd59bc39de65e0b8f88cd5d0d)
1 /***********************************************************************
2  * Copyright (c) 2002 Marcus Overhagen. All Rights Reserved.
3  * This file may be used under the terms of the OpenBeOS License.
4  *
5  * Used for BBufferGroup and BBuffer management across teams
6  ***********************************************************************/
7 #include <Buffer.h>
8 #include "SharedBufferList.h"
9 #include "debug.h"
10 
11 
12 status_t
13 _shared_buffer_list::Init()
14 {
15 	CALLED();
16 	locker_atom = 0;
17 	locker_sem = create_sem(0,"shared buffer list lock");
18 	if (locker_sem < B_OK)
19 		return (status_t) locker_sem;
20 
21 	for (int i = 0; i < MAX_BUFFER; i++) {
22 		info[i].id = -1;
23 		info[i].buffer = 0;
24 		info[i].reclaim_sem = 0;
25 		info[i].reclaimed = false;
26 	}
27 	return B_OK;
28 }
29 
30 _shared_buffer_list *
31 _shared_buffer_list::Clone(area_id id)
32 {
33 	CALLED();
34 	// if id == -1, we are in the media_server team,
35 	// and create the initial list, else we clone it
36 
37 	_shared_buffer_list *adr;
38 	status_t status;
39 
40 	if (id == -1) {
41 		size_t size = ((sizeof(_shared_buffer_list)) + (B_PAGE_SIZE - 1)) & ~(B_PAGE_SIZE - 1);
42 		status = create_area("shared buffer list",(void **)&adr,B_ANY_KERNEL_ADDRESS,size,B_LAZY_LOCK,B_READ_AREA | B_WRITE_AREA);
43 		if (status >= B_OK) {
44 			status = adr->Init();
45 			if (status != B_OK)
46 				delete_area(area_for(adr));
47 		}
48 	} else {
49 		status = clone_area("shared buffer list clone",(void **)&adr,B_ANY_KERNEL_ADDRESS,B_READ_AREA | B_WRITE_AREA,id);
50 		//TRACE("cloned area, id = 0x%08lx, ptr = 0x%08x\n",status,(int)adr);
51 	}
52 
53 	return (status < B_OK) ? 	NULL : adr;
54 }
55 
56 void
57 _shared_buffer_list::Unmap()
58 {
59 	CALLED();
60 	// unmap the memory used by this struct
61 	// XXX is this save?
62 	area_id id;
63 	id = area_for(this);
64 	if (id >= B_OK)
65 		delete_area(id);
66 }
67 
68 void
69 _shared_buffer_list::Terminate(sem_id group_reclaim_sem)
70 {
71 	CALLED();
72 
73 	// delete all BBuffers of this group, then unmap from memory
74 
75 	if (Lock() != B_OK) { // better not try to access the list unlocked
76 		// but at least try to unmap the memory
77 		Unmap();
78 		return;
79 	}
80 
81 	for (int32 i = 0; i < buffercount; i++) {
82 		if (info[i].reclaim_sem == group_reclaim_sem) {
83 			// delete the associated buffer
84 			delete info[i].buffer;
85 			// decrement buffer count by one
86 			buffercount--;
87 			// fill the gap in the list with the last entry
88 			if (buffercount > 0) {
89 				info[i] = info[buffercount];
90 				i--; // make sure we check this entry again
91 			}
92 		}
93 	}
94 
95 	Unlock();
96 
97 	Unmap();
98 }
99 
100 status_t
101 _shared_buffer_list::Lock()
102 {
103 	if (atomic_add(&locker_atom, 1) > 0) {
104 		status_t status;
105 		while (B_INTERRUPTED == (status = acquire_sem(locker_sem)))
106 			;
107 		return status; // will only return != B_OK if the media_server crashed or quit
108 	}
109 	return B_OK;
110 }
111 
112 status_t
113 _shared_buffer_list::Unlock()
114 {
115 	if (atomic_add(&locker_atom, -1) > 1)
116 		return release_sem(locker_sem); // will only return != B_OK if the media_server crashed or quit
117 	return B_OK;
118 }
119 
120 status_t
121 _shared_buffer_list::AddBuffer(sem_id group_reclaim_sem, BBuffer *buffer)
122 {
123 	CALLED();
124 
125 	if (buffer == NULL)
126 		return B_BAD_VALUE;
127 
128 	if (Lock() != B_OK)
129 		return B_ERROR;
130 
131 	if (buffercount == MAX_BUFFER) {
132 		Unlock();
133 		debugger("we are doomed");
134 		return B_ERROR;
135 	}
136 
137 	info[buffercount].id = buffer->ID();
138 	info[buffercount].buffer = buffer;
139 	info[buffercount].reclaim_sem = group_reclaim_sem;
140 	info[buffercount].reclaimed = true;
141 	buffercount++;
142 
143 	status_t status1 = release_sem_etc(group_reclaim_sem,1,B_DO_NOT_RESCHEDULE);
144 	status_t status2 = Unlock();
145 
146 	return (status1 == B_OK && status2 == B_OK) ? B_OK : B_ERROR;
147 }
148 
149 status_t
150 _shared_buffer_list::RequestBuffer(sem_id group_reclaim_sem, int32 buffers_in_group, size_t size, media_buffer_id wantID, BBuffer **buffer, bigtime_t timeout)
151 {
152 	CALLED();
153 	// we always search for a buffer from the group indicated by group_reclaim_sem first
154 	// if "size" != 0, we search for a buffer that is "size" bytes or larger
155 	// if "wantID" != 0, we search for a buffer with this id
156 	// if "*buffer" != NULL, we search for a buffer at this address
157 	// if we found a buffer, we also need to mark it in all other groups as requested
158 	// and also once need to acquire the reclaim_sem of the other groups
159 
160 	status_t status;
161 	uint32 acquire_flags;
162 	int32 count;
163 
164 	if (timeout <= 0) {
165 		timeout = 0;
166 		acquire_flags = B_RELATIVE_TIMEOUT;
167 	} else if (timeout != B_INFINITE_TIMEOUT) {
168 		timeout += system_time();
169 		acquire_flags = B_ABSOLUTE_TIMEOUT;
170 	} else {
171 	 	//timeout is B_INFINITE_TIMEOUT
172 		acquire_flags = B_RELATIVE_TIMEOUT;
173 	}
174 
175 	// with each itaration we request one more buffer, since we need to skip the buffers that don't fit the request
176 	count = 1;
177 
178 	do {
179 		while (B_INTERRUPTED == (status = acquire_sem_etc(group_reclaim_sem, count, acquire_flags, timeout)))
180 			;
181 		if (status != B_OK)
182 			return status;
183 
184 		// try to exit savely if the lock fails
185 		if (Lock() != B_OK) {
186 			release_sem_etc(group_reclaim_sem, count, 0);
187 			return B_ERROR;
188 		}
189 
190 		for (int32 i = 0; i < buffercount; i++) {
191 			// we need a BBuffer from the group, and it must be marked as reclaimed
192 			if (info[i].reclaim_sem == group_reclaim_sem && info[i].reclaimed) {
193 				if (
194 					  (size != 0 && size <= info[i].buffer->SizeAvailable()) ||
195 					  (*buffer != 0 && info[i].buffer == *buffer) ||
196 					  (wantID != 0 && info[i].id == wantID)
197 				   ) {
198 				   	// we found a buffer
199 					info[i].reclaimed = false;
200 					*buffer = info[i].buffer;
201 					// if we requested more than one buffer, release the rest
202 					if (count > 1)
203 						release_sem_etc(group_reclaim_sem, count - 1, B_DO_NOT_RESCHEDULE);
204 
205 					// and mark all buffers with the same ID as requested in all other buffer groups
206 					RequestBufferInOtherGroups(group_reclaim_sem, info[i].buffer->ID());
207 
208 					Unlock();
209 					return B_OK;
210 				}
211 			}
212 		}
213 
214 		release_sem_etc(group_reclaim_sem, count, B_DO_NOT_RESCHEDULE);
215 		if (Unlock() != B_OK)
216 			return B_ERROR;
217 
218 		// prepare to request one more buffer next time
219 		count++;
220 	} while (count <= buffers_in_group);
221 
222 	return B_ERROR;
223 }
224 
225 void
226 _shared_buffer_list::RequestBufferInOtherGroups(sem_id group_reclaim_sem, media_buffer_id id)
227 {
228 	for (int32 i = 0; i < buffercount; i++) {
229 		// find buffers with same id, but belonging to other groups
230 		if (info[i].id == id && info[i].reclaim_sem != group_reclaim_sem) {
231 
232 			// and mark them as requested
233 			// XXX this can deadlock if BBuffers with same media_buffer_id
234 			// XXX exist in more than one BBufferGroup, and RequestBuffer()
235 			// XXX is called on both groups (which should not be done).
236 			status_t status;
237 			while (B_INTERRUPTED == (status = acquire_sem(info[i].reclaim_sem)))
238 				;
239 			// try to skip entries that belong to crashed teams
240 			if (status != B_OK)
241 				continue;
242 
243 			if (info[i].reclaimed == false) {
244 				FATAL("_shared_buffer_list: Error, BBuffer %p, id = %ld not reclaimed while requesting\n", info[i].buffer, id);
245 				continue;
246 			}
247 
248 			info[i].reclaimed = false;
249 		}
250 	}
251 }
252 
253 status_t
254 _shared_buffer_list::RecycleBuffer(BBuffer *buffer)
255 {
256 	CALLED();
257 
258 	int reclaimed_count;
259 
260 	media_buffer_id id = buffer->ID();
261 
262 	if (Lock() != B_OK)
263 		return B_ERROR;
264 
265 	reclaimed_count = 0;
266 	for (int32 i = 0; i < buffercount; i++) {
267 		// find the buffer id, and reclaim it in all groups it belongs to
268 		if (info[i].id == id) {
269 			reclaimed_count++;
270 			if (info[i].reclaimed) {
271 				FATAL("_shared_buffer_list: Error, BBuffer %p, id = %ld already reclaimed\n", buffer, id);
272 				continue;
273 			}
274 			info[i].reclaimed = true;
275 			release_sem_etc(info[i].reclaim_sem, 1, B_DO_NOT_RESCHEDULE);
276 		}
277 	}
278 	if (Unlock() != B_OK)
279 		return B_ERROR;
280 
281 	if (reclaimed_count == 0) {
282 		FATAL("shared_buffer_list: Error, BBuffer %p, id = %ld NOT reclaimed\n", buffer, id);
283 		return B_ERROR;
284 	}
285 
286 	return B_OK;
287 }
288 
289 status_t
290 _shared_buffer_list::GetBufferList(sem_id group_reclaim_sem, int32 buf_count, BBuffer **out_buffers)
291 {
292 	CALLED();
293 
294 	int32 found;
295 
296 	found = 0;
297 
298 	if (Lock() != B_OK)
299 		return B_ERROR;
300 
301 	for (int32 i = 0; i < buffercount; i++)
302 		if (info[i].reclaim_sem == group_reclaim_sem) {
303 			out_buffers[found++] = info[i].buffer;
304 			if (found == buf_count)
305 				break;
306 		}
307 
308 	if (Unlock() != B_OK)
309 		return B_ERROR;
310 
311 	return (found == buf_count) ? B_OK : B_ERROR;
312 }
313 
314