| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Time-related runtime and pieces of package time. |
| |
| package runtime |
| |
| import ( |
| "runtime/internal/atomic" |
| "runtime/internal/sys" |
| "unsafe" |
| ) |
| |
| // Package time knows the layout of this structure. |
| // If this struct changes, adjust ../time/sleep.go:/runtimeTimer. |
| type timer struct { |
| // If this timer is on a heap, which P's heap it is on. |
| // puintptr rather than *p to match uintptr in the versions |
| // of this struct defined in other packages. |
| pp puintptr |
| |
| // Timer wakes up at when, and then at when+period, ... (period > 0 only) |
| // each time calling f(arg, now) in the timer goroutine, so f must be |
| // a well-behaved function and not block. |
| when int64 |
| period int64 |
| f func(interface{}, uintptr) |
| arg interface{} |
| seq uintptr |
| |
| // What to set the when field to in timerModifiedXX status. |
| nextwhen int64 |
| |
| // The status field holds one of the values below. |
| status uint32 |
| } |
| |
| // Code outside this file has to be careful in using a timer value. |
| // |
| // The pp, status, and nextwhen fields may only be used by code in this file. |
| // |
| // Code that creates a new timer value can set the when, period, f, |
| // arg, and seq fields. |
| // A new timer value may be passed to addtimer (called by time.startTimer). |
| // After doing that no fields may be touched. |
| // |
| // An active timer (one that has been passed to addtimer) may be |
| // passed to deltimer (time.stopTimer), after which it is no longer an |
| // active timer. It is an inactive timer. |
| // In an inactive timer the period, f, arg, and seq fields may be modified, |
| // but not the when field. |
| // It's OK to just drop an inactive timer and let the GC collect it. |
| // It's not OK to pass an inactive timer to addtimer. |
| // Only newly allocated timer values may be passed to addtimer. |
| // |
| // An active timer may be passed to modtimer. No fields may be touched. |
| // It remains an active timer. |
| // |
| // An inactive timer may be passed to resettimer to turn into an |
| // active timer with an updated when field. |
| // It's OK to pass a newly allocated timer value to resettimer. |
| // |
| // Timer operations are addtimer, deltimer, modtimer, resettimer, |
| // cleantimers, adjusttimers, and runtimer. |
| // |
| // We don't permit calling addtimer/deltimer/modtimer/resettimer simultaneously, |
| // but adjusttimers and runtimer can be called at the same time as any of those. |
| // |
| // Active timers live in heaps attached to P, in the timers field. |
| // Inactive timers live there too temporarily, until they are removed. |
| // |
| // addtimer: |
| // timerNoStatus -> timerWaiting |
| // anything else -> panic: invalid value |
| // deltimer: |
| // timerWaiting -> timerDeleted |
| // timerModifiedXX -> timerDeleted |
| // timerNoStatus -> do nothing |
| // timerDeleted -> do nothing |
| // timerRemoving -> do nothing |
| // timerRemoved -> do nothing |
| // timerRunning -> wait until status changes |
| // timerMoving -> wait until status changes |
| // timerModifying -> panic: concurrent deltimer/modtimer calls |
| // modtimer: |
| // timerWaiting -> timerModifying -> timerModifiedXX |
| // timerModifiedXX -> timerModifying -> timerModifiedYY |
| // timerNoStatus -> timerWaiting |
| // timerRemoved -> timerWaiting |
| // timerRunning -> wait until status changes |
| // timerMoving -> wait until status changes |
| // timerRemoving -> wait until status changes |
| // timerDeleted -> panic: concurrent modtimer/deltimer calls |
| // timerModifying -> panic: concurrent modtimer calls |
| // resettimer: |
| // timerNoStatus -> timerWaiting |
| // timerRemoved -> timerWaiting |
| // timerDeleted -> timerModifying -> timerModifiedXX |
| // timerRemoving -> wait until status changes |
| // timerRunning -> wait until status changes |
| // timerWaiting -> panic: resettimer called on active timer |
| // timerMoving -> panic: resettimer called on active timer |
| // timerModifiedXX -> panic: resettimer called on active timer |
| // timerModifying -> panic: resettimer called on active timer |
| // cleantimers (looks in P's timer heap): |
| // timerDeleted -> timerRemoving -> timerRemoved |
| // timerModifiedXX -> timerMoving -> timerWaiting |
| // adjusttimers (looks in P's timer heap): |
| // timerDeleted -> timerRemoving -> timerRemoved |
| // timerModifiedXX -> timerMoving -> timerWaiting |
| // runtimer (looks in P's timer heap): |
| // timerNoStatus -> panic: uninitialized timer |
| // timerWaiting -> timerWaiting or |
| // timerWaiting -> timerRunning -> timerNoStatus or |
| // timerWaiting -> timerRunning -> timerWaiting |
| // timerModifying -> wait until status changes |
| // timerModifiedXX -> timerMoving -> timerWaiting |
| // timerDeleted -> timerRemoving -> timerRemoved |
| // timerRunning -> panic: concurrent runtimer calls |
| // timerRemoved -> panic: inconsistent timer heap |
| // timerRemoving -> panic: inconsistent timer heap |
| // timerMoving -> panic: inconsistent timer heap |
| |
| // Values for the timer status field. |
| const ( |
| // Timer has no status set yet. |
| timerNoStatus = iota |
| |
| // Waiting for timer to fire. |
| // The timer is in some P's heap. |
| timerWaiting |
| |
| // Running the timer function. |
| // A timer will only have this status briefly. |
| timerRunning |
| |
| // The timer is deleted and should be removed. |
| // It should not be run, but it is still in some P's heap. |
| timerDeleted |
| |
| // The timer is being removed. |
| // The timer will only have this status briefly. |
| timerRemoving |
| |
| // The timer has been stopped. |
| // It is not in any P's heap. |
| timerRemoved |
| |
| // The timer is being modified. |
| // The timer will only have this status briefly. |
| timerModifying |
| |
| // The timer has been modified to an earlier time. |
| // The new when value is in the nextwhen field. |
| // The timer is in some P's heap, possibly in the wrong place. |
| timerModifiedEarlier |
| |
| // The timer has been modified to the same or a later time. |
| // The new when value is in the nextwhen field. |
| // The timer is in some P's heap, possibly in the wrong place. |
| timerModifiedLater |
| |
| // The timer has been modified and is being moved. |
| // The timer will only have this status briefly. |
| timerMoving |
| ) |
| |
| // maxWhen is the maximum value for timer's when field. |
| const maxWhen = 1<<63 - 1 |
| |
| // Package time APIs. |
| // Godoc uses the comments in package time, not these. |
| |
| // time.now is implemented in assembly. |
| |
| // timeSleep puts the current goroutine to sleep for at least ns nanoseconds. |
| //go:linkname timeSleep time.Sleep |
| func timeSleep(ns int64) { |
| if ns <= 0 { |
| return |
| } |
| |
| gp := getg() |
| t := gp.timer |
| if t == nil { |
| t = new(timer) |
| gp.timer = t |
| } |
| t.f = goroutineReady |
| t.arg = gp |
| t.nextwhen = nanotime() + ns |
| gopark(resetForSleep, unsafe.Pointer(t), waitReasonSleep, traceEvGoSleep, 1) |
| } |
| |
| // resetForSleep is called after the goroutine is parked for timeSleep. |
| // We can't call resettimer in timeSleep itself because if this is a short |
| // sleep and there are many goroutines then the P can wind up running the |
| // timer function, goroutineReady, before the goroutine has been parked. |
| func resetForSleep(gp *g, ut unsafe.Pointer) bool { |
| t := (*timer)(ut) |
| resettimer(t, t.nextwhen) |
| return true |
| } |
| |
| // startTimer adds t to the timer heap. |
| //go:linkname startTimer time.startTimer |
| func startTimer(t *timer) { |
| if raceenabled { |
| racerelease(unsafe.Pointer(t)) |
| } |
| addtimer(t) |
| } |
| |
| // stopTimer stops a timer. |
| // It reports whether t was stopped before being run. |
| //go:linkname stopTimer time.stopTimer |
| func stopTimer(t *timer) bool { |
| return deltimer(t) |
| } |
| |
| // resetTimer resets an inactive timer, adding it to the heap. |
| //go:linkname resetTimer time.resetTimer |
| func resetTimer(t *timer, when int64) { |
| if raceenabled { |
| racerelease(unsafe.Pointer(t)) |
| } |
| resettimer(t, when) |
| } |
| |
| // Go runtime. |
| |
| // Ready the goroutine arg. |
| func goroutineReady(arg interface{}, seq uintptr) { |
| goready(arg.(*g), 0) |
| } |
| |
| // addtimer adds a timer to the current P. |
| // This should only be called with a newly created timer. |
| // That avoids the risk of changing the when field of a timer in some P's heap, |
| // which could cause the heap to become unsorted. |
| func addtimer(t *timer) { |
| // when must never be negative; otherwise runtimer will overflow |
| // during its delta calculation and never expire other runtime timers. |
| if t.when < 0 { |
| t.when = maxWhen |
| } |
| if t.status != timerNoStatus { |
| badTimer() |
| } |
| t.status = timerWaiting |
| |
| addInitializedTimer(t) |
| } |
| |
| // addInitializedTimer adds an initialized timer to the current P. |
| func addInitializedTimer(t *timer) { |
| when := t.when |
| |
| pp := getg().m.p.ptr() |
| lock(&pp.timersLock) |
| ok := cleantimers(pp) && doaddtimer(pp, t) |
| unlock(&pp.timersLock) |
| if !ok { |
| badTimer() |
| } |
| |
| wakeNetPoller(when) |
| } |
| |
| // doaddtimer adds t to the current P's heap. |
| // It reports whether it saw no problems due to races. |
| // The caller must have locked the timers for pp. |
| func doaddtimer(pp *p, t *timer) bool { |
| // Timers rely on the network poller, so make sure the poller |
| // has started. |
| if netpollInited == 0 { |
| netpollGenericInit() |
| } |
| |
| if t.pp != 0 { |
| throw("doaddtimer: P already set in timer") |
| } |
| t.pp.set(pp) |
| i := len(pp.timers) |
| pp.timers = append(pp.timers, t) |
| return siftupTimer(pp.timers, i) |
| } |
| |
| // deltimer deletes the timer t. It may be on some other P, so we can't |
| // actually remove it from the timers heap. We can only mark it as deleted. |
| // It will be removed in due course by the P whose heap it is on. |
| // Reports whether the timer was removed before it was run. |
| func deltimer(t *timer) bool { |
| for { |
| switch s := atomic.Load(&t.status); s { |
| case timerWaiting, timerModifiedLater: |
| if atomic.Cas(&t.status, s, timerDeleted) { |
| // Timer was not yet run. |
| return true |
| } |
| case timerModifiedEarlier: |
| tpp := t.pp.ptr() |
| if atomic.Cas(&t.status, s, timerModifying) { |
| atomic.Xadd(&tpp.adjustTimers, -1) |
| if !atomic.Cas(&t.status, timerModifying, timerDeleted) { |
| badTimer() |
| } |
| // Timer was not yet run. |
| return true |
| } |
| case timerDeleted, timerRemoving, timerRemoved: |
| // Timer was already run. |
| return false |
| case timerRunning, timerMoving: |
| // The timer is being run or moved, by a different P. |
| // Wait for it to complete. |
| osyield() |
| case timerNoStatus: |
| // Removing timer that was never added or |
| // has already been run. Also see issue 21874. |
| return false |
| case timerModifying: |
| // Simultaneous calls to deltimer and modtimer. |
| badTimer() |
| default: |
| badTimer() |
| } |
| } |
| } |
| |
| // dodeltimer removes timer i from the current P's heap. |
| // We are locked on the P when this is called. |
| // It reports whether it saw no problems due to races. |
| // The caller must have locked the timers for pp. |
| func dodeltimer(pp *p, i int) bool { |
| if t := pp.timers[i]; t.pp.ptr() != pp { |
| throw("dodeltimer: wrong P") |
| } else { |
| t.pp = 0 |
| } |
| last := len(pp.timers) - 1 |
| if i != last { |
| pp.timers[i] = pp.timers[last] |
| } |
| pp.timers[last] = nil |
| pp.timers = pp.timers[:last] |
| ok := true |
| if i != last { |
| // Moving to i may have moved the last timer to a new parent, |
| // so sift up to preserve the heap guarantee. |
| if !siftupTimer(pp.timers, i) { |
| ok = false |
| } |
| if !siftdownTimer(pp.timers, i) { |
| ok = false |
| } |
| } |
| return ok |
| } |
| |
| // dodeltimer0 removes timer 0 from the current P's heap. |
| // We are locked on the P when this is called. |
| // It reports whether it saw no problems due to races. |
| // The caller must have locked the timers for pp. |
| func dodeltimer0(pp *p) bool { |
| if t := pp.timers[0]; t.pp.ptr() != pp { |
| throw("dodeltimer0: wrong P") |
| } else { |
| t.pp = 0 |
| } |
| last := len(pp.timers) - 1 |
| if last > 0 { |
| pp.timers[0] = pp.timers[last] |
| } |
| pp.timers[last] = nil |
| pp.timers = pp.timers[:last] |
| ok := true |
| if last > 0 { |
| ok = siftdownTimer(pp.timers, 0) |
| } |
| return ok |
| } |
| |
| // modtimer modifies an existing timer. |
| // This is called by the netpoll code. |
| func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) { |
| if when < 0 { |
| when = maxWhen |
| } |
| |
| status := uint32(timerNoStatus) |
| wasRemoved := false |
| loop: |
| for { |
| switch status = atomic.Load(&t.status); status { |
| case timerWaiting, timerModifiedEarlier, timerModifiedLater: |
| if atomic.Cas(&t.status, status, timerModifying) { |
| break loop |
| } |
| case timerNoStatus, timerRemoved: |
| // Timer was already run and t is no longer in a heap. |
| // Act like addtimer. |
| if atomic.Cas(&t.status, status, timerWaiting) { |
| wasRemoved = true |
| break loop |
| } |
| case timerRunning, timerRemoving, timerMoving: |
| // The timer is being run or moved, by a different P. |
| // Wait for it to complete. |
| osyield() |
| case timerDeleted: |
| // Simultaneous calls to modtimer and deltimer. |
| badTimer() |
| case timerModifying: |
| // Multiple simultaneous calls to modtimer. |
| badTimer() |
| default: |
| badTimer() |
| } |
| } |
| |
| t.period = period |
| t.f = f |
| t.arg = arg |
| t.seq = seq |
| |
| if wasRemoved { |
| t.when = when |
| addInitializedTimer(t) |
| } else { |
| // The timer is in some other P's heap, so we can't change |
| // the when field. If we did, the other P's heap would |
| // be out of order. So we put the new when value in the |
| // nextwhen field, and let the other P set the when field |
| // when it is prepared to resort the heap. |
| t.nextwhen = when |
| |
| newStatus := uint32(timerModifiedLater) |
| if when < t.when { |
| newStatus = timerModifiedEarlier |
| } |
| |
| // Update the adjustTimers field. Subtract one if we |
| // are removing a timerModifiedEarlier, add one if we |
| // are adding a timerModifiedEarlier. |
| tpp := t.pp.ptr() |
| adjust := int32(0) |
| if status == timerModifiedEarlier { |
| adjust-- |
| } |
| if newStatus == timerModifiedEarlier { |
| adjust++ |
| } |
| if adjust != 0 { |
| atomic.Xadd(&tpp.adjustTimers, adjust) |
| } |
| |
| // Set the new status of the timer. |
| if !atomic.Cas(&t.status, timerModifying, newStatus) { |
| badTimer() |
| } |
| |
| // If the new status is earlier, wake up the poller. |
| if newStatus == timerModifiedEarlier { |
| wakeNetPoller(when) |
| } |
| } |
| } |
| |
| // resettimer resets an existing inactive timer to turn it into an active timer, |
| // with a new time for when the timer should fire. |
| // This should be called instead of addtimer if the timer value has been, |
| // or may have been, used previously. |
| func resettimer(t *timer, when int64) { |
| if when < 0 { |
| when = maxWhen |
| } |
| |
| for { |
| switch s := atomic.Load(&t.status); s { |
| case timerNoStatus, timerRemoved: |
| if atomic.Cas(&t.status, s, timerWaiting) { |
| t.when = when |
| addInitializedTimer(t) |
| return |
| } |
| case timerDeleted: |
| if atomic.Cas(&t.status, s, timerModifying) { |
| t.nextwhen = when |
| newStatus := uint32(timerModifiedLater) |
| if when < t.when { |
| newStatus = timerModifiedEarlier |
| atomic.Xadd(&t.pp.ptr().adjustTimers, 1) |
| } |
| if !atomic.Cas(&t.status, timerModifying, newStatus) { |
| badTimer() |
| } |
| if newStatus == timerModifiedEarlier { |
| wakeNetPoller(when) |
| } |
| return |
| } |
| case timerRemoving: |
| // Wait for the removal to complete. |
| osyield() |
| case timerRunning: |
| // Even though the timer should not be active, |
| // we can see timerRunning if the timer function |
| // permits some other goroutine to call resettimer. |
| // Wait until the run is complete. |
| osyield() |
| case timerWaiting, timerModifying, timerModifiedEarlier, timerModifiedLater, timerMoving: |
| // Called resettimer on active timer. |
| badTimer() |
| default: |
| badTimer() |
| } |
| } |
| } |
| |
| // cleantimers cleans up the head of the timer queue. This speeds up |
| // programs that create and delete timers; leaving them in the heap |
| // slows down addtimer. Reports whether no timer problems were found. |
| // The caller must have locked the timers for pp. |
| func cleantimers(pp *p) bool { |
| for { |
| if len(pp.timers) == 0 { |
| return true |
| } |
| t := pp.timers[0] |
| if t.pp.ptr() != pp { |
| throw("cleantimers: bad p") |
| } |
| switch s := atomic.Load(&t.status); s { |
| case timerDeleted: |
| if !atomic.Cas(&t.status, s, timerRemoving) { |
| continue |
| } |
| if !dodeltimer0(pp) { |
| return false |
| } |
| if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { |
| return false |
| } |
| case timerModifiedEarlier, timerModifiedLater: |
| if !atomic.Cas(&t.status, s, timerMoving) { |
| continue |
| } |
| // Now we can change the when field. |
| t.when = t.nextwhen |
| // Move t to the right position. |
| if !dodeltimer0(pp) { |
| return false |
| } |
| if !doaddtimer(pp, t) { |
| return false |
| } |
| if s == timerModifiedEarlier { |
| atomic.Xadd(&pp.adjustTimers, -1) |
| } |
| if !atomic.Cas(&t.status, timerMoving, timerWaiting) { |
| return false |
| } |
| default: |
| // Head of timers does not need adjustment. |
| return true |
| } |
| } |
| } |
| |
| // moveTimers moves a slice of timers to pp. The slice has been taken |
| // from a different P. |
| // This is currently called when the world is stopped, but the caller |
| // is expected to have locked the timers for pp. |
| func moveTimers(pp *p, timers []*timer) { |
| for _, t := range timers { |
| loop: |
| for { |
| switch s := atomic.Load(&t.status); s { |
| case timerWaiting: |
| t.pp = 0 |
| if !doaddtimer(pp, t) { |
| badTimer() |
| } |
| break loop |
| case timerModifiedEarlier, timerModifiedLater: |
| if !atomic.Cas(&t.status, s, timerMoving) { |
| continue |
| } |
| t.when = t.nextwhen |
| t.pp = 0 |
| if !doaddtimer(pp, t) { |
| badTimer() |
| } |
| if !atomic.Cas(&t.status, timerMoving, timerWaiting) { |
| badTimer() |
| } |
| break loop |
| case timerDeleted: |
| if !atomic.Cas(&t.status, s, timerRemoved) { |
| continue |
| } |
| t.pp = 0 |
| // We no longer need this timer in the heap. |
| break loop |
| case timerModifying: |
| // Loop until the modification is complete. |
| osyield() |
| case timerNoStatus, timerRemoved: |
| // We should not see these status values in a timers heap. |
| badTimer() |
| case timerRunning, timerRemoving, timerMoving: |
| // Some other P thinks it owns this timer, |
| // which should not happen. |
| badTimer() |
| default: |
| badTimer() |
| } |
| } |
| } |
| } |
| |
| // adjusttimers looks through the timers in the current P's heap for |
| // any timers that have been modified to run earlier, and puts them in |
| // the correct place in the heap. While looking for those timers, |
| // it also moves timers that have been modified to run later, |
| // and removes deleted timers. The caller must have locked the timers for pp. |
| func adjusttimers(pp *p) { |
| if len(pp.timers) == 0 { |
| return |
| } |
| if atomic.Load(&pp.adjustTimers) == 0 { |
| return |
| } |
| var moved []*timer |
| for i := 0; i < len(pp.timers); i++ { |
| t := pp.timers[i] |
| if t.pp.ptr() != pp { |
| throw("adjusttimers: bad p") |
| } |
| switch s := atomic.Load(&t.status); s { |
| case timerDeleted: |
| if atomic.Cas(&t.status, s, timerRemoving) { |
| if !dodeltimer(pp, i) { |
| badTimer() |
| } |
| if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { |
| badTimer() |
| } |
| // Look at this heap position again. |
| i-- |
| } |
| case timerModifiedEarlier, timerModifiedLater: |
| if atomic.Cas(&t.status, s, timerMoving) { |
| // Now we can change the when field. |
| t.when = t.nextwhen |
| // Take t off the heap, and hold onto it. |
| // We don't add it back yet because the |
| // heap manipulation could cause our |
| // loop to skip some other timer. |
| if !dodeltimer(pp, i) { |
| badTimer() |
| } |
| moved = append(moved, t) |
| if s == timerModifiedEarlier { |
| if n := atomic.Xadd(&pp.adjustTimers, -1); int32(n) <= 0 { |
| addAdjustedTimers(pp, moved) |
| return |
| } |
| } |
| } |
| case timerNoStatus, timerRunning, timerRemoving, timerRemoved, timerMoving: |
| badTimer() |
| case timerWaiting: |
| // OK, nothing to do. |
| case timerModifying: |
| // Check again after modification is complete. |
| osyield() |
| i-- |
| default: |
| badTimer() |
| } |
| } |
| |
| if len(moved) > 0 { |
| addAdjustedTimers(pp, moved) |
| } |
| } |
| |
| // addAdjustedTimers adds any timers we adjusted in adjusttimers |
| // back to the timer heap. |
| func addAdjustedTimers(pp *p, moved []*timer) { |
| for _, t := range moved { |
| if !doaddtimer(pp, t) { |
| badTimer() |
| } |
| if !atomic.Cas(&t.status, timerMoving, timerWaiting) { |
| badTimer() |
| } |
| } |
| } |
| |
| // nobarrierWakeTime looks at P's timers and returns the time when we |
| // should wake up the netpoller. It returns 0 if there are no timers. |
| // This function is invoked when dropping a P, and must run without |
| // any write barriers. Therefore, if there are any timers that needs |
| // to be moved earlier, it conservatively returns the current time. |
| // The netpoller M will wake up and adjust timers before sleeping again. |
| //go:nowritebarrierrec |
| func nobarrierWakeTime(pp *p) int64 { |
| lock(&pp.timersLock) |
| ret := int64(0) |
| if len(pp.timers) > 0 { |
| if atomic.Load(&pp.adjustTimers) > 0 { |
| ret = nanotime() |
| } else { |
| ret = pp.timers[0].when |
| } |
| } |
| unlock(&pp.timersLock) |
| return ret |
| } |
| |
| // runtimer examines the first timer in timers. If it is ready based on now, |
| // it runs the timer and removes or updates it. |
| // Returns 0 if it ran a timer, -1 if there are no more timers, or the time |
| // when the first timer should run. |
| // The caller must have locked the timers for pp. |
| // If a timer is run, this will temporarily unlock the timers. |
| //go:systemstack |
| func runtimer(pp *p, now int64) int64 { |
| for { |
| t := pp.timers[0] |
| if t.pp.ptr() != pp { |
| throw("runtimer: bad p") |
| } |
| switch s := atomic.Load(&t.status); s { |
| case timerWaiting: |
| if t.when > now { |
| // Not ready to run. |
| return t.when |
| } |
| |
| if !atomic.Cas(&t.status, s, timerRunning) { |
| continue |
| } |
| // Note that runOneTimer may temporarily unlock |
| // pp.timersLock. |
| runOneTimer(pp, t, now) |
| return 0 |
| |
| case timerDeleted: |
| if !atomic.Cas(&t.status, s, timerRemoving) { |
| continue |
| } |
| if !dodeltimer0(pp) { |
| badTimer() |
| } |
| if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { |
| badTimer() |
| } |
| if len(pp.timers) == 0 { |
| return -1 |
| } |
| |
| case timerModifiedEarlier, timerModifiedLater: |
| if !atomic.Cas(&t.status, s, timerMoving) { |
| continue |
| } |
| t.when = t.nextwhen |
| if !dodeltimer0(pp) { |
| badTimer() |
| } |
| if !doaddtimer(pp, t) { |
| badTimer() |
| } |
| if s == timerModifiedEarlier { |
| atomic.Xadd(&pp.adjustTimers, -1) |
| } |
| if !atomic.Cas(&t.status, timerMoving, timerWaiting) { |
| badTimer() |
| } |
| |
| case timerModifying: |
| // Wait for modification to complete. |
| osyield() |
| |
| case timerNoStatus, timerRemoved: |
| // Should not see a new or inactive timer on the heap. |
| badTimer() |
| case timerRunning, timerRemoving, timerMoving: |
| // These should only be set when timers are locked, |
| // and we didn't do it. |
| badTimer() |
| default: |
| badTimer() |
| } |
| } |
| } |
| |
| // runOneTimer runs a single timer. |
| // The caller must have locked the timers for pp. |
| // This will temporarily unlock the timers while running the timer function. |
| //go:systemstack |
| func runOneTimer(pp *p, t *timer, now int64) { |
| if raceenabled { |
| ppcur := getg().m.p.ptr() |
| if ppcur.timerRaceCtx == 0 { |
| ppcur.timerRaceCtx = racegostart(funcPC(runtimer) + sys.PCQuantum) |
| } |
| raceacquirectx(ppcur.timerRaceCtx, unsafe.Pointer(t)) |
| } |
| |
| f := t.f |
| arg := t.arg |
| seq := t.seq |
| |
| if t.period > 0 { |
| // Leave in heap but adjust next time to fire. |
| delta := t.when - now |
| t.when += t.period * (1 + -delta/t.period) |
| if !siftdownTimer(pp.timers, 0) { |
| badTimer() |
| } |
| if !atomic.Cas(&t.status, timerRunning, timerWaiting) { |
| badTimer() |
| } |
| } else { |
| // Remove from heap. |
| if !dodeltimer0(pp) { |
| badTimer() |
| } |
| if !atomic.Cas(&t.status, timerRunning, timerNoStatus) { |
| badTimer() |
| } |
| } |
| |
| if raceenabled { |
| // Temporarily use the current P's racectx for g0. |
| gp := getg() |
| if gp.racectx != 0 { |
| throw("runOneTimer: unexpected racectx") |
| } |
| gp.racectx = gp.m.p.ptr().timerRaceCtx |
| } |
| |
| unlock(&pp.timersLock) |
| |
| f(arg, seq) |
| |
| lock(&pp.timersLock) |
| |
| if raceenabled { |
| gp := getg() |
| gp.racectx = 0 |
| } |
| } |
| |
| func timejump() *p { |
| if faketime == 0 { |
| return nil |
| } |
| |
| // Nothing is running, so we can look at all the P's. |
| // Determine a timer bucket with minimum when. |
| var ( |
| minT *timer |
| minWhen int64 |
| minP *p |
| ) |
| for _, pp := range allp { |
| if pp.status != _Pidle && pp.status != _Pdead { |
| throw("non-idle P in timejump") |
| } |
| if len(pp.timers) == 0 { |
| continue |
| } |
| c := pp.adjustTimers |
| for _, t := range pp.timers { |
| switch s := atomic.Load(&t.status); s { |
| case timerWaiting: |
| if minT == nil || t.when < minWhen { |
| minT = t |
| minWhen = t.when |
| minP = pp |
| } |
| case timerModifiedEarlier, timerModifiedLater: |
| if minT == nil || t.nextwhen < minWhen { |
| minT = t |
| minWhen = t.nextwhen |
| minP = pp |
| } |
| if s == timerModifiedEarlier { |
| c-- |
| } |
| case timerRunning, timerModifying, timerMoving: |
| badTimer() |
| } |
| // The timers are sorted, so we only have to check |
| // the first timer for each P, unless there are |
| // some timerModifiedEarlier timers. The number |
| // of timerModifiedEarlier timers is in the adjustTimers |
| // field, used to initialize c, above. |
| if c == 0 { |
| break |
| } |
| } |
| } |
| |
| if minT == nil || minWhen <= faketime { |
| return nil |
| } |
| |
| faketime = minWhen |
| return minP |
| } |
| |
| // timeSleepUntil returns the time when the next timer should fire. |
| // This is only called by sysmon. |
| func timeSleepUntil() int64 { |
| next := int64(maxWhen) |
| |
| // Prevent allp slice changes. This is like retake. |
| lock(&allpLock) |
| for _, pp := range allp { |
| if pp == nil { |
| // This can happen if procresize has grown |
| // allp but not yet created new Ps. |
| continue |
| } |
| |
| lock(&pp.timersLock) |
| c := atomic.Load(&pp.adjustTimers) |
| for _, t := range pp.timers { |
| switch s := atomic.Load(&t.status); s { |
| case timerWaiting: |
| if t.when < next { |
| next = t.when |
| } |
| case timerModifiedEarlier, timerModifiedLater: |
| if t.nextwhen < next { |
| next = t.nextwhen |
| } |
| if s == timerModifiedEarlier { |
| c-- |
| } |
| } |
| // The timers are sorted, so we only have to check |
| // the first timer for each P, unless there are |
| // some timerModifiedEarlier timers. The number |
| // of timerModifiedEarlier timers is in the adjustTimers |
| // field, used to initialize c, above. |
| // |
| // We don't worry about cases like timerModifying. |
| // New timers can show up at any time, |
| // so this function is necessarily imprecise. |
| // Do a signed check here since we aren't |
| // synchronizing the read of pp.adjustTimers |
| // with the check of a timer status. |
| if int32(c) <= 0 { |
| break |
| } |
| } |
| unlock(&pp.timersLock) |
| } |
| unlock(&allpLock) |
| |
| return next |
| } |
| |
| // Heap maintenance algorithms. |
| // These algorithms check for slice index errors manually. |
| // Slice index error can happen if the program is using racy |
| // access to timers. We don't want to panic here, because |
| // it will cause the program to crash with a mysterious |
| // "panic holding locks" message. Instead, we panic while not |
| // holding a lock. |
| |
| func siftupTimer(t []*timer, i int) bool { |
| if i >= len(t) { |
| return false |
| } |
| when := t[i].when |
| tmp := t[i] |
| for i > 0 { |
| p := (i - 1) / 4 // parent |
| if when >= t[p].when { |
| break |
| } |
| t[i] = t[p] |
| i = p |
| } |
| if tmp != t[i] { |
| t[i] = tmp |
| } |
| return true |
| } |
| |
| func siftdownTimer(t []*timer, i int) bool { |
| n := len(t) |
| if i >= n { |
| return false |
| } |
| when := t[i].when |
| tmp := t[i] |
| for { |
| c := i*4 + 1 // left child |
| c3 := c + 2 // mid child |
| if c >= n { |
| break |
| } |
| w := t[c].when |
| if c+1 < n && t[c+1].when < w { |
| w = t[c+1].when |
| c++ |
| } |
| if c3 < n { |
| w3 := t[c3].when |
| if c3+1 < n && t[c3+1].when < w3 { |
| w3 = t[c3+1].when |
| c3++ |
| } |
| if w3 < w { |
| w = w3 |
| c = c3 |
| } |
| } |
| if w >= when { |
| break |
| } |
| t[i] = t[c] |
| i = c |
| } |
| if tmp != t[i] { |
| t[i] = tmp |
| } |
| return true |
| } |
| |
| // badTimer is called if the timer data structures have been corrupted, |
| // presumably due to racy use by the program. We panic here rather than |
| // panicing due to invalid slice access while holding locks. |
| // See issue #25686. |
| func badTimer() { |
| panic(errorString("racy use of timers")) |
| } |