Skip to content

Commit d714fa0

Browse files
authored
Merge pull request #261 from rock-core/event_source_weakref_edge_cases
fix: handle some edgy behavior related to WeakRef and Set in Event source handling
2 parents 61ca363 + daf4d4b commit d714fa0

File tree

3 files changed

+75
-3
lines changed

3 files changed

+75
-3
lines changed

lib/roby/event.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Event
1111

1212
def initialize(generator, propagation_id, context, time = Time.now)
1313
@generator, @propagation_id, @context, @time = generator, propagation_id, context.freeze, time
14-
@sources = Set.new
14+
@sources = []
1515
end
1616

1717
def plan
@@ -65,7 +65,7 @@ def protect_all_sources
6565

6666
# Sets the sources. See #sources
6767
def sources=(new_sources) # :nodoc:
68-
@sources = Set.new
68+
@sources = []
6969
add_sources(new_sources)
7070
end
7171

test/test_event.rb

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# frozen_string_literal: true
2+
3+
require "roby/test/self"
4+
5+
module Roby
6+
describe Event do
7+
describe "source-related functionality" do
8+
before do
9+
generator = EventGenerator.new
10+
@event = Event.new(generator, 1, [])
11+
end
12+
13+
it "adds refs to the given sources" do
14+
sources = 3.times.map { flexmock }
15+
@event.add_sources(sources[0, 1])
16+
@event.add_sources(sources[1, 2])
17+
18+
assert_equal sources.to_set, @event.sources
19+
end
20+
21+
# Helper that creates an object deep in the call chain to more or less
22+
# guarantee its garbage-collection
23+
def recursive_add_source(event, klass, level)
24+
if level == 0
25+
event.add_sources([object = klass.new])
26+
WeakRef.new(object)
27+
else
28+
recursive_add_source(event, klass, level - 1)
29+
end
30+
end
31+
32+
it "automatically removes garbage-collected sources" do
33+
GC.disable
34+
refs = 1_000.times.map do |i|
35+
recursive_add_source(@event, Object, i)
36+
end
37+
GC.start
38+
objects = refs.find_all(&:weakref_alive?).map(&:__getobj__)
39+
assert_equal objects.size, @event.sources.size
40+
assert objects.size < 1_000
41+
ensure
42+
GC.enable
43+
end
44+
45+
it "handles refs whose object have been recycled colliding "\
46+
"with objects being added" do
47+
collide_me = Class.new do
48+
def hash
49+
10
50+
end
51+
end
52+
53+
# Yes. This happened in a test run. Never seen it in the wild, though.
54+
# But it is possible.
55+
#
56+
# Note that writing this test, I also got a problem while cleaning
57+
# up @sources, which was a set at the time. Set#delete_if finds the
58+
# objects to delete, and then deletes, which forces the hash to re-hash
59+
# the argument and boom
60+
61+
GC.disable
62+
1_000.times.map do |i|
63+
recursive_add_source(@event, collide_me, i)
64+
end
65+
GC.start
66+
@event.add_sources([object = collide_me.new])
67+
assert @event.sources.include?(object)
68+
assert @event.sources.size < 1_001
69+
end
70+
end
71+
end
72+
end

test/test_event_generator.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
require "roby/test/self"
44

5-
class TC_Event < Minitest::Test
5+
class TC_EventGenerator < Minitest::Test
66
def test_controlable_events
77
event = EventGenerator.new(true)
88
assert(event.controlable?)

0 commit comments

Comments
 (0)