1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
12
// TestLockingFunction verifies that a function obeys a given lock.
14
// Use this as a building block in your own tests for proper locking.
15
// Parameters are a lock that you expect your function to block on, and
16
// the function that you want to test for proper locking on that lock.
18
// This helper attempts to verify that the function both obtains and releases
19
// the lock. It will panic if the function fails to do either.
20
// TODO: Support generic sync.Locker instead of just Mutex.
21
// TODO: This could be a gocheck checker.
22
func TestLockingFunction(lock *sync.Mutex, function func()) {
23
// We record two events that must happen in the right order.
24
// Buffer the channel so that we don't get hung up during attempts
25
// to push the events in.
26
events := make(chan string, 2)
27
// Synchronization channel, to make sure that the function starts
28
// trying to run at the point where we're going to make it block.
29
proceed := make(chan bool, 1)
34
events <- "complete function"
39
// Make the goroutine start now. It should block inside "function()."
40
// (It's fine, technically even better, if the goroutine started right
41
// away, and this channel is buffered specifically so that it can.)
44
// Give a misbehaved function plenty of rope to hang itself. We don't
45
// want it to block for a microsecond, hand control back to here so we
46
// think it's stuck on the lock, and then later continue on its merry
47
// lockless journey to finish last, as expected but for the wrong
49
for counter := 0; counter < 10; counter++ {
50
// TODO: In Go 1.1, use runtime.GoSched instead.
54
// Unblock the goroutine.
55
events <- "release lock"
58
// Now that we've released the lock, the function is unblocked. Read
59
// the 2 events. (This will wait until the function has completed.)
60
firstEvent := <-events
61
secondEvent := <-events
62
if firstEvent != "release lock" || secondEvent != "complete function" {
63
panic(errors.New("function did not obey lock"))
66
// Also, the function must have released the lock.
67
blankLock := sync.Mutex{}
68
if *lock != blankLock {
69
panic(errors.New("function did not release lock"))