2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2005-2012 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/>.
22 #include "claws-features.h"
27 #include "etpan-thread-manager.h"
32 #include <libetpan/mailsem.h>
33 #include <semaphore.h>
37 #include "etpan-errors.h"
40 #define POOL_UNBOUND_MAX 4
42 #define POOL_INIT_SIZE 8
43 #define OP_INIT_SIZE 8
45 static int etpan_thread_start(struct etpan_thread
* thread
);
46 static void etpan_thread_free(struct etpan_thread
* thread
);
47 static unsigned int etpan_thread_get_load(struct etpan_thread
* thread
);
48 static int etpan_thread_is_bound(struct etpan_thread
* thread
);
49 static int etpan_thread_manager_is_stopped(struct etpan_thread_manager
* manager
);
50 static void etpan_thread_join(struct etpan_thread
* thread
);
51 static struct etpan_thread
* etpan_thread_new(void);
52 static int etpan_thread_op_cancelled(struct etpan_thread_op
* op
);
53 static void etpan_thread_op_lock(struct etpan_thread_op
* op
);
54 static void etpan_thread_op_unlock(struct etpan_thread_op
* op
);
55 static void etpan_thread_stop(struct etpan_thread
* thread
);
58 static void etpan_thread_bind(struct etpan_thread
* thread
);
59 static int etpan_thread_manager_op_schedule(struct etpan_thread_manager
* manager
,
60 struct etpan_thread_op
* op
);
61 static void etpan_thread_manager_start(struct etpan_thread_manager
* manager
);
62 static void etpan_thread_op_cancel(struct etpan_thread_op
* op
);
67 TERMINATE_STATE_REQUESTED
,
71 struct etpan_thread_manager
* etpan_thread_manager_new(void)
73 struct etpan_thread_manager
* manager
;
76 manager
= malloc(sizeof(* manager
));
80 manager
->thread_pool
= carray_new(POOL_INIT_SIZE
);
81 if (manager
->thread_pool
== NULL
)
84 manager
->thread_pending
= carray_new(POOL_INIT_SIZE
);
85 if (manager
->thread_pending
== NULL
)
88 manager
->can_create_thread
= 1;
89 manager
->unbound_count
= 0;
91 r
= pipe(manager
->notify_fds
);
98 carray_free(manager
->thread_pending
);
100 carray_free(manager
->thread_pool
);
107 void etpan_thread_manager_free(struct etpan_thread_manager
* manager
)
109 close(manager
->notify_fds
[1]);
110 close(manager
->notify_fds
[0]);
111 carray_free(manager
->thread_pending
);
112 carray_free(manager
->thread_pool
);
116 static struct etpan_thread
* etpan_thread_new(void)
118 struct etpan_thread
* thread
;
121 thread
= malloc(sizeof(* thread
));
125 r
= pthread_mutex_init(&thread
->lock
, NULL
);
129 thread
->op_list
= carray_new(OP_INIT_SIZE
);
130 if (thread
->op_list
== NULL
)
133 thread
->op_done_list
= carray_new(OP_INIT_SIZE
);
134 if (thread
->op_done_list
== NULL
)
137 thread
->start_sem
= mailsem_new();
138 if (thread
->start_sem
== NULL
)
139 goto free_op_done_list
;
141 thread
->stop_sem
= mailsem_new();
142 if (thread
->stop_sem
== NULL
)
145 thread
->op_sem
= mailsem_new();
146 if (thread
->op_sem
== NULL
)
149 thread
->manager
= NULL
;
150 thread
->bound_count
= 0;
151 thread
->terminate_state
= TERMINATE_STATE_NONE
;
156 mailsem_free(thread
->stop_sem
);
158 mailsem_free(thread
->start_sem
);
160 carray_free(thread
->op_done_list
);
162 carray_free(thread
->op_list
);
164 pthread_mutex_destroy(&thread
->lock
);
171 static void etpan_thread_free(struct etpan_thread
* thread
)
173 mailsem_free(thread
->op_sem
);
174 mailsem_free(thread
->stop_sem
);
175 mailsem_free(thread
->start_sem
);
176 carray_free(thread
->op_done_list
);
177 carray_free(thread
->op_list
);
178 pthread_mutex_destroy(&thread
->lock
);
182 struct etpan_thread_op
* etpan_thread_op_new(void)
184 struct etpan_thread_op
* op
;
187 op
= malloc(sizeof(* op
));
191 memset(op
, 0, sizeof(* op
));
193 r
= pthread_mutex_init(&op
->lock
, NULL
);
205 void etpan_thread_op_free(struct etpan_thread_op
* op
)
207 pthread_mutex_destroy(&op
->lock
);
211 static struct etpan_thread
*
212 etpan_thread_manager_create_thread(struct etpan_thread_manager
* manager
)
214 struct etpan_thread
* thread
;
217 thread
= etpan_thread_new();
221 thread
->manager
= manager
;
223 r
= etpan_thread_start(thread
);
227 r
= carray_add(manager
->thread_pool
, thread
, NULL
);
229 etpan_thread_stop(thread
);
236 etpan_thread_free(thread
);
242 etpan_thread_manager_terminate_thread(struct etpan_thread_manager
* manager
,
243 struct etpan_thread
* thread
)
248 for(i
= 0 ; i
< carray_count(manager
->thread_pool
) ; i
++) {
249 if (carray_get(manager
->thread_pool
, i
) == thread
) {
250 carray_delete(manager
->thread_pool
, i
);
255 if (!etpan_thread_is_bound(thread
))
256 manager
->unbound_count
--;
258 r
= carray_add(manager
->thread_pending
, thread
, NULL
);
260 g_warning("complete failure of thread due to lack of memory (thread stop)");
263 etpan_thread_stop(thread
);
266 static void manager_notify(struct etpan_thread_manager
* manager
)
272 r
= write(manager
->notify_fds
[1], &ch
, 1);
274 g_warning("error writing notification to etpan thread manager");
278 static void manager_ack(struct etpan_thread_manager
* manager
)
283 r
= read(manager
->notify_fds
[0], &ch
, 1);
285 g_warning("error reading notification from etpan thread manager");
288 /* done in the GIOChannel handler in imap-thread.c and nntp-thread.c */
292 static void thread_lock(struct etpan_thread
* thread
)
294 pthread_mutex_lock(&thread
->lock
);
297 static void thread_unlock(struct etpan_thread
* thread
)
299 pthread_mutex_unlock(&thread
->lock
);
302 static void thread_notify(struct etpan_thread
* thread
)
304 manager_notify(thread
->manager
);
307 static void * thread_run(void * data
)
309 struct etpan_thread
* thread
;
314 mailsem_up(thread
->start_sem
);
318 struct etpan_thread_op
* op
;
320 mailsem_down(thread
->op_sem
);
325 if (carray_count(thread
->op_list
) > 0) {
326 op
= carray_get(thread
->op_list
, 0);
327 carray_delete_slow(thread
->op_list
, 0);
332 thread_unlock(thread
);
338 if (!etpan_thread_op_cancelled(op
)) {
344 r
= carray_add(thread
->op_done_list
, op
, NULL
);
346 g_warning("complete failure of thread due to lack of memory (op done)");
348 thread_unlock(thread
);
350 thread_notify(thread
);
354 thread
->terminate_state
= TERMINATE_STATE_DONE
;
355 thread_unlock(thread
);
357 thread_notify(thread
);
359 mailsem_up(thread
->stop_sem
);
364 static int etpan_thread_start(struct etpan_thread
* thread
)
368 r
= pthread_create(&thread
->th_id
, NULL
, thread_run
, thread
);
372 mailsem_down(thread
->start_sem
);
377 static void etpan_thread_stop(struct etpan_thread
* thread
)
380 thread
->terminate_state
= TERMINATE_STATE_REQUESTED
;
381 thread_unlock(thread
);
383 mailsem_up(thread
->op_sem
);
385 /* this thread will be joined in the manager loop */
388 static int etpan_thread_is_stopped(struct etpan_thread
* thread
)
393 stopped
= (thread
->terminate_state
== TERMINATE_STATE_DONE
);
394 thread_unlock(thread
);
399 static void etpan_thread_join(struct etpan_thread
* thread
)
401 mailsem_down(thread
->stop_sem
);
402 pthread_join(thread
->th_id
, NULL
);
405 struct etpan_thread
*
406 etpan_thread_manager_get_thread(struct etpan_thread_manager
* manager
)
408 struct etpan_thread
* chosen_thread
;
409 unsigned int chosen_thread_load
;
411 struct etpan_thread
* thread
;
415 chosen_thread
= NULL
;
416 chosen_thread_load
= 0;
418 for(i
= 0 ; i
< carray_count(manager
->thread_pool
) ; i
++) {
419 thread
= carray_get(manager
->thread_pool
, i
);
420 if (etpan_thread_is_bound(thread
))
423 if (chosen_thread
== NULL
) {
424 chosen_thread
= thread
;
425 chosen_thread_load
= etpan_thread_get_load(thread
);
427 if (chosen_thread_load
== 0)
433 load
= etpan_thread_get_load(thread
);
435 if (load
< chosen_thread_load
) {
436 chosen_thread
= thread
;
437 chosen_thread_load
= load
;
442 if (chosen_thread
!= NULL
) {
443 if (manager
->can_create_thread
&& (chosen_thread_load
!= 0)) {
444 chosen_thread
= NULL
;
450 if (chosen_thread
!= NULL
)
451 return chosen_thread
;
453 thread
= etpan_thread_manager_create_thread(manager
);
457 manager
->unbound_count
++;
458 if (manager
->unbound_count
>= POOL_UNBOUND_MAX
)
459 manager
->can_create_thread
= 0;
467 static unsigned int etpan_thread_get_load(struct etpan_thread
* thread
)
472 load
= carray_count(thread
->op_list
);
473 thread_unlock(thread
);
479 static void etpan_thread_bind(struct etpan_thread
* thread
)
481 thread
->bound_count
++;
485 void etpan_thread_unbind(struct etpan_thread
* thread
)
487 thread
->bound_count
--;
490 static int etpan_thread_is_bound(struct etpan_thread
* thread
)
492 return (thread
->bound_count
!= 0);
495 int etpan_thread_op_schedule(struct etpan_thread
* thread
,
496 struct etpan_thread_op
* op
)
500 if (thread
->terminate_state
!= TERMINATE_STATE_NONE
)
504 r
= carray_add(thread
->op_list
, op
, NULL
);
505 thread_unlock(thread
);
511 mailsem_up(thread
->op_sem
);
516 static void etpan_thread_op_lock(struct etpan_thread_op
* op
)
518 pthread_mutex_lock(&op
->lock
);
521 static void etpan_thread_op_unlock(struct etpan_thread_op
* op
)
523 pthread_mutex_unlock(&op
->lock
);
526 static int etpan_thread_op_cancelled(struct etpan_thread_op
* op
)
531 etpan_thread_op_lock(op
);
533 cancelled
= op
->cancelled
;
534 etpan_thread_op_unlock(op
);
539 int etpan_thread_manager_get_fd(struct etpan_thread_manager
* manager
)
541 return manager
->notify_fds
[0];
544 static void loop_thread_list(carray
* op_to_notify
,
545 carray
* thread_list
)
550 for(i
= 0 ; i
< carray_count(thread_list
) ; i
++) {
551 struct etpan_thread
* thread
;
554 thread
= carray_get(thread_list
, i
);
558 for(j
= 0 ; j
< carray_count(thread
->op_done_list
) ; j
++) {
559 struct etpan_thread_op
* op
;
561 op
= carray_get(thread
->op_done_list
, j
);
562 r
= carray_add(op_to_notify
, op
, NULL
);
564 g_warning("complete failure of thread due to lack of memory (callback)");
568 carray_set_size(thread
->op_done_list
, 0);
570 thread_unlock(thread
);
574 void etpan_thread_manager_loop(struct etpan_thread_manager
* manager
)
576 carray
* op_to_notify
;
579 manager_ack(manager
);
581 op_to_notify
= carray_new(OP_INIT_SIZE
);
583 loop_thread_list(op_to_notify
, manager
->thread_pool
);
584 loop_thread_list(op_to_notify
, manager
->thread_pending
);
586 for(i
= 0 ; i
< carray_count(op_to_notify
) ; i
++) {
587 struct etpan_thread_op
* op
;
589 op
= carray_get(op_to_notify
, i
);
591 etpan_thread_op_lock(op
);
593 if (!op
->callback_called
) {
594 if (op
->callback
!= NULL
)
595 op
->callback(op
->cancelled
, op
->result
, op
->callback_data
);
598 etpan_thread_op_unlock(op
);
600 if (op
->cleanup
!= NULL
)
604 carray_free(op_to_notify
);
607 while (i
< carray_count(manager
->thread_pending
)) {
608 struct etpan_thread
* thread
;
610 thread
= carray_get(manager
->thread_pending
, i
);
612 if (etpan_thread_is_stopped(thread
)) {
613 etpan_thread_join(thread
);
615 etpan_thread_free(thread
);
617 carray_delete_slow(manager
->thread_pending
, i
);
626 static void etpan_thread_manager_start(struct etpan_thread_manager
* manager
)
632 void etpan_thread_manager_stop(struct etpan_thread_manager
* manager
)
634 while (carray_count(manager
->thread_pool
) > 0) {
635 struct etpan_thread
* thread
;
637 thread
= carray_get(manager
->thread_pool
, 0);
638 etpan_thread_manager_terminate_thread(manager
, thread
);
642 static int etpan_thread_manager_is_stopped(struct etpan_thread_manager
* manager
)
644 return ((carray_count(manager
->thread_pending
) == 0) &&
645 (carray_count(manager
->thread_pool
) == 0));
648 void etpan_thread_manager_join(struct etpan_thread_manager
* manager
)
650 while (!etpan_thread_manager_is_stopped(manager
)) {
651 etpan_thread_manager_loop(manager
);