Łukasz Łapiński
2008-Jun-12 08:00 UTC
Background thread - entension code - switch contexts
Hi, I am using wxRuby 1.9.7 and Ruby 1.8.6. I have built an extension in C++ and used swig to load it into Ruby interpreter, which works great. The extension is an often long executing algorithm, and I''ve noticed that if I fork a new Ruby thread in button clicked event, and in this thread run the algorithm the application freezes for the time of execution. I have looked through the archive and found that in the wxRuby main thread I need to use timer, but that does not work for the "algorithm thread", I dont know how to stop it or keep GUI responsive? Can anyone suggest what should I do. I know about Green Threads, and possibly that interpreter can not switch context when extension is running, so what should I do maybe use a thread at the side of extension (in c++ code) or maybe try to use the same mechanizm with timer as wxRuby does (but how to implement that). My code is simple: require ''wx'' require ''cppapp'' #myextension class MFrame < Wx::Frame STEPS = 100 def initialize super(nil,-1,"Title") set_client_size(Wx::Size.new(300,300)) panel = Wx::Panel.new(self) sizer = Wx::BoxSizer.new(Wx::VERTICAL) btn = Wx::Button.new(panel, -1, "Click me") sizer.add(btn, 0, Wx::GROW|Wx::ALL,2) evt_button(btn.get_id){ Thread.abort_on_exception = true #that does not work @th = Thread.new do Wx::Timer.every(20) do Thread.pass end method1 end } panel.set_sizer(sizer) sizer.fit(panel) end def method1 alg = Cppapp::Algorithm.new alg.form = Cppapp::Form.new alg.algorithmize end end class MApp < Wx::App def on_init MFrame.new.show Wx::Timer.every(20) {Thread.pass} end end MApp.new.main_loop -- Posted via http://www.ruby-forum.com/.
Mario Steele
2008-Jun-12 11:39 UTC
[wxruby-users] Background thread - entension code - switch contexts
Hey Lukasz, I looked over the code, and it looks good. Now you said you developed the C++ Extension yourself, so that gives you a bit more control then most. The problem is, Ruby''s threads are green, and if you use an extension that doesn''t recognize that Ruby is Green Threaded, and yield control back to it, it''ll effectivly do as you are seeing, freezing. With the examples we''ve given, we''re using Ruby''s core code, or using Sockets, with key point select() calls. Ruby''s core code, and select() are specifically programmed to allow for Green Threads to be used. When you use select() in Ruby, it''s not a "true" call to the os level select. It''s timed to be used within Ruby''s Threads itself. What you will need to do, is make your C++ Extension aware of Ruby Threads. You will need to look at how to add rb_thread_yield() to your swig''ed code, to allow when an interation through your algorithim is completed, it will call rb_thread_yield(), to allow a pass from the C/C++ Code, back to Ruby code, so that it can do it''s iteration/processing, before giving control back to your C/C++ Extension. That, unfortunatly, is the only way you will be able to improve upon the problem you are facing. Hope this helps, Mario Steele On Thu, Jun 12, 2008 at 3:00 AM, ?ukasz ?api?ski <lists at ruby-forum.com> wrote:> Hi, > > I am using wxRuby 1.9.7 and Ruby 1.8.6. I have built an extension in C++ > and used swig to load it into Ruby interpreter, which works great. The > extension is an often long executing algorithm, and I''ve noticed that if > I fork a new Ruby thread in button clicked event, and in this thread run > the algorithm the application freezes for the time of execution. I have > looked through the archive and found that in the wxRuby main thread I > need to use timer, but that does not work for the "algorithm thread", I > dont know how to stop it or keep GUI responsive? Can anyone suggest what > should I do. I know about Green Threads, and possibly that interpreter > can not switch context when extension is running, so what should I do > maybe use a thread at the side of extension (in c++ code) or maybe try > to use the same mechanizm with timer as wxRuby does (but how to > implement that). > > > My code is simple: > > > require ''wx'' > require ''cppapp'' #myextension > > class MFrame < Wx::Frame > STEPS = 100 > def initialize > super(nil,-1,"Title") > set_client_size(Wx::Size.new(300,300)) > panel = Wx::Panel.new(self) > sizer = Wx::BoxSizer.new(Wx::VERTICAL) > > btn = Wx::Button.new(panel, -1, "Click me") > sizer.add(btn, 0, Wx::GROW|Wx::ALL,2) > > evt_button(btn.get_id){ > > Thread.abort_on_exception = true > #that does not work > @th = Thread.new do > Wx::Timer.every(20) do > Thread.pass > end > method1 > end > > } > panel.set_sizer(sizer) > sizer.fit(panel) > end > > def method1 > alg = Cppapp::Algorithm.new > alg.form = Cppapp::Form.new > alg.algorithmize > end > end > > class MApp < Wx::App > def on_init > MFrame.new.show > Wx::Timer.every(20) {Thread.pass} > end > end > MApp.new.main_loop > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > wxruby-users mailing list > wxruby-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users >-- Mario Steele http://www.trilake.net http://www.ruby-im.net http://rubyforge.org/projects/wxruby/ http://rubyforge.org/projects/wxride/ -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/wxruby-users/attachments/20080612/b1fb6c3b/attachment-0001.html>
Łukasz Łapiński
2008-Jun-12 16:52 UTC
[wxruby-users] Background thread - entension code - switch contexts
Mario Steele wrote:> You will need to look at how to add rb_thread_yield() to your swig''ed > code, > to allow when an interation through your algorithim is completed, it > will > call rb_thread_yield(), to allow a pass from the C/C++ Code, back to > Ruby > code, so that it can do it''s iteration/processing, before giving control > back to your C/C++ Extension. That, unfortunatly, is the only way you > will > be able to improve upon the problem you are facing. > > Hope this helps, > > Mario SteeleThank you, I believe that is the only and good direction :). Now I will investigate that. If I could use some tips of yours :) I think I should place it somewhere here (the algorithm function) SWIGINTERN VALUE _wrap_Algorithm_algorithmize(int argc, VALUE *argv, VALUE self) { Algorithm *arg1 = (Algorithm *) 0 ; void *argp1 = 0 ; int res1 = 0 ; if ((argc < 0) || (argc > 0)) { rb_raise(rb_eArgError, "wrong # of arguments(%d for 0)",argc); SWIG_fail; } res1 = SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_Algorithm, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), Ruby_Format_TypeError( "", "Algorithm *","algorithmize", 1, self )); } arg1 = reinterpret_cast< Algorithm * >(argp1); #HERE CREATE A THREAD FROM algorithmize function? (arg1)->algorithmize(); return Qnil; fail: return Qnil; } I still don''t know where to get the ruby thread that I should pass to rb_thread_yield(VALUE arg, rb_thread_t th) Should I create it or get current ruby thread in the place marked above in the code and then pass it to the algorithmize(rb_thread_t th) and then in this function void Algorithm::algorithmize(rb_thread_t th) { time_t seconds; seconds = time (NULL); time_t tmp = 0; int nb = 0; //iterations while((tmp = time(NULL) - seconds) < 4) { if(tmp > nb) { nb++; #THIS GOES IN THE ITERATION rb_thread_yield(some_VALUE, rh); } } } Kind regards, ?ukasz ?api?ski -- Posted via http://www.ruby-forum.com/.
Mario Steele
2008-Jun-13 05:25 UTC
[wxruby-users] Background thread - entension code - switch contexts
Hello again Lukasz, On Thu, Jun 12, 2008 at 11:52 AM, ?ukasz ?api?ski <lists at ruby-forum.com> wrote:> Thank you, I believe that is the only and good direction :). Now I will > investigate that. >No problem.> > If I could use some tips of yours :) > > I think I should place it somewhere here (the algorithm function) >I''m sorry, I meant the use of rb_thread_pass(), instead of rb_thread_yield(). rb_thread_yield() is pretty much like the internal rb_yield(), which yields control back to Ruby, with some variable passed. Generally meant for Blocks. Where as rb_thread_pass() is meant to pass control back to the Ruby Thread Scheduler, and on to the next thread in the list of threads.> Should I create it or get current ruby thread in the place marked above > in the code and then pass it to the algorithmize(rb_thread_t th) and > then in this function >You are partly right, again rb_thread_yield() is not the method to go with, my apologizes, but your idea for Algorithim::algorithmize() would be the best solution, only replace rb_thread_yield() with rb_thread_pass(). The main thing you need to look at, is allowing Ruby''s Thread Scheduler back control, so that it can give processing time to other Ruby Threads. If you don''t do that, then all other threads will freeze till your C/C++ routine is completed. And that is not what you want.> void Algorithm::algorithmize(rb_thread_t th) { > > time_t seconds; > > seconds = time (NULL); > time_t tmp = 0; > int nb = 0; > > //iterations > while((tmp = time(NULL) - seconds) < 4) > { > if(tmp > nb) > { > nb++; > #THIS GOES IN THE ITERATION > rb_thread_yield(some_VALUE, rh); > } > } > } >Good luck, Mario Steele> > Kind regards, > ?ukasz ?api?ski > -- > Posted via http://www.ruby-forum.com/. > _______________________________________________ > wxruby-users mailing list > wxruby-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/wxruby-users >-- Mario Steele http://www.trilake.net http://www.ruby-im.net http://rubyforge.org/projects/wxruby/ http://rubyforge.org/projects/wxride/ -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/wxruby-users/attachments/20080613/cebce340/attachment.html>
Łukasz Łapiński
2008-Jun-13 09:19 UTC
[wxruby-users] Background thread - entension code - switch contexts
Hi, thanks for help, Finally I have used void rb_thread_schedule() function which is not static and I can declare it to be extern in my C++ extension, what I did is extern "C" { extern void rb_thread_schedule(); } using namespace std; void Algorithm::algorithmize() { time_t seconds; seconds = time (NULL); time_t tmp = 0; float nb = 0; //iterations while((tmp = time(NULL) - seconds) < 5) { if(tmp > nb) { cout<<nb<<endl; nb = nb + 0.01; rb_thread_schedule(); } } } And it works. Thanks, ?ukasz ?api?ski -- Posted via http://www.ruby-forum.com/.