File: mutex_spec.cr

package info (click to toggle)
crystal 1.14.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 24,384 kB
  • sloc: javascript: 6,400; sh: 695; makefile: 269; ansic: 121; python: 105; cpp: 77; xml: 32
file content (146 lines) | stat: -rw-r--r-- 2,823 bytes parent folder | download | duplicates (2)
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
require "spec"
require "../support/fibers"

describe Mutex do
  it "locks and unlocks" do
    mutex = Mutex.new
    mutex.lock
    mutex.unlock
  end

  it "works with multiple threads" do
    x = 0
    mutex = Mutex.new

    fibers = 10.times.map do
      spawn do
        100.times do
          mutex.synchronize { x += 1 }
        end
      end
    end.to_a

    fibers.each do |f|
      wait_until_finished f
    end

    x.should eq(1000)
  end

  describe "checked" do
    it "raises if locked recursively" do
      mutex = Mutex.new
      mutex.lock
      expect_raises(Exception, "Attempt to lock a mutex recursively (deadlock)") do
        mutex.lock
      end
    end

    it "raises if unlocks without lock" do
      mutex = Mutex.new
      expect_raises(Exception, "Attempt to unlock a mutex which is not locked") do
        mutex.unlock
      end
    end

    it "can't be unlocked by another fiber" do
      mutex = nil
      done = false

      spawn do
        mutex = Mutex.new
        mutex.not_nil!.lock
        done = true
      end

      until done
        Fiber.yield
      end

      expect_raises(Exception, "Attempt to unlock a mutex locked by another fiber") do
        mutex.not_nil!.unlock
      end
    end
  end

  describe "reentrant" do
    it "can be locked many times from the same fiber" do
      mutex = Mutex.new(:reentrant)
      mutex.lock
      mutex.lock
      mutex.unlock
      mutex.unlock
    end

    it "raises if unlocks without lock" do
      mutex = Mutex.new(:reentrant)
      expect_raises(Exception, "Attempt to unlock a mutex which is not locked") do
        mutex.unlock
      end
    end

    it "can't be unlocked by another fiber" do
      mutex = nil
      done = false

      spawn do
        mutex = Mutex.new(:reentrant)
        mutex.not_nil!.lock
        done = true
      end

      until done
        Fiber.yield
      end

      expect_raises(Exception, "Attempt to unlock a mutex locked by another fiber") do
        mutex.not_nil!.unlock
      end
    end
  end

  describe "unchecked" do
    it "can lock and unlock from multiple fibers" do
      mutex = Mutex.new(:unchecked)

      a = 1
      two = false
      three = false
      four = false
      ch = Channel(Nil).new

      spawn do
        mutex.synchronize do
          a = 2
          Fiber.yield
          two = a == 2
        end
        ch.send(nil)
      end

      spawn do
        mutex.synchronize do
          a = 3
          Fiber.yield
          three = a == 3
        end
        ch.send(nil)
      end

      spawn do
        mutex.synchronize do
          a = 4
          Fiber.yield
          four = a == 4
        end
        ch.send(nil)
      end

      3.times { ch.receive }

      two.should be_true
      three.should be_true
      four.should be_true
    end
  end
end