Created
June 26, 2013 16:36
-
-
Save colinsurprenant/5869025 to your computer and use it in GitHub Desktop.
examples of the closure frame sharing across threads. these are in the context of a class level dsl with a code block registration and its execution from its instance. DSL 1, 2 and 3 all reproduce the problem, DSL 4 works around it but defeats the dsl idea.
for the actual bug see https://jira.codehaus.org/browse/JRUBY-7167
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# block executed using instance_exec | |
class DSL1 | |
def self.register(&block) | |
@block = block | |
end | |
def execute(str) | |
instance_exec(str, &self.class.block) | |
end | |
def self.block | |
@block | |
end | |
end | |
class Test1 < DSL1 | |
register do |str| | |
puts "before: #{$~}" | |
/.*/ =~ str | |
puts "after: #{$~}\n\n" | |
end | |
end | |
puts("DSL1") | |
Thread.new {Test1.new.execute("foo")}.join | |
Test1.new.execute("bar") | |
Thread.new {Test1.new.execute("baz")}.join | |
# block executed as a method using define_method | |
class DSL2 | |
def self.register(&block) | |
define_method(:execute, block) | |
end | |
end | |
class Test2 < DSL2 | |
register do |str| | |
puts "before: #{$~}" | |
/.*/ =~ str | |
puts "after: #{$~}\n\n" | |
end | |
end | |
puts("DSL2") | |
Thread.new {Test2.new.execute("foo")}.join | |
Test2.new.execute("bar") | |
Thread.new {Test2.new.execute("baz")}.join | |
# block executed as a method using define_method but called from another method | |
class DSL3 | |
def self.register(&block) | |
define_method(:execute_block, block) | |
end | |
def execute(str) | |
execute_block(str) | |
end | |
end | |
class Test3 < DSL3 | |
register do |str| | |
puts "before: #{$~}" | |
/.*/ =~ str | |
puts "after: #{$~}\n\n" | |
end | |
end | |
puts("DSL3") | |
Thread.new {Test3.new.execute("foo")}.join | |
Test3.new.execute("bar") | |
Thread.new {Test3.new.execute("baz")}.join | |
# block executed as a method using define_method, calling out method with actual code, | |
class DSL4 | |
def self.register(&block) | |
define_method(:execute, block) | |
end | |
end | |
class Test4 < DSL4 | |
register do |str| | |
do_match(str) | |
end | |
def do_match(str) | |
puts "before: #{$~}" | |
/.*/ =~ str | |
puts "after: #{$~}\n\n" | |
end | |
end | |
puts("DSL4") | |
Thread.new {Test4.new.execute("foo")}.join | |
Test4.new.execute("bar") | |
Thread.new {Test4.new.execute("baz")}.join |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
DSL1 | |
before: | |
after: foo | |
before: foo | |
after: bar | |
before: bar | |
after: baz | |
DSL2 | |
before: | |
after: foo | |
before: foo | |
after: bar | |
before: bar | |
after: baz | |
DSL3 | |
before: | |
after: foo | |
before: foo | |
after: bar | |
before: bar | |
after: baz | |
DSL4 | |
before: | |
after: foo | |
before: | |
after: bar | |
before: | |
after: baz |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Procified method object basically just means a #method that has been to_proc'ed:
But as @tarcieri pointed out elsewhere, you can't easily rebind such a proc to a new self, so for DSL purposes it's perhaps not useful.
As for fixing the $~ issue...I have no answers for you. It is a flaw of how Ruby handles closures, and there's no clear fix. It probably should be raised with ruby-core.