Coverage, TLS, and atomic swaps in Go 1.2
Three Go 1.2 additions fixed three unrelated annoyances in one week. None is glamorous, which is usually a sign that I will use it.
First, go test now measures statement coverage directly:
The cover tool is still distributed separately in go.tools, so I installed it first:
$ go get code.google.com/p/go.tools/cmd/cover
$ go test -cover
PASS
coverage: 71.4% of statements
ok example/parser 0.012s
For detail I use go test -coverprofile=cover.out, followed by go tool cover -func=cover.out or go tool cover -html=cover.out. Coverage works by rewriting source with counters. It tells me which statements ran, not whether the assertions were intelligent. I can reach 100 percent while testing almost nothing; I have demonstrated this skill repeatedly.
Second, crypto/tls gained better control over protocol and cipher behavior, including TLS 1.2 support. For a client that must reject obsolete protocol versions I can set the minimum explicitly:
tr := &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS11,
},
}
client := &http.Client{Transport: tr}
I still let the standard library choose cipher suites unless interoperability forces my hand. A handwritten list ages, and a secure-looking list copied from a weblog ages especially well.
Finally, sync/atomic now has swap operations. I used one to replace a numeric generation and retrieve the previous value as a single atomic action:
old := atomic.SwapUint64(&generation, next)
log.Printf("generation %d -> %d", old, next)
The important phrase is “single atomic action.” Loading and then storing separately leaves a window in which another goroutine can intervene. A swap closes that window, but it does not magically protect related fields or establish a larger invariant. For that I still use a mutex.
The default coverage mode counts whether statements execute. When tests exercise code concurrently, -covermode=atomic makes counter updates safe, at additional cost. I do not compare benchmark timings from an instrumented coverage build with normal timings. The inserted counters are deliberately observable work.
Atomics also require aligned, correctly typed storage and a design whose state really fits in one word. My generation counter does. A configuration containing a counter, pointer, and status does not become coherent because each field can be manipulated atomically. Readers could observe a combination that never represented one published configuration.
I keep the atomic operation beside a comment describing that single-word invariant. Otherwise the next maintenance change tends to add a companion field and quietly invalidate the reasoning.
These features share a useful property: they turn homemade approximations into explicit operations. I deleted a coverage script, avoided a custom TLS dialer, and replaced a dubious load/store pair. Upgrades are pleasant when the final diff is mostly subtraction.