summaryrefslogtreecommitdiff
path: root/Chalice/tests/examples/AssociationList.chalice
blob: 418bcd12af3ac997cd999508e9eec59cdaadd0ef (plain)
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
/*
  Note: This example seems to be completely broken. The failing assertion
  about locking/unlocking too much causes an inconsistency and all following
  assertions pass by default.
  It seems that the specification, in particular the loop invariant in method
  Add, is wrong. (see also http://boogie.codeplex.com/workitem/10207)
  -- August 2011, Stefan Heule
*/

class Client {
  method Main(d: Data)
    requires d != null
  {
    var a := new AssociationList
    call a.Init()
    call a.Add(5, d)
    call a.Add(10, d)
    var t: Data
    call t := a.Get(10)
  }
}

class AssociationList {
  var head: Node  // sentinel
  invariant rd(head) && head != null
  invariant rd(mu) && rd(head.mu) && this << head

  method Init()
    requires acc(head) && acc(mu) && mu == lockbottom
    ensures acc(mu) && waitlevel << this
  {
    head := new Node
    head.next := null
    share head
    share this between waitlevel and head
  }

  method Add(key: int, value: Data)
    requires value != null
    requires rd(mu) && waitlevel << this
    ensures rd(mu)
  {
    acquire this
    var p: Node := head
    acquire p
    release this

    var n := new Node
    n.key := key
    n.value := value
    n.next := p.next
    p.next := n
    share n between p and n.next
    release p
  }

  method Get(key: int) returns (d: Data)
    requires rd(mu) && waitlevel << this
    ensures rd(mu)
  {
    d := null
    acquire this
    var p: Node := head
    acquire p
    release this

    if (p.next != null) {
      acquire p.next
      if (p.next.key == key) {
        d := p.next.value
      } else {
        var done := false
        while (!done)
          // invariant:  holds p and p.next
          invariant p != null && rd(p.key) && rd(p.value) && acc(p.next) && acc(p.mu,50) && p.next != null
          invariant acc(p.next.mu) && p << p.next
          invariant rd(p.next.key) && rd(p.next.value) && acc(p.next.next)
          invariant p.next.next != null ==>
              acc(p.next.next.mu,50) && p.next << p.next.next
          invariant holds(p) && holds(p.next) && waitlevel == p.next.mu
          invariant p.next.next != null ==> waitlevel << p.next.next
          lockchange p, p.next.next
        {
          if (p.next.next == null) {
            done := true  // key not present
          } else {
            acquire p.next.next
            if (p.next.next.key == key) {
              done := true  // key is present
              d := p.next.next.value
              // move p.next.next closer to the head by one step

              var t: Node := p.next
              p.next := t.next
              t.next := p.next.next
              p.next.next := t
              reorder t between p.next and t.next
              release t
            } else {
              var t: Node := p
              p := p.next
              release t
            }
          }
        }
      }
      release p.next
    }
    release p
  }
}

class Data { }

class Node
{
  var key: int
  var value: Data
  var next: Node
  invariant rd(key) && rd(value) && acc(next) && acc(mu,50)
  invariant next != null ==> acc(next.mu,50) && this << next
}