Closed gdonald closed 2 years ago
Are you using autoload
? autoload
is broken on Ruby < 3.2.0 unfortunately so this will only be fixed when Ruby 3.2.0 is released. Backporting the change to 3.1 was deemed too extensive. The solution is to ensure all files are loaded before executing your app. Maybe @fxn can help advise how to do this?
I haven't made any calls to autoload
, no.
My F
model is in scope just before my Async do
block, not sure why it would then not be in scope a couple of lines later. Does Async
not follow normal Ruby scoping?
My code is fully loaded as far as I can tell. I'm running this using bundle exec rails console
, then inside there I run my Async
code I posted above.
Zeitwerk uses Module#autoload
.
If you use fibers by hand you are in control, but custom fiber schedulers escape the synchronization of autoloads and constant references builtin in CRuby (for < 3.2 ?).
WIth current Ruby, the workaround is to load F
before the Async
call.
IIRC, any reference F
makes to autoload can also break in the same way (NameError
). So it's not just that F
should be in scope but any class F
references. @fxn isn't there a way to trigger all autoload
s?
config.eager_load = true
should do It (force all autoloads), can you try this @gdonald
IRC, any reference F makes to autoload can also break in the same way
Correct. If the reference happens at class-level, it is going to be resolved in cascade. No problem in this case. However, if the methods being executed trigger autoloads in turn, you'd have the same sync issues. Eager loading is the safest bet if that is the case.
WIth current Ruby, the workaround is to load
F
before theAsync
call.
My ruby is:
> ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [arm64-darwin21]
Adding a call to autoload
doesn't seem to help, I get the same NameError
as before:
autoload(:F, "#{Rails.root}/app/models/f")
Async do
# Queue of up to 10 items:
items = Async::LimitedQueue.new(10)
# Five producers:
5.times do
Async do |task|
# while true
while F.unchecked.count.positive?
# t = rand
f = F.unchecked.first
f.do_checking!
t = check_f(f)
task.sleep(t)
items.enqueue(t)
end
end
end
# A single consumer:
Async do |task|
while item = items.dequeue
puts "dequeue -> #{item}"
end
end
end
Also tried with ::F
. Same.
autoload
is what is causing the problem. You'll need to add config.eager_load = true
.
autoload
is what is causing the problem. You'll need to addconfig.eager_load = true
.
Ok, I guess I misunderstood. I thought you guys were suggesting to make a manual call to autoload
.
config.eager_load = true
gets me to the next error. Thanks.
My next error is TypeError: true can't be coerced into Float
. I'm supposed to return a Float from my task?
Can you give me a line number or backtrace?
Can you give me a line number or backtrace?
0.13s warn: Async::Task [oid=0x5c94] [ec=0x5ca8] [pid=7907] [2022-07-17 08:14:43 -0500]
| Task may have ended with unhandled exception.
| TypeError: true can't be coerced into Float
| → /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/timers-4.3.3/lib/timers/timer.rb:103 in `+'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/timers-4.3.3/lib/timers/timer.rb:103 in `reset'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/timers-4.3.3/lib/timers/timer.rb:44 in `initialize'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/timers-4.3.3/lib/timers/group.rb:60 in `new'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/timers-4.3.3/lib/timers/group.rb:60 in `after'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/async-2.0.3/lib/async/scheduler.rb:115 in `block'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/async-2.0.3/lib/async/scheduler.rb:144 in `kernel_sleep'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/async-2.0.3/lib/async/task.rb:92 in `sleep'
| /Users/gd/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/async-2.0.3/lib/async/task.rb:92 in `sleep'
You are probably calling task.sleep(t)
with t = nil
. I should add more explicit error checking for this.
A boolean actually. I changed it to task.sleep(0.1)
and got it working.
Thanks for all your help :)
You're welcome.
I don't seem to have scope for my ActiveRecord model inside an
Async::Task
.For my model named
F
I get:NameError: uninitialized constant F
My implementation:
I also tried using
::F
but got the same error.