Skip to content

Commit 639efef

Browse files
committed
test: synchronize competing caller attempts
1 parent fc45fd1 commit 639efef

2 files changed

Lines changed: 34 additions & 4 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
synchronous calls retaining their staged effects and for caller process
77
re-registration after stale-process cleanup and retention. Both cases
88
already work in Ruby; no runtime behavior or configuration changes.
9+
The concurrency test waits for the second caller's busy-actor wait before
10+
allowing the first caller to commit its staged effect.
911

1012
## 0.17.4 - 2026-10-12
1113

‎test/integration/caller_lifecycle_test.rb‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,29 @@
44
require "timeout"
55

66
class CallerLifecycleTest < ActiveSupport::TestCase
7+
class BlockingWakeUp
8+
# @rbs @waiting: Thread::Queue
9+
# @rbs @release: Thread::Queue
10+
11+
attr_reader :waiting, :release
12+
13+
# @rbs () -> void
14+
def initialize
15+
@waiting = Queue.new
16+
@release = Queue.new
17+
end
18+
19+
# @rbs () -> void
20+
def signal
21+
end
22+
23+
# @rbs (timeout: Numeric) -> void
24+
def wait(timeout:)
25+
waiting << timeout
26+
release.pop
27+
end
28+
end
29+
730
class Counter < SolidObjects::Actor
831
actor_type "caller-lifecycle-counter"
932
attribute :count, default: 0
@@ -36,16 +59,21 @@ def increment(label:)
3659
test "concurrent callers preserve staged effects and ordered state" do
3760
Counter.started = Queue.new
3861
Counter.release = Queue.new
39-
enqueued = Queue.new
40-
subscription = ActiveSupport::Notifications.subscribe("solid_objects.message.enqueued") { enqueued << true }
62+
wake_up = BlockingWakeUp.new
63+
SolidObjects.configuration.wake_up_adapter = wake_up
4164
reference = Counter.ref("concurrent")
4265

4366
first = Thread.new { reference.increment(label: "first") }
4467
assert_equal "first", Timeout.timeout(10) { Counter.started.pop }
4568
second = Thread.new { reference.increment(label: "second") }
46-
2.times { Timeout.timeout(10) { enqueued.pop } }
69+
Timeout.timeout(10) { wake_up.waiting.pop }
70+
assert_raises(ThreadError) { Counter.started.pop(true) }
71+
assert_equal 1, SolidObjects::ClaimedMessage.count
72+
assert_equal 1, SolidObjects::ReadyMessage.count
73+
assert_empty SolidObjects::Effect.all
4774
Counter.release << true
4875
assert_equal 1, Timeout.timeout(10) { first.value }
76+
wake_up.release << true
4977
assert_equal "second", Timeout.timeout(10) { Counter.started.pop }
5078
Counter.release << true
5179
assert_equal 2, Timeout.timeout(10) { second.value }
@@ -59,9 +87,9 @@ def increment(label:)
5987
ensure
6088
Counter.release << true if first&.alive?
6189
Counter.release << true if second&.alive?
90+
wake_up&.release&.push(true) if second&.alive?
6291
first&.join(10)
6392
second&.join(10)
64-
ActiveSupport::Notifications.unsubscribe(subscription) if subscription
6593
end
6694

6795
test "caller re-registers after stale cleanup and process retention" do

0 commit comments

Comments
 (0)