d8fab29d1fb7b203ad663123bd457cb54180ab3e
[apps/home/gallery.git] / src / util / gl-thread-util.c
1 /*
2   * Copyright 2012  Samsung Electronics Co., Ltd
3   *
4   * Licensed under the Flora License, Version 1.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *     http://www.tizenopensource.org/license
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16
17 #include <string.h>
18 #include <Ecore_X.h>
19 #include <glib.h>
20 #include <linux/unistd.h>
21 #include <vconf.h>
22 #include <vconf-keys.h>
23 #include <pthread.h>
24 #include "gl-thread-util.h"
25 #include "gl-ui-util.h"
26 #include "gl-util.h"
27 #include "gl-debug.h"
28 #include "gl-db-handler.h"
29 #include "gl-popup.h"
30 #include "gl-progressbar.h"
31 #include "gl-strings.h"
32
33 #define GALLERY_MAGIC_MAIN_CONTEXT           (0x1983cdaf)
34 #define GALLERY_MAGIC_DETAIL_LIST_ITEM       (0x1977abcd)
35 #define GALLERY_MAGIC_PIPE_DATA                          (0x0716ffcc)
36
37 #define GALLERY_MAGIC                 unsigned int  __magic
38 #define GALLERY_MAGIC_SET(d, m)       (d)->__magic = (m)
39 #define GALLERY_MAGIC_CHECK(d, m)     ((d) && ((d)->__magic == (m)))
40
41 typedef struct
42 {
43         GALLERY_MAGIC;
44         int finished_cnt;
45         int state;              /* 0: operation is over; 1: operation is in process */
46         int popup_op;
47 } gl_thread_pipe_data;
48
49
50 static pid_t
51 _gl_thread_gettid(void)
52 {
53         return syscall(__NR_gettid);
54 }
55
56 static int _gl_thread_operate_medias(void *data)
57 {
58         GL_CHECK_VAL(data, -1);
59         gl_appdata *ad = (gl_appdata *)data;
60         int ret = -1;
61
62         switch (ad->maininfo.medias_op_type) {
63         case GL_MEDIA_OP_DELETE:
64                 gl_dbg("Delete!");
65                 ret = gl_del_selected(data);
66                 break;
67         case GL_MEDIA_OP_MOVE:
68                 gl_dbg("Move!");
69                 ret = gl_move_selected(data);
70                 break;
71         default:
72                 gl_dbgE("Unknow media operation mode!");
73                 break;
74         }
75
76         if (ret < 0) {
77                 gl_dbgE("Operation failed!");
78                 return -1;
79         }
80
81         return 0;
82 }
83
84 static int
85 _gl_thread_update_view(void *data)
86 {
87         GL_CHECK_VAL(data, -1);
88         gl_appdata *ad = (gl_appdata *)data;
89
90         switch (ad->maininfo.medias_op_type) {
91         case GL_MEDIA_OP_DELETE:
92                 gl_dbg("Delete!");
93                 gl_update_del_view(data);
94                 break;
95         case GL_MEDIA_OP_MOVE:
96                 gl_dbg("Move!");
97                 gl_update_move_view(data);
98                 break;
99         default:
100                 gl_dbgE("Unknow media operation mode!");
101                 break;
102         }
103
104         return 0;
105 }
106
107 static void *
108 _gl_thread_data_thread(void *data)
109 {
110         GL_CHECK_NULL(data);
111         gl_appdata *ad = (gl_appdata *)data;
112         int cancel_flag = 0;
113
114         gl_dbg("@@@@@@@@@@ :::: Child thread ID = %d :::: @@@@@@@@@@", _gl_thread_gettid());
115         //Different thread, need to initialize libmedia-info.
116         gl_db_init(ad);
117
118         /* Media movement/deleting operation */
119         _gl_thread_operate_medias(data);
120
121         /*  send finish signal  */
122         gl_thread_pipe_data pipe_data;
123         memset(&pipe_data, 0x00, sizeof(gl_thread_pipe_data));
124         GALLERY_MAGIC_SET(&pipe_data, GALLERY_MAGIC_PIPE_DATA);
125         /* Set over state */
126         pipe_data.state = 0;
127         gl_thread_get_cancel_state(ad, &cancel_flag);
128
129         ecore_pipe_write(ad->pbarinfo.sync_pipe, &pipe_data, sizeof(gl_thread_pipe_data));
130
131         gl_dbg("@@@@@@@@@@ :::: Child thread done :::: @@@@@@@@@@");
132
133         return (void *)0;
134 }
135
136 static Eina_Bool _gl_thread_del_pbar_idler_cb(void *data)
137 {
138         gl_dbg("Delete progressbar...");
139         GL_CHECK_CANCEL(data);
140         gl_appdata *ad = (gl_appdata *)data;
141         /* Reset pb_cancel */
142         gl_thread_set_cancel_state(ad, GL_PB_CANCEL_NONE);
143         /* Operation done, destroy progressbar */
144         gl_pb_del_pbar(ad);
145
146         /* Destroy lock */
147         gl_thread_destroy_lock(ad);
148
149         if (ad->pbarinfo.del_pbar_idler) {
150                 ecore_idler_del(ad->pbarinfo.del_pbar_idler);
151                 ad->pbarinfo.del_pbar_idler = NULL;
152         }
153         return ECORE_CALLBACK_CANCEL;
154 }
155
156 static void
157 _gl_thread_pipe_cb(void *data, void *buffer, unsigned int nbyte)
158 {
159         gl_dbg(":::::::::: Main thread ID = %d ::::::::::", _gl_thread_gettid());
160         GL_CHECK(data);
161         GL_CHECK(buffer);
162         gl_appdata *ad = (gl_appdata *)data;
163         gl_thread_pipe_data *p_pipe_data = (gl_thread_pipe_data *) buffer;
164         gl_dbg("Pipe state is %d", p_pipe_data->state);
165
166         if (!GALLERY_MAGIC_CHECK(p_pipe_data, GALLERY_MAGIC_PIPE_DATA)) {
167                 gl_dbgE("##### :: Check p_pipe_data Magic failed :: #####");
168                 return;
169         }
170
171         int cancel_flag = false;
172         gl_thread_get_cancel_state(ad, &cancel_flag);
173
174         if (p_pipe_data->popup_op)
175         {
176                 if (p_pipe_data->state)
177                 {
178                         /* Check cancel_flag */
179                         if (cancel_flag != GL_PB_CANCEL_NORMAL) {
180                                 gl_dbgE("Failed to kill thread, try again!");
181                                 gl_thread_emit_next_signal(ad);
182                                 return;
183                         }
184                         char msg[GL_FILE_PATH_LEN_MAX] = { 0, };
185                         gl_item *cur_gitem = NULL;
186                         /* Get selected media */
187                         gl_db_get_item_by_index(ad, p_pipe_data->finished_cnt, true, &cur_gitem);
188                         GL_CHECK(cur_gitem);
189                         GL_CHECK(cur_gitem->item);
190                         GL_CHECK(cur_gitem->item->display_name);
191
192                         if (p_pipe_data->popup_op == GL_POPUP_OP_SAME_ALBUM)
193                         {
194                                 snprintf(msg, sizeof(msg), "Cannot move %s to the same album!",
195                                          cur_gitem->item->display_name);
196                         }
197                         else if (p_pipe_data->popup_op == GL_POPUP_OP_DUPLICATED_NAME)
198                         {
199                                 snprintf(msg, sizeof(msg), "%s is duplicated, rename it!",
200                                          cur_gitem->item->display_name);
201                         }
202                         else if (p_pipe_data->popup_op == GL_POPUP_OP_PROTECTED_FILE)
203                         {
204                                 snprintf(msg, sizeof(msg), "Cannot removed protected file %s!",
205                                          cur_gitem->item->display_name);
206                         }
207                         gl_dbg("Popup description: %s", msg);
208                         gl_popup_create_popup(ad, GL_POPUP_NOBUT_MOV_DEL, msg);
209                 }
210                 else if (ad->popupinfo.popup)
211                 {
212                         /*
213                          * If p_pipe_data->state is equal to 0,
214                          * it means thread should be destoryed.
215                          */
216                         evas_object_del(ad->popupinfo.popup);
217                         ad->popupinfo.popup = NULL;
218                 }
219         }
220
221         /* Update progressbar state */
222         int all_cnt = gl_db_selected_list_count(ad);
223
224         if (p_pipe_data->state) {
225                 /* Check cancel_flag */
226                 if (cancel_flag != GL_PB_CANCEL_NORMAL) {
227                         gl_dbgE("Failed to kill thread, try again!");
228                         gl_thread_emit_next_signal(ad);
229                         return;
230                 }
231                 /* 1. Moving/deleting is in porcess */
232                 gl_pb_refresh_thread_pbar(ad, p_pipe_data->finished_cnt, all_cnt);
233                 gl_dbg("@@@ finished/all = %d/%d, updating progressbar @@@",
234                        p_pipe_data->finished_cnt, all_cnt);
235                 /* Emit signal to notice child thread handle next media */
236                 gl_dbg("Emit next signal...");
237                 gl_thread_emit_next_signal(ad);
238         } else {
239                 /* 2. Moving/deleting is over, show finished count */
240                 gl_dbg("@@@ finished: %d, updating progressbar @@@",
241                        ad->pbarinfo.finished_cnt);
242                 gl_pb_refresh_thread_pbar(ad, ad->pbarinfo.finished_cnt,
243                                           ad->pbarinfo.finished_cnt);
244                 ad->pbarinfo.finished_cnt = 0;
245                 gl_dbg("@@@@@@@ :::: Pipe close && Update view :::: @@@@@@@");
246                 int cancel_flag = false;
247                 bool b_reset = false;
248                 gl_thread_get_cancel_state(ad, &cancel_flag);
249                 if (cancel_flag == GL_PB_CANCEL_RESET) {
250                         /* Set medias_op_type none to stop refreshing view*/
251                         ad->maininfo.medias_op_type = GL_MEDIA_OP_NONE;
252                         gl_dbgW("Cancel error case, set reset state!");
253                         b_reset = true;
254                 }
255
256                 /* Use idler to delete progressbar to refresh status totally */
257                 if (ad->pbarinfo.del_pbar_idler) {
258                         ecore_idler_del(ad->pbarinfo.del_pbar_idler);
259                         ad->pbarinfo.del_pbar_idler = NULL;
260                 }
261                 Ecore_Idler *idler= NULL;
262                 idler = ecore_idler_add(_gl_thread_del_pbar_idler_cb, ad);
263                 ad->pbarinfo.del_pbar_idler = idler;
264
265                 int mmc_state = ad->maininfo.mmc_state;
266                 int op_type = ad->maininfo.medias_op_type;
267                 gl_dbg("MMC state: %d, OP type: %d.", mmc_state, op_type);
268                 /* Operate medias related to MMC while moving medias */
269                 if (mmc_state == GL_MMC_STATE_REMOVED_MOVING) {
270                         ad->maininfo.mmc_state = GL_MMC_STATE_REMOVED;
271
272                         /**
273                         * Albums: Move/Delete.
274                         *
275                         * Case 1: Source folder does exist, update view.
276                         * Case 2: Source folder is MMC, and it vanished.
277                         */
278                         gl_cluster *cur_album = ad->albuminfo.current_album;
279                         if (cur_album && cur_album->cluster &&
280                             cur_album->cluster->type == MINFO_MMC) {
281                                 gl_dbgW("MMC removed, change to albums view!");
282                                 gl_pop_to_ctrlbar_ly(ad, true);
283                         } else {
284                                 _gl_thread_update_view(ad);
285                         }
286                 } else {
287                         /* Operated files on MMC, reset MMC state */
288                         if (mmc_state == GL_MMC_STATE_ADDED_MOVING)
289                                 ad->maininfo.mmc_state = GL_MMC_STATE_ADDED;
290                         else if (mmc_state == GL_MMC_STATE_ADDING_MOVING)
291                                 ad->maininfo.mmc_state = GL_MMC_STATE_ADDED;
292                         /* Refresh view */
293                         _gl_thread_update_view(ad);
294                 }
295
296                 /* Free Ecore_Pipe object created */
297                 if (ad->pbarinfo.sync_pipe) {
298                         ecore_pipe_del(ad->pbarinfo.sync_pipe);
299                         ad->pbarinfo.sync_pipe = NULL;
300                 }
301
302                 if (b_reset) {
303                         /* Free selected list */
304                         gl_db_selected_list_finalize(ad);
305                         gl_dbgW("Thread cancellation is over, reset gallery!");
306                         /* Continue remake gallery */
307                         gallery_reset_app(ad, ad->albuminfo.aul_album_id);
308                 } else {
309                         /* Set medias_op_type none to stop refreshing view*/
310                         ad->maininfo.medias_op_type = GL_MEDIA_OP_NONE;
311                 }
312         }
313 }
314
315 /*******************************************************
316 ** Prototype            : gl_thread_emit_next_signal
317 ** Description          : Emit signal to notice child thread handle next media.
318 ** Input                : void *data
319 ** Output               : None
320 ** Return Value         :
321 ** Calls                :
322 ** Called By            :
323 **
324 **  History             :
325 **  1.Date              : 2011/06/10
326 **    Author            : Samsung
327 **    Modification : Created function
328 **
329 *********************************************************/
330 int
331 gl_thread_emit_next_signal(void *data)
332 {
333         GL_CHECK_VAL(data, -1);
334         gl_appdata *ad = (gl_appdata *)data;
335
336         pthread_mutex_lock(&(ad->pbarinfo.refresh_lock));
337         gl_dbg("refresh_flag: %d.", ad->pbarinfo.refresh_flag);
338         if (ad->pbarinfo.refresh_flag == 0)
339         {
340                 ad->pbarinfo.refresh_flag = 1;
341                 pthread_cond_signal(&(ad->pbarinfo.refresh_cond));
342         }
343         pthread_mutex_unlock(&(ad->pbarinfo.refresh_lock));
344
345         return 0;
346 }
347
348 /*******************************************************
349 ** Prototype            : gl_thread_wait_next_signal
350 ** Description          : Wait start signal to handle next media.
351 ** Input                : void *data
352 ** Output               : None
353 ** Return Value         :
354 ** Calls                :
355 ** Called By            :
356 **
357 **  History             :
358 **  1.Date              : 2011/06/10
359 **    Author            : Samsung
360 **    Modification : Created function
361 **
362 *********************************************************/
363 int
364 gl_thread_wait_next_signal(void *data)
365 {
366         GL_CHECK_VAL(data, -1);
367         gl_appdata *ad = (gl_appdata *)data;
368
369         pthread_mutex_lock(&(ad->pbarinfo.refresh_lock));
370         gl_dbg("refresh_flag: %d.", ad->pbarinfo.refresh_flag);
371         while (ad->pbarinfo.refresh_flag == 0)
372         {
373                 gl_dbg("Thread waiting...");
374                 pthread_cond_wait(&(ad->pbarinfo.refresh_cond), &(ad->pbarinfo.refresh_lock));
375         }
376         ad->pbarinfo.refresh_flag = 0;
377         pthread_mutex_unlock(&(ad->pbarinfo.refresh_lock));
378
379         return 0;
380 }
381
382 /*******************************************************
383 ** Prototype            : gl_thread_set_cancel_state
384 ** Description          : Set the value of cancel flag.
385 ** Input                : void *data
386 ** Input                : int val
387 ** Output               : None
388 ** Return Value         :
389 ** Calls                :
390 ** Called By            :
391 **
392 **  History             :
393 **  1.Date              : 2011/06/10
394 **    Author            : Samsung
395 **    Modification : Created function
396 **
397 *********************************************************/
398 int
399 gl_thread_set_cancel_state(void *data, int val)
400 {
401         GL_CHECK_VAL(data, -1);
402         gl_appdata *ad = (gl_appdata *)data;
403
404         pthread_mutex_lock(&(ad->pbarinfo.pbar_lock));
405         ad->pbarinfo.pbar_cancel = val;
406         pthread_mutex_unlock(&(ad->pbarinfo.pbar_lock));
407
408         return 0;
409 }
410
411 /*******************************************************
412 ** Prototype            : gl_thread_get_cancel_state
413 ** Description          : Get the value of cancel flag.
414 ** Input                : void *data
415 ** Output               : int* val
416 ** Return Value         :
417 ** Calls                :
418 ** Called By            :
419 **
420 **  History             :
421 **  1.Date              : 2011/06/10
422 **    Author            : Samsung
423 **    Modification : Created function
424 **
425 *********************************************************/
426 int
427 gl_thread_get_cancel_state(void *data, int *val)
428 {
429         GL_CHECK_VAL(val, -1);
430         gl_appdata *ad = (gl_appdata *)data;
431
432         pthread_mutex_lock(&(ad->pbarinfo.pbar_lock));
433         *val = ad->pbarinfo.pbar_cancel;
434         pthread_mutex_unlock(&(ad->pbarinfo.pbar_lock));
435
436         return 0;
437 }
438
439
440 /*******************************************************
441 ** Prototype            : gl_thread_write_pipe
442 ** Description          : Write date to pipe in order to make progressbar refreshed
443 ** Input                : void *data
444 ** Input                : int finished_cnt
445 ** Input                : int popup_op
446 ** Output               : None
447 ** Return Value         :
448 ** Calls                :
449 ** Called By            :
450 **
451 **  History             :
452 **  1.Date              : 2011/06/10
453 **    Author            : Samsung
454 **    Modification : Created function
455 **
456 *********************************************************/
457 void
458 gl_thread_write_pipe(void *data, int finished_cnt, int popup_op)
459 {
460         GL_CHECK(data);
461         gl_appdata *ad = (gl_appdata *)data;
462         int cancel_flag = false;
463
464         gl_dbg("Wait next signal...");
465         gl_thread_wait_next_signal(ad);
466
467         gl_thread_pipe_data pipe_data;
468         memset(&pipe_data, 0x00, sizeof(gl_thread_pipe_data));
469         GALLERY_MAGIC_SET(&pipe_data, GALLERY_MAGIC_PIPE_DATA);
470
471         pipe_data.state = 1;
472         pipe_data.finished_cnt = finished_cnt;
473         pipe_data.popup_op = popup_op;
474
475         gl_thread_get_cancel_state(ad, &cancel_flag);
476
477         if (cancel_flag == GL_PB_CANCEL_BUTTON ||
478                 cancel_flag == GL_PB_CANCEL_MMC ||
479                 cancel_flag == GL_PB_CANCEL_ERROR ||
480                 cancel_flag == GL_PB_CANCEL_RESET)
481         {
482                 //send cancel signal through pipe
483                 pipe_data.finished_cnt = -1;
484                 /* Set over state */
485                 pipe_data.state = 0;
486                 ecore_pipe_write(ad->pbarinfo.sync_pipe, &pipe_data, sizeof(gl_thread_pipe_data));
487                 //exit the child thread
488                 if (cancel_flag == GL_PB_CANCEL_BUTTON)
489                 {
490                         gl_dbg("Cancel button tapped, child thread exit!");
491                 }
492                 else if (cancel_flag == GL_PB_CANCEL_MMC)
493                 {
494                         gl_dbg("MMC removed, child thread exit!");
495                 }
496                 else if (cancel_flag == GL_PB_CANCEL_ERROR)
497                 {
498                         gl_dbg("Error happened, child thread exit!");
499                 }
500                 else if (cancel_flag == GL_PB_CANCEL_RESET) {
501                         gl_dbg("Reset gallery, child thread exit!");
502                 }
503
504                 pthread_exit((void *)1);
505         }
506         else
507         {
508                 gl_dbg("Writing pipe...");
509                 ecore_pipe_write(ad->pbarinfo.sync_pipe, &pipe_data, sizeof(gl_thread_pipe_data));
510         }
511 }
512
513 /*******************************************************
514 ** Prototype            : gl_thread_gen_data_thread
515 ** Description          : Create child thread for moving or deleting medias
516 ** Input                : void *data
517 ** Output               : None
518 ** Return Value         :
519 ** Calls                :
520 ** Called By            :
521 **
522 **  History             :
523 **  1.Date              : 2011/06/10
524 **    Author            : Samsung
525 **    Modification : Created function
526 **
527 *********************************************************/
528 int
529 gl_thread_gen_data_thread(void *data)
530 {
531         GL_CHECK_VAL(data, -1);
532         gl_appdata *ad = (gl_appdata *)data;
533         pthread_t tid;
534         pthread_attr_t attr;
535         int ret = -1;
536
537         gl_dbg("Creating child thread.");
538         //add pipe for update progressbar status
539         ad->pbarinfo.sync_pipe = ecore_pipe_add(_gl_thread_pipe_cb, ad);
540         //initialize thread attributes
541         ret = pthread_attr_init(&attr);
542         if (ret == 0)
543         {
544                 ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
545                 if (ret == 0)
546                 {
547                         //create child thread
548                         ret = pthread_create(&tid, &attr, _gl_thread_data_thread, ad);
549                         if (ret != 0)
550                         {
551                                 gl_dbgE("[Error] ##### :: pthread_create failed :: #####");
552                                 pthread_attr_destroy(&attr);
553                                 return -1;
554                         }
555                 }
556         }
557
558         gl_dbg("\n\n[Done] @@@@@@@@@@ :::: pthread_create successfull :::: @@@@@@@@@@\n");
559         pthread_attr_destroy(&attr);
560
561         return 0;
562 }
563
564 /*******************************************************
565 ** Prototype            : gl_thread_destroy_lock
566 ** Description          : Destroy mutex lock.
567 ** Input                : void *data
568 ** Output               : None
569 ** Return Value         :
570 ** Calls                :
571 ** Called By            :
572 **
573 **  History             :
574 **  1.Date              : 2011/06/10
575 **    Author            : Samsung
576 **    Modification : Created function
577 **
578 *********************************************************/
579 int
580 gl_thread_destroy_lock(void *data)
581 {
582         GL_CHECK_VAL(data, -1);
583         gl_appdata *ad = (gl_appdata *)data;
584         gl_dbg("@@@@@@@@@@ :::: Destroy MUTEX :::: @@@@@@@@@@");
585
586         /**
587         * The variable below was accessed without holding a guarding lock.
588         * In a multithreaded environment, this can lead to a race condition.
589         * Add lock to prevent from RC.
590         */
591         pthread_mutex_lock(&(ad->pbarinfo.refresh_lock));
592         ad->pbarinfo.refresh_flag = 0;
593         pthread_mutex_unlock(&(ad->pbarinfo.refresh_lock));
594
595         pthread_cond_destroy(&(ad->pbarinfo.refresh_cond));
596         pthread_mutex_destroy(&(ad->pbarinfo.pbar_lock));
597         pthread_mutex_destroy(&(ad->pbarinfo.refresh_lock));
598
599         return 0;
600 }
601
602 /*******************************************************
603 ** Prototype            : gl_thread_init_lock
604 ** Description          : Initialize mutex lock
605 ** Input                : void *data
606 ** Output               : None
607 ** Return Value         :
608 ** Calls                :
609 ** Called By            :
610 **
611 **  History             :
612 **  1.Date              : 2011/06/10
613 **    Author            : Samsung
614 **    Modification : Created function
615 **
616 *********************************************************/
617 int
618 gl_thread_init_lock(void *data)
619 {
620         GL_CHECK_VAL(data, -1);
621         gl_appdata *ad = (gl_appdata *)data;
622         gl_dbg("@@@@@@@@@@ :::: Initialize MUTEX :::: @@@@@@@@@@");
623
624         pthread_mutex_init(&(ad->pbarinfo.pbar_lock), NULL);
625         pthread_mutex_init(&(ad->pbarinfo.refresh_lock), NULL);
626         pthread_cond_init(&(ad->pbarinfo.refresh_cond), NULL);
627
628         /**
629         * The variable below was accessed without holding a guarding lock.
630         * In a multithreaded environment, this can lead to a race condition.
631         * Add lock to prevent from RC.
632         */
633         pthread_mutex_lock(&(ad->pbarinfo.refresh_lock));
634         ad->pbarinfo.refresh_flag = 0;
635         pthread_mutex_unlock(&(ad->pbarinfo.refresh_lock));
636
637         return 0;
638 }