timing_event.cpp (18142B)
1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "timing_event.h" 5 #include "cpu_core.h" 6 #include "cpu_core_private.h" 7 #include "system.h" 8 9 #include "util/state_wrapper.h" 10 11 #include "common/assert.h" 12 #include "common/log.h" 13 #include "common/thirdparty/SmallVector.h" 14 15 Log_SetChannel(TimingEvents); 16 17 namespace TimingEvents { 18 19 static GlobalTicks GetTimestampForNewEvent(); 20 21 static void SortEvent(TimingEvent* event); 22 static void AddActiveEvent(TimingEvent* event); 23 static void RemoveActiveEvent(TimingEvent* event); 24 static void SortEvents(); 25 static TimingEvent* FindActiveEvent(const std::string_view name); 26 static void CommitGlobalTicks(const GlobalTicks new_global_ticks); 27 28 namespace { 29 struct TimingEventsState 30 { 31 TimingEvent* active_events_head = nullptr; 32 TimingEvent* active_events_tail = nullptr; 33 TimingEvent* current_event = nullptr; 34 u32 active_event_count = 0; 35 GlobalTicks current_event_next_run_time = 0; 36 GlobalTicks global_tick_counter = 0; 37 GlobalTicks event_run_tick_counter = 0; 38 }; 39 } // namespace 40 41 ALIGN_TO_CACHE_LINE static TimingEventsState s_state; 42 43 } // namespace TimingEvents 44 45 GlobalTicks TimingEvents::GetGlobalTickCounter() 46 { 47 return s_state.global_tick_counter; 48 } 49 50 GlobalTicks TimingEvents::GetTimestampForNewEvent() 51 { 52 // we want to schedule relative to the currently-being processed event, but if we haven't run events in a while, it 53 // needs to include the pending time. so explicitly add the two. 54 return s_state.global_tick_counter + CPU::GetPendingTicks(); 55 } 56 57 GlobalTicks TimingEvents::GetEventRunTickCounter() 58 { 59 return s_state.event_run_tick_counter; 60 } 61 62 void TimingEvents::Initialize() 63 { 64 Reset(); 65 } 66 67 void TimingEvents::Reset() 68 { 69 s_state.global_tick_counter = 0; 70 s_state.event_run_tick_counter = 0; 71 } 72 73 void TimingEvents::Shutdown() 74 { 75 Assert(s_state.active_event_count == 0); 76 } 77 78 void TimingEvents::UpdateCPUDowncount() 79 { 80 DebugAssert(s_state.active_events_head->m_next_run_time >= s_state.global_tick_counter); 81 const u32 event_downcount = 82 static_cast<u32>(s_state.active_events_head->m_next_run_time - s_state.global_tick_counter); 83 CPU::g_state.downcount = CPU::HasPendingInterrupt() ? 0 : event_downcount; 84 } 85 86 TimingEvent** TimingEvents::GetHeadEventPtr() 87 { 88 return &s_state.active_events_head; 89 } 90 91 void TimingEvents::SortEvent(TimingEvent* event) 92 { 93 const GlobalTicks event_runtime = event->m_next_run_time; 94 95 if (event->prev && event->prev->m_next_run_time > event_runtime) 96 { 97 // move backwards 98 TimingEvent* current = event->prev; 99 while (current && current->m_next_run_time > event_runtime) 100 current = current->prev; 101 102 // unlink 103 if (event->prev) 104 event->prev->next = event->next; 105 else 106 s_state.active_events_head = event->next; 107 if (event->next) 108 event->next->prev = event->prev; 109 else 110 s_state.active_events_tail = event->prev; 111 112 // insert after current 113 if (current) 114 { 115 event->next = current->next; 116 if (current->next) 117 current->next->prev = event; 118 else 119 s_state.active_events_tail = event; 120 121 event->prev = current; 122 current->next = event; 123 } 124 else 125 { 126 // insert at front 127 DebugAssert(s_state.active_events_head); 128 s_state.active_events_head->prev = event; 129 event->prev = nullptr; 130 event->next = s_state.active_events_head; 131 s_state.active_events_head = event; 132 UpdateCPUDowncount(); 133 } 134 } 135 else if (event->next && event_runtime > event->next->m_next_run_time) 136 { 137 // move forwards 138 TimingEvent* current = event->next; 139 while (current && event_runtime > current->m_next_run_time) 140 current = current->next; 141 142 // unlink 143 if (event->prev) 144 { 145 event->prev->next = event->next; 146 } 147 else 148 { 149 s_state.active_events_head = event->next; 150 if (!s_state.current_event) 151 UpdateCPUDowncount(); 152 } 153 if (event->next) 154 event->next->prev = event->prev; 155 else 156 s_state.active_events_tail = event->prev; 157 158 // insert before current 159 if (current) 160 { 161 event->next = current; 162 event->prev = current->prev; 163 164 if (current->prev) 165 { 166 current->prev->next = event; 167 } 168 else 169 { 170 s_state.active_events_head = event; 171 if (!s_state.current_event) 172 UpdateCPUDowncount(); 173 } 174 175 current->prev = event; 176 } 177 else 178 { 179 // insert at back 180 DebugAssert(s_state.active_events_tail); 181 s_state.active_events_tail->next = event; 182 event->next = nullptr; 183 event->prev = s_state.active_events_tail; 184 s_state.active_events_tail = event; 185 } 186 } 187 } 188 189 void TimingEvents::AddActiveEvent(TimingEvent* event) 190 { 191 DebugAssert(!event->prev && !event->next); 192 s_state.active_event_count++; 193 194 const GlobalTicks event_runtime = event->m_next_run_time; 195 TimingEvent* current = nullptr; 196 TimingEvent* next = s_state.active_events_head; 197 while (next && event_runtime > next->m_next_run_time) 198 { 199 current = next; 200 next = next->next; 201 } 202 203 if (!next) 204 { 205 // new tail 206 event->prev = s_state.active_events_tail; 207 if (s_state.active_events_tail) 208 { 209 s_state.active_events_tail->next = event; 210 s_state.active_events_tail = event; 211 } 212 else 213 { 214 // first event 215 s_state.active_events_tail = event; 216 s_state.active_events_head = event; 217 UpdateCPUDowncount(); 218 } 219 } 220 else if (!current) 221 { 222 // new head 223 event->next = s_state.active_events_head; 224 s_state.active_events_head->prev = event; 225 s_state.active_events_head = event; 226 UpdateCPUDowncount(); 227 } 228 else 229 { 230 // inbetween current < event > next 231 event->prev = current; 232 event->next = next; 233 current->next = event; 234 next->prev = event; 235 } 236 } 237 238 void TimingEvents::RemoveActiveEvent(TimingEvent* event) 239 { 240 DebugAssert(s_state.active_event_count > 0); 241 242 if (event->next) 243 { 244 event->next->prev = event->prev; 245 } 246 else 247 { 248 s_state.active_events_tail = event->prev; 249 } 250 251 if (event->prev) 252 { 253 event->prev->next = event->next; 254 } 255 else 256 { 257 s_state.active_events_head = event->next; 258 if (s_state.active_events_head && !s_state.current_event) 259 UpdateCPUDowncount(); 260 } 261 262 event->prev = nullptr; 263 event->next = nullptr; 264 265 s_state.active_event_count--; 266 } 267 268 void TimingEvents::SortEvents() 269 { 270 llvm::SmallVector<TimingEvent*, 16> events; 271 events.reserve(s_state.active_event_count); 272 273 TimingEvent* next = s_state.active_events_head; 274 while (next) 275 { 276 TimingEvent* current = next; 277 events.push_back(current); 278 next = current->next; 279 current->prev = nullptr; 280 current->next = nullptr; 281 } 282 283 s_state.active_events_head = nullptr; 284 s_state.active_events_tail = nullptr; 285 s_state.active_event_count = 0; 286 287 for (TimingEvent* event : events) 288 AddActiveEvent(event); 289 } 290 291 static TimingEvent* TimingEvents::FindActiveEvent(const std::string_view name) 292 { 293 for (TimingEvent* event = s_state.active_events_head; event; event = event->next) 294 { 295 if (event->GetName() == name) 296 return event; 297 } 298 299 return nullptr; 300 } 301 302 bool TimingEvents::IsRunningEvents() 303 { 304 return (s_state.current_event != nullptr); 305 } 306 307 void TimingEvents::CancelRunningEvent() 308 { 309 TimingEvent* const event = s_state.current_event; 310 if (!event) 311 return; 312 313 // Might need to sort it, since we're bailing out. 314 if (event->IsActive()) 315 { 316 event->m_next_run_time = s_state.current_event_next_run_time; 317 SortEvent(event); 318 } 319 320 s_state.current_event = nullptr; 321 } 322 323 ALWAYS_INLINE_RELEASE void TimingEvents::CommitGlobalTicks(const GlobalTicks new_global_ticks) 324 { 325 s_state.event_run_tick_counter = new_global_ticks; 326 327 do 328 { 329 TimingEvent* event = s_state.active_events_head; 330 s_state.global_tick_counter = std::min(new_global_ticks, event->m_next_run_time); 331 332 // Now we can actually run the callbacks. 333 while (s_state.global_tick_counter >= event->m_next_run_time) 334 { 335 s_state.current_event = event; 336 337 // Factor late time into the time for the next invocation. 338 const TickCount ticks_late = static_cast<TickCount>(s_state.global_tick_counter - event->m_next_run_time); 339 const TickCount ticks_to_execute = static_cast<TickCount>(s_state.global_tick_counter - event->m_last_run_time); 340 341 // Why don't we modify event->m_downcount directly? Because otherwise the event list won't be sorted. 342 // Adding the interval may cause this event to have a greater downcount than the next, and a new event 343 // may be inserted at the front, despite having a higher downcount than the next. 344 s_state.current_event_next_run_time = event->m_next_run_time + static_cast<u32>(event->m_interval); 345 event->m_last_run_time = s_state.global_tick_counter; 346 347 // The cycles_late is only an indicator, it doesn't modify the cycles to execute. 348 event->m_callback(event->m_callback_param, ticks_to_execute, ticks_late); 349 if (event->m_active) 350 { 351 event->m_next_run_time = s_state.current_event_next_run_time; 352 SortEvent(event); 353 } 354 355 event = s_state.active_events_head; 356 } 357 } while (new_global_ticks > s_state.global_tick_counter); 358 s_state.current_event = nullptr; 359 } 360 361 void TimingEvents::RunEvents() 362 { 363 DebugAssert(!s_state.current_event); 364 DebugAssert(CPU::GetPendingTicks() >= CPU::g_state.downcount); 365 366 do 367 { 368 const GlobalTicks new_global_ticks = 369 s_state.event_run_tick_counter + static_cast<GlobalTicks>(CPU::GetPendingTicks()); 370 if (new_global_ticks >= s_state.active_events_head->m_next_run_time) 371 { 372 CPU::ResetPendingTicks(); 373 CommitGlobalTicks(new_global_ticks); 374 } 375 376 if (CPU::HasPendingInterrupt()) 377 CPU::DispatchInterrupt(); 378 379 UpdateCPUDowncount(); 380 } while (CPU::GetPendingTicks() >= CPU::g_state.downcount); 381 } 382 383 void TimingEvents::CommitLeftoverTicks() 384 { 385 #ifdef _DEBUG 386 if (s_state.event_run_tick_counter > s_state.global_tick_counter) 387 DEV_LOG("Late-running {} ticks before execution", s_state.event_run_tick_counter - s_state.global_tick_counter); 388 #endif 389 390 CommitGlobalTicks(s_state.event_run_tick_counter); 391 392 if (CPU::HasPendingInterrupt()) 393 CPU::DispatchInterrupt(); 394 395 UpdateCPUDowncount(); 396 } 397 398 bool TimingEvents::DoState(StateWrapper& sw) 399 { 400 if (sw.GetVersion() < 71) [[unlikely]] 401 { 402 u32 old_global_tick_counter = 0; 403 sw.Do(&old_global_tick_counter); 404 s_state.global_tick_counter = static_cast<GlobalTicks>(old_global_tick_counter); 405 s_state.event_run_tick_counter = s_state.global_tick_counter; 406 407 // Load timestamps for the clock events. 408 // Any oneshot events should be recreated by the load state method, so we can fix up their times here. 409 u32 event_count = 0; 410 sw.Do(&event_count); 411 412 for (u32 i = 0; i < event_count; i++) 413 { 414 TinyString event_name; 415 TickCount downcount, time_since_last_run, period, interval; 416 sw.Do(&event_name); 417 sw.Do(&downcount); 418 sw.Do(&time_since_last_run); 419 sw.Do(&period); 420 sw.Do(&interval); 421 if (sw.HasError()) 422 return false; 423 424 TimingEvent* event = FindActiveEvent(event_name); 425 if (!event) 426 { 427 WARNING_LOG("Save state has event '{}', but couldn't find this event when loading.", event_name); 428 continue; 429 } 430 431 event->m_next_run_time = s_state.global_tick_counter + static_cast<u32>(downcount); 432 event->m_last_run_time = s_state.global_tick_counter - static_cast<u32>(time_since_last_run); 433 event->m_period = period; 434 event->m_interval = interval; 435 } 436 437 if (sw.GetVersion() < 43) [[unlikely]] 438 { 439 u32 last_event_run_time = 0; 440 sw.Do(&last_event_run_time); 441 } 442 443 DEBUG_LOG("Loaded {} events from save state.", event_count); 444 s_state.current_event = nullptr; 445 446 // Add pending ticks to the CPU, this'll happen if we saved state when we weren't paused. 447 const TickCount pending_ticks = 448 static_cast<TickCount>(s_state.event_run_tick_counter - s_state.global_tick_counter); 449 DebugAssert(pending_ticks >= 0); 450 CPU::AddPendingTicks(pending_ticks); 451 SortEvents(); 452 UpdateCPUDowncount(); 453 } 454 else 455 { 456 sw.Do(&s_state.global_tick_counter); 457 sw.Do(&s_state.event_run_tick_counter); 458 459 if (sw.IsReading()) 460 { 461 // Load timestamps for the clock events. 462 // Any oneshot events should be recreated by the load state method, so we can fix up their times here. 463 u32 event_count = 0; 464 sw.Do(&event_count); 465 466 for (u32 i = 0; i < event_count; i++) 467 { 468 TinyString event_name; 469 GlobalTicks next_run_time, last_run_time; 470 TickCount period, interval; 471 sw.Do(&event_name); 472 sw.Do(&next_run_time); 473 sw.Do(&last_run_time); 474 sw.Do(&period); 475 sw.Do(&interval); 476 if (sw.HasError()) 477 return false; 478 479 TimingEvent* event = FindActiveEvent(event_name); 480 if (!event) 481 { 482 WARNING_LOG("Save state has event '{}', but couldn't find this event when loading.", event_name); 483 continue; 484 } 485 486 event->m_next_run_time = next_run_time; 487 event->m_last_run_time = last_run_time; 488 event->m_period = period; 489 event->m_interval = interval; 490 } 491 492 DEBUG_LOG("Loaded {} events from save state.", event_count); 493 494 // Even if we're actually running an event, we don't want to set it to a new counter. 495 s_state.current_event = nullptr; 496 497 SortEvents(); 498 UpdateCPUDowncount(); 499 } 500 else 501 { 502 sw.Do(&s_state.active_event_count); 503 504 for (TimingEvent* event = s_state.active_events_head; event; event = event->next) 505 { 506 sw.Do(&event->m_name); 507 GlobalTicks next_run_time = 508 (s_state.current_event == event) ? s_state.current_event_next_run_time : event->m_next_run_time; 509 sw.Do(&next_run_time); 510 sw.Do(&event->m_last_run_time); 511 sw.Do(&event->m_period); 512 sw.Do(&event->m_interval); 513 } 514 515 DEBUG_LOG("Wrote {} events to save state.", s_state.active_event_count); 516 } 517 } 518 519 return !sw.HasError(); 520 } 521 522 TimingEvent::TimingEvent(const std::string_view name, TickCount period, TickCount interval, 523 TimingEventCallback callback, void* callback_param) 524 : m_callback(callback), m_callback_param(callback_param), m_period(period), m_interval(interval), m_name(name) 525 { 526 const GlobalTicks ts = TimingEvents::GetTimestampForNewEvent(); 527 m_last_run_time = ts; 528 m_next_run_time = ts + static_cast<u32>(interval); 529 } 530 531 TimingEvent::~TimingEvent() 532 { 533 DebugAssert(!m_active); 534 } 535 536 TickCount TimingEvent::GetTicksSinceLastExecution() const 537 { 538 // Can be negative if event A->B invoked B early while in the event loop. 539 const GlobalTicks ts = TimingEvents::GetTimestampForNewEvent(); 540 return (ts >= m_last_run_time) ? static_cast<TickCount>(ts - m_last_run_time) : 0; 541 } 542 543 TickCount TimingEvent::GetTicksUntilNextExecution() const 544 { 545 const GlobalTicks ts = TimingEvents::GetTimestampForNewEvent(); 546 return (ts >= m_next_run_time) ? 0 : static_cast<TickCount>(m_next_run_time - ts); 547 } 548 549 void TimingEvent::Delay(TickCount ticks) 550 { 551 using namespace TimingEvents; 552 553 if (!m_active) 554 { 555 Panic("Trying to delay an inactive event"); 556 return; 557 } 558 559 DebugAssert(TimingEvents::s_state.current_event != this); 560 561 m_next_run_time += static_cast<u32>(ticks); 562 SortEvent(this); 563 if (s_state.active_events_head == this) 564 UpdateCPUDowncount(); 565 } 566 567 void TimingEvent::Schedule(TickCount ticks) 568 { 569 using namespace TimingEvents; 570 571 const GlobalTicks ts = GetTimestampForNewEvent(); 572 const GlobalTicks next_run_time = ts + static_cast<u32>(ticks); 573 574 // See note in RunEvents(). 575 s_state.current_event_next_run_time = 576 (s_state.current_event == this) ? next_run_time : s_state.current_event_next_run_time; 577 578 if (!m_active) 579 { 580 // Event is going active, so we want it to only execute ticks from the current timestamp. 581 m_next_run_time = next_run_time; 582 m_last_run_time = ts; 583 m_active = true; 584 AddActiveEvent(this); 585 } 586 else 587 { 588 // Event is already active, so we leave the time since last run alone, and just modify the downcount. 589 // If this is a call from an IO handler for example, re-sort the event queue. 590 if (s_state.current_event != this) 591 { 592 m_next_run_time = next_run_time; 593 SortEvent(this); 594 if (s_state.active_events_head == this) 595 UpdateCPUDowncount(); 596 } 597 } 598 } 599 600 void TimingEvent::SetIntervalAndSchedule(TickCount ticks) 601 { 602 DebugAssert(ticks > 0); 603 SetInterval(ticks); 604 Schedule(ticks); 605 } 606 607 void TimingEvent::SetPeriodAndSchedule(TickCount ticks) 608 { 609 SetPeriod(ticks); 610 SetInterval(ticks); 611 Schedule(ticks); 612 } 613 614 void TimingEvent::InvokeEarly(bool force /* = false */) 615 { 616 using namespace TimingEvents; 617 618 if (!m_active) 619 return; 620 621 // Might happen due to other InvokeEarly()'s mid event loop. 622 const GlobalTicks ts = GetTimestampForNewEvent(); 623 if (ts <= m_last_run_time) 624 return; 625 626 // Shouldn't be invoking early when we're the current event running. 627 // TODO: Make DebugAssert instead. 628 Assert(s_state.current_event != this); 629 630 const TickCount ticks_to_execute = static_cast<TickCount>(ts - m_last_run_time); 631 if (!force && ticks_to_execute < m_period) 632 return; 633 634 m_next_run_time = ts + static_cast<u32>(m_interval); 635 m_last_run_time = ts; 636 637 // Since we've changed the downcount, we need to re-sort the events. 638 SortEvent(this); 639 if (s_state.active_events_head == this) 640 UpdateCPUDowncount(); 641 642 m_callback(m_callback_param, ticks_to_execute, 0); 643 } 644 645 void TimingEvent::Activate() 646 { 647 using namespace TimingEvents; 648 649 if (m_active) 650 return; 651 652 const GlobalTicks ts = GetTimestampForNewEvent(); 653 const GlobalTicks next_run_time = ts + static_cast<u32>(m_interval); 654 m_next_run_time = next_run_time; 655 m_last_run_time = ts; 656 657 s_state.current_event_next_run_time = 658 (s_state.current_event == this) ? next_run_time : s_state.current_event_next_run_time; 659 660 m_active = true; 661 AddActiveEvent(this); 662 } 663 664 void TimingEvent::Deactivate() 665 { 666 using namespace TimingEvents; 667 668 if (!m_active) 669 return; 670 671 m_active = false; 672 RemoveActiveEvent(this); 673 }