Skip to content

Commit dd629fe

Browse files
authored
Import Sync::Exclusive and Sync::Shared (#16487)
Imports `Sync::Exclusive` and `Sync::Shared` from the [sync shard](https://github.com/ysbaddaden/sync/) with the latest API (`#lock(&)` and `#shared(&)`) and without the experimental methods.
1 parent e1c2168 commit dd629fe

File tree

6 files changed

+596
-0
lines changed

6 files changed

+596
-0
lines changed

spec/std/sync/exclusive_spec.cr

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
require "./spec_helper"
2+
require "sync/exclusive"
3+
4+
private class Foo
5+
INSTANCE = Foo.new
6+
class_getter foo = Sync::Exclusive(Int64 | Foo).new(0_i64)
7+
@value = 123
8+
end
9+
10+
describe Sync::Exclusive do
11+
it "#lock(&)" do
12+
ary = [1, 2, 3, 4, 5]
13+
var = Sync::Exclusive.new(ary)
14+
var.lock { |val| val.should be(ary) }
15+
end
16+
17+
it "#get" do
18+
ary = [1, 2, 3, 4, 5]
19+
var = Sync::Exclusive.new(ary)
20+
var.get.should be(ary)
21+
end
22+
23+
it "#set" do
24+
ary1 = [1, 2, 3, 4, 5]
25+
ary2 = [4, 5, 8]
26+
27+
var = Sync::Exclusive.new(ary1)
28+
var.set(ary2)
29+
var.get.should be(ary2)
30+
end
31+
32+
it "#replace" do
33+
ary1 = [1, 2, 3, 4, 5]
34+
ary2 = [4, 5, 8]
35+
36+
var = Sync::Exclusive.new(ary1)
37+
var.replace do |value|
38+
value.should be(ary1)
39+
ary2
40+
end
41+
var.get.should be(ary2)
42+
end
43+
44+
it "#unsafe_get" do
45+
ary = [1, 2, 3, 4, 5]
46+
var = Sync::Exclusive.new(ary)
47+
var.unsafe_get.should be(ary)
48+
end
49+
50+
it "#unsafe_set" do
51+
ary1 = [1, 2, 3, 4, 5]
52+
ary2 = [1, 2, 3, 4]
53+
54+
var = Sync::Exclusive.new(ary1)
55+
var.unsafe_set(ary2)
56+
var.get.should be(ary2)
57+
end
58+
59+
it "synchronizes" do
60+
var = Sync::Exclusive.new([] of Int32)
61+
wg = WaitGroup.new
62+
63+
counter = Atomic(Int64).new(0)
64+
65+
10.times do
66+
spawn(name: "exclusive-read") do
67+
100.times do
68+
var.lock do |value|
69+
value.each { counter.add(1, :relaxed) }
70+
end
71+
Fiber.yield
72+
end
73+
end
74+
end
75+
76+
5.times do
77+
wg.spawn(name: "exclusive-write") do
78+
100.times do
79+
var.lock do |value|
80+
100.times { value << value.size }
81+
end
82+
Fiber.yield
83+
end
84+
end
85+
end
86+
87+
4.times do
88+
wg.spawn(name: "set-replace") do
89+
50.times do |i|
90+
if i % 2 == 1
91+
var.set([] of Int32)
92+
else
93+
var.replace { |value| value[0...10] }
94+
end
95+
Fiber.yield
96+
end
97+
end
98+
99+
wg.spawn(name: "dup-clone") do
100+
100.times do |i|
101+
if i % 2 == 0
102+
var.lock(&.dup)
103+
else
104+
var.lock(&.clone)
105+
end
106+
Fiber.yield
107+
end
108+
end
109+
end
110+
111+
wg.wait
112+
113+
counter.get(:relaxed).should be > 0
114+
end
115+
116+
{% if flag?(:execution_context) %}
117+
# see https://github.com/crystal-lang/crystal/issues/15085
118+
it "synchronizes reads/writes of mixed unions" do
119+
ready = WaitGroup.new(1)
120+
running = true
121+
contexts = Array(Fiber::ExecutionContext::Isolated).new(3)
122+
123+
contexts << Fiber::ExecutionContext::Isolated.new("set:foo") do
124+
ready.wait
125+
while running
126+
Foo.foo.set(Foo::INSTANCE)
127+
end
128+
end
129+
130+
contexts << Fiber::ExecutionContext::Isolated.new("set:zero") do
131+
ready.wait
132+
while running
133+
Foo.foo.set(0_i64)
134+
end
135+
end
136+
137+
contexts << Fiber::ExecutionContext::Isolated.new("get") do
138+
ready.wait
139+
while running
140+
Foo.foo.lock do |value|
141+
case value
142+
in Foo
143+
value.as(Void*).address.should eq(Foo::INSTANCE.as(Void*).address)
144+
in Int64
145+
value.should eq(0_i64)
146+
end
147+
end
148+
end
149+
end
150+
151+
ready.done
152+
153+
sleep 100.milliseconds
154+
running = false
155+
156+
contexts.each(&.wait)
157+
end
158+
{% end %}
159+
end

spec/std/sync/shared_spec.cr

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
require "./spec_helper"
2+
require "sync/shared"
3+
4+
private class Foo
5+
INSTANCE = Foo.new
6+
class_getter foo = Sync::Shared(Int64 | Foo).new(0_i64)
7+
@value = 123
8+
end
9+
10+
describe Sync::Shared do
11+
it "#shared(&)" do
12+
ary = [1, 2, 3, 4, 5]
13+
var = Sync::Shared.new(ary)
14+
var.shared { |val| val.should be(ary) }
15+
end
16+
17+
it "#lock(&)" do
18+
ary1 = [1, 2, 3, 4, 5]
19+
var = Sync::Shared.new(ary1)
20+
var.lock { |val| val << 6 }
21+
var.get.should be(ary1)
22+
ary1.should eq([1, 2, 3, 4, 5, 6])
23+
end
24+
25+
it "#get" do
26+
ary = [1, 2, 3, 4, 5]
27+
var = Sync::Shared.new(ary)
28+
var.get.should be(ary)
29+
end
30+
31+
it "#set" do
32+
ary1 = [1, 2, 3, 4, 5]
33+
ary2 = [4, 5, 8]
34+
35+
var = Sync::Shared.new(ary1)
36+
var.set(ary2)
37+
var.get.should be(ary2)
38+
end
39+
40+
it "#replace" do
41+
ary1 = [1, 2, 3, 4, 5]
42+
ary2 = [4, 5, 8]
43+
44+
var = Sync::Shared.new(ary1)
45+
var.replace do |value|
46+
value.should be(ary1)
47+
ary2
48+
end
49+
var.get.should be(ary2)
50+
end
51+
52+
it "#unsafe_get" do
53+
ary = [1, 2, 3, 4, 5]
54+
var = Sync::Shared.new(ary)
55+
var.unsafe_get.should be(ary)
56+
end
57+
58+
it "#unsafe_set" do
59+
ary1 = [1, 2, 3, 4, 5]
60+
ary2 = [1, 2, 3, 4]
61+
62+
var = Sync::Shared.new(ary1)
63+
var.unsafe_set(ary2)
64+
var.get.should be(ary2)
65+
end
66+
67+
it "synchronizes" do
68+
var = Sync::Shared.new([] of Int32)
69+
wg = WaitGroup.new
70+
71+
counter = Atomic(Int64).new(0)
72+
73+
10.times do
74+
spawn(name: "shared-read") do
75+
100.times do
76+
var.shared do |value|
77+
value.each { counter.add(1, :relaxed) }
78+
end
79+
Fiber.yield
80+
end
81+
end
82+
end
83+
84+
5.times do
85+
wg.spawn(name: "exclusive-write") do
86+
100.times do
87+
var.lock do |value|
88+
100.times { value << value.size }
89+
end
90+
Fiber.yield
91+
end
92+
end
93+
end
94+
95+
4.times do
96+
wg.spawn(name: "set-replace") do
97+
50.times do |i|
98+
if i % 2 == 1
99+
var.set([] of Int32)
100+
else
101+
var.replace { |value| value[0...10] }
102+
end
103+
Fiber.yield
104+
end
105+
end
106+
107+
wg.spawn(name: "dup-clone") do
108+
100.times do |i|
109+
if i % 2 == 0
110+
var.lock(&.dup)
111+
else
112+
var.lock(&.clone)
113+
end
114+
Fiber.yield
115+
end
116+
end
117+
end
118+
119+
wg.wait
120+
121+
counter.get(:relaxed).should be > 0
122+
end
123+
124+
{% if flag?(:execution_context) %}
125+
# see https://github.com/crystal-lang/crystal/issues/15085
126+
it "synchronizes reads/writes of mixed unions" do
127+
ready = WaitGroup.new(1)
128+
running = true
129+
contexts = Array(Fiber::ExecutionContext::Isolated).new(3)
130+
131+
contexts << Fiber::ExecutionContext::Isolated.new("set:foo") do
132+
ready.wait
133+
while running
134+
Foo.foo.set(Foo::INSTANCE)
135+
end
136+
end
137+
138+
contexts << Fiber::ExecutionContext::Isolated.new("set:zero") do
139+
ready.wait
140+
while running
141+
Foo.foo.set(0_i64)
142+
end
143+
end
144+
145+
contexts << Fiber::ExecutionContext::Isolated.new("get") do
146+
ready.wait
147+
while running
148+
Foo.foo.shared do |value|
149+
case value
150+
in Foo
151+
value.as(Void*).address.should eq(Foo::INSTANCE.as(Void*).address)
152+
in Int64
153+
value.should eq(0_i64)
154+
end
155+
end
156+
end
157+
end
158+
159+
ready.done
160+
161+
sleep 100.milliseconds
162+
running = false
163+
164+
contexts.each(&.wait)
165+
end
166+
{% end %}
167+
end

0 commit comments

Comments
 (0)