summaryrefslogtreecommitdiff
path: root/libgo/go/time/sleep.go
blob: 3538775adfb28c9dc0864182308a13296d1a9b28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// 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.

package time

import (
	"os"
	"syscall"
	"sync"
	"container/heap"
)

// The event type represents a single After or AfterFunc event.
type event struct {
	t        int64       // The absolute time that the event should fire.
	f        func(int64) // The function to call when the event fires.
	sleeping bool        // A sleeper is sleeping for this event.
}

type eventHeap []*event

var events eventHeap
var eventMutex sync.Mutex

func init() {
	events.Push(&event{1 << 62, nil, true}) // sentinel
}

// Sleep pauses the current goroutine for at least ns nanoseconds.
// Higher resolution sleeping may be provided by syscall.Nanosleep 
// on some operating systems.
func Sleep(ns int64) os.Error {
	_, err := sleep(Nanoseconds(), ns)
	return err
}

// sleep takes the current time and a duration,
// pauses for at least ns nanoseconds, and
// returns the current time and an error.
func sleep(t, ns int64) (int64, os.Error) {
	// TODO(cw): use monotonic-time once it's available
	end := t + ns
	for t < end {
		errno := syscall.Sleep(end - t)
		if errno != 0 && errno != syscall.EINTR {
			return 0, os.NewSyscallError("sleep", errno)
		}
		t = Nanoseconds()
	}
	return t, nil
}

// After waits at least ns nanoseconds before sending the current time
// on the returned channel.
func After(ns int64) <-chan int64 {
	c := make(chan int64, 1)
	after(ns, func(t int64) { c <- t })
	return c
}

// AfterFunc waits at least ns nanoseconds before calling f
// in its own goroutine.
func AfterFunc(ns int64, f func()) {
	after(ns, func(_ int64) {
		go f()
	})
}

// after is the implementation of After and AfterFunc.
// When the current time is after ns, it calls f with the current time.
// It assumes that f will not block.
func after(ns int64, f func(int64)) {
	t := Nanoseconds() + ns
	eventMutex.Lock()
	t0 := events[0].t
	heap.Push(events, &event{t, f, false})
	if t < t0 {
		go sleeper()
	}
	eventMutex.Unlock()
}

// sleeper continually looks at the earliest event in the queue, marks it
// as sleeping, waits until it happens, then removes any events
// in the queue that are due. It stops when it finds an event that is
// already marked as sleeping. When an event is inserted before the first item,
// a new sleeper is started.
//
// Scheduling vagaries mean that sleepers may not wake up in
// exactly the order of the events that they are waiting for,
// but this does not matter as long as there are at least as
// many sleepers as events marked sleeping (invariant). This ensures that
// there is always a sleeper to service the remaining events.
//
// A sleeper will remove at least the event it has been waiting for
// unless the event has already been removed by another sleeper.  Both
// cases preserve the invariant described above.
func sleeper() {
	eventMutex.Lock()
	e := events[0]
	for !e.sleeping {
		t := Nanoseconds()
		if dt := e.t - t; dt > 0 {
			e.sleeping = true
			eventMutex.Unlock()
			if nt, err := sleep(t, dt); err != nil {
				// If sleep has encountered an error,
				// there's not much we can do. We pretend
				// that time really has advanced by the required
				// amount and lie to the rest of the system.
				t = e.t
			} else {
				t = nt
			}
			eventMutex.Lock()
			e = events[0]
		}
		for t >= e.t {
			e.f(t)
			heap.Pop(events)
			e = events[0]
		}
	}
	eventMutex.Unlock()
}

func (eventHeap) Len() int {
	return len(events)
}

func (eventHeap) Less(i, j int) bool {
	return events[i].t < events[j].t
}

func (eventHeap) Swap(i, j int) {
	events[i], events[j] = events[j], events[i]
}

func (eventHeap) Push(x interface{}) {
	events = append(events, x.(*event))
}

func (eventHeap) Pop() interface{} {
	// TODO: possibly shrink array.
	n := len(events) - 1
	e := events[n]
	events[n] = nil
	events = events[0:n]
	return e
}