2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2005-2007 DINH Viet Hoa and the Claws Mail team
5 * This program 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 3 of the License, or
8 * (at your option) any later version.
10 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "etpan-thread-manager.h"
31 #include <libetpan/mailsem.h>
32 #include <semaphore.h>
35 #include "etpan-errors.h"
37 #define POOL_UNBOUND_MAX 4
39 #define POOL_INIT_SIZE 8
40 #define OP_INIT_SIZE 8
42 static int etpan_thread_start(struct etpan_thread * thread);
43 static void etpan_thread_free(struct etpan_thread * thread);
44 static unsigned int etpan_thread_get_load(struct etpan_thread * thread);
45 static int etpan_thread_is_bound(struct etpan_thread * thread);
46 static int etpan_thread_manager_is_stopped(struct etpan_thread_manager * manager);
47 static void etpan_thread_join(struct etpan_thread * thread);
48 static struct etpan_thread * etpan_thread_new(void);
49 static int etpan_thread_op_cancelled(struct etpan_thread_op * op);
50 static void etpan_thread_op_lock(struct etpan_thread_op * op);
51 static void etpan_thread_op_unlock(struct etpan_thread_op * op);
52 static void etpan_thread_stop(struct etpan_thread * thread);
55 static void etpan_thread_bind(struct etpan_thread * thread);
56 static int etpan_thread_manager_op_schedule(struct etpan_thread_manager * manager,
57 struct etpan_thread_op * op);
58 static void etpan_thread_manager_start(struct etpan_thread_manager * manager);
59 static void etpan_thread_op_cancel(struct etpan_thread_op * op);
64 TERMINATE_STATE_REQUESTED,
68 struct etpan_thread_manager * etpan_thread_manager_new(void)
70 struct etpan_thread_manager * manager;
73 manager = malloc(sizeof(* manager));
77 manager->thread_pool = carray_new(POOL_INIT_SIZE);
78 if (manager->thread_pool == NULL)
81 manager->thread_pending = carray_new(POOL_INIT_SIZE);
82 if (manager->thread_pending == NULL)
85 manager->can_create_thread = 1;
86 manager->unbound_count = 0;
88 r = pipe(manager->notify_fds);
95 carray_free(manager->thread_pending);
97 carray_free(manager->thread_pool);
104 void etpan_thread_manager_free(struct etpan_thread_manager * manager)
106 close(manager->notify_fds[1]);
107 close(manager->notify_fds[0]);
108 carray_free(manager->thread_pending);
109 carray_free(manager->thread_pool);
113 static struct etpan_thread * etpan_thread_new(void)
115 struct etpan_thread * thread;
118 thread = malloc(sizeof(* thread));
122 r = pthread_mutex_init(&thread->lock, NULL);
126 thread->op_list = carray_new(OP_INIT_SIZE);
127 if (thread->op_list == NULL)
130 thread->op_done_list = carray_new(OP_INIT_SIZE);
131 if (thread->op_done_list == NULL)
134 thread->start_sem = mailsem_new();
135 if (thread->start_sem == NULL)
136 goto free_op_done_list;
138 thread->stop_sem = mailsem_new();
139 if (thread->stop_sem == NULL)
142 thread->op_sem = mailsem_new();
143 if (thread->op_sem == NULL)
146 thread->manager = NULL;
147 thread->bound_count = 0;
148 thread->terminate_state = TERMINATE_STATE_NONE;
153 mailsem_free(thread->stop_sem);
155 mailsem_free(thread->start_sem);
157 carray_free(thread->op_done_list);
159 carray_free(thread->op_list);
161 pthread_mutex_destroy(&thread->lock);
168 static void etpan_thread_free(struct etpan_thread * thread)
170 mailsem_free(thread->op_sem);
171 mailsem_free(thread->stop_sem);
172 mailsem_free(thread->start_sem);
173 carray_free(thread->op_done_list);
174 carray_free(thread->op_list);
175 pthread_mutex_destroy(&thread->lock);
179 struct etpan_thread_op * etpan_thread_op_new(void)
181 struct etpan_thread_op * op;
184 op = malloc(sizeof(* op));
191 op->callback_data = NULL;
192 op->callback_called = 0;
200 r = pthread_mutex_init(&op->lock, NULL);
212 void etpan_thread_op_free(struct etpan_thread_op * op)
214 pthread_mutex_destroy(&op->lock);
218 static struct etpan_thread *
219 etpan_thread_manager_create_thread(struct etpan_thread_manager * manager)
221 struct etpan_thread * thread;
224 thread = etpan_thread_new();
228 thread->manager = manager;
230 r = etpan_thread_start(thread);
234 r = carray_add(manager->thread_pool, thread, NULL);
236 etpan_thread_stop(thread);
243 etpan_thread_free(thread);
249 etpan_thread_manager_terminate_thread(struct etpan_thread_manager * manager,
250 struct etpan_thread * thread)
255 for(i = 0 ; i < carray_count(manager->thread_pool) ; i ++) {
256 if (carray_get(manager->thread_pool, i) == thread) {
257 carray_delete(manager->thread_pool, i);
262 if (!etpan_thread_is_bound(thread))
263 manager->unbound_count --;
265 r = carray_add(manager->thread_pending, thread, NULL);
267 g_warning("complete failure of thread due to lack of memory (thread stop)");
270 etpan_thread_stop(thread);
273 static void manager_notify(struct etpan_thread_manager * manager)
279 r = write(manager->notify_fds[1], &ch, 1);
282 static void manager_ack(struct etpan_thread_manager * manager)
287 r = read(manager->notify_fds[0], &ch, 1);
290 static void thread_lock(struct etpan_thread * thread)
292 pthread_mutex_lock(&thread->lock);
295 static void thread_unlock(struct etpan_thread * thread)
297 pthread_mutex_unlock(&thread->lock);
300 static void thread_notify(struct etpan_thread * thread)
302 manager_notify(thread->manager);
305 static void * thread_run(void * data)
307 struct etpan_thread * thread;
312 mailsem_up(thread->start_sem);
316 struct etpan_thread_op * op;
318 mailsem_down(thread->op_sem);
323 if (carray_count(thread->op_list) > 0) {
324 op = carray_get(thread->op_list, 0);
325 carray_delete_slow(thread->op_list, 0);
330 thread_unlock(thread);
336 if (!etpan_thread_op_cancelled(op)) {
342 r = carray_add(thread->op_done_list, op, NULL);
344 g_warning("complete failure of thread due to lack of memory (op done)");
346 thread_unlock(thread);
348 thread_notify(thread);
352 thread->terminate_state = TERMINATE_STATE_DONE;
353 thread_unlock(thread);
355 thread_notify(thread);
357 mailsem_up(thread->stop_sem);
362 static int etpan_thread_start(struct etpan_thread * thread)
366 r = pthread_create(&thread->th_id, NULL, thread_run, thread);
370 mailsem_down(thread->start_sem);
375 static void etpan_thread_stop(struct etpan_thread * thread)
378 thread->terminate_state = TERMINATE_STATE_REQUESTED;
379 thread_unlock(thread);
381 mailsem_up(thread->op_sem);
383 /* this thread will be joined in the manager loop */
386 static int etpan_thread_is_stopped(struct etpan_thread * thread)
391 stopped = (thread->terminate_state == TERMINATE_STATE_DONE);
392 thread_unlock(thread);
397 static void etpan_thread_join(struct etpan_thread * thread)
399 mailsem_down(thread->stop_sem);
400 pthread_join(thread->th_id, NULL);
403 struct etpan_thread *
404 etpan_thread_manager_get_thread(struct etpan_thread_manager * manager)
406 struct etpan_thread * chosen_thread;
407 unsigned int chosen_thread_load;
409 struct etpan_thread * thread;
413 chosen_thread = NULL;
414 chosen_thread_load = 0;
416 for(i = 0 ; i < carray_count(manager->thread_pool) ; i ++) {
417 thread = carray_get(manager->thread_pool, i);
418 if (etpan_thread_is_bound(thread))
421 if (chosen_thread == NULL) {
422 chosen_thread = thread;
423 chosen_thread_load = etpan_thread_get_load(thread);
425 if (chosen_thread_load == 0)
431 load = etpan_thread_get_load(thread);
433 if (load < chosen_thread_load) {
434 chosen_thread = thread;
435 chosen_thread_load = load;
440 if (chosen_thread != NULL) {
441 if (manager->can_create_thread && (chosen_thread_load != 0)) {
442 chosen_thread = NULL;
448 if (chosen_thread != NULL)
449 return chosen_thread;
451 thread = etpan_thread_manager_create_thread(manager);
455 manager->unbound_count ++;
456 if (manager->unbound_count >= POOL_UNBOUND_MAX)
457 manager->can_create_thread = 0;
465 static unsigned int etpan_thread_get_load(struct etpan_thread * thread)
470 load = carray_count(thread->op_list);
471 thread_unlock(thread);
477 static void etpan_thread_bind(struct etpan_thread * thread)
479 thread->bound_count ++;
483 void etpan_thread_unbind(struct etpan_thread * thread)
485 thread->bound_count --;
488 static int etpan_thread_is_bound(struct etpan_thread * thread)
490 return (thread->bound_count != 0);
493 int etpan_thread_op_schedule(struct etpan_thread * thread,
494 struct etpan_thread_op * op)
498 if (thread->terminate_state != TERMINATE_STATE_NONE)
502 r = carray_add(thread->op_list, op, NULL);
503 thread_unlock(thread);
509 mailsem_up(thread->op_sem);
514 static void etpan_thread_op_lock(struct etpan_thread_op * op)
516 pthread_mutex_lock(&op->lock);
519 static void etpan_thread_op_unlock(struct etpan_thread_op * op)
521 pthread_mutex_unlock(&op->lock);
524 static int etpan_thread_op_cancelled(struct etpan_thread_op * op)
529 etpan_thread_op_lock(op);
531 cancelled = op->cancelled;
532 etpan_thread_op_unlock(op);
538 static void etpan_thread_op_cancel(struct etpan_thread_op * op)
540 etpan_thread_op_lock(op);
542 g_warning("cancelled twice");
545 if ((op->callback != NULL) && (!op->callback_called)) {
546 op->callback(op->cancelled, op->result, op->callback_data);
547 op->callback_called = 1;
549 etpan_thread_op_unlock(op);
554 static int etpan_thread_manager_op_schedule(struct etpan_thread_manager * manager,
555 struct etpan_thread_op * op)
557 struct etpan_thread * thread;
559 thread = etpan_thread_manager_get_thread(manager);
564 return etpan_thread_op_schedule(thread, op);
571 int etpan_thread_manager_get_fd(struct etpan_thread_manager * manager)
573 return manager->notify_fds[0];
576 static void loop_thread_list(carray * op_to_notify,
577 carray * thread_list)
582 for(i = 0 ; i < carray_count(thread_list) ; i ++) {
583 struct etpan_thread * thread;
586 thread = carray_get(thread_list, i);
590 for(j = 0 ; j < carray_count(thread->op_done_list) ; j ++) {
591 struct etpan_thread_op * op;
593 op = carray_get(thread->op_done_list, j);
594 r = carray_add(op_to_notify, op, NULL);
596 g_warning("complete failure of thread due to lack of memory (callback)");
600 carray_set_size(thread->op_done_list, 0);
602 thread_unlock(thread);
606 void etpan_thread_manager_loop(struct etpan_thread_manager * manager)
608 carray * op_to_notify;
611 manager_ack(manager);
613 op_to_notify = carray_new(OP_INIT_SIZE);
615 loop_thread_list(op_to_notify, manager->thread_pool);
616 loop_thread_list(op_to_notify, manager->thread_pending);
618 for(i = 0 ; i < carray_count(op_to_notify) ; i ++) {
619 struct etpan_thread_op * op;
622 op = carray_get(op_to_notify, i);
624 cancelled = etpan_thread_op_cancelled(op);
626 etpan_thread_op_lock(op);
628 if (!op->callback_called) {
629 if (op->callback != NULL)
630 op->callback(op->cancelled, op->result, op->callback_data);
633 etpan_thread_op_unlock(op);
635 if (op->cleanup != NULL)
639 carray_free(op_to_notify);
642 while (i < carray_count(manager->thread_pending)) {
643 struct etpan_thread * thread;
645 thread = carray_get(manager->thread_pending, i);
647 if (etpan_thread_is_stopped(thread)) {
648 etpan_thread_join(thread);
650 etpan_thread_free(thread);
652 carray_delete_slow(manager->thread_pending, i);
661 static void etpan_thread_manager_start(struct etpan_thread_manager * manager)
667 void etpan_thread_manager_stop(struct etpan_thread_manager * manager)
669 while (carray_count(manager->thread_pool) > 0) {
670 struct etpan_thread * thread;
672 thread = carray_get(manager->thread_pool, 0);
673 etpan_thread_manager_terminate_thread(manager, thread);
677 static int etpan_thread_manager_is_stopped(struct etpan_thread_manager * manager)
679 return ((carray_count(manager->thread_pending) == 0) &&
680 (carray_count(manager->thread_pool) == 0));
683 void etpan_thread_manager_join(struct etpan_thread_manager * manager)
685 while (!etpan_thread_manager_is_stopped(manager)) {
686 etpan_thread_manager_loop(manager);