Skip to content

Commit 9145741

Browse files
authored
Merge pull request #435 from corhere/monotonic_usec
daemon: add `SdNotifyMonotonicUsec` helper function
2 parents 68cc651 + 6d96518 commit 9145741

File tree

5 files changed

+142
-1
lines changed

5 files changed

+142
-1
lines changed

daemon/sdnotify_linux_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2025
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package daemon
16+
17+
import (
18+
"strconv"
19+
"strings"
20+
"testing"
21+
"time"
22+
23+
"golang.org/x/sys/unix"
24+
)
25+
26+
// TestSdNotifyMonotonicUsec checks that SdNotifyMonotonicUsec is probably not returning complete garbage.
27+
func TestSdNotifyMonotonicUsec(t *testing.T) {
28+
var resolution unix.Timespec
29+
if err := unix.ClockGetres(unix.CLOCK_MONOTONIC, &resolution); err != nil {
30+
if err == unix.EINVAL {
31+
t.Log("CLOCK_MONOTONIC is not supported on this system")
32+
if got := SdNotifyMonotonicUsec(); got != "" {
33+
t.Errorf("SdNotifyMonotonicUsec() = %q; want empty string", got)
34+
}
35+
return
36+
}
37+
t.Fatalf("ClockGetres(CLOCK_MONOTONIC) failed: %v", err)
38+
}
39+
40+
now := func() uint64 {
41+
got := SdNotifyMonotonicUsec()
42+
t.Logf("SdNotifyMonotonicUsec() = %q", got)
43+
if got == "" {
44+
t.Fatal("SdNotifyMonotonicUsec() returned empty string on system which supports CLOCK_MONOTONIC")
45+
}
46+
fields := strings.SplitN(got, "=", 2)
47+
if len(fields) != 2 {
48+
t.Fatal("string is not a well-formed variable assignment")
49+
}
50+
tag, val := fields[0], fields[1]
51+
if tag != "MONOTONIC_USEC" {
52+
t.Errorf("expected tag MONOTONIC_USEC, got %q", tag)
53+
}
54+
if val[len(val)-1] != '\n' {
55+
t.Errorf("expected value to end with newline, got %q", val)
56+
}
57+
ts, err := strconv.ParseUint(val[:len(val)-1], 10, 64)
58+
if err != nil {
59+
t.Fatalf("value %q is not well-formed: %v", val, err)
60+
}
61+
if ts == 0 {
62+
// CLOCK_MONOTONIC is defined on Linux as the number of seconds
63+
// since boot, per clock_gettime(2). A timestamp of zero is
64+
// almost certainly bogus.
65+
t.Fatal("timestamp is zero")
66+
}
67+
return ts
68+
}
69+
70+
start := now()
71+
time.Sleep(time.Duration(resolution.Nano()) * 3)
72+
ts := now()
73+
if ts < start {
74+
t.Errorf("timestamp went backwards: %d < %d", ts, start)
75+
} else if ts == start {
76+
t.Errorf("timestamp did not advance: %d == %d", ts, start)
77+
}
78+
}

daemon/sdnotify_other.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2025
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//go:build !unix
16+
17+
package daemon
18+
19+
// SdNotifyMonotonicUsec returns the empty string on unsupported platforms.
20+
func SdNotifyMonotonicUsec() string {
21+
return ""
22+
}

daemon/sdnotify_unix.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2025
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//go:build unix
16+
17+
package daemon
18+
19+
import (
20+
"strconv"
21+
22+
"golang.org/x/sys/unix"
23+
)
24+
25+
// SdNotifyMonotonicUsec returns a MONOTONIC_USEC=... assignment for the current time
26+
// with a trailing newline included. This is typically used with [SdNotifyReloading].
27+
//
28+
// If the monotonic clock is not available on the system, the empty string is returned.
29+
func SdNotifyMonotonicUsec() string {
30+
var ts unix.Timespec
31+
if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts); err != nil {
32+
// Monotonic clock is not available on this system.
33+
return ""
34+
}
35+
return "MONOTONIC_USEC=" + strconv.FormatInt(ts.Nano()/1000, 10) + "\n"
36+
}

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/coreos/go-systemd/v22
22

33
go 1.23
44

5-
require github.com/godbus/dbus/v5 v5.1.0
5+
require (
6+
github.com/godbus/dbus/v5 v5.1.0
7+
golang.org/x/sys v0.1.0
8+
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
22
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
3+
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
4+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

0 commit comments

Comments
 (0)