3 * sushivision copyright (C) 2006-2007 Monty <monty@xiph.org>
5 * sushivision is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * sushivision is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with sushivision; see the file COPYING. If not, write to the
17 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <sys/types.h>
34 #include <sys/types.h>
40 #include "sushi-gtkrc.h"
42 // All API and GTK/GDK access is locked by a single recursive mutex
43 // installed into GDK.
45 // Worker threads exist to handle high latency tasks asynchronously.
46 // The worker threads (with one exception) never touch the main GDK
47 // mutex; any data protected by the main mutex needed for a worker
48 // task is pulled out and encapsulated when that task is set up from a
49 // GTK or API call. The one exception to worker threads and the GDK
50 // lock is expose requests after rendering; in this case, the worker
51 // waits until it can drop all locks, does so, and then issues an
52 // expose request locked by GDK.
54 // All data object memory strutures that are used by the worker
55 // threads and can be manipulated by API/GTK/GDK and the worker
56 // threads re locked by r/w locks; any thread 'inside' the abstraction
57 // read-locks the memory while it is in use. GDK/API access
58 // write-locks this memory to manipulate it (allocation, deallocation,
59 // structural mutation).
61 // lock acquisition order must move to the right:
62 // GDK -> panel_m -> status_m -> plot_m
64 // mutex condm is only for protecting the worker condvar
65 static pthread_mutex_t worker_condm
= PTHREAD_MUTEX_INITIALIZER
;
66 static pthread_cond_t worker_cond
= PTHREAD_COND_INITIALIZER
;
67 static pthread_cond_t idle_cond
= PTHREAD_COND_INITIALIZER
;
68 static sig_atomic_t _sv_exiting
=0;
69 static sig_atomic_t _sv_running
=0;
70 static int wake_pending
= 0;
71 static int idling
= 0;
72 static int num_threads
=0;
74 static int num_proccies(){
75 FILE *f
= fopen("/proc/cpuinfo","r");
81 if (f
== NULL
) return 1;
82 while ((read
= getline(&line
, &len
, f
)) != -1) {
83 if(sscanf(line
,"processor : %d",&arg
)==1)
84 if(arg
+1>num
)num
=arg
+1;
89 fprintf(stderr
,"Number of processors: %d\n",num
);
94 static void *worker_thread(void *dummy
){
99 pthread_rwlock_rdlock(panellist_m
);
100 for(i
=0;i
<_sv_panels
;i
++){
101 sv_panel_t
*p
= _sv_panel_list
[i
];
104 if(_sv_exiting
)break;
105 if(!_sv_running
)break;
108 _sv_spinner_set_busy(p
->spinner
);
109 ret
= _sv_panel_work(p
);
110 if(ret
== STATUS_WORKING
){
112 sv_wake(); // result of this completion might have
113 // generated more work
115 if(ret
== STATUS_IDLE
)
116 _sv_spinner_set_idle(p
->spinner
);
122 // nothing to do, wait
123 pthread_mutex_lock(&worker_condm
);
125 pthread_cond_signal(&idle_cond
);
127 pthread_cond_wait(&worker_cond
,&worker_condm
);
131 pthread_mutex_unlock(&worker_condm
);
134 pthread_mutex_unlock(&worker_condm
);
138 static char *gtkrc_string(){
139 return _SUSHI_GTKRC_STRING
;
142 char *_sv_appname
= NULL
;
143 char *_sv_filename
= NULL
;
144 char *_sv_filebase
= NULL
;
145 char *_sv_dirname
= NULL
;
146 char *_sv_cwdname
= NULL
;
148 static void *eventloop(void *dummy
){
154 // in case there's another mainloop in the main app
156 if(!gtk_main_iteration_do(FALSE
)) // side effect: returns true if
157 // there are no main loops active
164 /* externally visible interface */
167 if((ret
=pthread_key_create(&_sv_dim_key
,NULL
)))
169 if((ret
=pthread_key_create(&_sv_obj_key
,NULL
)))
171 if((ret
=pthread_key_create(&_sv_panel_key
,NULL
)))
174 num_threads
= ((num_proccies()*3)>>2);
177 gtk_init (NULL
,NULL
);
179 _sv_appname
= g_get_prgname ();
180 _sv_cwdname
= getcwd(NULL
,0);
181 _sv_dirname
= strdup(_sv_cwdname
);
184 char *base
= strrchr(_sv_appname
,'/');
190 asprintf(&_sv_filebase
, "%s.sushi",base
);
192 _sv_filebase
= strdup("unnamed.sushi");
193 asprintf(&_sv_filename
,"%s/%s",_sv_dirname
,_sv_filebase
);
195 if (!g_thread_supported ()) g_thread_init (NULL
);
198 gtk_rc_parse_string(gtkrc_string());
199 gtk_rc_add_default_file("sushi-gtkrc");
201 _gtk_button3_fixup();
206 int threads
= num_threads
;
208 pthread_create(&dummy
, NULL
, &worker_thread
,NULL
);
211 // eventloop for panels (in the event we're injected into an app
212 // with no gtk main loop; multiple such loops can coexist)
215 return pthread_create(&dummy
, NULL
, &event_thread
,NULL
);
223 pthread_mutex_lock(&worker_condm
);
224 pthread_cond_wait(&worker_cond
,&worker_condm
);
225 pthread_mutex_unlock(&worker_condm
);
232 pthread_mutex_lock(&worker_condm
);
233 wake_pending
= num_threads
;
234 pthread_cond_broadcast(&worker_cond
);
235 pthread_mutex_unlock(&worker_condm
);
246 if(!gtk_main_iteration_do(FALSE
)) // side effect: returns true if
247 // there are no main loops active
254 int sv_suspend(int block
){
257 // block until all worker threads idle
258 while(idling
< num_threads
){
259 pthread_mutex_lock(&worker_condm
);
260 pthread_cond_wait(&idle_cond
,&worker_condm
);
261 pthread_mutex_unlock(&worker_condm
);
272 void _sv_first_load_warning(int *warn
){
274 fprintf(stderr
,"\nWARNING: The data file to be opened is not a perfect match to\n"
275 "%s.\n\nThis may be because the file is for a different version of\n"
276 "the executable, or because it is is not a save file for \n%s at all.\n\n"
277 "Specific warnings follow:\n\n",_sv_appname
,_sv_appname
);
281 static void set_internal_filename(char *filename
){
282 // save the filename for internal menu seeding purposes
283 char *base
= strrchr(filename
,'/');
285 // filename may include a path; pull it off and apply it toward dirname
288 char *dirbit
= strdup(_sv_filebase
);
289 _sv_filebase
= base
+1;
290 if(g_path_is_absolute(dirbit
)){
293 _sv_dirname
= dirbit
;
297 asprintf(&buf
,"%s/%s",_sv_dirname
,dirbit
);
302 asprintf(&_sv_filename
,"%s/%s",_sv_dirname
,_sv_filebase
);
305 int sv_save(char *filename
){
311 fd
= open(_sv_filename
, O_RDWR
|O_CREAT
, 0660);
319 xmlSaveCtxtPtr xmlptr
=
320 xmlSaveToFd (fd
, "UTF-8", XML_SAVE_FORMAT
);
322 xmlDocPtr doc
= xmlNewDoc((xmlChar
*)"1.0");
323 xmlNodePtr root_node
= xmlNewNode(NULL
, (xmlChar
*)_sv_appname
);
324 xmlDocSetRootElement(doc
, root_node
);
326 // dimension values are independent of panel
327 for(i
=0;i
<_sv_dimensions
;i
++)
328 ret
|=_sv_dim_save(_sv_dimension_list
[i
], root_node
);
330 // objectives have no independent settings
332 // panel settings (by panel)
333 for(i
=0;i
<_sv_panels
;i
++)
334 ret
|=_sv_panel_save(_sv_panel_list
[i
], root_node
);
336 if(xmlSaveDoc(xmlptr
,doc
)<0)
339 if(xmlSaveClose(xmlptr
)<0)
344 if(ret
==0) set_internal_filename(filename
);
354 int sv_load(filename
){
356 xmlNode
*root
= NULL
;
362 fd
= open(_sv_filename
, O_RDONLY
);
365 doc
= xmlReadFd(fd
, NULL
, NULL
, 0);
373 root
= xmlDocGetRootElement(doc
);
375 // piggyback off undo (as it already goes through the trouble of
376 // doing correct unrolling, which can be tricky)
378 // if this instance has an undo stack, pop it all, then log current state into it
383 _sv_undo_t
*u
= _sv_undo_stack
[_sv_undo_level
];
386 for(i
=0;i
<_sv_dimensions
;i
++){
387 sv_dim_t
*d
= _sv_dimension_list
[i
];
389 xmlNodePtr dn
= _xmlGetChildI(root
,"dimension","number",d
->number
);
391 _sv_first_load_warning(&warn
);
392 fprintf(stderr
,"Could not find data for dimension \"%s\" in save file.\n",
395 warn
|= _sv_dim_load(d
,u
,dn
,warn
);
402 for(i
=0;i
<_sv_panels
;i
++){
403 sv_panel_t
*p
= _sv_panel_list
[i
];
405 xmlNodePtr pn
= _xmlGetChildI(root
,"panel","number",p
->number
);
407 _sv_first_load_warning(&warn
);
408 fprintf(stderr
,"Could not find data for panel \"%s\" in save file.\n",
411 warn
|= _sv_panel_load(p
,u
->panels
+i
,pn
,warn
);
417 // if any elements are unclaimed, warn
418 xmlNodePtr node
= root
->xmlChildrenNode
;
420 if (node
->type
== XML_ELEMENT_NODE
) {
421 xmlChar
*name
= xmlGetProp(node
, (xmlChar
*)"name");
422 _sv_first_load_warning(&warn
);
424 fprintf(stderr
,"Save file contains data for nonexistant object \"%s\".\n",
428 fprintf(stderr
,"Save file root node contains an extra unknown elements.\n");
433 if(ret
==0) set_internal_filename(filename
);
435 // effect the loaded values