| // 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 must be positive on an active timer. | |
| 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 -> timerModifying -> timerDeleted | |
| // timerModifiedEarlier -> timerModifying -> timerDeleted | |
| // timerModifiedLater -> timerModifying -> timerDeleted | |
| // timerNoStatus -> do nothing | |
| // timerDeleted -> do nothing | |
| // timerRemoving -> do nothing | |
| // timerRemoved -> do nothing | |
| // timerRunning -> wait until status changes | |
| // timerMoving -> wait until status changes | |
| // timerModifying -> wait until status changes | |
| // modtimer: | |
| // timerWaiting -> timerModifying -> timerModifiedXX | |
| // timerModifiedXX -> timerModifying -> timerModifiedYY | |
| // timerNoStatus -> timerModifying -> timerWaiting | |
| // timerRemoved -> timerModifying -> timerWaiting | |
| // timerDeleted -> timerModifying -> timerModifiedXX | |
| // timerRunning -> wait until status changes | |
| // timerMoving -> wait until status changes | |
| // timerRemoving -> wait until status changes | |
| // timerModifying -> wait until status changes | |
| // 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 | |
| // verifyTimers can be set to true to add debugging checks that the | |
| // timer heaps are valid. | |
| const verifyTimers = false | |
| // 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 | |
| if t.nextwhen < 0 { // check for overflow. | |
| t.nextwhen = maxWhen | |
| } | |
| 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 | |
| // Reports whether the timer was modified before it was run. | |
| func resetTimer(t *timer, when int64) bool { | |
| if raceenabled { | |
| racerelease(unsafe.Pointer(t)) | |
| } | |
| return resettimer(t, when) | |
| } | |
| // modTimer modifies an existing timer. | |
| //go:linkname modTimer time.modTimer | |
| func modTimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) { | |
| modtimer(t, when, period, f, arg, seq) | |
| } | |
| // 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 be positive. A negative value will cause runtimer to | |
| // overflow during its delta calculation and never expire other runtime | |
| // timers. Zero will cause checkTimers to fail to notice the timer. | |
| if t.when <= 0 { | |
| throw("timer when must be positive") | |
| } | |
| if t.period < 0 { | |
| throw("timer period must be non-negative") | |
| } | |
| if t.status != timerNoStatus { | |
| throw("addtimer called with initialized timer") | |
| } | |
| t.status = timerWaiting | |
| when := t.when | |
| pp := getg().m.p.ptr() | |
| lock(&pp.timersLock) | |
| cleantimers(pp) | |
| doaddtimer(pp, t) | |
| unlock(&pp.timersLock) | |
| wakeNetPoller(when) | |
| } | |
| // doaddtimer adds t to the current P's heap. | |
| // The caller must have locked the timers for pp. | |
| func doaddtimer(pp *p, t *timer) { | |
| // 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) | |
| siftupTimer(pp.timers, i) | |
| if t == pp.timers[0] { | |
| atomic.Store64(&pp.timer0When, uint64(t.when)) | |
| } | |
| atomic.Xadd(&pp.numTimers, 1) | |
| } | |
| // 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: | |
| // Prevent preemption while the timer is in timerModifying. | |
| // This could lead to a self-deadlock. See #38070. | |
| mp := acquirem() | |
| if atomic.Cas(&t.status, s, timerModifying) { | |
| // Must fetch t.pp before changing status, | |
| // as cleantimers in another goroutine | |
| // can clear t.pp of a timerDeleted timer. | |
| tpp := t.pp.ptr() | |
| if !atomic.Cas(&t.status, timerModifying, timerDeleted) { | |
| badTimer() | |
| } | |
| releasem(mp) | |
| atomic.Xadd(&tpp.deletedTimers, 1) | |
| // Timer was not yet run. | |
| return true | |
| } else { | |
| releasem(mp) | |
| } | |
| case timerModifiedEarlier: | |
| // Prevent preemption while the timer is in timerModifying. | |
| // This could lead to a self-deadlock. See #38070. | |
| mp := acquirem() | |
| if atomic.Cas(&t.status, s, timerModifying) { | |
| // Must fetch t.pp before setting status | |
| // to timerDeleted. | |
| tpp := t.pp.ptr() | |
| atomic.Xadd(&tpp.adjustTimers, -1) | |
| if !atomic.Cas(&t.status, timerModifying, timerDeleted) { | |
| badTimer() | |
| } | |
| releasem(mp) | |
| atomic.Xadd(&tpp.deletedTimers, 1) | |
| // Timer was not yet run. | |
| return true | |
| } else { | |
| releasem(mp) | |
| } | |
| 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. | |
| // Wait for the other call to complete. | |
| osyield() | |
| 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) { | |
| 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] | |
| if i != last { | |
| // Moving to i may have moved the last timer to a new parent, | |
| // so sift up to preserve the heap guarantee. | |
| siftupTimer(pp.timers, i) | |
| siftdownTimer(pp.timers, i) | |
| } | |
| if i == 0 { | |
| updateTimer0When(pp) | |
| } | |
| atomic.Xadd(&pp.numTimers, -1) | |
| } | |
| // 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) { | |
| 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] | |
| if last > 0 { | |
| siftdownTimer(pp.timers, 0) | |
| } | |
| updateTimer0When(pp) | |
| atomic.Xadd(&pp.numTimers, -1) | |
| } | |
| // modtimer modifies an existing timer. | |
| // This is called by the netpoll code or time.Ticker.Reset or time.Timer.Reset. | |
| // Reports whether the timer was modified before it was run. | |
| func modtimer(t *timer, when, period int64, f func(interface{}, uintptr), arg interface{}, seq uintptr) bool { | |
| if when <= 0 { | |
| throw("timer when must be positive") | |
| } | |
| if period < 0 { | |
| throw("timer period must be non-negative") | |
| } | |
| status := uint32(timerNoStatus) | |
| wasRemoved := false | |
| var pending bool | |
| var mp *m | |
| loop: | |
| for { | |
| switch status = atomic.Load(&t.status); status { | |
| case timerWaiting, timerModifiedEarlier, timerModifiedLater: | |
| // Prevent preemption while the timer is in timerModifying. | |
| // This could lead to a self-deadlock. See #38070. | |
| mp = acquirem() | |
| if atomic.Cas(&t.status, status, timerModifying) { | |
| pending = true // timer not yet run | |
| break loop | |
| } | |
| releasem(mp) | |
| case timerNoStatus, timerRemoved: | |
| // Prevent preemption while the timer is in timerModifying. | |
| // This could lead to a self-deadlock. See #38070. | |
| mp = acquirem() | |
| // Timer was already run and t is no longer in a heap. | |
| // Act like addtimer. | |
| if atomic.Cas(&t.status, status, timerModifying) { | |
| wasRemoved = true | |
| pending = false // timer already run or stopped | |
| break loop | |
| } | |
| releasem(mp) | |
| case timerDeleted: | |
| // Prevent preemption while the timer is in timerModifying. | |
| // This could lead to a self-deadlock. See #38070. | |
| mp = acquirem() | |
| if atomic.Cas(&t.status, status, timerModifying) { | |
| atomic.Xadd(&t.pp.ptr().deletedTimers, -1) | |
| pending = false // timer already stopped | |
| break loop | |
| } | |
| releasem(mp) | |
| case timerRunning, timerRemoving, timerMoving: | |
| // The timer is being run or moved, by a different P. | |
| // Wait for it to complete. | |
| osyield() | |
| case timerModifying: | |
| // Multiple simultaneous calls to modtimer. | |
| // Wait for the other call to complete. | |
| osyield() | |
| default: | |
| badTimer() | |
| } | |
| } | |
| t.period = period | |
| t.f = f | |
| t.arg = arg | |
| t.seq = seq | |
| if wasRemoved { | |
| t.when = when | |
| pp := getg().m.p.ptr() | |
| lock(&pp.timersLock) | |
| doaddtimer(pp, t) | |
| unlock(&pp.timersLock) | |
| if !atomic.Cas(&t.status, timerModifying, timerWaiting) { | |
| badTimer() | |
| } | |
| releasem(mp) | |
| wakeNetPoller(when) | |
| } 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 | |
| } | |
| tpp := t.pp.ptr() | |
| // Update the adjustTimers field. Subtract one if we | |
| // are removing a timerModifiedEarlier, add one if we | |
| // are adding a timerModifiedEarlier. | |
| adjust := int32(0) | |
| if status == timerModifiedEarlier { | |
| adjust-- | |
| } | |
| if newStatus == timerModifiedEarlier { | |
| adjust++ | |
| updateTimerModifiedEarliest(tpp, when) | |
| } | |
| if adjust != 0 { | |
| atomic.Xadd(&tpp.adjustTimers, adjust) | |
| } | |
| // Set the new status of the timer. | |
| if !atomic.Cas(&t.status, timerModifying, newStatus) { | |
| badTimer() | |
| } | |
| releasem(mp) | |
| // If the new status is earlier, wake up the poller. | |
| if newStatus == timerModifiedEarlier { | |
| wakeNetPoller(when) | |
| } | |
| } | |
| return pending | |
| } | |
| // resettimer resets the time when a timer should fire. | |
| // If used for an inactive timer, the timer will become active. | |
| // This should be called instead of addtimer if the timer value has been, | |
| // or may have been, used previously. | |
| // Reports whether the timer was modified before it was run. | |
| func resettimer(t *timer, when int64) bool { | |
| return modtimer(t, when, t.period, t.f, t.arg, t.seq) | |
| } | |
| // 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) { | |
| gp := getg() | |
| for { | |
| if len(pp.timers) == 0 { | |
| return | |
| } | |
| // This loop can theoretically run for a while, and because | |
| // it is holding timersLock it cannot be preempted. | |
| // If someone is trying to preempt us, just return. | |
| // We can clean the timers later. | |
| if gp.preemptStop { | |
| return | |
| } | |
| 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 | |
| } | |
| dodeltimer0(pp) | |
| if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { | |
| badTimer() | |
| } | |
| atomic.Xadd(&pp.deletedTimers, -1) | |
| 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. | |
| dodeltimer0(pp) | |
| doaddtimer(pp, t) | |
| if s == timerModifiedEarlier { | |
| atomic.Xadd(&pp.adjustTimers, -1) | |
| } | |
| if !atomic.Cas(&t.status, timerMoving, timerWaiting) { | |
| badTimer() | |
| } | |
| default: | |
| // Head of timers does not need adjustment. | |
| return | |
| } | |
| } | |
| } | |
| // 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: | |
| if !atomic.Cas(&t.status, s, timerMoving) { | |
| continue | |
| } | |
| t.pp = 0 | |
| doaddtimer(pp, t) | |
| if !atomic.Cas(&t.status, timerMoving, timerWaiting) { | |
| badTimer() | |
| } | |
| break loop | |
| case timerModifiedEarlier, timerModifiedLater: | |
| if !atomic.Cas(&t.status, s, timerMoving) { | |
| continue | |
| } | |
| t.when = t.nextwhen | |
| t.pp = 0 | |
| doaddtimer(pp, t) | |
| 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, now int64) { | |
| if atomic.Load(&pp.adjustTimers) == 0 { | |
| if verifyTimers { | |
| verifyTimerHeap(pp) | |
| } | |
| // There are no timers to adjust, so it is safe to clear | |
| // timerModifiedEarliest. Do so in case it is stale. | |
| // Everything will work if we don't do this, | |
| // but clearing here may save future calls to adjusttimers. | |
| atomic.Store64(&pp.timerModifiedEarliest, 0) | |
| return | |
| } | |
| // If we haven't yet reached the time of the first timerModifiedEarlier | |
| // timer, don't do anything. This speeds up programs that adjust | |
| // a lot of timers back and forth if the timers rarely expire. | |
| // We'll postpone looking through all the adjusted timers until | |
| // one would actually expire. | |
| if first := atomic.Load64(&pp.timerModifiedEarliest); first != 0 { | |
| if int64(first) > now { | |
| if verifyTimers { | |
| verifyTimerHeap(pp) | |
| } | |
| return | |
| } | |
| // We are going to clear all timerModifiedEarlier timers. | |
| atomic.Store64(&pp.timerModifiedEarliest, 0) | |
| } | |
| var moved []*timer | |
| loop: | |
| 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) { | |
| dodeltimer(pp, i) | |
| if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { | |
| badTimer() | |
| } | |
| atomic.Xadd(&pp.deletedTimers, -1) | |
| // 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. | |
| dodeltimer(pp, i) | |
| moved = append(moved, t) | |
| if s == timerModifiedEarlier { | |
| if n := atomic.Xadd(&pp.adjustTimers, -1); int32(n) <= 0 { | |
| break loop | |
| } | |
| } | |
| // Look at this heap position again. | |
| i-- | |
| } | |
| 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) | |
| } | |
| if verifyTimers { | |
| verifyTimerHeap(pp) | |
| } | |
| } | |
| // addAdjustedTimers adds any timers we adjusted in adjusttimers | |
| // back to the timer heap. | |
| func addAdjustedTimers(pp *p, moved []*timer) { | |
| for _, t := range moved { | |
| doaddtimer(pp, t) | |
| 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. | |
| //go:nowritebarrierrec | |
| func nobarrierWakeTime(pp *p) int64 { | |
| next := int64(atomic.Load64(&pp.timer0When)) | |
| nextAdj := int64(atomic.Load64(&pp.timerModifiedEarliest)) | |
| if next == 0 || (nextAdj != 0 && nextAdj < next) { | |
| next = nextAdj | |
| } | |
| return next | |
| } | |
| // 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 | |
| } | |
| dodeltimer0(pp) | |
| if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { | |
| badTimer() | |
| } | |
| atomic.Xadd(&pp.deletedTimers, -1) | |
| if len(pp.timers) == 0 { | |
| return -1 | |
| } | |
| case timerModifiedEarlier, timerModifiedLater: | |
| if !atomic.Cas(&t.status, s, timerMoving) { | |
| continue | |
| } | |
| t.when = t.nextwhen | |
| dodeltimer0(pp) | |
| doaddtimer(pp, t) | |
| 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 t.when < 0 { // check for overflow. | |
| t.when = maxWhen | |
| } | |
| siftdownTimer(pp.timers, 0) | |
| if !atomic.Cas(&t.status, timerRunning, timerWaiting) { | |
| badTimer() | |
| } | |
| updateTimer0When(pp) | |
| } else { | |
| // Remove from heap. | |
| dodeltimer0(pp) | |
| 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 | |
| } | |
| } | |
| // clearDeletedTimers removes all deleted timers from the P's timer heap. | |
| // This is used to avoid clogging up the heap if the program | |
| // starts a lot of long-running timers and then stops them. | |
| // For example, this can happen via context.WithTimeout. | |
| // | |
| // This is the only function that walks through the entire timer heap, | |
| // other than moveTimers which only runs when the world is stopped. | |
| // | |
| // The caller must have locked the timers for pp. | |
| func clearDeletedTimers(pp *p) { | |
| // We are going to clear all timerModifiedEarlier timers. | |
| // Do this now in case new ones show up while we are looping. | |
| atomic.Store64(&pp.timerModifiedEarliest, 0) | |
| cdel := int32(0) | |
| cearlier := int32(0) | |
| to := 0 | |
| changedHeap := false | |
| timers := pp.timers | |
| nextTimer: | |
| for _, t := range timers { | |
| for { | |
| switch s := atomic.Load(&t.status); s { | |
| case timerWaiting: | |
| if changedHeap { | |
| timers[to] = t | |
| siftupTimer(timers, to) | |
| } | |
| to++ | |
| continue nextTimer | |
| case timerModifiedEarlier, timerModifiedLater: | |
| if atomic.Cas(&t.status, s, timerMoving) { | |
| t.when = t.nextwhen | |
| timers[to] = t | |
| siftupTimer(timers, to) | |
| to++ | |
| changedHeap = true | |
| if !atomic.Cas(&t.status, timerMoving, timerWaiting) { | |
| badTimer() | |
| } | |
| if s == timerModifiedEarlier { | |
| cearlier++ | |
| } | |
| continue nextTimer | |
| } | |
| case timerDeleted: | |
| if atomic.Cas(&t.status, s, timerRemoving) { | |
| t.pp = 0 | |
| cdel++ | |
| if !atomic.Cas(&t.status, timerRemoving, timerRemoved) { | |
| badTimer() | |
| } | |
| changedHeap = true | |
| continue nextTimer | |
| } | |
| case timerModifying: | |
| // Loop until modification complete. | |
| osyield() | |
| case timerNoStatus, timerRemoved: | |
| // We should not see these status values in a timer heap. | |
| badTimer() | |
| case timerRunning, timerRemoving, timerMoving: | |
| // Some other P thinks it owns this timer, | |
| // which should not happen. | |
| badTimer() | |
| default: | |
| badTimer() | |
| } | |
| } | |
| } | |
| // Set remaining slots in timers slice to nil, | |
| // so that the timer values can be garbage collected. | |
| for i := to; i < len(timers); i++ { | |
| timers[i] = nil | |
| } | |
| atomic.Xadd(&pp.deletedTimers, -cdel) | |
| atomic.Xadd(&pp.numTimers, -cdel) | |
| atomic.Xadd(&pp.adjustTimers, -cearlier) | |
| timers = timers[:to] | |
| pp.timers = timers | |
| updateTimer0When(pp) | |
| if verifyTimers { | |
| verifyTimerHeap(pp) | |
| } | |
| } | |
| // verifyTimerHeap verifies that the timer heap is in a valid state. | |
| // This is only for debugging, and is only called if verifyTimers is true. | |
| // The caller must have locked the timers. | |
| func verifyTimerHeap(pp *p) { | |
| for i, t := range pp.timers { | |
| if i == 0 { | |
| // First timer has no parent. | |
| continue | |
| } | |
| // The heap is 4-ary. See siftupTimer and siftdownTimer. | |
| p := (i - 1) / 4 | |
| if t.when < pp.timers[p].when { | |
| print("bad timer heap at ", i, ": ", p, ": ", pp.timers[p].when, ", ", i, ": ", t.when, "\n") | |
| throw("bad timer heap") | |
| } | |
| } | |
| if numTimers := int(atomic.Load(&pp.numTimers)); len(pp.timers) != numTimers { | |
| println("timer heap len", len(pp.timers), "!= numTimers", numTimers) | |
| throw("bad timer heap len") | |
| } | |
| } | |
| // updateTimer0When sets the P's timer0When field. | |
| // The caller must have locked the timers for pp. | |
| func updateTimer0When(pp *p) { | |
| if len(pp.timers) == 0 { | |
| atomic.Store64(&pp.timer0When, 0) | |
| } else { | |
| atomic.Store64(&pp.timer0When, uint64(pp.timers[0].when)) | |
| } | |
| } | |
| // updateTimerModifiedEarliest updates the recorded nextwhen field of the | |
| // earlier timerModifiedEarier value. | |
| // The timers for pp will not be locked. | |
| func updateTimerModifiedEarliest(pp *p, nextwhen int64) { | |
| for { | |
| old := atomic.Load64(&pp.timerModifiedEarliest) | |
| if old != 0 && int64(old) < nextwhen { | |
| return | |
| } | |
| if atomic.Cas64(&pp.timerModifiedEarliest, old, uint64(nextwhen)) { | |
| return | |
| } | |
| } | |
| } | |
| // timeSleepUntil returns the time when the next timer should fire, | |
| // and the P that holds the timer heap that that timer is on. | |
| // This is only called by sysmon and checkdead. | |
| func timeSleepUntil() (int64, *p) { | |
| next := int64(maxWhen) | |
| var pret *p | |
| // 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 | |
| } | |
| w := int64(atomic.Load64(&pp.timer0When)) | |
| if w != 0 && w < next { | |
| next = w | |
| pret = pp | |
| } | |
| w = int64(atomic.Load64(&pp.timerModifiedEarliest)) | |
| if w != 0 && w < next { | |
| next = w | |
| pret = pp | |
| } | |
| } | |
| unlock(&allpLock) | |
| return next, pret | |
| } | |
| // 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) { | |
| if i >= len(t) { | |
| badTimer() | |
| } | |
| when := t[i].when | |
| if when <= 0 { | |
| badTimer() | |
| } | |
| 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 | |
| } | |
| } | |
| func siftdownTimer(t []*timer, i int) { | |
| n := len(t) | |
| if i >= n { | |
| badTimer() | |
| } | |
| when := t[i].when | |
| if when <= 0 { | |
| badTimer() | |
| } | |
| 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 | |
| } | |
| } | |
| // 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() { | |
| throw("timer data corruption") | |
| } |