|
View:
New views
2 Messages
—
Rating Filter:
Alert me
|
|
|
[PATCH] Backend notifiers.Hello
Assigned patch adds backend notifiers to graphics code. The patch is cumulative, contains previously presented locking facilities. I ask You for review, and - eventually - inclusion into main source tree. How does it work: Graphics backend implements three methods: virtual void property_changed (const graphics_handle&, const std::string&); virtual void object_created (const graphics_handle&); virtual void object_destroyed (const graphics_handle&); (stubs added to gnuplot and fltk backend) - property_changed is called from base_property::set - object_destroyed is called from gh_manager::do_free - object_created is called from gh_manager::do_make_graphics_handle and gh_manager::do_make_figure_handle. I've been using this code for last few days with my Qt backend, and it works good, events are going back and forth between threads, UI is responsive and changes are visible immediately. You can event do animations! For instance, this code: x = [-pi:0.2:pi]; l = plot( x, sin(x)) for s = [0:0.1:100]; set(l,'ydata',sin(x+s)); usleep(10000);endfor creates figure window with sine wave moving slowly like signal on oscilloscope screen. Figure remains responsive, can be moved and resized, while the wave still moves. Maciek Gajewski [thread-safe-graphics-notifiers.patch] diff -r d56511a02884 src/DLD-FUNCTIONS/fltk_backend.cc --- a/src/DLD-FUNCTIONS/fltk_backend.cc Thu Jun 26 16:06:14 2008 -0400 +++ b/src/DLD-FUNCTIONS/fltk_backend.cc Thu Jul 10 20:37:42 2008 +0200 @@ -723,6 +723,12 @@ public: sz(1) = Fl::h (); return sz; } + + virtual void property_changed (const graphics_handle& h, const std::string& n) { } // do nothing + + virtual void object_created (const graphics_handle&) { } // do nothing + + virtual void object_destroyed (const graphics_handle&) { } // do nothing }; static bool backend_registered = false; diff -r d56511a02884 src/graphics.cc --- a/src/graphics.cc Thu Jun 26 16:06:14 2008 -0400 +++ b/src/graphics.cc Thu Jul 10 20:37:42 2008 +0200 @@ -533,6 +533,26 @@ make_graphics_object_from_type (const ca } // --------------------------------------------------------------------- + +void +base_property::set (const octave_value& v, bool do_run ) +{ + do_set (v); + + // notify backend + graphics_object go = gh_manager::get_object (parent); + if (go) + { + graphics_backend backend = go.get_backend(); + if (backend) + backend.property_changed (parent, name); + } + + // run listeners + if (do_run && ! error_state) + run_listeners (POSTSET); +} + void base_property::run_listeners (listener_mode mode) @@ -1255,7 +1275,13 @@ gh_manager::do_free (const graphics_hand { p->second.get_properties ().set_beingdeleted (true); p->second.get_properties ().execute_deletefcn (); - + // notify backend + graphics_backend backend = p->second.get_backend (); + if (backend) + backend.object_destroyed (h); + // note - this will be valid only for first explicitly deleted object. + // All his children will have unknown backend then. + handle_map.erase (p); if (h.value () < 0) @@ -1268,6 +1294,56 @@ gh_manager::do_free (const graphics_hand error ("graphics_handle::free: can't delete root figure"); } } +#if defined (__WIN32__) && ! defined (__CYGWIN__) +CRITICAL_SECTION __go_lock__; +#else +pthread_mutex_t __go_lock__; +#endif + +void +gh_manager::init_mutex (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + InitializeCriticalSection (&__go_lock__); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); // mutex needs to be recursive + pthread_mutex_init (&__go_lock__, &attr); + pthread_mutexattr_destroy (&attr); +#endif +} + +void +gh_manager::cleanup_mutex (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + DeleteCriticalSection (&__go_lock__); +#else + pthread_mutex_destroy (&__go_lock__); +#endif +} + +void +gh_manager::do_lock (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + EnterCriticalSection (&__go_lock__); +#else + pthread_mutex_lock (&__go_lock__); +#endif +} + +void +gh_manager::do_unlock (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + LeaveCriticalSection (&__go_lock__); +#else + pthread_mutex_unlock (&__go_lock__); +#endif +} + gh_manager *gh_manager::instance = 0; @@ -1877,6 +1953,12 @@ public: Matrix get_screen_size (void) const { return Matrix (1, 2, 0.0); } + + virtual void property_changed (const graphics_handle& h, const std::string& n) { } // do nothing + + virtual void object_created (const graphics_handle&) { } // do nothing + + virtual void object_destroyed (const graphics_handle&) { } // do nothing }; graphics_backend @@ -3591,10 +3673,16 @@ gh_manager::gh_manager (void) : handle_map (), handle_free_list (), next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)) { + init_mutex(); handle_map[0] = graphics_object (new root_figure ()); // Make sure the default backend is registered. graphics_backend::default_backend (); +} + +gh_manager::~gh_manager (void) +{ + cleanup_mutex (); } graphics_handle @@ -3612,6 +3700,10 @@ gh_manager::do_make_graphics_handle (con handle_map[h] = graphics_object (go); if (do_createfcn) go->get_properties ().execute_createfcn (); + // notify backend + graphics_backend backend = go->get_backend (); + if (backend) + backend.object_created (h); } else error ("gh_manager::do_make_graphics_handle: invalid object type `%s'", @@ -3625,8 +3717,14 @@ gh_manager::do_make_figure_handle (doubl { graphics_handle h = val; - handle_map[h] = graphics_object (new figure (h, 0)); - + base_graphics_object* go = new figure (h, 0); + handle_map[h] = graphics_object (go); + + // notify backend + graphics_backend backend = go->get_backend (); + if (backend) + backend.object_created (h); + return h; } @@ -3678,6 +3776,7 @@ Return true if @var{h} is a graphics han Return true if @var{h} is a graphics handle and false otherwise.\n\ @end deftypefn") { + gh_manager::lock_guard guard; octave_value retval; if (args.length () == 1) @@ -3695,6 +3794,8 @@ for the graphics handle @var{h}.\n\ for the graphics handle @var{h}.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; int nargin = args.length (); @@ -3745,6 +3846,8 @@ values or lists respectively.\n\ values or lists respectively.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; octave_value_list vlist; @@ -3815,6 +3918,8 @@ values or lists respectively.\n\ values or lists respectively.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; octave_value_list vlist; @@ -3936,6 +4041,8 @@ Undocumented internal function.\n\ Undocumented internal function.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; if (args.length () > 0) @@ -4064,6 +4171,8 @@ Undocumented internal function.\n\ Undocumented internal function.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value_list retval; if (args.length () == 1) @@ -4112,6 +4221,8 @@ Undocumented internal function.\n\ Undocumented internal function.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; int nargin = args.length (); @@ -4338,6 +4449,7 @@ addlistener (gcf, \"position\", @{@@my_l \n\ @end deftypefn") { + gh_manager::lock_guard guard; octave_value retval; if (args.length () == 3) @@ -4354,6 +4466,7 @@ addlistener (gcf, \"position\", @{@@my_l if (gh.ok ()) { + graphics_object go = gh_manager::get_object (gh); go.add_property_listener (pname, args(2), POSTSET); @@ -4434,6 +4547,7 @@ addproperty (\"my_style\", gcf, \"lineli \n\ @end deftypefn") { + gh_manager::lock_guard guard; octave_value retval; if (args.length () >= 3) diff -r d56511a02884 src/graphics.h.in --- a/src/graphics.h.in Thu Jun 26 16:06:14 2008 -0400 +++ b/src/graphics.h.in Thu Jul 10 20:37:42 2008 +0200 @@ -371,14 +371,10 @@ public: void set_hidden (bool flag) { hidden = flag; } - void set (const octave_value& v, bool do_run = true) - { - do_set (v); - - if (do_run && ! error_state) - run_listeners (POSTSET); - } - + /// Sets property value, notifies backend. + /// If do_run is \b true, runs associated listeners. + void set (const octave_value& v, bool do_run = true); + virtual octave_value get (void) const { error ("get: invalid property \"%s\"", name.c_str ()); @@ -1355,6 +1351,18 @@ public: virtual void set_figure_position (const graphics_handle&, const Matrix&) const { gripe_invalid ("set_figure_position"); } + /// Called when graphics object using this backend changes it's property. + virtual void property_changed (const graphics_handle&, const std::string&) + { gripe_invalid ("property_changed"); } + + /// Caled when new object using this backend is created. + virtual void object_created (const graphics_handle&) + { gripe_invalid ("object_created"); } + + /// Caled when object using this backend is destroyed. + virtual void object_destroyed (const graphics_handle&) + { gripe_invalid ("object_destroyed"); } + private: std::string name; int count; @@ -1434,7 +1442,21 @@ public: void set_figure_position (const graphics_handle& h, const Matrix& pos) const { rep->set_figure_position (h, pos); } + + /// Notifies backend that object't property has changed. + void property_changed (const graphics_handle& h, const std::string& prop) + { rep->property_changed (h, prop); } + /// Notifies backend that new object was created. + void object_created (const graphics_handle& h) + { rep->object_created (h); } + + /// Notifies backend that object was destroyed. + /// This is called only for explicitly deleted object. Are his children are then + /// deleted implicitly and backend isn't notified. + void object_destroyed (const graphics_handle& h) + { rep->object_destroyed (h); } + OCTINTERP_API static graphics_backend default_backend (void); static void register_backend (const graphics_backend& b) @@ -3486,6 +3508,8 @@ protected: public: + ~gh_manager (void); + static bool instance_ok (void) { bool retval = true; @@ -3561,6 +3585,38 @@ public: { return instance_ok () ? instance->do_figure_handle_list () : Matrix (); } + + /// Locks gh_manager instance. Use to protect property access. + /// Locking is recursive, this method can be called multiple times, and a corresponding number of unlock() calls + /// is required in order to unlock mutex. + static void lock (void) + { + if (instance_ok ()) + instance->do_lock (); + } + + /// Unlocks gh_manager instance. + ///\see lock(void) + static void unlock (void) + { + if (instance_ok ()) + instance->do_unlock (); + } + + /// Lock guard object, allows for simple RIIA locking. + class lock_guard + { + public: + lock_guard (void) + { + lock(); + } + + ~lock_guard (void) + { + unlock(); + } + }; private: @@ -3645,6 +3701,14 @@ private: { return figure_list.empty () ? graphics_handle () : figure_list.front (); } + + void do_lock (void); + + void do_unlock (void); + + void init_mutex (void); + + void cleanup_mutex (void); }; |
|
|
[CHANGESET] Backend notifiers.Patch from my previous mail, this time as mercurial changeset.
Maciek Gajewski > Hello > > Assigned patch adds backend notifiers to graphics code. The patch is > cumulative, contains previously presented locking facilities. > > I ask You for review, and - eventually - inclusion into main source tree. > > How does it work: > > Graphics backend implements three methods: > > virtual void property_changed (const graphics_handle&, const std::string&); > virtual void object_created (const graphics_handle&); > virtual void object_destroyed (const graphics_handle&); > (stubs added to gnuplot and fltk backend) > > > - property_changed is called from base_property::set > - object_destroyed is called from gh_manager::do_free > - object_created is called from gh_manager::do_make_graphics_handle > and gh_manager::do_make_figure_handle. > > I've been using this code for last few days with my Qt backend, and it > works good, events are going back and forth between threads, UI is > responsive and changes are visible immediately. You can event do > animations! > > For instance, this code: > > x = [-pi:0.2:pi]; > l = plot( x, sin(x)) > for s = [0:0.1:100]; set(l,'ydata',sin(x+s)); usleep(10000);endfor > > creates figure window with sine wave moving slowly like signal on > oscilloscope screen. Figure remains responsive, can be moved and resized, > while the wave still moves. > > Maciek Gajewski [thread-safe-graphics-notifiers.cset] # HG changeset patch # User Maciej Gajewski <maciej.gajewski0@...> # Date 1215720127 -7200 # Node ID ef779a943728f80cdc867e8f0cfa5acac436c525 # Parent e56bb65186f63d4bf82f8a9308fbef45548df372 graphics, fltk_backend.cc: Locking facilities added for thread safety, backend notifications. diff -r e56bb65186f6 -r ef779a943728 src/DLD-FUNCTIONS/fltk_backend.cc --- a/src/DLD-FUNCTIONS/fltk_backend.cc Wed Jun 25 22:11:07 2008 +0200 +++ b/src/DLD-FUNCTIONS/fltk_backend.cc Thu Jul 10 22:02:07 2008 +0200 @@ -723,6 +723,12 @@ public: sz(1) = Fl::h (); return sz; } + + virtual void property_changed (const graphics_handle& h, const std::string& n) { } // do nothing + + virtual void object_created (const graphics_handle&) { } // do nothing + + virtual void object_destroyed (const graphics_handle&) { } // do nothing }; static bool backend_registered = false; diff -r e56bb65186f6 -r ef779a943728 src/graphics.cc --- a/src/graphics.cc Wed Jun 25 22:11:07 2008 +0200 +++ b/src/graphics.cc Thu Jul 10 22:02:07 2008 +0200 @@ -533,6 +533,26 @@ make_graphics_object_from_type (const ca } // --------------------------------------------------------------------- + +void +base_property::set (const octave_value& v, bool do_run ) +{ + do_set (v); + + // notify backend + graphics_object go = gh_manager::get_object (parent); + if (go) + { + graphics_backend backend = go.get_backend(); + if (backend) + backend.property_changed (parent, name); + } + + // run listeners + if (do_run && ! error_state) + run_listeners (POSTSET); +} + void base_property::run_listeners (listener_mode mode) @@ -1255,7 +1275,13 @@ gh_manager::do_free (const graphics_hand { p->second.get_properties ().set_beingdeleted (true); p->second.get_properties ().execute_deletefcn (); - + // notify backend + graphics_backend backend = p->second.get_backend (); + if (backend) + backend.object_destroyed (h); + // note - this will be valid only for first explicitly deleted object. + // All his children will have unknown backend then. + handle_map.erase (p); if (h.value () < 0) @@ -1268,6 +1294,56 @@ gh_manager::do_free (const graphics_hand error ("graphics_handle::free: can't delete root figure"); } } +#if defined (__WIN32__) && ! defined (__CYGWIN__) +CRITICAL_SECTION __go_lock__; +#else +pthread_mutex_t __go_lock__; +#endif + +void +gh_manager::init_mutex (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + InitializeCriticalSection (&__go_lock__); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); // mutex needs to be recursive + pthread_mutex_init (&__go_lock__, &attr); + pthread_mutexattr_destroy (&attr); +#endif +} + +void +gh_manager::cleanup_mutex (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + DeleteCriticalSection (&__go_lock__); +#else + pthread_mutex_destroy (&__go_lock__); +#endif +} + +void +gh_manager::do_lock (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + EnterCriticalSection (&__go_lock__); +#else + pthread_mutex_lock (&__go_lock__); +#endif +} + +void +gh_manager::do_unlock (void) +{ +#if defined (__WIN32__) && ! defined (__CYGWIN__) + LeaveCriticalSection (&__go_lock__); +#else + pthread_mutex_unlock (&__go_lock__); +#endif +} + gh_manager *gh_manager::instance = 0; @@ -1877,6 +1953,12 @@ public: Matrix get_screen_size (void) const { return Matrix (1, 2, 0.0); } + + virtual void property_changed (const graphics_handle& h, const std::string& n) { } // do nothing + + virtual void object_created (const graphics_handle&) { } // do nothing + + virtual void object_destroyed (const graphics_handle&) { } // do nothing }; graphics_backend @@ -3591,10 +3673,16 @@ gh_manager::gh_manager (void) : handle_map (), handle_free_list (), next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)) { + init_mutex(); handle_map[0] = graphics_object (new root_figure ()); // Make sure the default backend is registered. graphics_backend::default_backend (); +} + +gh_manager::~gh_manager (void) +{ + cleanup_mutex (); } graphics_handle @@ -3612,6 +3700,10 @@ gh_manager::do_make_graphics_handle (con handle_map[h] = graphics_object (go); if (do_createfcn) go->get_properties ().execute_createfcn (); + // notify backend + graphics_backend backend = go->get_backend (); + if (backend) + backend.object_created (h); } else error ("gh_manager::do_make_graphics_handle: invalid object type `%s'", @@ -3625,8 +3717,14 @@ gh_manager::do_make_figure_handle (doubl { graphics_handle h = val; - handle_map[h] = graphics_object (new figure (h, 0)); - + base_graphics_object* go = new figure (h, 0); + handle_map[h] = graphics_object (go); + + // notify backend + graphics_backend backend = go->get_backend (); + if (backend) + backend.object_created (h); + return h; } @@ -3678,6 +3776,7 @@ Return true if @var{h} is a graphics han Return true if @var{h} is a graphics handle and false otherwise.\n\ @end deftypefn") { + gh_manager::lock_guard guard; octave_value retval; if (args.length () == 1) @@ -3695,6 +3794,8 @@ for the graphics handle @var{h}.\n\ for the graphics handle @var{h}.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; int nargin = args.length (); @@ -3745,6 +3846,8 @@ values or lists respectively.\n\ values or lists respectively.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; octave_value_list vlist; @@ -3815,6 +3918,8 @@ values or lists respectively.\n\ values or lists respectively.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; octave_value_list vlist; @@ -3936,6 +4041,8 @@ Undocumented internal function.\n\ Undocumented internal function.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; if (args.length () > 0) @@ -4064,6 +4171,8 @@ Undocumented internal function.\n\ Undocumented internal function.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value_list retval; if (args.length () == 1) @@ -4112,6 +4221,8 @@ Undocumented internal function.\n\ Undocumented internal function.\n\ @end deftypefn") { + gh_manager::lock_guard guard; + octave_value retval; int nargin = args.length (); @@ -4338,6 +4449,7 @@ addlistener (gcf, \"position\", @{@@my_l \n\ @end deftypefn") { + gh_manager::lock_guard guard; octave_value retval; if (args.length () == 3) @@ -4354,6 +4466,7 @@ addlistener (gcf, \"position\", @{@@my_l if (gh.ok ()) { + graphics_object go = gh_manager::get_object (gh); go.add_property_listener (pname, args(2), POSTSET); @@ -4434,6 +4547,7 @@ addproperty (\"my_style\", gcf, \"lineli \n\ @end deftypefn") { + gh_manager::lock_guard guard; octave_value retval; if (args.length () >= 3) diff -r e56bb65186f6 -r ef779a943728 src/graphics.h.in --- a/src/graphics.h.in Wed Jun 25 22:11:07 2008 +0200 +++ b/src/graphics.h.in Thu Jul 10 22:02:07 2008 +0200 @@ -371,14 +371,10 @@ public: void set_hidden (bool flag) { hidden = flag; } - void set (const octave_value& v, bool do_run = true) - { - do_set (v); - - if (do_run && ! error_state) - run_listeners (POSTSET); - } - + /// Sets property value, notifies backend. + /// If do_run is \b true, runs associated listeners. + void set (const octave_value& v, bool do_run = true); + virtual octave_value get (void) const { error ("get: invalid property \"%s\"", name.c_str ()); @@ -1355,6 +1351,18 @@ public: virtual void set_figure_position (const graphics_handle&, const Matrix&) const { gripe_invalid ("set_figure_position"); } + /// Called when graphics object using this backend changes it's property. + virtual void property_changed (const graphics_handle&, const std::string&) + { gripe_invalid ("property_changed"); } + + /// Caled when new object using this backend is created. + virtual void object_created (const graphics_handle&) + { gripe_invalid ("object_created"); } + + /// Caled when object using this backend is destroyed. + virtual void object_destroyed (const graphics_handle&) + { gripe_invalid ("object_destroyed"); } + private: std::string name; int count; @@ -1434,7 +1442,21 @@ public: void set_figure_position (const graphics_handle& h, const Matrix& pos) const { rep->set_figure_position (h, pos); } + + /// Notifies backend that object't property has changed. + void property_changed (const graphics_handle& h, const std::string& prop) + { rep->property_changed (h, prop); } + /// Notifies backend that new object was created. + void object_created (const graphics_handle& h) + { rep->object_created (h); } + + /// Notifies backend that object was destroyed. + /// This is called only for explicitly deleted object. Are his children are then + /// deleted implicitly and backend isn't notified. + void object_destroyed (const graphics_handle& h) + { rep->object_destroyed (h); } + OCTINTERP_API static graphics_backend default_backend (void); static void register_backend (const graphics_backend& b) @@ -3486,6 +3508,8 @@ protected: public: + ~gh_manager (void); + static bool instance_ok (void) { bool retval = true; @@ -3561,6 +3585,38 @@ public: { return instance_ok () ? instance->do_figure_handle_list () : Matrix (); } + + /// Locks gh_manager instance. Use to protect property access. + /// Locking is recursive, this method can be called multiple times, and a corresponding number of unlock() calls + /// is required in order to unlock mutex. + static void lock (void) + { + if (instance_ok ()) + instance->do_lock (); + } + + /// Unlocks gh_manager instance. + ///\see lock(void) + static void unlock (void) + { + if (instance_ok ()) + instance->do_unlock (); + } + + /// Lock guard object, allows for simple RIIA locking. + class lock_guard + { + public: + lock_guard (void) + { + lock(); + } + + ~lock_guard (void) + { + unlock(); + } + }; private: @@ -3645,6 +3701,14 @@ private: { return figure_list.empty () ? graphics_handle () : figure_list.front (); } + + void do_lock (void); + + void do_unlock (void); + + void init_mutex (void); + + void cleanup_mutex (void); }; |
| Free Forum Powered by Nabble | Forum Help |