From octave-graphics-request at bevo dot che dot wisc dot edu Fri Jun 28 10:12:33 2002 Subject: Re: GTK and Octave From: Paul Kienzle To: =?iso-8859-1?Q?Jo=E3o_Cardoso?= , octave-graphics@bevo.che.wisc.edu Date: Fri, 28 Jun 2002 11:12:21 -0400 On Fri, Jun 28, 2002 at 04:18:16AM +0100, Joćo Cardoso wrote: > On Thursday 27 June 2002 20:33, Paul Kienzle wrote: . . . > > you can't do cooperative > > multitasking in octave (i.e., by inserting process_event_loop calls > > in your scripts), so you are left with preemptive multitasking > > (i.e., threads), and all that that entails: > > * each callback running in its own thread since you don't know > > which callbacks are going to take a long time. > > * managing a pool of threads because you don't want to create > > and destroy one on every call. > > * making sure that all static data is thread local (including > > the stuff in libcruft --- aren't all fortran variables static > > by default?) > > * making sure all shared data is protected by mutex (including > > file access, symbol table access, etc.) > > * deciding what to do with state variables that modify octave's > > behaviour. What if one thread wants colormap(gray) and another > > wants colormap(ocean). > > In fact you will have to deal with most of these issues even with > > cooperative multitasking, assuming things like read process the > > event queue until data is available instead of blocking the entire > > process. > > I don't want to share variables between octave and the GUI, as you do ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > in your tk_octave, and I don't want to have multiple octave processes > running the same problem. Nice concept, but too complex. I just want > to give an opportunity for the GUI event loop to run from time to > time, so I can do > > done=0; > while(!done) > compute; > ...and compute... > endwhile > > where "done" is a variable shared between the cancel button callback ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > and my lengthly function. That's why I say that evaluating callbacks ^^^^^^^^^^^^^^^^^^^^^^^^ > when the parser reaches a terminal symbol is "safe", as (scalar) > variables are in a consistent state. Of course you want to share variables. Otherwise you wouldn't need the GUI in octave 8-) Maybe your fit is converging too slowly, so you want to change the step size. This is just like the "done" case. Maybe you want to see the fit as it progresses. Here you could ask the gui to redisplay after each step through the loop, or if the loop is going fast enough, just grab the variable periodically. You still have to consider some issues: * the interpreter must be reentrant. * callbacks should not block * state variables (e.g., colormap, string_fill_char, etc.) must be used with extreme caution. I think the first is true. You can do an eval from within an eval. Everything which would otherwise be declared global or static is pushed on a stack and unwind_protected. Bugs are still popping up occasionally because of improper protection, and I would expect to see a bunch more as more code relies on it. If the event hook were preemptive (so that e.g., code running in libcruft could be called recursively), I would expect to see a lot more problems. Regarding the second, what happens if you have debug on error set, and you have an error in the callback? The GUI blocks. If you do not have debug on error then you will probably need an implicit try-catch around each callback, otherwise you will return to the toplevel. You may even need to catch longjumps for really nasty exceptions. We can leave it to the user to not write callbacks which block on purpose. State variables is where I foresee the biggest problems. Consider the following code: function callback1 s = state; state = 1; do_stuff state = s; end function callback2 s = state; state = 0; do_more_stuff state = s; end This will work. If callback2 is triggered during callback1, its just like inserting a call to callback2 in the code for callback1, and octave is already written to handle those cases. Now add the equivalent to tcl's vwait into do_more_stuff of callback2 and consider what happens if callback2 is triggered during do_stuff of callback1. The vwait allows callback1 to continue before callback2 completes, but callback2 has changed state so callback1 continues with the wrong state. This is what I was trying to say before. In retrospect it is clearly a red herring: letting callback1 continue while in callback2 would require a 'stackless' interpreter (one that simulates its own stack), which isn't going to happen any time soon. Even the pthreads model is safe if only one thread may run the octave interpreter. Assuming you can live with delays during oct-files, I see no obstacles to triggering the event loop either implicitly or explicitly in octave scripts. You might want to take a time-stamp each time you process the event loop, and cancel pending events after a long delay. I know I will click more than once if I don't get an immediate response because I can't be sure that I did indeed click in the right spot. It would also be nice to change the cursor to hourglass if the delay is too long. Maybe you can play games with SIGALRM to do this even though the octave interpreter is busy. > > That's the way I always have done it, as I couldn't found other way... > even a X program can be irresponsible it the user callbacks don't give > an oportunity for the main event loop to run. That's all I desire. If you want to experiment, try adding the following to the start of tree_statement::eval in pt-stmt.cc: #include if (rl_event_hook != NULL) (*rl_event_hook)(); Or you could add the following to widget.cc and put an update statement in each of your loops: DEFUN_DLD(update,args,,"process pending events") { if (rl_event_hook != NULL) (*rl_event_hook)(); } Paul Kienzle pkienzle at users dot sf dot net