Merge lp:~matttbe/cairo-dock-core/task-one-thread-async-new-struct into lp:~cairo-dock-team/cairo-dock-core/cairo-dock

Proposed by Matthieu Baerts
Status: Needs review
Proposed branch: lp:~matttbe/cairo-dock-core/task-one-thread-async-new-struct
Merge into: lp:~cairo-dock-team/cairo-dock-core/cairo-dock
Diff against target: 384 lines (+182/-75)
4 files modified
src/gldit/cairo-dock-task.c (+165/-70)
src/gldit/cairo-dock-task.h (+17/-2)
src/implementations/cairo-dock-graph.c (+0/-1)
src/implementations/cairo-dock-graph.h (+0/-2)
To merge this branch: bzr merge lp:~matttbe/cairo-dock-core/task-one-thread-async-new-struct
Reviewer Review Type Date Requested Status
Fabounet Pending
Review via email: mp+120581@code.launchpad.net

Description of the change

A better implementation of the task.
Now thread's datas are separated in order to better prevent crash if the task is discarded and allow a task to be stopped before the end of 'get_data' and re-launch a new thread.

To post a comment you must log in.
1182. By Matthieu Baerts

Task:
 * fixed a typo with the check of bDiscard
 * cancel: check if pThread has not been freeed before talking with it
 * convert g_print to cd_debug

1183. By Matthieu Baerts

Task: be sure to cancel the next iteration if 'pTask->iPeriod == 0'

Unmerged revisions

1183. By Matthieu Baerts

Task: be sure to cancel the next iteration if 'pTask->iPeriod == 0'

1182. By Matthieu Baerts

Task:
 * fixed a typo with the check of bDiscard
 * cancel: check if pThread has not been freeed before talking with it
 * convert g_print to cd_debug

1181. By Matthieu Baerts

Task: only used one thread and the thread clean itself to avoid crash

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/gldit/cairo-dock-task.c'
2--- src/gldit/cairo-dock-task.c 2012-08-02 17:43:50 +0000
3+++ src/gldit/cairo-dock-task.c 2012-08-24 06:39:17 +0000
4@@ -24,99 +24,182 @@
5 #include "cairo-dock-log.h"
6 #include "cairo-dock-task.h"
7
8-
9-#define cairo_dock_schedule_next_iteration(pTask) do {\
10- if (pTask->iSidTimer == 0 && pTask->iPeriod)\
11- pTask->iSidTimer = g_timeout_add_seconds (pTask->iPeriod, (GSourceFunc) _cairo_dock_timer, pTask); } while (0)
12-
13-#define cairo_dock_cancel_next_iteration(pTask) do {\
14- if (pTask->iSidTimer != 0) {\
15- g_source_remove (pTask->iSidTimer);\
16- pTask->iSidTimer = 0; } } while (0)
17-
18-#define cairo_dock_perform_task_update(pTask) do {\
19- gboolean bContinue = pTask->update (pTask->pSharedMemory);\
20- if (! bContinue) {\
21- cairo_dock_cancel_next_iteration (pTask); }\
22- else {\
23- pTask->iFrequencyState = CAIRO_DOCK_FREQUENCY_NORMAL;\
24- cairo_dock_schedule_next_iteration (pTask); } } while (0)
25-
26-#define cairo_dock_set_elapsed_time(pTask) do {\
27- pTask->fElapsedTime = g_timer_elapsed (pTask->pClock, NULL);\
28- g_timer_start (pTask->pClock); } while (0)
29-
30-#define _free_task(pTask) do {\
31- if (pTask->free_data)\
32- pTask->free_data (pTask->pSharedMemory);\
33- g_timer_destroy (pTask->pClock);\
34- g_free (pTask); } while (0)
35+#if (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32)
36+#define CD_GMUTEX pThread->pMutex
37+#define CD_TASK_GMUTEX pTask->CD_GMUTEX
38+#else
39+#define CD_GMUTEX &pThread->aMutex
40+#define CD_TASK_GMUTEX &pTask->pThread->aMutex
41+#endif
42+
43
44 static gboolean _cairo_dock_timer (CairoDockTask *pTask)
45 {
46 cairo_dock_launch_task (pTask);
47 return TRUE;
48 }
49+
50+static void cairo_dock_schedule_next_iteration (CairoDockTask *pTask)
51+{
52+ if (pTask->iSidTimer == 0)
53+ pTask->iSidTimer = g_timeout_add_seconds (pTask->iPeriod, (GSourceFunc) _cairo_dock_timer, pTask);
54+}
55+
56+static void cairo_dock_cancel_next_iteration (CairoDockTask *pTask)
57+{
58+ if (! pTask || pTask->bDiscard) // already did in cairo_dock_discard_task (_free_disc. -> free -> stop -> pause -> _free)
59+ return;
60+ if (pTask->iSidTimer != 0)
61+ {
62+ g_source_remove (pTask->iSidTimer);
63+ pTask->iSidTimer = 0;
64+ }
65+ if (pTask->get_data != NULL && pTask->pThread) // if there is a thread and no problem
66+ {
67+ cd_debug ("Task: Unlock thread if it's running (%p)", pTask);
68+ // we notify the thread that it can stop
69+ g_atomic_int_set (&pTask->pThread->iUpdateIsEnded, 0); // just to be sure
70+ g_atomic_int_set (&pTask->pThread->iThreadCanRun, 0);
71+ // Maybe the thread is waiting
72+ if (g_atomic_int_get (&pTask->pThread->iThreadIsRunning) == 0)
73+ {
74+ g_mutex_unlock (CD_TASK_GMUTEX); // unlock to stop the thread
75+ cd_debug ("Task: Unlocked (%p)", pTask);
76+ }
77+ pTask->pThread = NULL; // now, if we want to relaunch the task, we'll have to relaunch a new thread
78+ /// g_mutex_unlock (CD_GMUTEX); // locked at the beginning of the thread or in the thread
79+ /// g_mutex_trylock (CD_GMUTEX); // if was
80+ /// g_mutex_unlock (CD_GMUTEX);
81+ }
82+}
83+
84+static void cairo_dock_perform_task_update (CairoDockTask *pTask)
85+{
86+ gboolean bContinue = pTask->update (pTask->pSharedMemory);
87+ if (! bContinue || pTask->iPeriod == 0) // we can strop the thread if the update function says that we have to stop or if we want a one shot
88+ cairo_dock_cancel_next_iteration (pTask);
89+ else
90+ {
91+ pTask->iFrequencyState = CAIRO_DOCK_FREQUENCY_NORMAL;
92+ cairo_dock_schedule_next_iteration (pTask);
93+ }
94+}
95+
96+static void cairo_dock_set_elapsed_time (CairoDockTask *pTask)
97+{
98+ pTask->fElapsedTime = g_timer_elapsed (pTask->pClock, NULL);
99+ g_timer_start (pTask->pClock);
100+}
101+
102+static gboolean _cairo_dock_check_for_update (CairoDockTask *pTask)
103+{
104+ if (! pTask || pTask->bDiscard || ! pTask->pThread) // task has been discarded
105+ return FALSE;
106+ if (g_atomic_int_get (&pTask->pThread->iThreadIsRunning) == 0) // data have been produced by the thread
107+ {
108+ cd_debug ("Task: Perform task update (%p)", pTask);
109+ if (pTask->bDiscard) // task has been discarded
110+ return FALSE;
111+
112+ pTask->iSidTimerUpdate = 0; // timer for the update
113+ // We can perform task update and continue/stop the task's timer.
114+ cairo_dock_perform_task_update (pTask);
115+
116+ return FALSE;
117+ }
118+ return TRUE; // continue to check if it's possible to perform task update
119+}
120+
121 static gpointer _cairo_dock_threaded_calculation (CairoDockTask *pTask)
122 {
123- //\_______________________ On obtient nos donnees.
124- cairo_dock_set_elapsed_time (pTask);
125- pTask->get_data (pTask->pSharedMemory);
126-
127- //\_______________________ On indique qu'on a fini.
128- g_atomic_int_set (&pTask->iThreadIsRunning, 0);
129+ CairoDockTaskThread *pThread = pTask->pThread; // needed for the thread
130+ cd_debug ("Task: Start a new thread (%p - %p)", pTask, pThread);
131+ if (! pThread || ! g_mutex_trylock (CD_GMUTEX)
132+ || ! g_atomic_int_compare_and_exchange (&pThread->iThreadIsRunning, 0, 1))
133+ return NULL; // was locked and is running: should not happen...
134+ while (g_atomic_int_get (&pThread->iThreadCanRun) == 1)
135+ {
136+ if (! pTask || pTask->bDiscard) // discarded just after its launch...
137+ break;
138+
139+ cd_debug ("Task: thread: Get data (%p - %p)", pTask, pThread);
140+ //\_______________________ Get data
141+ cairo_dock_set_elapsed_time (pTask);
142+ pTask->get_data (pTask->pSharedMemory);
143+ g_atomic_int_set (&pThread->iUpdateIsEnded, 1);
144+ cd_debug ("Task: thread: Data received (%p - %p)", pTask, pThread);
145+
146+ if (g_atomic_int_get (&pThread->iThreadCanRun) == 0 || (! pTask || pTask->bDiscard))
147+ break; // if the task has been cancelled, we should not wait... we should stop !
148+
149+ // we launch the update in the main loop
150+ if (pTask->iSidTimerUpdate == 0)
151+ pTask->iSidTimerUpdate = g_idle_add ((GSourceFunc) _cairo_dock_check_for_update, pTask);
152+
153+ g_atomic_int_set (&pThread->iThreadIsRunning, 0);
154+
155+ //\_______________________ We lock to wait for the next update
156+ if (g_atomic_int_get (&pThread->iUpdateIsEnded) == 1) // TODO: is it possible? it takes a few time to launch the update in the main loop + wait for the end + the timer... Maybe... if we stop the task.
157+ g_mutex_lock (CD_GMUTEX);
158+ }
159+ g_atomic_int_set (&pThread->iThreadIsRunning, -1); // the task can be freed
160+
161+ g_mutex_unlock (CD_GMUTEX); // was locked at the beginning of the thread
162+
163+ //\______________________ Free
164+ #if (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32)
165+ g_mutex_free (CD_GMUTEX);
166+ #else
167+ g_mutex_clear (CD_GMUTEX);
168+ #endif
169+ g_free (pThread);
170+ cd_debug ("Task: Thread: Stop it (%p - %p)", pTask, pThread);
171 return NULL;
172 }
173-static gboolean _cairo_dock_check_for_update (CairoDockTask *pTask)
174-{
175- int iThreadIsRunning = g_atomic_int_get (&pTask->iThreadIsRunning);
176- if (! iThreadIsRunning) // le thread a fini.
177- {
178- if (pTask->bDiscard) // la tache s'est faite abandonnee.
179- {
180- //g_print ("free discared task...\n");
181- _free_task (pTask);
182- //g_print ("done.\n");
183- return FALSE;
184- }
185-
186- // On met a jour avec ces nouvelles donnees et on lance/arrete le timer.
187- pTask->iSidTimerUpdate = 0;
188- cairo_dock_perform_task_update (pTask);
189-
190- return FALSE;
191- }
192- return TRUE;
193-}
194+
195 void cairo_dock_launch_task (CairoDockTask *pTask)
196 {
197 g_return_if_fail (pTask != NULL);
198- if (pTask->get_data == NULL) // pas de thread, tout est dans la fonction d'update.
199+ if (pTask->get_data == NULL) // no threads, only update
200 {
201 cairo_dock_set_elapsed_time (pTask);
202 cairo_dock_perform_task_update (pTask);
203 }
204 else
205 {
206- if (g_atomic_int_compare_and_exchange (&pTask->iThreadIsRunning, 0, 1)) // il etait egal a 0, on lui met 1 et on lance le thread.
207+ if (pTask->pThread == NULL) //g_atomic_int_compare_and_exchange (&pTask->iThreadCanRun, 0, 1) // if was 0, now 1 => we can launch a new thread
208 {
209- GError *erreur = NULL;
210+ GError *error = NULL;
211+
212+ pTask->pThread = g_new0 (CairoDockTaskThread, 1);
213+ pTask->pThread->iThreadCanRun = 1;
214+
215 #if (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32)
216- GThread* pThread = g_thread_create ((GThreadFunc) _cairo_dock_threaded_calculation, pTask, FALSE, &erreur);
217+ CD_TASK_GMUTEX = g_mutex_new ();
218+ g_thread_create ((GThreadFunc) _cairo_dock_threaded_calculation, pTask, FALSE, &error);
219 #else
220- GThread* pThread = g_thread_try_new ("Cairo-Dock Task", (GThreadFunc) _cairo_dock_threaded_calculation, pTask, &erreur);
221+ g_mutex_init (CD_TASK_GMUTEX);
222+ GThread* pThread = g_thread_try_new ("Task", (GThreadFunc) _cairo_dock_threaded_calculation, pTask, &error);
223 g_thread_unref (pThread);
224 #endif
225- if (erreur != NULL) // on n'a pas pu lancer le thread.
226+
227+ if (error != NULL)
228 {
229- cd_warning (erreur->message);
230- g_error_free (erreur);
231- g_atomic_int_set (&pTask->iThreadIsRunning, 0);
232+ cd_warning (error->message);
233+ g_error_free (error);
234+ pTask->pThread->iThreadCanRun = 0;
235 }
236 }
237+ else if (pTask->iSidTimerUpdate == 0 // to be sure...
238+ && g_atomic_int_compare_and_exchange (&pTask->pThread->iThreadIsRunning, 0, 1)) // we announce that we thread is now running
239+ {
240+ g_atomic_int_set (&pTask->pThread->iUpdateIsEnded, 0);
241+ // the thread is launched but is waiting
242+ g_mutex_unlock (CD_TASK_GMUTEX); // unlock to get new data
243+ }
244
245- if (pTask->iSidTimerUpdate == 0)
246- pTask->iSidTimerUpdate = g_timeout_add (MAX (100, MIN (0.10 * pTask->iPeriod, 333)), (GSourceFunc) _cairo_dock_check_for_update, pTask);
247+ /* if (pTask->iSidTimerUpdate == 0)
248+ pTask->iSidTimerUpdate = g_timeout_add (MAX (100, MIN (0.10 * pTask->iPeriod, 333)), (GSourceFunc) _cairo_dock_check_for_update, pTask);*/
249 }
250 }
251
252@@ -127,6 +210,7 @@
253 cairo_dock_launch_task (pTask);
254 return FALSE;
255 }
256+
257 void cairo_dock_launch_task_delayed (CairoDockTask *pTask, double fDelay)
258 {
259 cairo_dock_cancel_next_iteration (pTask);
260@@ -155,6 +239,7 @@
261 if (pTask == NULL)
262 return ;
263
264+ cd_debug ("Task: %s (%p)", __func__, pTask);
265 cairo_dock_cancel_next_iteration (pTask);
266
267 if (pTask->iSidTimerUpdate != 0)
268@@ -171,11 +256,11 @@
269
270 _cairo_dock_pause_task (pTask);
271
272- cd_message ("***waiting for thread's end...(%d)", g_atomic_int_get (&pTask->iThreadIsRunning));
273- while (g_atomic_int_get (&pTask->iThreadIsRunning))
274+ cd_debug ("Task: Waiting for thread's end..."); ///"(%d)\n", g_atomic_int_get (&pTask->iThreadIsRunning));
275+ /// while (g_atomic_int_get (&pTask->iThreadIsRunning))
276+ while (pTask->pThread)
277 g_usleep (10);
278- ///gtk_main_iteration ();
279- cd_message ("***ended.");
280+ cd_debug ("Task: ended.");
281 }
282
283
284@@ -190,6 +275,7 @@
285 if (pTask == NULL)
286 return ;
287
288+ cd_debug ("Task: %s (%p)", __func__, pTask);
289 cairo_dock_cancel_next_iteration (pTask);
290 g_atomic_int_set (&pTask->bDiscard, 1);
291
292@@ -197,6 +283,15 @@
293 pTask->iSidTimerUpdate = g_idle_add ((GSourceFunc) _free_discarded_task, pTask);
294 }
295
296+static void _free_task (CairoDockTask *pTask)
297+{
298+ cd_debug ("Task: Free task (%p)", pTask);
299+ if (pTask->free_data)
300+ pTask->free_data (pTask->pSharedMemory);
301+ g_timer_destroy (pTask->pClock);
302+ g_free (pTask);
303+}
304+
305 void cairo_dock_free_task (CairoDockTask *pTask)
306 {
307 if (pTask == NULL)
308@@ -263,7 +358,7 @@
309 break ;
310 }
311
312- cd_message ("degradation de la mesure (etat <- %d/%d)", pTask->iFrequencyState, CAIRO_DOCK_NB_FREQUENCIES-1);
313+ cd_message ("degradation of the frequency (state <- %d/%d)", pTask->iFrequencyState, CAIRO_DOCK_NB_FREQUENCIES-1);
314 _cairo_dock_restart_timer_with_frequency (pTask, iNewPeriod);
315 }
316 }
317
318=== modified file 'src/gldit/cairo-dock-task.h'
319--- src/gldit/cairo-dock-task.h 2010-10-31 00:14:40 +0000
320+++ src/gldit/cairo-dock-task.h 2012-08-24 06:39:17 +0000
321@@ -53,14 +53,27 @@
322 /// Definition of the synchronous job, that update the dock with the results of the previous job. Returns TRUE to continue, FALSE to stop
323 typedef gboolean (* CairoDockUpdateSyncFunc ) (gpointer pSharedMemory);
324
325+typedef struct _CairoDockTaskThread {
326+ /// Atomic value, set to 1 when the thread is running.
327+ volatile gint iThreadIsRunning;
328+ /// Mutex, to know if the thread can receive data
329+ #if (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32)
330+ GMutex *pMutex;
331+ #else
332+ GMutex aMutex;
333+ #endif
334+ /// Atomic value, set to 1 when the thread can run
335+ volatile gint iThreadCanRun;
336+ /// Atomic value, set to 1 when the main-loop is doing the update
337+ volatile gint iUpdateIsEnded;
338+} CairoDockTaskThread;
339+
340 /// Definition of a periodic and asynchronous Task.
341 struct _CairoDockTask {
342 /// ID of the timer of the Task.
343 gint iSidTimer;
344 /// ID of the timer to check the end of the thread.
345 gint iSidTimerUpdate;
346- /// Atomic value, set to 1 when the thread is running.
347- gint iThreadIsRunning;
348 /// function carrying out the heavy job.
349 CairoDockGetDataAsyncFunc get_data;
350 /// function carrying out the update of the dock. Returns TRUE to continue, FALSE to stop.
351@@ -79,6 +92,8 @@
352 GFreeFunc free_data;
353 /// TRUE when the task has been discarded.
354 gboolean bDiscard;
355+ /// structure needed to manage threads
356+ CairoDockTaskThread *pThread;
357 } ;
358
359
360
361=== modified file 'src/implementations/cairo-dock-graph.c'
362--- src/implementations/cairo-dock-graph.c 2012-07-19 23:59:06 +0000
363+++ src/implementations/cairo-dock-graph.c 2012-08-24 06:39:17 +0000
364@@ -40,7 +40,6 @@
365 gdouble fBackGroundColor[4];
366 cairo_surface_t *pBackgroundSurface;
367 GLuint iBackgroundTexture;
368- gint iRadius; // deprecated
369 gint iMargin;
370 gboolean bMixGraphs;
371 } Graph;
372
373=== modified file 'src/implementations/cairo-dock-graph.h'
374--- src/implementations/cairo-dock-graph.h 2011-09-13 00:07:51 +0000
375+++ src/implementations/cairo-dock-graph.h 2012-08-24 06:39:17 +0000
376@@ -58,8 +58,6 @@
377 gdouble *fLowColor;
378 /// color of the background.
379 gdouble fBackGroundColor[4];
380- // radius of the corners of the background.
381- gint iRadius; // deprecated
382 /// TRUE to draw all the values on the same graph.
383 gboolean bMixGraphs;
384 };

Subscribers

People subscribed via source and target branches