1 /*
2  * e-operation-pool.c
3  *
4  * Copyright (C) 2011 Novell, Inc. (www.novell.com)
5  *
6  * This library is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include "e-operation-pool.h"
21 
22 struct _EOperationPool {
23 	GThreadPool *pool;
24 
25 	GMutex ops_lock;
26 	GHashTable *ops;
27 	guint32 last_opid;
28 };
29 
30 /**
31  * e_operation_pool_new: (skip)
32  * @max_threads: Maximum number of threads for this pool
33  * @thread_func: Function to run for a given thread
34  * @user_data: The user data to pass to @thread_func
35  *
36  * Returns: (transfer full): a new #EOperationPool with the given settings.
37  *   Free it with e_operation_pool_free(), when no longer needed.
38  *
39  * Since: 3.2
40  **/
41 EOperationPool *
e_operation_pool_new(guint max_threads,GFunc thread_func,gpointer user_data)42 e_operation_pool_new (guint max_threads,
43                       GFunc thread_func,
44                       gpointer user_data)
45 {
46 	EOperationPool *pool;
47 	GThreadPool *thread_pool;
48 	GError *error = NULL;
49 
50 	g_return_val_if_fail (thread_func != NULL, NULL);
51 
52 	thread_pool = g_thread_pool_new (thread_func, user_data, max_threads, FALSE, &error);
53 	if (error) {
54 		g_warning ("%s: Failed to create thread pool: %s", G_STRFUNC, error->message);
55 		g_error_free (error);
56 		return NULL;
57 	}
58 
59 	pool = g_slice_new0 (EOperationPool);
60 	pool->pool = thread_pool;
61 	g_mutex_init (&pool->ops_lock);
62 	pool->ops = g_hash_table_new (g_direct_hash, g_direct_equal);
63 	pool->last_opid = 0;
64 
65 	/* Kill threads which don't do anything for 10 seconds */
66 	g_thread_pool_set_max_idle_time (10 * 1000);
67 
68 	return pool;
69 }
70 
71 /**
72  * e_operation_pool_free:
73  * @pool: an #EOperationPool
74  *
75  * Frees previously created @pool.
76  *
77  * Since: 3.2
78  **/
79 void
e_operation_pool_free(EOperationPool * pool)80 e_operation_pool_free (EOperationPool *pool)
81 {
82 	g_return_if_fail (pool != NULL);
83 
84 	g_thread_pool_free (pool->pool, FALSE, FALSE);
85 	g_mutex_clear (&pool->ops_lock);
86 	g_hash_table_destroy (pool->ops);
87 	g_slice_free (EOperationPool, pool);
88 }
89 
90 /**
91  * e_operation_pool_reserve_opid:
92  * @pool: an #EOperationPool
93  *
94  * Reserves new operation ID, which is returned. This operation ID may
95  * be released by e_operation_pool_release_opid() when the operation
96  * is finished.
97  *
98  * Returns: a new operation ID
99  *
100  * Since: 3.2
101  **/
102 guint32
e_operation_pool_reserve_opid(EOperationPool * pool)103 e_operation_pool_reserve_opid (EOperationPool *pool)
104 {
105 	guint32 opid;
106 
107 	g_return_val_if_fail (pool != NULL, 0);
108 	g_return_val_if_fail (pool->ops != NULL, 0);
109 
110 	g_mutex_lock (&pool->ops_lock);
111 
112 	pool->last_opid++;
113 	if (!pool->last_opid)
114 		pool->last_opid = 1;
115 
116 	while (pool->last_opid && g_hash_table_lookup (pool->ops, GUINT_TO_POINTER (pool->last_opid)))
117 		pool->last_opid++;
118 
119 	opid = pool->last_opid;
120 	if (opid)
121 		g_hash_table_insert (pool->ops, GUINT_TO_POINTER (opid), GUINT_TO_POINTER (1));
122 
123 	g_mutex_unlock (&pool->ops_lock);
124 
125 	g_return_val_if_fail (opid != 0, 0);
126 
127 	return opid;
128 }
129 
130 /**
131  * e_operation_pool_release_opid:
132  * @pool: an #EOperationPool
133  * @opid: an operation ID
134  *
135  * Releases @opid previously reserved by e_operation_pool_reserve_opid().
136  *
137  * Since: 3.2
138  **/
139 void
e_operation_pool_release_opid(EOperationPool * pool,guint32 opid)140 e_operation_pool_release_opid (EOperationPool *pool,
141                                guint32 opid)
142 {
143 	g_return_if_fail (pool != NULL);
144 	g_return_if_fail (pool->ops != NULL);
145 
146 	g_mutex_lock (&pool->ops_lock);
147 	g_hash_table_remove (pool->ops, GUINT_TO_POINTER (opid));
148 	g_mutex_unlock (&pool->ops_lock);
149 }
150 
151 /**
152  * e_operation_pool_push:
153  * @pool: an #EOperationPool
154  * @opdata: user data for the operation
155  *
156  * Pushes an operation to be processed.  @opdata is passed to the function
157  * provided in e_operation_pool_new().
158  *
159  * Since: 3.2
160  **/
161 void
e_operation_pool_push(EOperationPool * pool,gpointer opdata)162 e_operation_pool_push (EOperationPool *pool,
163                        gpointer opdata)
164 {
165 	GError *error = NULL;
166 
167 	g_return_if_fail (pool != NULL);
168 	g_return_if_fail (pool->pool != NULL);
169 
170 	g_thread_pool_push (pool->pool, opdata, &error);
171 
172 	if (error) {
173 		g_warning ("%s: Failed to push to thread pool: %s", G_STRFUNC, error->message);
174 		g_error_free (error);
175 	}
176 }
177