// 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 time #include "runtime.h" #include "defs.h" #include "arch.h" #include "malloc.h" static Timers timers; static void addtimer(Timer*); static bool deltimer(Timer*); // Package time APIs. // Godoc uses the comments in package time, not these. // time.now is implemented in assembly. // Sleep puts the current goroutine to sleep for at least ns nanoseconds. func Sleep(ns int64) { G *g; g = runtime_g(); g->status = Gwaiting; g->waitreason = "sleep"; runtime_tsleep(ns); } // startTimer adds t to the timer heap. func startTimer(t *Timer) { addtimer(t); } // stopTimer removes t from the timer heap if it is there. // It returns true if t was removed, false if t wasn't even there. func stopTimer(t *Timer) (stopped bool) { stopped = deltimer(t); } // C runtime. static void timerproc(void*); static void siftup(int32); static void siftdown(int32); // Ready the goroutine e.data. static void ready(int64 now, Eface e) { USED(now); runtime_ready(e.__object); } // Put the current goroutine to sleep for ns nanoseconds. // The caller must have set g->status and g->waitreason. void runtime_tsleep(int64 ns) { Timer t; if(ns <= 0) return; t.when = runtime_nanotime() + ns; t.period = 0; t.f = ready; t.arg.__object = runtime_g(); addtimer(&t); runtime_gosched(); } // Add a timer to the heap and start or kick the timer proc // if the new timer is earlier than any of the others. static void addtimer(Timer *t) { int32 n; Timer **nt; runtime_lock(&timers); if(timers.len >= timers.cap) { // Grow slice. n = 16; if(n <= timers.cap) n = timers.cap*3 / 2; nt = runtime_malloc(n*sizeof nt[0]); runtime_memmove(nt, timers.t, timers.len*sizeof nt[0]); runtime_free(timers.t); timers.t = nt; timers.cap = n; } t->i = timers.len++; timers.t[t->i] = t; siftup(t->i); if(t->i == 0) { // siftup moved to top: new earliest deadline. if(timers.sleeping) { timers.sleeping = false; runtime_notewakeup(&timers.waitnote); } if(timers.rescheduling) { timers.rescheduling = false; runtime_ready(timers.timerproc); } } if(timers.timerproc == nil) timers.timerproc = __go_go(timerproc, nil); runtime_unlock(&timers); } // Delete timer t from the heap. // Do not need to update the timerproc: // if it wakes up early, no big deal. static bool deltimer(Timer *t) { int32 i; runtime_lock(&timers); // t may not be registered anymore and may have // a bogus i (typically 0, if generated by Go). // Verify it before proceeding. i = t->i; if(i < 0 || i >= timers.len || timers.t[i] != t) { runtime_unlock(&timers); return false; } timers.len--; if(i == timers.len) { timers.t[i] = nil; } else { timers.t[i] = timers.t[timers.len]; timers.t[timers.len] = nil; timers.t[i]->i = i; siftup(i); siftdown(i); } runtime_unlock(&timers); return true; } // Timerproc runs the time-driven events. // It sleeps until the next event in the timers heap. // If addtimer inserts a new earlier event, addtimer // wakes timerproc early. static void timerproc(void* dummy __attribute__ ((unused))) { G *g; int64 delta, now; Timer *t; void (*f)(int64, Eface); Eface arg; g = runtime_g(); for(;;) { runtime_lock(&timers); now = runtime_nanotime(); for(;;) { if(timers.len == 0) { delta = -1; break; } t = timers.t[0]; delta = t->when - now; if(delta > 0) break; if(t->period > 0) { // leave in heap but adjust next time to fire t->when += t->period * (1 + -delta/t->period); siftdown(0); } else { // remove from heap timers.t[0] = timers.t[--timers.len]; timers.t[0]->i = 0; siftdown(0); t->i = -1; // mark as removed } f = t->f; arg = t->arg; runtime_unlock(&timers); f(now, arg); runtime_lock(&timers); } if(delta < 0) { // No timers left - put goroutine to sleep. timers.rescheduling = true; g->status = Gwaiting; g->waitreason = "timer goroutine (idle)"; runtime_unlock(&timers); runtime_gosched(); continue; } // At least one timer pending. Sleep until then. timers.sleeping = true; runtime_noteclear(&timers.waitnote); runtime_unlock(&timers); runtime_entersyscall(); runtime_notetsleep(&timers.waitnote, delta); runtime_exitsyscall(); } } // heap maintenance algorithms. static void siftup(int32 i) { int32 p; Timer **t, *tmp; t = timers.t; while(i > 0) { p = (i-1)/2; // parent if(t[i]->when >= t[p]->when) break; tmp = t[i]; t[i] = t[p]; t[p] = tmp; t[i]->i = i; t[p]->i = p; i = p; } } static void siftdown(int32 i) { int32 c, len; Timer **t, *tmp; t = timers.t; len = timers.len; for(;;) { c = i*2 + 1; // left child if(c >= len) { break; } if(c+1 < len && t[c+1]->when < t[c]->when) c++; if(t[c]->when >= t[i]->when) break; tmp = t[i]; t[i] = t[c]; t[c] = tmp; t[i]->i = i; t[c]->i = c; i = c; } } void runtime_time_scan(void (*scan)(byte*, int64)) { scan((byte*)&timers, sizeof timers); }