Hey Charles,
Sure, this is as good a place as any to report issues on ControlTower. There's also a component for ControlTower on the MacRuby trac site, but I guess we don't really call that out on the web site.
Regarding the potential bug...well...where to begin? So, yes, MacRuby blocks have the same semantics as regular Ruby blocks, *except* when they are dispatched through GCD. In that case, ivars are shared, but local variable get copied. I'm sure Laurent can explain why better than I, but it has to do with the semantics of libdispatch and the uncertainty inherent in when a dispatched block or function will execute (i.e. if local variables were not copied during dispatch, they might go out of scope and be collected before GCD ever gets around to running the code).
To illustrate how this impacts async semantics wrt ruby, here's a sample done with MacRuby/GCD and canonical Ruby:
Earth: ~/Desktop > cat macfruit.rb
fruit_sellers = Dispatch::Group.new
offerings = %w|apples oranges bananas pinaples coconuts|
idx = 0
while(idx < 5)
fruit = offerings[idx]
Dispatch::Queue.concurrent.async(fruit_sellers) do
sleep 5-idx
puts "I sell #{fruit}, fresh as can be. Come and get some!"
end
idx += 1
end
fruit_sellers.wait
puts "What would you like?"
Earth: ~/Desktop > macruby macfruit.rb
I sell coconuts, fresh as can be. Come and get some!
I sell pinaples, fresh as can be. Come and get some!
I sell bananas, fresh as can be. Come and get some!
I sell oranges, fresh as can be. Come and get some!
I sell apples, fresh as can be. Come and get some!
What would you like?
Earth: ~/Desktop > macruby macfruit.rb
I sell coconuts, fresh as can be. Come and get some!
I sell pinaples, fresh as can be. Come and get some!
I sell bananas, fresh as can be. Come and get some!
I sell oranges, fresh as can be. Come and get some!
I sell apples, fresh as can be. Come and get some!
What would you like?
Earth: ~/Desktop > cat mrifruit.rb fruit_sellers = []
offerings = %w|apples oranges bananas pinaples coconuts|
idx = 0
while(idx < 5)
fruit = offerings[idx]
fruit_sellers << Thread.new do
sleep 5-idx
puts "I sell #{fruit}, fresh as can be. Come and get some!"
end
idx += 1
end
fruit_sellers.each(&:join)
puts "What would you like?"
Earth: ~/Desktop > ruby mrifruit.rb
I sell coconuts, fresh as can be. Come and get some!
I sell coconuts, fresh as can be. Come and get some!
I sell coconuts, fresh as can be. Come and get some!
I sell coconuts, fresh as can be. Come and get some!
I sell coconuts, fresh as can be. Come and get some!
What would you like?
In other words, not a bug in MacRuby...also, this code is scheduled for a rewrite that will turn it into a bit more of a continuation-passing style of handling the connections. I don't think tap is really the solution we are looking for here, since after dispatching, the main #accept loop doesn't really care about that connection (other than that, at some point, it is responsibly closed).
Cheers,
Josh
On Sat, Jan 22, 2011 at 2:14 AM, Charles Oliver Nutter
<headius@headius.com> wrote:
Apologies reporting this here; I'm not sure where I should report bugs
in ControlTower.
I believe there's a bug in ControlTower around line 34:
def open
@status = :open
while (@status == :open)
connection = @socket.accept # << here
@request_queue.async(@request_group) do
The "connection" local variable is used in the async block below to
parse the request and send the response. Unless MacRuby's blocks
behave differently than regular Ruby, this variable is shared across
all activations of that block. As a result, it's possible that two
concurrent requests will end up using each others' connection, and
usually just blowing up as a result.
The fix I've come up with is to wrap the @request_queue.async call in
a tap call:
def open
@status = :open
while (@status == :open)
conn = @socket.accept
conn.tap do |connection|
@request_queue.async(@request_group) do
Since async can't accept any explicitly-passed state, this seems like
the safest way to ensure the connection reference is not shared by
separate invocations. I'm not sure if it's possible in GCD, but having
async take an optional argument with explicitly-passed state might
also be a good way to fix this.
- Charlie
_______________________________________________
MacRuby-devel mailing list
MacRuby-devel@lists.macosforge.org
http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel