# HG changeset patch # User Maciej Gajewski # 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); };