@@ -364,25 +364,13 @@ bool MessagePumpKqueue::DoInternalWork(Delegate::NextWorkInfo* next_work_info) {
364
364
365
365
bool poll = next_work_info == nullptr ;
366
366
int flags = poll ? KEVENT_FLAG_IMMEDIATE : 0 ;
367
- bool indefinite =
368
- next_work_info != nullptr && next_work_info->delayed_run_time .is_max ();
369
-
370
- int rv = 0 ;
371
- do {
372
- timespec timeout{};
373
- if (!indefinite && !poll) {
374
- if (rv != 0 ) {
375
- // The wait was interrupted and made |next_work_info|'s view of
376
- // TimeTicks::Now() stale. Refresh it before doing another wait.
377
- next_work_info->recent_now = TimeTicks::Now ();
378
- }
379
- timeout = next_work_info->remaining_delay ().ToTimeSpec ();
380
- }
381
- // This does not use HANDLE_EINTR, since retrying the syscall requires
382
- // adjusting the timeout to account for time already waited.
383
- rv = kevent64 (kqueue_.get (), nullptr , 0 , events_.data (), events_.size (),
384
- flags, indefinite ? nullptr : &timeout);
385
- } while (rv < 0 && errno == EINTR);
367
+ if (!poll && scheduled_wakeup_time_ != next_work_info->delayed_run_time ) {
368
+ UpdateWakeupTimer (next_work_info->delayed_run_time );
369
+ DCHECK_EQ (scheduled_wakeup_time_, next_work_info->delayed_run_time );
370
+ }
371
+
372
+ int rv = HANDLE_EINTR (kevent64 (kqueue_.get (), nullptr , 0 , events_.data (),
373
+ events_.size (), flags, nullptr ));
386
374
387
375
PCHECK (rv >= 0 ) << " kevent64" ;
388
376
return ProcessEvents (rv);
@@ -445,6 +433,12 @@ bool MessagePumpKqueue::ProcessEvents(int count) {
445
433
if (controller) {
446
434
controller->watcher ()->OnMachMessageReceived (port);
447
435
}
436
+ } else if (event->filter == EVFILT_TIMER) {
437
+ // The wakeup timer fired.
438
+ DCHECK_LE (scheduled_wakeup_time_, base::TimeTicks::Now ());
439
+ DCHECK_NE (scheduled_wakeup_time_, base::TimeTicks::Max ());
440
+ scheduled_wakeup_time_ = base::TimeTicks::Max ();
441
+ --event_count_;
448
442
} else {
449
443
NOTREACHED () << " Unexpected event for filter " << event->filter ;
450
444
}
@@ -453,4 +447,47 @@ bool MessagePumpKqueue::ProcessEvents(int count) {
453
447
return did_work;
454
448
}
455
449
450
+ void MessagePumpKqueue::UpdateWakeupTimer (const base::TimeTicks& wakeup_time) {
451
+ DCHECK_NE (wakeup_time, scheduled_wakeup_time_);
452
+
453
+ // The ident of the wakeup timer. There's only the one timer as the pair
454
+ // (ident, filter) is the identity of the event.
455
+ constexpr uint64_t kWakeupTimerIdent = 0x0 ;
456
+ if (wakeup_time == base::TimeTicks::Max ()) {
457
+ // Clear the timer.
458
+ kevent64_s timer{};
459
+ timer.ident = kWakeupTimerIdent ;
460
+ timer.filter = EVFILT_TIMER;
461
+ timer.flags = EV_DELETE;
462
+
463
+ int rv = ChangeOneEvent (kqueue_, &timer);
464
+ PCHECK (rv == 0 ) << " kevent64, delete timer" ;
465
+ --event_count_;
466
+ } else {
467
+ // Set/reset the timer.
468
+ kevent64_s timer{};
469
+ timer.ident = kWakeupTimerIdent ;
470
+ timer.filter = EVFILT_TIMER;
471
+ // This updates the timer if it already exists in |kqueue_|.
472
+ timer.flags = EV_ADD | EV_ONESHOT;
473
+ // Specify the sleep in microseconds to avoid undersleeping due to
474
+ // numeric problems. The sleep is computed from TimeTicks::Now rather than
475
+ // NextWorkInfo::recent_now because recent_now is strictly earlier than
476
+ // current wall-clock. Using an earlier wall clock time to compute the
477
+ // delta to the next wakeup wall-clock time would guarantee oversleep.
478
+ // If wakeup_time is in the past, the delta below will be negative and the
479
+ // timer is set immediately.
480
+ timer.fflags = NOTE_USECONDS;
481
+ timer.data = (wakeup_time - base::TimeTicks::Now ()).InMicroseconds ();
482
+ int rv = ChangeOneEvent (kqueue_, &timer);
483
+ PCHECK (rv == 0 ) << " kevent64, set timer" ;
484
+
485
+ // Bump the event count if we just added the timer.
486
+ if (scheduled_wakeup_time_ == base::TimeTicks::Max ())
487
+ ++event_count_;
488
+ }
489
+
490
+ scheduled_wakeup_time_ = wakeup_time;
491
+ }
492
+
456
493
} // namespace base
0 commit comments