1 /*
2 Copyright 2007, 2008, 2009, 2010 Geyer Klaus
3
4 This file is part of Cat'sEyE.
5
6 Cat'sEyE is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Cat'sEyE is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Cat'sEyE. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #ifndef NO_GIO
20
21 #include <gtk/gtk.h>
22 #ifndef NO_GIO
23 #include <gio/gio.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h> //e.g. chdir, usleep
28
29 #include "helpers.h"
30 #include "Lists.h"
31 #include "userdata.h"
32 #include "myfm.h"
33 #include "automaticUpdate.h"
34 #include "mytrace.h"
35
36 #include "allInclude.h"
37
38
39 #define AUTOUPDATE_MINTIME 500000 //us
40 #define MONITORRATELIMIT 5 //ms
41
42
43 struct autoUpdateList *g_listAutoUpdateList;
44
45
autoUpdateList_UpdateThread(struct AllInformationAllUseStruct * info)46 void autoUpdateList_UpdateThread (struct AllInformationAllUseStruct *info){
47 //we will not update emediatly but waiting for AUTOUPDATE_MINTIME useconds before udating.
48 //the monitor signal can check for the flag in buflist
49 //infostruct:
50 //Info_1 = struct autoUpdateList
51 //Info_2 = monitor
52 TRACEIT(11,"autoUpdateList_UpdateThread start");
53
54 if (info == NULL){
55 TRACEIT (2, "autoUpdateList_UpdateThread parameter NULL");
56 g_thread_exit(0);
57 }
58
59 struct autoUpdateList *buflist = (struct autoUpdateList *)(info->Info_1);
60 GFileMonitor *monitor = (GFileMonitor *)info->Info_2;
61 free (info);
62 info = NULL;
63
64 if (buflist == NULL){
65 TRACEIT (2, "autoUpdateList_UpdateThread parameter 2 NULL");
66 g_thread_exit(0);
67 }
68
69 usleep (AUTOUPDATE_MINTIME);
70
71 if ( 1 != autoUpdateList_ValidateItem (buflist, monitor)){
72 TRACEIT(11,"autoUpdateList_UpdateThread end, invalid");
73 g_thread_exit(0);
74 }
75
76 myWrap_gdk_threads_enter ("autoUpdateList_UpdateThread");
77 updateObjectsViewingFolder(buflist->gstrPath->str, TRUE);
78 myWrap_gdk_threads_leave ("autoUpdateList_UpdateThread");
79
80 buflist->iUpdateThreadStarted=0;
81
82 TRACEIT(11,"autoUpdateList_UpdateThread end");
83 g_thread_exit(0);
84 }
85
86
autoUpdateList_monitoredDirChanged(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,struct autoUpdateList * buflist)87 void autoUpdateList_monitoredDirChanged (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, struct autoUpdateList *buflist){
88 TRACEIT(11,"autoUpdateList_monitoredDirChanged start");
89 GError *Error=NULL;
90
91 if ( 1 != autoUpdateList_ValidateItem (buflist, monitor)){
92 TRACEIT(2,"autoUpdateList_monitoredDirChanged invalid list item");
93 return;
94 }
95
96 if (buflist->iUpdateThreadStarted == 0){
97 if ( TRUE == g_mutex_trylock (buflist->mtxJoinUpdateThread)){
98 if (buflist->gthreadUpdateThread != NULL){
99 g_thread_join (buflist->gthreadUpdateThread);
100 buflist->gthreadUpdateThread = NULL;
101 }
102 g_mutex_unlock (buflist->mtxJoinUpdateThread);
103 }
104 else {
105 TRACEIT(11,"autoUpdateList_monitoredDirChanged end 1");
106 return;
107 }
108
109 struct AllInformationAllUseStruct *info = malloc (sizeof (struct AllInformationAllUseStruct));
110 if (info == NULL){
111 TRACEIT(2,"autoUpdateList_monitoredDirChanged unable to allocate memory");
112 return;
113 }
114 info->Info_1 = (void *)buflist;
115 info->Info_2 = (void *)monitor;
116
117 buflist->iUpdateThreadStarted = 1; //this should be close to the thread creating call
118 buflist->gthreadUpdateThread = g_thread_create((GThreadFunc)autoUpdateList_UpdateThread, (void *)info , TRUE, &Error);
119
120 if (Error != NULL){
121 buflist->iUpdateThreadStarted = 0;
122 free (info);
123 TRACEIT(2,"autoUpdateList_monitoredDirChanged error starting update thread:");
124 TRACEIT(2,Error->message);
125 g_error_free (Error);
126 Error = NULL;
127 }
128 }
129
130 //printf("monitor: update signal end %s\n",buflist->gstrPath->str);
131
132 TRACEIT(11,"autoUpdateList_monitoredDirChanged end");
133 }
134
135
autoUpdateList_DeleteObject(struct autoUpdateList * Object)136 void autoUpdateList_DeleteObject(struct autoUpdateList *Object){
137 TRACEIT(10,"autoUpdateList_DeleteObject start");
138 if (Object == NULL){
139 TRACEIT (2, "autoUpdateList_DeleteObject parameter NULL");
140 return;
141 }
142 GError *Error=NULL;
143
144 if (Object->PrevItem != NULL){
145 Object->PrevItem->NextItem = Object->NextItem;
146 }
147 else{
148 g_listAutoUpdateList = Object->NextItem;
149 if (g_listAutoUpdateList != NULL){
150 g_listAutoUpdateList->PrevItem = NULL;
151 }
152 }
153
154 if (Object->NextItem != NULL){
155 Object->NextItem->PrevItem = Object->PrevItem;
156 }
157 //printf("autoUpdateList_DeleteObject: going to start delete thread\n");
158 //g_thread_create((GThreadFunc)autoUpdateList_deleteItemMembersAndItem, (void *)Object , FALSE, &Error);
159 g_thread_create((GThreadFunc)autoUpdateList_deleteItemMembersAndItem_threadWrapper, (void *)Object , FALSE, &Error);
160
161 if (Error != NULL){
162 TRACEIT(2,"autoUpdateList_DeleteObject error starting delete thread:");
163 TRACEIT(2,Error->message);
164 g_error_free (Error);
165 Error = NULL;
166 }
167 //why we create this thread:
168 //remember the bug where we unpacked some tar file which created a directory which we entered emediatly after it apeared and
169 //CatsEyE froze?
170 //The error was: while such a directory creation fires 4 monitor-callbacks waiting 800ms (default now its 5ms) between each
171 //callback call. The user (we) entered the directory that leads to the deletion of the monitor in some of our UpdateViewWith ...
172 //functions. However, this functions are thread save because they are signal driven (remember the mainloop). But the monitor-
173 //callback is called in between and the update thread has allready started, the update thread then, tries to gdk_thread_enter
174 //but can not because the UpdateView... fucntion is running. But the UpdateView function waits for the monitor list to delete
175 //this monitor, and the function to delete this monitor waits for the updateThread to finish. And there it is, the deadlock.
176
177 //TZo solve this, I decided to create anohter thread when deleting a monitor, the monitor will be extracted from the list
178 //and the thread will delete the item after the update thread has quit. In this case, the deletefunction will not block
179 //if the updatethread is running and the UpdateView... function can continue.
180
181 //But, in this case, the update thread has to validate the monitor item before using it, this is done too.
182
183
184 TRACEIT(10,"autoUpdateList_DeleteObject ends");
185 return;
186 }
187
188
autoUpdateList_deleteItemMembersAndItem_threadWrapper(struct autoUpdateList * item)189 void autoUpdateList_deleteItemMembersAndItem_threadWrapper (struct autoUpdateList *item){
190 autoUpdateList_deleteItemMembersAndItem (item);
191 g_thread_exit(0);
192 }
193
194
autoUpdateList_deleteItemMembersAndItem(struct autoUpdateList * item)195 void autoUpdateList_deleteItemMembersAndItem (struct autoUpdateList *item){
196 //this function will delete (free) all the members of item and after that will delete item itself
197 //this function WILL NOT care about the list and linked items, this is the matter of the functions calling this
198 //please mind, that this function could be a thread
199 TRACEIT(10,"autoUpdateList_deleteItemMembersAndItem start");
200 if (item == NULL){
201 TRACEIT(2,"autoUpdateList_deleteItemMembersAndItem parameter NULL");
202 return;
203 }
204
205 if (FALSE == g_file_monitor_cancel ( item->monitor )){
206 TRACEIT(2,"autoUpdateList_DeleteObject monitor could not be cancelled");
207 }
208
209 if (item->mtxJoinUpdateThread != NULL){
210 g_mutex_lock (item->mtxJoinUpdateThread);
211 if (item->gthreadUpdateThread != NULL){
212 g_thread_join (item->gthreadUpdateThread);
213 item->gthreadUpdateThread = NULL;
214 }
215 g_mutex_unlock (item->mtxJoinUpdateThread);
216 }
217 //printf("deleting monitor for: %s\n",item->gstrPath->str);
218
219 g_string_free(item->gstrPath, TRUE);
220 g_object_unref(item->monitor);
221 g_object_unref(item->gfilePath);
222
223 g_thread_yield (); //this gives CPU time to other threads
224 g_mutex_free (item->mtxJoinUpdateThread);
225 item->mtxJoinUpdateThread = NULL;
226 //printf ("autoUpdateList_deleteItemMembersAndItem: deleting item: %p\n",item);
227
228 free(item);
229
230 char cpBuf[41];
231 getcwd (cpBuf, 40);
232 //printf("current working directory: %s\n", cpBuf);
233
234 TRACEIT(10,"autoUpdateList_deleteItemMembersAndItem end");
235 return;
236 }
237
238
autoUpdateList_deleteList()239 void autoUpdateList_deleteList (){
240 TRACEIT(10,"autoUpdateList_deleteList start");
241 //printf("monitor: deleting LIST\n");
242
243 struct autoUpdateList *ThisObject;
244 struct autoUpdateList *ObjectBuf;
245
246 //autoUpdateList_debugOut ();
247
248 ThisObject = autoUpdateList_getFirstItem (g_listAutoUpdateList);
249 if (ThisObject == NULL && g_listAutoUpdateList!=NULL){
250 TRACEIT (2,"autoUpdateList_deleteList NoFirstItem?");
251 ThisObject = g_listAutoUpdateList;
252 }
253
254 while (ThisObject){
255 ObjectBuf = ThisObject->NextItem;
256
257 //delete ThisObject from global list as our updatethread will try to validate its object, therefor
258 //we have to make sure that the global variable does not point to an allready freed memory.
259 //TODO, think about this again, its very late now.
260 if (ThisObject->PrevItem != NULL){
261 ThisObject->PrevItem->NextItem = ThisObject->NextItem;
262 }
263 else{
264 g_listAutoUpdateList = ThisObject->NextItem;
265 if (g_listAutoUpdateList != NULL){
266 g_listAutoUpdateList->PrevItem = NULL;
267 }
268 }
269 if (ThisObject->NextItem != NULL){
270 ThisObject->NextItem->PrevItem = ThisObject->PrevItem;
271 }
272
273 autoUpdateList_deleteItemMembersAndItem (ThisObject);
274 ThisObject = ObjectBuf;
275 }
276 g_listAutoUpdateList = NULL;
277 //printf("monitor: LIST deleted\n");
278 TRACEIT(10,"autoUpdateList_deleteList end");
279 }
280
281
autoUpdateList_RemoveMonitor(char * Path)282 int autoUpdateList_RemoveMonitor (char *Path){
283 TRACEIT(10,"autoUpdateList_RemoveMonitor start");
284
285 if (g_listAutoUpdateList == NULL){
286 TRACEIT(9,"autoUpdateList_RemoveMonitor end, No Monitor at all");
287 return 0;
288 }
289
290 if (Path == NULL){
291 TRACEIT(2,"autoUpdateList_RemoveMonitor end, Parameter NULL");
292 return -1;
293 }
294 //printf("monitor: deleting %s\n",Path);
295 //search list for path,
296 struct autoUpdateList *buflist = autoUpdateList_getFirstItem (g_listAutoUpdateList);
297 while (buflist){
298 if (strcmp(Path, buflist->gstrPath->str) == 0){
299 buflist->iCountRef--;
300 //printf("monitor: countRef decreased\n");
301 break;
302 }
303 buflist = buflist->NextItem;
304 }
305 if (buflist == NULL){
306 TRACEIT(4,"autoUpdateList_RemoveMonitor end, path not found, not monitored");
307 return -1;
308 }
309
310 //printf("autoUpdateList_RemoveMonitor: buflist->iCountRef= %i\n",buflist->iCountRef);
311
312 if (buflist->iCountRef == 0){
313 //printf("monitor: deleting monitor\n");
314 autoUpdateList_DeleteObject(buflist);
315 }
316
317 TRACEIT(10,"autoUpdateList_RemoveMonitor end");
318 return 0;
319 }
320
321
autoUpdateList_AddMonitor(char * Path)322 int autoUpdateList_AddMonitor (char *Path){
323 TRACEIT(10,"autoUpdateList_AddMonitor start");
324 if (Path == NULL){
325 TRACEIT(2,"autoUpdateList_AddMonitor end, Parameter NULL");
326 return -1;
327 }
328 //printf("monitor: adding %s\n",Path);
329
330 struct autoUpdateList *buf2list=NULL;
331 //search list for path, if we allready monitor it we increase the reference count
332 struct autoUpdateList *buflist = g_listAutoUpdateList;
333
334 while (buflist){
335 if (strcmp(Path, buflist->gstrPath->str) == 0){
336 buflist->iCountRef++;
337 //printf("monitor: CountRef increased\n");
338 TRACEIT(10,"autoUpdateList_AddMonitor end");
339 return 0;
340 }
341 buflist = buflist->NextItem;
342 }
343
344 //if we dont find the path, start monitoring it
345 if (buflist == NULL){
346 // printf("monitor: new monitor\n");
347 buflist = malloc( sizeof(struct autoUpdateList) );
348
349 //printf ("autoUpdateList_AddMonitor: added item: %p, path: %s\n",buflist, Path);
350
351 if (buflist){
352 buflist->iUpdateThreadStarted = 0;
353 buflist->gthreadUpdateThread = NULL;
354 buflist->mtxJoinUpdateThread = g_mutex_new();
355
356 buflist->gfilePath = g_file_new_for_path (Path);
357 buflist->monitor = g_file_monitor_directory (buflist->gfilePath,G_FILE_MONITOR_NONE,NULL,NULL);
358 if (buflist->monitor == NULL){
359 g_object_unref(buflist->gfilePath);
360 free(buflist);
361 TRACEIT(2,"autoUpdateList_AddMonitor end, cant get monitor");
362 return -1;
363 }
364 g_file_monitor_set_rate_limit (buflist->monitor, MONITORRATELIMIT); //default value is 800, but this does not mean, that
365 //the monitor skipps any messages but simply waits for X ms before calling the callback function.
366 //In a matter of fact, there are several messages for each change fired. This leads to a porblem in
367 //our case, and the only solution so far is to set the rate limit to very low value, where the define for
368 // our AUTOUPDATE_MINTIME is much bigger than this.
369
370 buflist->iCountRef=1;
371 buflist->gstrPath = g_string_new(Path);
372 g_signal_connect(buflist->monitor,"changed",G_CALLBACK(autoUpdateList_monitoredDirChanged),buflist);
373
374 //add new item to the list
375 buflist->NextItem = NULL;
376 buflist->PrevItem = NULL;
377 if (NULL == g_listAutoUpdateList){
378 g_listAutoUpdateList = buflist;
379 }
380 else{
381 //find the last item
382 for (buf2list = g_listAutoUpdateList; buf2list->NextItem; buf2list=buf2list->NextItem );
383 //printf("buf2list=%p\tbuf2list->NextItem=%p",buf2list,buf2list->NextItem);
384
385 buf2list->NextItem = buflist;
386 buflist->PrevItem = buf2list;
387 }
388 }
389 else{
390 TRACEIT(2,"autoUpdateList_AddMonitor end, malloc failed");
391 return -1;
392 }
393 }
394
395 //printf("monitor: added\n");
396 TRACEIT(10,"autoUpdateList_AddMonitor end");
397 return 0;
398 }
399
400
autoUpdateList_ValidateItem(struct autoUpdateList * item,GFileMonitor * monitor)401 int autoUpdateList_ValidateItem (struct autoUpdateList *item, GFileMonitor *monitor){
402 //call this function to validate a given item.
403 //monitor is used as an additional indicator as the pointer adress may exist (after it was deleted and newly allocated)
404 //but then, the monitor pointer is hopefully different.
405 //returns -1=error, 0=invalid, 1=valid
406 //monitor can be NULL
407 TRACEIT(11,"autoUpdateList_ValidateItem start");
408 if (item == NULL){
409 TRACEIT (2, "autoUpdateList_ValidateItem parameter NULL");
410 return -1;
411 }
412 int iRet = 0;
413
414 struct autoUpdateList *buflist = autoUpdateList_getFirstItem (g_listAutoUpdateList);
415
416 while (buflist){
417 if (buflist == item){
418 if (monitor != NULL){
419 if (monitor == buflist->monitor){
420 iRet = 1;
421 }
422 }
423 else{
424 iRet = 1;
425 }
426 }
427 buflist = buflist->NextItem;
428 }
429
430 TRACEIT(11,"autoUpdateList_ValidateItem end");
431 return iRet;
432 }
433
434
autoUpdateList_getFirstItem(struct autoUpdateList * theList)435 struct autoUpdateList *autoUpdateList_getFirstItem (struct autoUpdateList *theList){
436 TRACEIT(11,"autoUpdateList_getFirstItem start");
437 if (theList == NULL){
438 TRACEIT (2, "autoUpdateList_getFirstItem parameter NULL");
439 return NULL;
440 }
441 while (theList->PrevItem != NULL){
442 theList = theList->PrevItem;
443 }
444
445 TRACEIT(11,"autoUpdateList_getFirstItem end");
446 return theList;
447 }
448
449
autoUpdateList_debugOut()450 void autoUpdateList_debugOut (){
451 struct autoUpdateList *theList = autoUpdateList_getFirstItem (g_listAutoUpdateList);
452 int i=0;
453
454 printf("Debug Out Start\n");
455 while (theList){
456 printf("autoUpdateList [%d]: %p\n",i++, theList);
457
458 theList = theList->NextItem;
459 }
460 printf("Debug Out End\n");
461
462 }
463
464
465 #endif
466
467
468
469
470
471
472
473
474
475
476
477
478
479