|
View:
New views
12 Messages
—
Rating Filter:
Alert me
|
|
|
Octave/backend synchronizationHi,
I'd like to propose the following patch. The goal is to implement a basic symchronization scheme between octave and a graphics backend. The idea is not to implement a fully-featured multithread support, but at least to provide a way to perform rendering in a separate thread, without race condition problems. This is motivated by the fact that in some graphics system (like Java), the GUI event-loop run automatically in a separate thread; hence the redraw requests will occur in this separate thread. The proposed scheme allows to "lock" completely access to the graphics system and should be enough for rendering purposes (and maybe special actions like 3D rotation). Note that it is not my intention to run callbacks (like when pushing a button) in a separate thread; these should be run synchronously with octave. The proposed code contains only the Windows implementation. I leave the UNIX implementation for people able to compile and run it. I tried to keep the implementation as lightweight as possible. Michael. src/ChangeLog 2008-01-23 Michael Goffioul <michael.goffioul@...> * graphics.h.in (class gh_manager): New static methods lock/unlock and instance methods do_lock/do_unlock. * graphics.cc (class gh_manager): Likewise (Windows implementation). (Fishandle, Fset, Fget, F__get__, F__go_figure__, F__go_axes__, F__go_line__, F__go_text__, F__go_image__, F__go_surface__, F__go_patch__, F__go_delete__, F__go_axes_init__, F__go_handles__, F__go_figure_handles__, Fdrawnow, get_property_from_handle, set_property_in_handle): Use locking mechanism. |
|
|
Re: Octave/backend synchronizationThis time, with the attachment...
Michael. On Jan 23, 2008 10:55 AM, Michael Goffioul <michael.goffioul@...> wrote: > Hi, > > I'd like to propose the following patch. The goal is to implement a > basic symchronization > scheme between octave and a graphics backend. The idea is not to implement a > fully-featured multithread support, but at least to provide a way to > perform rendering > in a separate thread, without race condition problems. This is > motivated by the fact > that in some graphics system (like Java), the GUI event-loop run > automatically in > a separate thread; hence the redraw requests will occur in this separate thread. > The proposed scheme allows to "lock" completely access to the graphics system > and should be enough for rendering purposes (and maybe special actions like > 3D rotation). > > Note that it is not my intention to run callbacks (like when pushing a > button) in > a separate thread; these should be run synchronously with octave. > > The proposed code contains only the Windows implementation. I leave the UNIX > implementation for people able to compile and run it. I tried to keep > the implementation > as lightweight as possible. > > Michael. > > src/ChangeLog > > 2008-01-23 Michael Goffioul <michael.goffioul@...> > > * graphics.h.in (class gh_manager): New static methods lock/unlock > and instance methods do_lock/do_unlock. > * graphics.cc (class gh_manager): Likewise (Windows implementation). > (Fishandle, Fset, Fget, F__get__, F__go_figure__, F__go_axes__, > F__go_line__, F__go_text__, F__go_image__, F__go_surface__, > F__go_patch__, F__go_delete__, F__go_axes_init__, > F__go_handles__, F__go_figure_handles__, Fdrawnow, > get_property_from_handle, set_property_in_handle): Use locking > mechanism. > Index: src/graphics.h.in =================================================================== RCS file: /cvs/octave/src/graphics.h.in,v retrieving revision 1.48 diff -c -p -r1.48 graphics.h.in *** src/graphics.h.in 22 Jan 2008 19:42:48 -0000 1.48 --- src/graphics.h.in 23 Jan 2008 09:39:49 -0000 *************** public: *** 2625,2630 **** --- 2625,2642 ---- return instance_ok () ? instance->do_figure_handle_list () : Matrix (); } + static void lock (void) + { + if (instance_ok ()) + instance->do_lock (); + } + + static void unlock (void) + { + if (instance_ok ()) + instance->do_unlock (); + } + private: static gh_manager *instance; *************** private: *** 2708,2713 **** --- 2720,2729 ---- { return figure_list.empty () ? graphics_handle () : figure_list.front (); } + + void do_lock (void); + + void do_unlock (void); }; Index: src/graphics.cc =================================================================== RCS file: /cvs/octave/src/graphics.cc,v retrieving revision 1.78 diff -c -p -r1.78 graphics.cc *** src/graphics.cc 22 Jan 2008 20:32:00 -0000 1.78 --- src/graphics.cc 23 Jan 2008 09:39:49 -0000 *************** gh_manager::do_free (const graphics_hand *** 703,708 **** --- 703,742 ---- } } + #if defined (__WIN32__) && ! defined (__CYGWIN__) + CRITICAL_SECTION __go_lock__; + #else + pthread_mutex_t __go_lock__; + #endif + static bool __go_lock_initialized__ = false; + + void + gh_manager::do_lock (void) + { + #if defined (__WIN32__) && ! defined (__CYGWIN__) + if (! __go_lock_initialized__) + { + InitializeCriticalSection (&__go_lock__); + __go_lock_initialized__ = true; + } + + EnterCriticalSection (&__go_lock__); + #else + // FIXME: implement this + #endif + } + + void + gh_manager::do_unlock (void) + { + #if defined (__WIN32__) && ! defined (__CYGWIN__) + if (__go_lock_initialized__) + LeaveCriticalSection (&__go_lock__); + #else + // FIXME: implement this + #endif + } + gh_manager *gh_manager::instance = 0; static void *************** Return true if @var{h} is a graphics han *** 1912,1922 **** --- 1946,1960 ---- { octave_value retval; + gh_manager::lock (); + if (args.length () == 1) retval = is_handle (args(0)); else print_usage (); + gh_manager::unlock (); + return retval; } *************** for the graphics handle @var{h}.\n\ *** 1929,1934 **** --- 1967,1974 ---- { octave_value retval; + gh_manager::lock (); + int nargin = args.length (); if (nargin > 0) *************** for the graphics handle @var{h}.\n\ *** 1965,1970 **** --- 2005,2012 ---- else print_usage (); + gh_manager::unlock (); + return retval; } *************** values or lists respectively.\n\ *** 1980,1985 **** --- 2022,2029 ---- octave_value retval; octave_value_list vlist; + gh_manager::lock (); + int nargin = args.length (); if (nargin == 1 || nargin == 2) *************** values or lists respectively.\n\ *** 2036,2041 **** --- 2080,2087 ---- retval = vlist(0); } + gh_manager::unlock (); + return retval; } *************** values or lists respectively.\n\ *** 2050,2055 **** --- 2096,2103 ---- octave_value retval; octave_value_list vlist; + gh_manager::lock (); + int nargin = args.length (); if (nargin == 1) *************** values or lists respectively.\n\ *** 2091,2096 **** --- 2139,2146 ---- retval = vlist(0); } + gh_manager::unlock (); + return retval; } *************** Undocumented internal function.\n\ *** 2144,2149 **** --- 2194,2201 ---- { octave_value retval; + gh_manager::lock (); + if (args.length () > 0) { double val = args(0).double_value (); *************** Undocumented internal function.\n\ *** 2188,2204 **** --- 2240,2262 ---- else print_usage (); + gh_manager::unlock (); + return retval; } #define GO_BODY(TYPE) \ octave_value retval; \ \ + gh_manager::lock (); \ + \ if (args.length () > 0) \ retval = make_graphics_object (#TYPE, args); \ else \ print_usage (); \ \ + gh_manager::unlock (); \ + \ return retval DEFUN (__go_axes__, args, , *************** Undocumented internal function.\n\ *** 2263,2268 **** --- 2321,2328 ---- { octave_value_list retval; + gh_manager::lock (); + if (args.length () == 1) { graphics_handle h = octave_NaN; *************** Undocumented internal function.\n\ *** 2298,2303 **** --- 2358,2365 ---- else print_usage (); + gh_manager::unlock (); + return retval; } *************** Undocumented internal function.\n\ *** 2321,2326 **** --- 2383,2390 ---- return retval; } + gh_manager::lock (); + if (nargin == 1 || nargin == 2) { graphics_handle h = octave_NaN; *************** Undocumented internal function.\n\ *** 2346,2351 **** --- 2410,2417 ---- else print_usage (); + gh_manager::unlock (); + return retval; } *************** DEFUN (__go_handles__, , , *** 2355,2361 **** Undocumented internal function.\n\ @end deftypefn") { ! return octave_value (gh_manager::handle_list ()); } DEFUN (__go_figure_handles__, , , --- 2421,2435 ---- Undocumented internal function.\n\ @end deftypefn") { ! octave_value retval; ! ! gh_manager::lock (); ! ! retval = gh_manager::handle_list (); ! ! gh_manager::unlock (); ! ! return retval; } DEFUN (__go_figure_handles__, , , *************** DEFUN (__go_figure_handles__, , , *** 2364,2370 **** Undocumented internal function.\n\ @end deftypefn") { ! return octave_value (gh_manager::figure_handle_list ()); } static void --- 2438,2452 ---- Undocumented internal function.\n\ @end deftypefn") { ! octave_value retval; ! ! gh_manager::lock (); ! ! retval = gh_manager::figure_handle_list (); ! ! gh_manager::unlock (); ! ! return retval; } static void *************** Undocumented internal function.\n\ *** 2385,2390 **** --- 2467,2474 ---- octave_value retval; + gh_manager::lock (); + unwind_protect::begin_frame ("Fdrawnow"); unwind_protect::add (clear_drawnow_request); *************** Undocumented internal function.\n\ *** 2492,2497 **** --- 2576,2583 ---- unwind_protect::run_frame ("Fdrawnow"); + gh_manager::unlock (); + return retval; } *************** octave_value *** 2499,2504 **** --- 2585,2592 ---- get_property_from_handle (double handle, const std::string &property, const std::string &func) { + gh_manager::lock (); + graphics_object obj = gh_manager::get_object (handle); octave_value retval; *************** get_property_from_handle (double handle, *** 2510,2515 **** --- 2598,2605 ---- else error ("%s: invalid handle (= %g)", func.c_str(), handle); + gh_manager::unlock (); + return retval; } *************** bool *** 2517,2522 **** --- 2607,2614 ---- set_property_in_handle (double handle, const std::string &property, const octave_value &arg, const std::string &func) { + gh_manager::lock (); + graphics_object obj = gh_manager::get_object (handle); int ret = false; *************** set_property_in_handle (double handle, c *** 2530,2535 **** --- 2622,2629 ---- else error ("%s: invalid handle (= %g)", func.c_str(), handle); + gh_manager::unlock (); + return ret; } |
|
|
Re: Octave/backend synchronizationOn Jan 23, 2008 12:17 PM, Michael Goffioul <michael.goffioul@...> wrote:
> This time, with the attachment... > > Michael. > > > > On Jan 23, 2008 10:55 AM, Michael Goffioul <michael.goffioul@...> wrote: > > Hi, > > > > I'd like to propose the following patch. The goal is to implement a > > basic symchronization > > scheme between octave and a graphics backend. The idea is not to implement a > > fully-featured multithread support, but at least to provide a way to > > perform rendering > > in a separate thread, without race condition problems. This is > > motivated by the fact > > that in some graphics system (like Java), the GUI event-loop run > > automatically in > > a separate thread; hence the redraw requests will occur in this separate thread. > > The proposed scheme allows to "lock" completely access to the graphics system > > and should be enough for rendering purposes (and maybe special actions like > > 3D rotation). > > > > Note that it is not my intention to run callbacks (like when pushing a > > button) in > > a separate thread; these should be run synchronously with octave. > > > > The proposed code contains only the Windows implementation. I leave the UNIX > > implementation for people able to compile and run it. I tried to keep > > the implementation > > as lightweight as possible. I have zero knowledge in multithreading, but I did find this: http://www.gamasutra.com/features/20051205/paquet_pfv.htm which seems to provide a way to translate between the windows code Michael has written and linux system calls does anyone know aht is the situation in other OS's (OSX, solaris , ....?) Shai > > Michael. > > > > src/ChangeLog > > > > 2008-01-23 Michael Goffioul <michael.goffioul@...> > > > > * graphics.h.in (class gh_manager): New static methods lock/unlock > > and instance methods do_lock/do_unlock. > > * graphics.cc (class gh_manager): Likewise (Windows implementation). > > (Fishandle, Fset, Fget, F__get__, F__go_figure__, F__go_axes__, > > F__go_line__, F__go_text__, F__go_image__, F__go_surface__, > > F__go_patch__, F__go_delete__, F__go_axes_init__, > > F__go_handles__, F__go_figure_handles__, Fdrawnow, > > get_property_from_handle, set_property_in_handle): Use locking > > mechanism. > > > |
|
|
Re: Octave/backend synchronizationOn Jan 23, 2008 12:29 PM, Shai Ayal <shaiay@...> wrote:
> I have zero knowledge in multithreading, but I did find this: > http://www.gamasutra.com/features/20051205/paquet_pfv.htm > which seems to provide a way to translate between the windows code > Michael has written and linux system calls It's indeed only about creating and locking/unlocking a mutex. > does anyone know aht is the situation in other OS's (OSX, solaris , ....?) I think OSX has pthread support (I've seen OSX code using pthread calls). Michael. |
|
|
Octave/backend synchronizationOn 23-Jan-2008, Michael Goffioul wrote:
| I'd like to propose the following patch. The goal is to implement a | basic symchronization | scheme between octave and a graphics backend. The idea is not to implement a | fully-featured multithread support, but at least to provide a way to | perform rendering | in a separate thread, without race condition problems. This is | motivated by the fact | that in some graphics system (like Java), the GUI event-loop run | automatically in | a separate thread; hence the redraw requests will occur in this separate thread. | The proposed scheme allows to "lock" completely access to the graphics system | and should be enough for rendering purposes (and maybe special actions like | 3D rotation). | | Note that it is not my intention to run callbacks (like when pushing a | button) in | a separate thread; these should be run synchronously with octave. | | The proposed code contains only the Windows implementation. I leave the UNIX | implementation for people able to compile and run it. I tried to keep | the implementation | as lightweight as possible. A change like this is acceptable to me, but would it maybe be better to refactor the code so that the lock/unlock can be limited to a single class? Maybe we could do that by making the bodies of the set, get, __get__, etc. functions static functions in the gh_manager class? Then the lock/unlock methods could be made private and users would not have to worry about the details of locking unless they are writing new functions for the gh_manager class. It seems to me that the code would be easier to maintain and more reliable if we could do that. Which objects/variables need to be protected? Thanks, jwe |
|
|
Re: Octave/backend synchronizationOn 1/24/08, John W. Eaton <jwe@...> wrote:
> A change like this is acceptable to me, but would it maybe be better > to refactor the code so that the lock/unlock can be limited to a > single class? Maybe we could do that by making the bodies of the set, > get, __get__, etc. functions static functions in the gh_manager class? > Then the lock/unlock methods could be made private and users would not > have to worry about the details of locking unless they are writing new > functions for the gh_manager class. This would mean that the rendering process would have to go through the gh_manager class. Is this what you want? > It seems to me that the code > would be easier to maintain and more reliable if we could do that. > > Which objects/variables need to be protected? Almost all objects and properties: rendering can potentially access most of the properties. Rendering is really the main use case I'm thinking about (all the rest should be done synchronously with octave). The idea is "if an external backend performs rendering in a separate thread, it has the possibility to lock the graphics system such that no other thread can make changes while rendering is running". So my idea was that the backend would be responsible for locking/unlocking the graphics system; this does not fit well with making these functions private. Michael. |
|
|
Re: Octave/backend synchronizationOn 24-Jan-2008, Michael Goffioul wrote:
| On 1/24/08, John W. Eaton <jwe@...> wrote: | > A change like this is acceptable to me, but would it maybe be better | > to refactor the code so that the lock/unlock can be limited to a | > single class? Maybe we could do that by making the bodies of the set, | > get, __get__, etc. functions static functions in the gh_manager class? | > Then the lock/unlock methods could be made private and users would not | > have to worry about the details of locking unless they are writing new | > functions for the gh_manager class. | | This would mean that the rendering process would have to go through | the gh_manager class. Is this what you want? | | > It seems to me that the code | > would be easier to maintain and more reliable if we could do that. | > | > Which objects/variables need to be protected? | | Almost all objects and properties: rendering can potentially access | most of the properties. Rendering is really the main use case I'm | thinking about (all the rest should be done synchronously with octave). | The idea is "if an external backend performs rendering in a separate | thread, it has the possibility to lock the graphics system such that | no other thread can make changes while rendering is running". So | my idea was that the backend would be responsible for locking/unlocking | the graphics system; this does not fit well with making these functions | private. What I'm thinking about is that if the users of the graphics objects must know when to call the lock/unlock functions, and that they must do it in many places, then I think we will end up with reliability and maintenance problems. What about restricting access to the properties via set_X and get_X functions? If we do that, couldn't we put the locking in the set_X functions so that the backend code would not have to deal with locking directly? jwe |
|
|
Re: Octave/backend synchronizationOn Jan 24, 2008 8:32 AM, John W. Eaton <jwe@...> wrote:
> What I'm thinking about is that if the users of the graphics objects > must know when to call the lock/unlock functions, and that they must > do it in many places, then I think we will end up with reliability and > maintenance problems. > > What about restricting access to the properties via set_X and get_X > functions? If we do that, couldn't we put the locking in the set_X > functions so that the backend code would not have to deal with locking > directly? I'm not sure you'll gain anything. First, I think that you would have lock in the get_X functions (otherwise, how can you avoid a property change while you're accessing it?). I thought about that possibility, but found it less lightweight (each property access would trigger a lock/unlock) than the global approach I used. Moreover, this means that any manually written set_X would need to include the lock, so you're still left with reliability and maintenance issues. Given these issues, I took a different global approach. The main ideas are: 1) it's the responsbility of any C++ code (typically a graphics backend) to lock/unlock the graphics system when this is required; for instance, a rendering event handler would look like void handle_draw_event (draw_event *evt) { gh_manager::lock (); // Do all rendering stuff gh_manager::unlock (); } 2) on the octave-side, the only places where you have to put lock/unlock are the builtin DEFUN (in graphics.cc), because these are the only entry points from a user point of view: any code within the graphics system derives from a call to one of these builtin DEFUN. As there aren'y many of them, I found that the maintenance effort was pretty low. Of course, this is only my opinion. If you prefer another approach, we can go for it. I know I'll need some kind of locking mechanism in JHandles (and basically, any integrated GUI that runs octave in a separate thread will also need it), so I wanted to propose something at early stage in the graphics code (re)development. Michael. |
|
|
Re: Octave/backend synchronizationOn 24-Jan-2008, Michael Goffioul wrote:
| On Jan 24, 2008 8:32 AM, John W. Eaton <jwe@...> wrote: | > What I'm thinking about is that if the users of the graphics objects | > must know when to call the lock/unlock functions, and that they must | > do it in many places, then I think we will end up with reliability and | > maintenance problems. | > | > What about restricting access to the properties via set_X and get_X | > functions? If we do that, couldn't we put the locking in the set_X | > functions so that the backend code would not have to deal with locking | > directly? | | I'm not sure you'll gain anything. First, I think that you would have lock | in the get_X functions (otherwise, how can you avoid a property change | while you're accessing it?). I thought about that possibility, but found it | less lightweight (each property access would trigger a lock/unlock) | than the global approach I used. Moreover, this means that any manually | written set_X would need to include the lock, so you're still left with | reliability and maintenance issues. Yes, but that maintenance problem is limited to the graphics properties classes, not the users of those classes. | Given these issues, I took a different global approach. The main ideas are: | | 1) it's the responsbility of any C++ code (typically a graphics backend) | to lock/unlock the graphics system when this is required; for instance, | a rendering event handler would look like | | void handle_draw_event (draw_event *evt) | { | gh_manager::lock (); | | // Do all rendering stuff | | gh_manager::unlock (); | } I think the problem with this is that it would be easy to forget, and then hard to debug. To me, an analogous situation is reference counting. It might be more efficient if we could skip incrementing and decrementing reference counts when they aren't actually necessary, but I wouldn't want to have to remember that level of detail every time I used an octave_value object. | 2) on the octave-side, the only places where you have to put lock/unlock | are the builtin DEFUN (in graphics.cc), because these are the only entry | points from a user point of view: any code within the graphics system | derives from a call to one of these builtin DEFUN. As there aren'y many | of them, I found that the maintenance effort was pretty low. For the scripting language, yes, there wouldn't be many places to lock, though anyone adding a new C++ function that accesses graphics properties would have to know about locking. jwe |
|
|
Re: Octave/backend synchronizationOn Jan 25, 2008 9:24 AM, John W. Eaton <jwe@...> wrote:
> | 2) on the octave-side, the only places where you have to put lock/unlock > | are the builtin DEFUN (in graphics.cc), because these are the only entry > | points from a user point of view: any code within the graphics system > | derives from a call to one of these builtin DEFUN. As there aren'y many > | of them, I found that the maintenance effort was pretty low. > > For the scripting language, yes, there wouldn't be many places to > lock, though anyone adding a new C++ function that accesses graphics > properties would have to know about locking. I have the impression that whatever C++ code you would add to octave, once called, you would always have in your call stack a frame corresponding to one of the builtin DEFUN of graphics.cc (because this is what the user has access to). So it would protected anyway. Nevermind, forget it for the time being. Michael. |
|
|
Re: Octave/backend synchronizationOn 1/25/08, John W. Eaton <jwe@...> wrote:
> | 1) it's the responsbility of any C++ code (typically a graphics backend) > | to lock/unlock the graphics system when this is required; for instance, > | a rendering event handler would look like > | > | void handle_draw_event (draw_event *evt) > | { > | gh_manager::lock (); > | > | // Do all rendering stuff > | > | gh_manager::unlock (); > | } > > I think the problem with this is that it would be easy to forget, and > then hard to debug. One final note maybe (before closing the subject). While implementing auto-locking in the set_X/get_X methods seems easier, it only avoids local problems. For instance it does not guarantee that 2 get_X calls within the same function body will return the same value. For instance, consider the following code executed in a separate thread: void my_function (void) { ... x = props.get_X (); // do something x = props.get_X (); ... } The second get_X call might return something different, if another thread has call set_X in between. One workaround is to cache the returned value from the first call, but you have to know it... However, I admit this is a kind of corner case. Michael. |
|
|
Re: Octave/backend synchronizationOn 25-Jan-2008, Michael Goffioul wrote:
| On 1/25/08, John W. Eaton <jwe@...> wrote: | > | 1) it's the responsbility of any C++ code (typically a graphics backend) | > | to lock/unlock the graphics system when this is required; for instance, | > | a rendering event handler would look like | > | | > | void handle_draw_event (draw_event *evt) | > | { | > | gh_manager::lock (); | > | | > | // Do all rendering stuff | > | | > | gh_manager::unlock (); | > | } | > | > I think the problem with this is that it would be easy to forget, and | > then hard to debug. | | One final note maybe (before closing the subject). While implementing | auto-locking in the set_X/get_X methods seems easier, it only avoids | local problems. For instance it does not guarantee that 2 get_X calls | within the same function body will return the same value. For | instance, consider the following code executed in a separate thread: | | void my_function (void) | { | ... | x = props.get_X (); | | // do something | | x = props.get_X (); | ... | } | | The second get_X call might return something different, if another | thread has call set_X in between. One workaround is to cache the | returned value from the first call, but you have to know it... | However, I admit this is a kind of corner case. It seems to me that this would be a smaller problem than having to remember when and where to insert lock/unlock calls. If it is necessary to expose the lock/unlock calls, then we can do it. I would just like to avoid it if possible. Would you like to go ahead and add the lock/unlock functions? It seems it should also be relatively easy to modify the genprops.awk script to insert calls in the set and get methods. jwe |
| Free embeddable forum powered by Nabble | Forum Help |