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