-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplatform_linux_cgo.go
More file actions
143 lines (124 loc) · 2.97 KB
/
platform_linux_cgo.go
File metadata and controls
143 lines (124 loc) · 2.97 KB
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
//go:build linux && cgo
package hpt
/*
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdlib.h>
#include <stdatomic.h>
static long long hpt_clock_now(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (long long)ts.tv_sec * 1000000000LL + ts.tv_nsec;
}
static void hpt_sleep_until_impl(long long deadline_ns) {
struct timespec ts;
ts.tv_sec = deadline_ns / 1000000000LL;
ts.tv_nsec = deadline_ns % 1000000000LL;
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, NULL) == EINTR) {}
}
// --- pthread ticker ---
typedef struct {
int pipe_w;
long long start_ns;
long long period_ns;
atomic_int stop;
pthread_t thread;
} hpt_ticker_t;
static void* hpt_ticker_thread(void* arg) {
hpt_ticker_t* t = (hpt_ticker_t*)arg;
long long tick = 0;
while (!atomic_load(&t->stop)) {
tick++;
hpt_sleep_until_impl(t->start_ns + tick * t->period_ns);
if (atomic_load(&t->stop)) break;
char b = 1;
write(t->pipe_w, &b, 1);
}
close(t->pipe_w);
return NULL;
}
static hpt_ticker_t* hpt_ticker_start(long long period_ns, int pipe_w) {
hpt_ticker_t* t = (hpt_ticker_t*)malloc(sizeof(hpt_ticker_t));
t->pipe_w = pipe_w;
t->start_ns = hpt_clock_now();
t->period_ns = period_ns;
atomic_store(&t->stop, 0);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&t->thread, &attr, hpt_ticker_thread, t);
pthread_attr_destroy(&attr);
return t;
}
static void hpt_ticker_stop(hpt_ticker_t* t) {
atomic_store(&t->stop, 1);
pthread_join(t->thread, NULL);
free(t);
}
static int hpt_pipe(int fds[2]) {
return pipe(fds);
}
*/
import "C"
import (
"os"
"time"
"unsafe"
)
func monotonicNow() int64 {
return int64(C.hpt_clock_now())
}
func sleepUntil(deadline int64) {
C.hpt_sleep_until_impl(C.longlong(deadline))
}
func startTickerLoop(period time.Duration, c chan time.Time) (stop func()) {
var fds [2]C.int
if C.hpt_pipe((*C.int)(unsafe.Pointer(&fds[0]))) != 0 {
// Pipe creation failed — fall back to goroutine loop.
return startTickerLoopFallback(period, c)
}
state := C.hpt_ticker_start(C.longlong(period.Nanoseconds()), fds[1])
pipeR := os.NewFile(uintptr(fds[0]), "hpt-ticker")
go func() {
defer pipeR.Close()
buf := make([]byte, 1)
for {
if _, err := pipeR.Read(buf); err != nil {
return
}
select {
case c <- time.Now():
default:
}
}
}()
return func() {
C.hpt_ticker_stop(state)
// pipeR.Read returns error once the C thread closes the write end,
// causing the goroutine to exit and close pipeR.
}
}
func startTickerLoopFallback(period time.Duration, c chan time.Time) (stop func()) {
done := make(chan struct{})
go func() {
start := monotonicNow()
d := period.Nanoseconds()
var tick int64
for {
tick++
sleepUntil(start + tick*d)
select {
case <-done:
return
default:
}
select {
case c <- time.Now():
default:
}
}
}()
return func() { close(done) }
}