I have a long running ruby process (working with a contour mesh) which updates a progress bar in a webdialog. My initial implementation I split it into parts and uses UI.start_timer to give time for the backend to update the progressbar in the webdialog. That works flawlessly across various Sketchup versions. But the code needs to be compartmentalized to fit into the UI.start_timer paradigm.
I recently ran into Ruby 2.0’s light-weight thread feature called Fiber. While, many folks have tried mult-threading inside Sketchup, the response on the web has not been overwhelmingly positive.
I did an implementation of fiber which takes a sequential long running process and gives it to a fiber with intermediate calls of Fiber.yield. It works on faster core i7 machines flawlessly, but occasionally crashes on slower i5 machines. But my long running code is much easier to understand now rather than the compartmentalized state driven code using UI.start_timer. I tried Thomas’s TT_Lib refresh sketchup, but that does not seem to refresh webdialog/ie HTML type progressbars.
Question to the team is, is it worth going down the Fiber route. I can post the code if it will help.
Aren’t fibers using threads? I would have thought you’d run into the same threading issues…
Here is the official Fiber introduction
"Fibers are primitives for implementing light weight cooperative concurrency
in Ruby. Basically they are a means of creating code blocks that can be
paused and resumed, much like threads. The main difference is that they are
never preempted and that the scheduling must be done by the programmer and
not the VM. each fiber comes with a small 4KB stack. This enables the fiber to be paused from
deeply nested function calls within the fiber block."
The 4K stack may cause problems if I run a heavy duty process.
I have a hack to allow sketchup and the internal IE browser to refresh itself (essentially a variant of sleep X) which pauses the current ruby code to pause to allow refresh of the HTML progressbar. I open a small webdialog and hide it, but use show_modal, and close it using a timer after X sec. It works well.
Are there plans for Sketchup team to offer a cleaner refresh_sketchup which takes care of sketchup and browser updates. It really will help to write cleaner long running code with intermediate calls to refresh the screens.
We have been talking about various ways to handle long running scripts. It’s a highly desired improvement.
The same problems as with Threads occur with Fibers but with the added complexity that the programmer takes on the job of doing the scheduling instead of leaving it to the Ruby interpreter. Within the interpreter, only one Thread or Fiber is ever run at a time despite Ruby using native OS Threads to manage them. But the bigger problem is that the SketchUp main thread is blocked while Ruby is running. As TT says, this needs a deeper look from the Trimble development team.
Thanks Steve and Thomas. Will defer the Fiber implementation for now. Thanks.
Could you post sample code for your “hack”? I am trying to get a progress window to update during a long script, and would like to avoid using Fibers.
I dropped the Fiber implementation as while it worked on one machine I had
problems on a few other machines running it. However, for long running
processes with progress bar I did another implementation with Sketchup’s
UI.start_timer, which has been stable. While I am not sure I can share the
code due to contractual obligations, here is the strategy.
Make sure your long running process is the only activity performed by
the user during that time. If you allow the user to change the model during
the long running process, am not sure of the results. In my code, I
disabled all my web controls in the web dialog while the long running
process is running except the progress bar.
Break your long running code into distinct code blocks and feed the code
block into a UI.start timer implementation. The timer code will call your
code blocks in sequence and after each code block is completed get out of
the timer loop. This gives Sketchup time to breathe and update the
progress bar. It is your duty to maintain states.
If you feel adventurous, you could potentially have multiple levels of
code blocks and maintain level states and let the timer code handle the
state management. My implementation had three levels where my I had a few
level1 code blocks spawn a set of level 2 code. These multiple levels are
similar to nested function calls in a typical program flow.
Hope it helps…