~rogpeppe/juju-core/azure

« back to all changes in this revision

Viewing changes to testing/locking.go

  • Committer: William Reade
  • Date: 2012-01-20 21:32:53 UTC
  • mto: This revision was merged to the branch mainline in revision 47.
  • Revision ID: fwereade@gmail.com-20120120213253-csks5e12opl8t1rq
hefty rearrangement, few actual changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2013 Canonical Ltd.
2
 
// Licensed under the AGPLv3, see LICENCE file for details.
3
 
 
4
 
package testing
5
 
 
6
 
import (
7
 
        "errors"
8
 
        "sync"
9
 
        "time"
10
 
)
11
 
 
12
 
// TestLockingFunction verifies that a function obeys a given lock.
13
 
//
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.
17
 
//
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)
30
 
 
31
 
        goroutine := func() {
32
 
                proceed <- true
33
 
                function()
34
 
                events <- "complete function"
35
 
        }
36
 
 
37
 
        lock.Lock()
38
 
        go goroutine()
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.)
42
 
        <-proceed
43
 
 
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
48
 
        // reason.
49
 
        for counter := 0; counter < 10; counter++ {
50
 
                // TODO: In Go 1.1, use runtime.GoSched instead.
51
 
                time.Sleep(0)
52
 
        }
53
 
 
54
 
        // Unblock the goroutine.
55
 
        events <- "release lock"
56
 
        lock.Unlock()
57
 
 
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"))
64
 
        }
65
 
 
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"))
70
 
        }
71
 
}