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/* Implements _event_queue_imp used by BTimedEventQueue, not thread save! 31 */ 32#include <string.h> 33 34#include <Autolock.h> 35#include <Buffer.h> 36#include <InterfaceDefs.h> //defines B_DELETE 37#include <TimedEventQueue.h> 38#include <TimeSource.h> 39 40#include "TimedEventQueuePrivate.h" 41 42#include "Debug.h" 43#include "debug.h" 44 45_event_queue_imp::_event_queue_imp() : 46 fLock(new BLocker("BTimedEventQueue locker")), 47 fEventCount(0), 48 fFirstEntry(NULL), 49 fLastEntry(NULL), 50 fCleanupHookContext(NULL), 51 fCleanupHook(0) 52{ 53} 54 55 56_event_queue_imp::~_event_queue_imp() 57{ 58 event_queue_entry *entry; 59 entry = fFirstEntry; 60 while (entry) { 61 event_queue_entry *deleteme; 62 deleteme = entry; 63 entry = entry->next; 64 delete deleteme; 65 } 66 delete fLock; 67} 68 69 70status_t 71_event_queue_imp::AddEvent(const media_timed_event &event) 72{ 73 BAutolock lock(fLock); 74 75// printf(" adding %12Ld at %12Ld\n", event.event_time, system_time()); 76 77 if (event.type <= 0) { 78 return B_BAD_VALUE; 79 } 80 81 *(bigtime_t *)&event.queued_time = BTimeSource::RealTime(); 82 83 //create a new queue 84 if (fFirstEntry == NULL) { 85 ASSERT(fEventCount == 0); 86 ASSERT(fLastEntry == NULL); 87 fFirstEntry = fLastEntry = new event_queue_entry; 88 fFirstEntry->event = event; 89 fFirstEntry->prev = NULL; 90 fFirstEntry->next = NULL; 91 fEventCount++; 92 return B_OK; 93 } 94 95 //insert at queue begin 96 if (fFirstEntry->event.event_time >= event.event_time) { 97 event_queue_entry *newentry = new event_queue_entry; 98 newentry->event = event; 99 newentry->prev = NULL; 100 newentry->next = fFirstEntry; 101 fFirstEntry->prev = newentry; 102 fFirstEntry = newentry; 103 fEventCount++; 104 return B_OK; 105 } 106 107 //insert at queue end 108 if (fLastEntry->event.event_time <= event.event_time) { 109 event_queue_entry *newentry = new event_queue_entry; 110 newentry->event = event; 111 newentry->prev = fLastEntry; 112 newentry->next = NULL; 113 fLastEntry->next = newentry; 114 fLastEntry = newentry; 115 fEventCount++; 116 return B_OK; 117 } 118 119 //insert into the queue 120 for (event_queue_entry *entry = fLastEntry; entry; entry = entry->prev) { 121 if (entry->event.event_time <= event.event_time) { 122 //insert after entry 123 event_queue_entry *newentry = new event_queue_entry; 124 newentry->event = event; 125 newentry->prev = entry; 126 newentry->next = entry->next; 127 (entry->next)->prev = newentry; 128 entry->next = newentry; 129 fEventCount++; 130 return B_OK; 131 } 132 } 133 134 debugger("_event_queue_imp::AddEvent failed, should not be here\n"); 135 return B_OK; 136} 137 138 139status_t 140_event_queue_imp::RemoveEvent(const media_timed_event *event) 141{ 142 BAutolock lock(fLock); 143 144 for (event_queue_entry *entry = fFirstEntry; entry; entry = entry->next) { 145 if (entry->event == *event) { 146 // No cleanup here 147 RemoveEntry(entry); 148 return B_OK; 149 } 150 } 151 152 return B_ERROR; 153} 154 155 156status_t 157_event_queue_imp::RemoveFirstEvent(media_timed_event * outEvent) 158{ 159 BAutolock lock(fLock); 160 161 if (fFirstEntry == 0) 162 return B_ERROR; 163 164 if (outEvent != 0) { 165 // No cleanup here 166 *outEvent = fFirstEntry->event; 167 } else { 168 CleanupEvent(&fFirstEntry->event); 169 } 170 171 RemoveEntry(fFirstEntry); 172 173 return B_OK; 174} 175 176 177bool 178_event_queue_imp::HasEvents() const 179{ 180 return fEventCount != 0; 181} 182 183 184int32 185_event_queue_imp::EventCount() const 186{ 187 #if DEBUG > 1 188 Dump(); 189 #endif 190 return fEventCount; 191} 192 193 194const media_timed_event * 195_event_queue_imp::FirstEvent() const 196{ 197 return fFirstEntry ? &fFirstEntry->event : NULL; 198} 199 200 201bigtime_t 202_event_queue_imp::FirstEventTime() const 203{ 204 return fFirstEntry ? fFirstEntry->event.event_time : B_INFINITE_TIMEOUT; 205} 206 207 208const media_timed_event * 209_event_queue_imp::LastEvent() const 210{ 211 return fLastEntry ? &fLastEntry->event : NULL; 212} 213 214 215bigtime_t 216_event_queue_imp::LastEventTime() const 217{ 218 return fLastEntry ? fLastEntry->event.event_time : B_INFINITE_TIMEOUT; 219} 220 221 222const media_timed_event * 223_event_queue_imp::FindFirstMatch(bigtime_t eventTime, 224 BTimedEventQueue::time_direction direction, 225 bool inclusive, 226 int32 eventType) 227{ 228 event_queue_entry *end; 229 event_queue_entry *entry; 230 231 BAutolock lock(fLock); 232 233 switch (direction) { 234 case BTimedEventQueue::B_ALWAYS: 235 for (entry = fFirstEntry; entry; entry = entry->next) { 236 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) 237 return &entry->event; 238 } 239 return NULL; 240 241 case BTimedEventQueue::B_BEFORE_TIME: 242 end = GetEnd_BeforeTime(eventTime, inclusive); 243 if (end == NULL) 244 return NULL; 245 end = end->next; 246 for (entry = fFirstEntry; entry != end; entry = entry->next) { 247 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 248 return &entry->event; 249 } 250 } 251 return NULL; 252 253 case BTimedEventQueue::B_AT_TIME: 254 { 255 bool found_time = false; 256 for (entry = fFirstEntry; entry; entry = entry->next) { 257 if (eventTime == entry->event.event_time) { 258 found_time = true; 259 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) 260 return &entry->event; 261 } else if (found_time) 262 return NULL; 263 } 264 return NULL; 265 } 266 267 case BTimedEventQueue::B_AFTER_TIME: 268 for (entry = GetStart_AfterTime(eventTime, inclusive); entry; entry = entry->next) { 269 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 270 return &entry->event; 271 } 272 } 273 return NULL; 274 } 275 276 return NULL; 277} 278 279 280status_t 281_event_queue_imp::DoForEach(BTimedEventQueue::for_each_hook hook, 282 void *context, 283 bigtime_t eventTime, 284 BTimedEventQueue::time_direction direction, 285 bool inclusive, 286 int32 eventType) 287{ 288 event_queue_entry *end; 289 event_queue_entry *entry; 290 event_queue_entry *remove; 291 BTimedEventQueue::queue_action action; 292 293 BAutolock lock(fLock); 294 295 switch (direction) { 296 case BTimedEventQueue::B_ALWAYS: 297 for (entry = fFirstEntry; entry; ) { 298 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 299 action = (*hook)(&entry->event, context); 300 switch (action) { 301 case BTimedEventQueue::B_DONE: 302 return B_OK; 303 case BTimedEventQueue::B_REMOVE_EVENT: 304 CleanupEvent(&entry->event); 305 remove = entry; 306 entry = entry->next; 307 RemoveEntry(remove); 308 break; 309 case BTimedEventQueue::B_NO_ACTION: 310 case BTimedEventQueue::B_RESORT_QUEUE: 311 default: 312 entry = entry->next; 313 break; 314 } 315 } else { 316 entry = entry->next; 317 } 318 } 319 return B_OK; 320 321 case BTimedEventQueue::B_BEFORE_TIME: 322 end = GetEnd_BeforeTime(eventTime, inclusive); 323 if (end == NULL) 324 return B_OK; 325 end = end->next; 326 for (entry = fFirstEntry; entry != end; ) { 327 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 328 action = (*hook)(&entry->event, context); 329 switch (action) { 330 case BTimedEventQueue::B_DONE: 331 return B_OK; 332 case BTimedEventQueue::B_REMOVE_EVENT: 333 CleanupEvent(&entry->event); 334 remove = entry; 335 entry = entry->next; 336 RemoveEntry(remove); 337 break; 338 case BTimedEventQueue::B_NO_ACTION: 339 case BTimedEventQueue::B_RESORT_QUEUE: 340 default: 341 entry = entry->next; 342 break; 343 } 344 } else { 345 entry = entry->next; 346 } 347 } 348 return B_OK; 349 350 case BTimedEventQueue::B_AT_TIME: 351 { 352 bool found_time = false; 353 for (entry = fFirstEntry; entry; ) { 354 if (eventTime == entry->event.event_time) { 355 found_time = true; 356 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 357 action = (*hook)(&entry->event, context); 358 switch (action) { 359 case BTimedEventQueue::B_DONE: 360 return B_OK; 361 case BTimedEventQueue::B_REMOVE_EVENT: 362 CleanupEvent(&entry->event); 363 remove = entry; 364 entry = entry->next; 365 RemoveEntry(remove); 366 break; 367 case BTimedEventQueue::B_NO_ACTION: 368 case BTimedEventQueue::B_RESORT_QUEUE: 369 default: 370 entry = entry->next; 371 break; 372 } 373 } else { 374 entry = entry->next; 375 } 376 } else if (found_time) { 377 break; 378 } else { 379 entry = entry->next; 380 } 381 } 382 return B_OK; 383 } 384 385 case BTimedEventQueue::B_AFTER_TIME: 386 for (entry = GetStart_AfterTime(eventTime, inclusive); entry; ) { 387 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 388 action = (*hook)(&entry->event, context); 389 switch (action) { 390 case BTimedEventQueue::B_DONE: 391 return B_OK; 392 case BTimedEventQueue::B_REMOVE_EVENT: 393 CleanupEvent(&entry->event); 394 remove = entry; 395 entry = entry->next; 396 RemoveEntry(remove); 397 break; 398 case BTimedEventQueue::B_NO_ACTION: 399 case BTimedEventQueue::B_RESORT_QUEUE: 400 default: 401 entry = entry->next; 402 break; 403 } 404 } else { 405 entry = entry->next; 406 } 407 } 408 return B_OK; 409 } 410 411 return B_ERROR; 412} 413 414 415status_t 416_event_queue_imp::FlushEvents(bigtime_t eventTime, 417 BTimedEventQueue::time_direction direction, 418 bool inclusive, 419 int32 eventType) 420{ 421 event_queue_entry *end; 422 event_queue_entry *entry; 423 event_queue_entry *remove; 424 425 BAutolock lock(fLock); 426 427 switch (direction) { 428 case BTimedEventQueue::B_ALWAYS: 429 for (entry = fFirstEntry; entry; ) { 430 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 431 CleanupEvent(&entry->event); 432 remove = entry; 433 entry = entry->next; 434 RemoveEntry(remove); 435 } else { 436 entry = entry->next; 437 } 438 } 439 return B_OK; 440 441 case BTimedEventQueue::B_BEFORE_TIME: 442 end = GetEnd_BeforeTime(eventTime, inclusive); 443 if (end == NULL) 444 return B_OK; 445 end = end->next; 446 for (entry = fFirstEntry; entry != end; ) { 447 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 448 CleanupEvent(&entry->event); 449 remove = entry; 450 entry = entry->next; 451 RemoveEntry(remove); 452 } else { 453 entry = entry->next; 454 } 455 } 456 return B_OK; 457 458 case BTimedEventQueue::B_AT_TIME: 459 { 460 bool found_time = false; 461 for (entry = fFirstEntry; entry; ) { 462 if (eventTime == entry->event.event_time) { 463 found_time = true; 464 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 465 CleanupEvent(&entry->event); 466 remove = entry; 467 entry = entry->next; 468 RemoveEntry(remove); 469 } else { 470 entry = entry->next; 471 } 472 } else if (found_time) { 473 break; 474 } else { 475 entry = entry->next; 476 } 477 } 478 return B_OK; 479 } 480 481 case BTimedEventQueue::B_AFTER_TIME: 482 for (entry = GetStart_AfterTime(eventTime, inclusive); entry; ) { 483 if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) { 484 CleanupEvent(&entry->event); 485 remove = entry; 486 entry = entry->next; 487 RemoveEntry(remove); 488 } else { 489 entry = entry->next; 490 } 491 } 492 return B_OK; 493 } 494 495 return B_ERROR; 496} 497 498 499void 500_event_queue_imp::SetCleanupHook(BTimedEventQueue::cleanup_hook hook, void *context) 501{ 502 BAutolock lock(fLock); 503 fCleanupHookContext = context; 504 fCleanupHook = hook; 505} 506 507 508void 509_event_queue_imp::RemoveEntry(event_queue_entry *entry) 510{ 511 //remove the entry from double-linked list 512 //and delete it 513 if (entry->prev != NULL) 514 (entry->prev)->next = entry->next; 515 if (entry->next != NULL) 516 (entry->next)->prev = entry->prev; 517 if (entry == fFirstEntry) { 518 fFirstEntry = entry->next; 519 if (fFirstEntry != NULL) 520 fFirstEntry->prev = NULL; 521 } 522 if (entry == fLastEntry) { 523 fLastEntry = entry->prev; 524 if (fLastEntry != NULL) 525 fLastEntry->next = NULL; 526 } 527 delete entry; 528 fEventCount--; 529 ASSERT(fEventCount >= 0); 530 ASSERT(fEventCount != 0 || ((fFirstEntry == NULL) && (fLastEntry == NULL))); 531} 532 533 534void 535_event_queue_imp::CleanupEvent(media_timed_event *event) 536{ 537 //perform the cleanup action required 538 //when deleting an event from the queue 539 540 //BeBook says: 541 // Each event has a cleanup flag associated with it that indicates 542 // what sort of special action needs to be performed when the event is 543 // removed from the queue. If this value is B_NO_CLEANUP, nothing is done. 544 // If it's B_RECYCLE, and the event is a B_HANDLE_BUFFER event, BTimedEventQueue 545 // will automatically recycle the buffer associated with the event. 546 // If the cleanup flag is B_DELETE or is B_USER_CLEANUP or greater, 547 // the cleanup hook function will be called. 548 //and: 549 // cleanup hook function specified by hook to be called for events 550 // as they're removed from the queue. The hook will be called only 551 // for events with cleanup_flag values of B_DELETE or B_USER_CLEANUP or greater. 552 //and: 553 // These values define how BTimedEventQueue should handle removing 554 // events from the queue. If the flag is B_USER_CLEANUP or greater, 555 // the cleanup hook function is called when the event is removed. 556 //Problems: 557 // B_DELETE is a keyboard code! (seems to have existed in early 558 // sample code as a cleanup flag) 559 // 560 // exiting cleanup flags are: 561 // B_NO_CLEANUP = 0, 562 // B_RECYCLE_BUFFER, // recycle buffers handled by BTimedEventQueue 563 // B_EXPIRE_TIMER, // call TimerExpired() on the event->data 564 // B_USER_CLEANUP = 0x4000 // others go to the cleanup func 565 // 566 567 if (event->cleanup == BTimedEventQueue::B_NO_CLEANUP) { 568 // do nothing 569 } else if (event->type == BTimedEventQueue::B_HANDLE_BUFFER && event->cleanup == BTimedEventQueue::B_RECYCLE_BUFFER) { 570 ((BBuffer *)event->pointer)->Recycle(); 571 DEBUG_ONLY(*const_cast<void **>(&event->pointer) = NULL); 572 } else if (event->cleanup == BTimedEventQueue::B_EXPIRE_TIMER) { 573 // call TimerExpired() on the event->data 574 debugger("BTimedEventQueue cleanup: calling TimerExpired() should be implemented here\n"); 575 } else if (event->cleanup == B_DELETE || event->cleanup >= BTimedEventQueue::B_USER_CLEANUP) { 576 if (fCleanupHook) 577 (*fCleanupHook)(event,fCleanupHookContext); 578 } else { 579 ERROR("BTimedEventQueue cleanup unhandled! type = %" B_PRId32 580 ", cleanup = %" B_PRId32 "\n", event->type, event->cleanup); 581 } 582} 583 584 585_event_queue_imp::event_queue_entry * 586_event_queue_imp::GetEnd_BeforeTime(bigtime_t eventTime, bool inclusive) 587{ 588 event_queue_entry *entry; 589 590 entry = fLastEntry; 591 while (entry) { 592 if ((entry->event.event_time > eventTime) || (!inclusive && entry->event.event_time == eventTime)) 593 entry = entry->prev; 594 else 595 break; 596 } 597 return entry; 598} 599 600 601_event_queue_imp::event_queue_entry * 602_event_queue_imp::GetStart_AfterTime(bigtime_t eventTime, bool inclusive) 603{ 604 event_queue_entry *entry; 605 606 entry = fFirstEntry; 607 while (entry) { 608 if ((entry->event.event_time < eventTime) || (!inclusive && entry->event.event_time == eventTime)) 609 entry = entry->next; 610 else 611 break; 612 } 613 return entry; 614} 615 616 617#if DEBUG > 1 618void 619_event_queue_imp::Dump() const 620{ 621 TRACE("fEventCount = 0x%x\n",(int)fEventCount); 622 TRACE("fFirstEntry = 0x%x\n",(int)fFirstEntry); 623 TRACE("fLastEntry = 0x%x\n",(int)fLastEntry); 624 for (event_queue_entry *entry = fFirstEntry; entry; entry = entry->next) { 625 TRACE("entry = 0x%x\n",(int)entry); 626 TRACE(" entry.prev = 0x%x\n",(int)entry->prev); 627 TRACE(" entry.next = 0x%x\n",(int)entry->next); 628 TRACE(" entry.event.event_time = 0x%x\n",(int)entry->event.event_time); 629 } 630} 631#endif 632