[xiph-commits] r13797 - trunk/sushivision
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Thu Sep 13 16:29:43 PDT 2007
Author: xiphmont
Date: 2007-09-13 16:29:43 -0700 (Thu, 13 Sep 2007)
New Revision: 13797
Modified:
trunk/sushivision/gtksucks.c
trunk/sushivision/gtksucks.h
trunk/sushivision/internal.h
trunk/sushivision/main.c
trunk/sushivision/panel-2d.c
trunk/sushivision/panel-2d.h
Log:
Modified: trunk/sushivision/gtksucks.c
===================================================================
--- trunk/sushivision/gtksucks.c 2007-09-13 23:05:16 UTC (rev 13796)
+++ trunk/sushivision/gtksucks.c 2007-09-13 23:29:43 UTC (rev 13797)
@@ -196,6 +196,54 @@
}
/**********************************************************************/
+/* fixup number 3: GDK uses whatever default mutex type offered by the
+ system, and this usually means non-recursive ('fast') mutextes.
+ The problem with this is that gdk_threads_enter() and
+ gdk_threads_leave() cannot be used in any call originating from the
+ main loop, but are required in calls from idle handlers and other
+ threads. In effect we would need seperate identical versions of
+ each widget method, one locked, one unlocked, depending on where
+ the call originated. Eliminate this problem by installing a
+ recursive mutex. */
+
+static pthread_mutex_t gdkm;
+static pthread_mutexattr_t gdkma;
+static int depth = 0;
+static int firstunder = 0;
+
+void gdk_lock(void){
+ pthread_mutex_lock(&gdkm);
+ depth++;
+}
+
+void gdk_unlock(void){
+ depth--;
+ if(depth<0){
+ if(!firstunder){ // annoying detail of gtk locking; in apps that
+ // don't normally use threads, onr does not lock before entering
+ // mainloop; in apps that do thread, the mainloop must be
+ // locked. We can't tell which situation was in place before
+ // setting up our own threading, so allow one refcount error
+ // which we assume was the unlocked mainloop of a normally
+ // unthreaded gtk app.
+ firstunder++;
+ depth=0;
+ }else{
+ fprintf(stderr,"Internal locking error; refcount < 0. Dumping core for debugging\n");
+ abort();
+ }
+ }else
+ pthread_mutex_unlock(&gdkm);
+}
+
+void _gtk_mutex_fixup(){
+ pthread_mutexattr_init(&gdkma);
+ pthread_mutexattr_settype(&gdkma,PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&gdkm,&gdkma);
+ gdk_threads_set_lock_functions(gdk_lock,gdk_unlock);
+}
+
+/**********************************************************************/
/* Not really a fixup; generate menus that declare what the keyboard
shortcuts are */
Modified: trunk/sushivision/gtksucks.h
===================================================================
--- trunk/sushivision/gtksucks.h 2007-09-13 23:05:16 UTC (rev 13796)
+++ trunk/sushivision/gtksucks.h 2007-09-13 23:29:43 UTC (rev 13797)
@@ -24,9 +24,7 @@
extern void _gtk_widget_set_sensitive_fixup(GtkWidget *w, gboolean state);
extern void _gtk_widget_remove_events (GtkWidget *widget, gint events);
-extern void _gtk_button3_fixup();
-extern void _gtk_mutex_fixup();
-extern pthread_mutex_t *_gtk_get_mutex();
+extern void _gtk_button3_fixup(void);
extern GtkWidget *_gtk_menu_new_twocol(GtkWidget *bind,
_sv_propmap_t **items,
@@ -40,4 +38,8 @@
extern void _gtk_box_freeze_child (GtkBox *box, GtkWidget *child);
extern void _gtk_box_unfreeze_child (GtkBox *box, GtkWidget *child);
+extern void _gtk_mutex_fixup(void);
+extern void _gdk_lock(void);
+extern void _gdk_unlock(void);
+
#endif
Modified: trunk/sushivision/internal.h
===================================================================
--- trunk/sushivision/internal.h 2007-09-13 23:05:16 UTC (rev 13796)
+++ trunk/sushivision/internal.h 2007-09-13 23:29:43 UTC (rev 13797)
@@ -29,38 +29,6 @@
#include <libxml/tree.h>
#include "sushivision.h"
-/* locking *****************************************************/
-
-// strict mutex acquisation ordering:
-// gdk -> panel -> objective -> dimension -> scale
-// functions below check/enforce
-
-extern void _sv_gdk_lock_i(const char *func, int line);
-extern void _sv_gdk_unlock_i(const char *func, int line);
-extern void _sv_panel_lock_i(const char *func, int line);
-extern void _sv_panel_unlock_i(const char *func, int line);
-extern void _sv_objective_lock_i(const char *func, int line);
-extern void _sv_objective_unlock_i(const char *func, int line);
-extern void _sv_dimension_lock_i(const char *func, int line);
-extern void _sv_dimension_unlock_i(const char *func, int line);
-extern void _sv_scale_lock_i(const char *func, int line);
-extern void _sv_scale_unlock_i(const char *func, int line);
-
-#define gdk_lock() _sv_gdk_lock_i(__FUNCTION__,__LINE__);
-#define gdk_unlock() _sv_gdk_lock_i(__FUNCTION__,__LINE__);
-
-#define panel_lock() _sv_panel_lock_i(__FUNCTION__,__LINE__);
-#define panel_unlock() _sv_panel_lock_i(__FUNCTION__,__LINE__);
-
-#define obj_lock() _sv_objective_lock_i(__FUNCTION__,__LINE__);
-#define obj_unlock() _sv_objective_lock_i(__FUNCTION__,__LINE__);
-
-#define dim_lock() _sv_dimension_lock_i(__FUNCTION__,__LINE__);
-#define dim_unlock() _sv_dimension_lock_i(__FUNCTION__,__LINE__);
-
-#define scale_lock() _sv_scale_lock_i(__FUNCTION__,__LINE__);
-#define scale_unlock() _sv_scale_lock_i(__FUNCTION__,__LINE__);
-
typedef struct {
char *s;
double v;
Modified: trunk/sushivision/main.c
===================================================================
--- trunk/sushivision/main.c 2007-09-13 23:05:16 UTC (rev 13796)
+++ trunk/sushivision/main.c 2007-09-13 23:29:43 UTC (rev 13797)
@@ -39,277 +39,48 @@
#include "internal.h"
#include "sushi-gtkrc.h"
-// strict mutex acquisation ordering: gdk -> panel -> objective -> dimension -> scale
-typedef struct {
- int holding_gdk;
+// The locking model in sushivision is as simple as possible given the
+// computational and rendering concurrency.
- int holding_panel;
- const char *func_panel;
- int line_panel;
+// All API and GTK/GDK access is locked by a single recursive mutex
+// installed into GDK.
- int holding_obj;
- const char *func_obj;
- int line_obj;
+// Worker threads exist to handle high latency tasks asynchronously.
+// Tasks fall into three categories: data computation, plane
+// rendering, and scale/legend rendering. The worker threads (with
+// one exception) never touch the main GDK mutex; any data protected
+// by the main mutex needed for a task is pulled out and encapsulated
+// when that task is set up from a GTK or API call. The one exception
+// is expose requests after rendering; in this case, the worker waits
+// until it can drop all locks (usually as the very last step), does
+// so, and then issues an expose request locked by GDK.
- int holding_dim;
- const char *func_dim;
- int line_dim;
+// All data objects that can be touched simultaneously by API/GTK/GDK
+// and the worker threads must also be locked. This includes the
+// global panel list, each panel, each plane within the panel (and
+// subunits of the planes), the plot widget's canvas buffer, and the
+// plot widget's other render access. Plot widget access is guarded
+// by standard mutexes. The panel lists, panels and planes require
+// greater concurrency and use read/write mutexes.
- int holding_scale;
- const char *func_scale;
- int line_scale;
+// lock acquisition order must move to the right:
+// GDK -> panel_list -> panel -> plane, plane_internal -> plot_main -> plot_bg
+//
+// Multiple panels (if needed) must be locked in order of list
+// Multiple planes (if needed) must be locked in order of list
+// Each plane type has an internal order for locking internal subunits
-} mutexcheck;
+// Memory pointers/structures are protected by a r/w lock that is held
+// by any thread 'entering the abstraction' for the duration the
+// thread is assuming the structure will continue to exist. It is
+// held even during long-latency operations to guarantee memory
+// consistency.
-static pthread_mutex_t panel_m = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t obj_m = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t dim_m = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t scale_m = PTHREAD_MUTEX_INITIALIZER;
-static pthread_key_t _sv_mutexcheck_key;
+// The data inside a memory structure is protected by a second r/w
+// lock inside the structure that is dropped when possible during
+// long-duration operations.
-/**********************************************************************/
-/* GDK uses whatever default mutex type offered by the system, and
- this usually means non-recursive ('fast') mutextes. The problem
- with this is that gdk_threads_enter() and gdk_threads_leave()
- cannot be used in any call originating from the main loop, but are
- required in calls from idle handlers and other threads. In effect
- we would need seperate identical versions of each widget method,
- one locked, one unlocked, depending on where the call originated.
- Eliminate this problem by installing a recursive mutex. */
-
-static pthread_mutex_t gdk_m;
-static pthread_mutexattr_t gdk_ma;
-static int gdk_firstunder = 0;
-
-void _sv_gdk_lock_i(const char *func, int line){
- mutexcheck *check = pthread_getspecific(_sv_mutexcheck_key);
- const char *m=NULL;
- const char *f=NULL;
- int l=-1;
-
- if(!check){
- check = calloc(1,sizeof(*check));
- pthread_setspecific(_sv_mutexcheck_key, (void *)check);
- }
-
- if(check->holding_panel){ m="panel"; f=check->func_panel; l=check->line_panel; }
- if(check->holding_obj){ m="objective"; f=check->func_obj; l=check->line_obj; }
- if(check->holding_dim){ m="dimension"; f=check->func_dim; l=check->line_dim; }
- if(check->holding_scale){ m="scale"; f=check->func_scale; l=check->line_scale; }
-
- if(f){
- if(func)
- fprintf(stderr,"sushivision: Out of order locking error.\n"
- "\tTrying to acquire gdk lock in %s line %d,\n"
- "\t%s lock already held from %s line %d.\n",
- func,line,m,f,l);
- else
- fprintf(stderr,"sushivision: Out of order locking error.\n"
- "\tTrying to acquire gdk lock, %s lock already held\n"
- "\tfrom %s line %d.\n",
- m,f,l);
- }
-
- pthread_mutex_lock(&gdk_m);
- check->holding_gdk++;
-
-}
-
-void _sv_gdk_unlock_i(const char *func, int line){
- mutexcheck *check = pthread_getspecific(_sv_mutexcheck_key);
-
- if(!check){
- if(!gdk_firstunder){ // annoying detail of gtk locking; in apps that
- // don't normally use threads, one does not lock before entering
- // mainloop; in apps that do thread, the mainloop must be
- // locked. We can't tell which situation was in place before
- // setting up our own threading, so allow one refcount error
- // which we assume was the unlocked mainloop of a normally
- // unthreaded gtk app.
- check = calloc(1,sizeof(*check));
- pthread_setspecific(_sv_mutexcheck_key, (void *)check);
- gdk_firstunder++;
- }else{
- if(func)
- fprintf(stderr,"sushivision: locking error.\n"
- "\tTrying to unlock gdk when no gdk lock held\n"
- "\tin %s line %d.\n",
- func,line);
- else
- fprintf(stderr,"sushivision: locking error.\n"
- "\tTrying to unlock gdk when no gdk lock held.\n");
-
- }
- }else{
- check->holding_gdk--;
- pthread_mutex_unlock(&gdk_m);
- }
-}
-
-// dumbed down version to pass into gdk
-static void replacement_gdk_lock(void){
- _sv_gdk_lock_i(NULL,-1);
-}
-
-static void replacement_gdk_unlock(void){
- _sv_gdk_unlock_i(NULL,-1);
-}
-
-static mutexcheck *lockcheck(){
- mutexcheck *check = pthread_getspecific(_sv_mutexcheck_key);
- if(!check){
- check = calloc(1,sizeof(*check));
- pthread_setspecific(_sv_mutexcheck_key, (void *)check);
- }
- return check;
-}
-
-void _sv_panel_lock_i(const char *func, int line){
- mutexcheck *check = lockcheck();
- const char *m=NULL, *f=NULL;
- int l=-1;
-
- if(check->holding_obj){ m="objective"; f=check->func_obj; l=check->line_obj; }
- if(check->holding_dim){ m="dimension"; f=check->func_dim; l=check->line_dim; }
- if(check->holding_scale){ m="scale"; f=check->func_scale; l=check->line_scale; }
-
- if(f)
- fprintf(stderr,"sushivision: Out of order locking error.\n"
- "\tTrying to acquire panel lock in %s line %d,\n"
- "\t%s lock already held from %s line %d.\n",
- func,line,m,f,l);
-
- pthread_mutex_lock(&panel_m);
- check->holding_panel++;
- check->func_panel = func;
- check->line_panel = line;
-
-}
-
-void _sv_panel_unlock_i(const char *func, int line){
- mutexcheck *check = pthread_getspecific(_sv_mutexcheck_key);
-
- if(!check || check->holding_panel<1){
- fprintf(stderr,"sushivision: locking error.\n"
- "\tTrying to unlock panel when no panel lock held\n"
- "\tin %s line %d.\n",
- func,line);
- }else{
-
- check->holding_panel--;
- check->func_panel = "(unknown)";
- check->line_panel = -1;
- pthread_mutex_unlock(&panel_m);
-
- }
-}
-
-void _sv_objective_lock_i(const char *func, int line){
- mutexcheck *check = lockcheck();
- const char *m=NULL, *f=NULL;
- int l=-1;
-
- if(check->holding_dim){ m="dimension"; f=check->func_dim; l=check->line_dim; }
- if(check->holding_scale){ m="scale"; f=check->func_scale; l=check->line_scale; }
-
- if(f)
- fprintf(stderr,"sushivision: Out of order locking error.\n"
- "\tTrying to acquire objective lock in %s line %d,\n"
- "\t%s lock already held from %s line %d.\n",
- func,line,m,f,l);
-
- pthread_mutex_lock(&obj_m);
- check->holding_obj++;
- check->func_obj = func;
- check->line_obj = line;
-
-}
-
-void _sv_objective_unlock_i(const char *func, int line){
- mutexcheck *check = pthread_getspecific(_sv_mutexcheck_key);
-
- if(!check || check->holding_obj<1){
- fprintf(stderr,"sushivision: locking error.\n"
- "\tTrying to unlock objective when no objective lock held\n"
- "\tin %s line %d.\n",
- func,line);
- }else{
-
- check->holding_obj--;
- check->func_obj = "(unknown)";
- check->line_obj = -1;
- pthread_mutex_unlock(&obj_m);
-
- }
-}
-
-void _sv_dimension_lock_i(const char *func, int line){
- mutexcheck *check = lockcheck();
- const char *m=NULL, *f=NULL;
- int l=-1;
-
- if(check->holding_scale){ m="scale"; f=check->func_scale; l=check->line_scale; }
-
- if(f)
- fprintf(stderr,"sushivision: Out of order locking error.\n"
- "\tTrying to acquire dimension lock in %s line %d,\n"
- "\t%s lock already held from %s line %d.\n",
- func,line,m,f,l);
-
- pthread_mutex_lock(&dim_m);
- check->holding_dim++;
- check->func_dim = func;
- check->line_dim = line;
-
-}
-
-void _sv_dimension_unlock_i(const char *func, int line){
- mutexcheck *check = pthread_getspecific(_sv_mutexcheck_key);
-
- if(!check || check->holding_dim<1){
- fprintf(stderr,"sushivision: locking error.\n"
- "\tTrying to unlock dimension when no dimension lock held\n"
- "\tin %s line %d.\n",
- func,line);
- }else{
-
- check->holding_dim--;
- check->func_dim = "(unknown)";
- check->line_dim = -1;
- pthread_mutex_unlock(&dim_m);
-
- }
-}
-
-void _sv_scale_lock_i(const char *func, int line){
- mutexcheck *check = lockcheck();
-
- pthread_mutex_lock(&scale_m);
- check->holding_scale++;
- check->func_scale = func;
- check->line_scale = line;
-
-}
-
-void _sv_scale_unlock_i(const char *func, int line){
- mutexcheck *check = pthread_getspecific(_sv_mutexcheck_key);
-
- if(!check || check->holding_scale<1){
- fprintf(stderr,"sushivision: locking error.\n"
- "\tTrying to unlock scale when no scale lock held\n"
- "\tin %s line %d.\n",
- func,line);
- }else{
-
- check->holding_scale--;
- check->func_scale = "(unknown)";
- check->line_scale = -1;
- pthread_mutex_unlock(&scale_m);
-
- }
-}
-
-// mutex condm is only for protecting the condvar
+// mutex condm is only for protecting the worker condvar
static pthread_mutex_t worker_condm = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t worker_cond = PTHREAD_COND_INITIALIZER;
sig_atomic_t _sv_exiting=0;
@@ -338,12 +109,12 @@
_sv_exiting = 1;
_sv_wake_workers();
- gdk_lock();
+ _gdk_lock();
if(!gtk_main_iteration_do(FALSE)) // side effect: returns true if
// there are no main loops active
gtk_main_quit();
- gdk_unlock();
+ _gdk_unlock();
}
static int num_proccies(){
@@ -485,11 +256,7 @@
num_threads = ((num_proccies()*3)>>2);
- pthread_mutexattr_init(&gdk_ma);
- pthread_mutexattr_settype(&gdk_ma,PTHREAD_MUTEX_RECURSIVE);
- pthread_mutex_init(&gdk_m,&gdk_ma);
- gdk_threads_set_lock_functions(replacement_gdk_lock,
- replacement_gdk_unlock);
+ _gtk_mutex_fixup();
gtk_init (NULL,NULL);
return 0;
Modified: trunk/sushivision/panel-2d.c
===================================================================
--- trunk/sushivision/panel-2d.c 2007-09-13 23:05:16 UTC (rev 13796)
+++ trunk/sushivision/panel-2d.c 2007-09-13 23:29:43 UTC (rev 13797)
@@ -32,11 +32,40 @@
#include <cairo-ft.h>
#include "internal.h"
-/* helper functions for performing progressive computation and rendering */
+/* a panel is essentially four things:
+
+ A collection of UI widgets
+ A collection of graphical planes
+ Code for managing concurrent data computation for planes
+ Code for managing concurrent rendering operations for planes
+*/
+
+
+
+/* from API or GTK thread */
+void _sv_panel_recompute(sv_panel_t *p){
+ gdk_lock();
+
+ // collect and cache the data a panel needs to recompute
+ // dimension value snapshots
+ // pending axes scalespaces
+
+ // collect and cache the data a panel needs to remap
+ //
+
+ gdk_unlock();
+}
+
+
static void _sv_plane2d_set_recompute(_sv_plane2d_t *z){
- pthread_mutex_lock(z->data_m);
+ pthread_rwlock_wrlock(z->data_m);
+ pthread_rwlock_wrlock(z->image_m);
+
+
+ pthread_rwlock_unlock(z->data_m);
+ pthread_rwlock_unlock(z->image_m);
}
static void _sv_plane2d_set_remap(){
Modified: trunk/sushivision/panel-2d.h
===================================================================
--- trunk/sushivision/panel-2d.h 2007-09-13 23:05:16 UTC (rev 13796)
+++ trunk/sushivision/panel-2d.h 2007-09-13 23:29:43 UTC (rev 13797)
@@ -19,16 +19,8 @@
*
*/
-// plane mutex policy:
-//
-// gdk_m -> panel_m -> obj_m -> dim_m -> scale_m
-// |
-// -> z.data_m -> z.image_m -> bg.image_m
-// |
-// -> z.status_m -> bg.status_m
+// plane mutex locking order: forward in plane list, down the plane struct
-#define PLANE_Z 1
-
typedef union _sv_plane _sv_plane_t;
typedef struct _sv_plane_proto _sv_plane_proto_t;
typedef struct _sv_plane_bg _sv_plane_bg_t;
@@ -36,35 +28,34 @@
struct _sv_plane_proto {
// in common with all plane types
- // common fields locked via panel
- int plane_type;
- sv_obj_t *o;
- _sv_plane_t *share_next;
- _sv_plane_t *share_prev;
+
+ // only GTK/API manipulates the common data
+ // GTK/API read access: GDK lock
+ // GTK/API write access: GDK lock -> panel.memlock(write);
+ // worker read access: panel.memlock(read)
+ int plane_type;
+ sv_obj_t *o;
+ _sv_plane_t *share_next;
+ _sv_plane_t *share_prev;
_sv_panel2d_t *panel;
};
-// bg plane mutex policy: [mutexes from one other plane] -> image -> status -> [back to other plane]
struct _sv_plane_bg {
- // in common with all plane types
- // common fields locked via panel
- int plane_type;
- sv_obj_t *o;
- _sv_plane_t *share_next;
- _sv_plane_t *share_prev;
- _sv_panel2d_t *panel;
+ int plane_type;
+ sv_obj_t *o;
+ _sv_plane_t *share_next;
+ _sv_plane_t *share_prev;
+ _sv_panel2d_t *panel;
- // image data
- _sv_ucolor_t *image; // panel size
- int image_serialno;
- int image_waiting;
- int image_incomplete;
- int image_nextline;
- pthread_mutex_t image_m;
+ // image data and concurrency tracking
+ _sv_ucolor_t *image; // panel size
+ int image_serialno;
+ int image_waiting;
+ int image_incomplete;
+ int image_nextline;
+ unsigned char *image_status; // rendering flags
+ pthread_rwlock_t image_m;
- // concurrency tracking
- unsigned char *line_status; // rendering flags
- pthread_mutex_t status_m;
};
struct sv_zmap {
@@ -85,38 +76,42 @@
struct _sv_plane_2d {
// in common with all plane types
// common fields locked via panel
- int plane_type; // == Z
- sv_obj_t *o;
- _sv_plane_t *share_next;
- _sv_plane_t *share_prev;
- _sv_panel2d_t *panel;
+ int plane_type; // == Z
+ sv_obj_t *o;
+ _sv_plane_t *share_next;
+ _sv_plane_t *share_prev;
+ _sv_panel2d_t *panel;
// subtype
// objective data
- float *data; // data size
- int data_serialno;
- int xscale_waiting;
- int xscale_incomplete;
- int yscale_waiting;
- int yscale_incomplete;
- int compute_waiting;
- int compute_incomplete;
- int data_nextline;
- pthread_mutex_t data_m;
+ float *data; // data size
+ _sv_scalespace_t data_x;
+ _sv_scalespace_t data_y;
+ _sv_scalespace_t data_x_it;
+ _sv_scalespace_t data_y_it;
+ int data_serialno;
+ int data_xscale_waiting;
+ int data_xscale_incomplete;
+ int data_yscale_waiting;
+ int data_yscale_incomplete;
+ int data_compute_waiting;
+ int data_compute_incomplete;
+ int data_nextline;
+ unsigned char *data_flags;
+ pthread_mutex_t data_m;
// image plane
- _sv_ucolor_t *image; // panel size;
- int image_serialno;
- struct sv_zmap image_map;
- int image_waiting;
- int image_incomplete;
- int image_nextline;
- pthread_mutex_t image_m;
+ _sv_ucolor_t *image; // panel size;
+ int image_serialno;
+ _sv_scalespace_t image_x;
+ _sv_scalespace_t image_y;
+ struct sv_zmap image_map;
+ int image_waiting;
+ int image_incomplete;
+ int image_nextline;
+ unsigned char *image_flags;
+ pthread_mutex_t image_m;
- // concurrency tracking
- unsigned char *lineflags; // rendering flags
- pthread_mutex_t status_m;
-
// ui elements; use gdk lock
_sv_mapping_t *mapping;
_sv_slider_t *scale;
@@ -133,6 +128,8 @@
} _sv_plane;
typedef struct {
+ pthread_rwlock_t memlock;
+ pthread_rwlock_t datalock;
GtkWidget *obj_table;
GtkWidget *dim_table;
More information about the commits
mailing list