Merge "Removed unused result variable from sound_codec_asm_callback" into tizen
[platform/core/multimedia/libmm-sound.git] / server / mm_sound_thread_pool.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Seungbae Shin <seungbae.shin@samsung.com>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <pthread.h>
25 #include <unistd.h>
26
27 #include <mm_error.h>
28 #include <mm_debug.h>
29 #include <mm_sound_thread_pool.h>
30
31 #define USE_G_THREAD_POOL
32 #ifdef USE_G_THREAD_POOL
33 #include <glib.h>
34 GThreadPool* g_pool;
35
36 #define MAX_UNUSED_THREADS_IN_THREADPOOL        10
37
38 typedef struct __THREAD_INFO
39 {
40         void (*func)(gpointer data);
41         void *param;
42 } THREAD_INFO;
43
44 static void __DummyWork (void* param)
45 {
46         debug_msg ("thread index = %d\n", (int)param);
47         sleep (1);
48 }
49
50 static void __ThreadWork(gpointer data, gpointer user_data)
51 {
52         THREAD_INFO* info = (THREAD_INFO*)data;
53         if (info) {
54                  if (info->func) {
55                                 debug_msg ("Calling [%p] with param [%p]\n", info->func, info->param);
56                                 info->func (info->param);
57                  } else {
58                                 debug_warning ("No func to call....\n");
59                  }
60
61                  /* Info was allocated by MMSoundThreadPoolRun(). 
62                         The actual content of info should be  freed whether inside func or outside (if handle) */
63                  debug_msg ("free [%p]\n", info);
64                  free (info);
65                  info = NULL;
66         } else {
67                 debug_warning ("No valid thread info...Nothing to do...\n");
68         }
69 }
70
71 int MMSoundThreadPoolDump(int fulldump)
72 {
73         if (g_pool == NULL) {
74                 debug_error ("No thread pool initialized....\n");
75                 return MM_ERROR_SOUND_INTERNAL;
76         }
77
78         if (fulldump) {
79                 debug_msg ("##### [ThreadPool] max threads=[%d], max unused=[%d], max idle time=[%d]\n",
80                                 g_thread_pool_get_max_threads (g_pool),
81                                 g_thread_pool_get_max_unused_threads(),
82                                 g_thread_pool_get_max_idle_time()       );
83         }
84         debug_msg ("***** [ThreadPool] running=[%d], unused=[%d]\n",
85                         g_thread_pool_get_num_threads (g_pool),
86                         g_thread_pool_get_num_unused_threads() );
87
88         return MM_ERROR_NONE;
89 }
90
91 int MMSoundThreadPoolInit()
92 {
93         int i=0;
94         GError* error = NULL;
95
96         /* Create thread pool (non-exclude mode with infinite max threads) */
97         g_pool = g_thread_pool_new (__ThreadWork, NULL, -1, FALSE, &error);
98         if (g_pool == NULL && error != NULL) {
99                 debug_error ("thread pool created failed : %s\n", error->message);
100                 g_error_free (error);
101                 return MM_ERROR_SOUND_INTERNAL;
102         }
103         debug_msg ("thread pool created successfully\n");
104
105         MMSoundThreadPoolDump(TRUE);
106
107         /* Thread pool setting : this will maintain at least 10 unused threads and this will be reused. */
108         /* If no unused thread left, new thread will be created, but always maintain 10 unused thread */
109         debug_msg ("thread pool set max unused threads to %d\n", MAX_UNUSED_THREADS_IN_THREADPOOL);
110         g_thread_pool_set_max_unused_threads (MAX_UNUSED_THREADS_IN_THREADPOOL);
111
112         /* To reserve unused threads, let's start some threads for beggining 
113            This dummy thread will be remained unused as soon as it started */    
114         debug_msg ("run threads to reserve minimum thread\n");
115         for (i=0; i<MAX_UNUSED_THREADS_IN_THREADPOOL; i++) {
116                 MMSoundThreadPoolRun ((void *)i, __DummyWork);
117         }
118
119         MMSoundThreadPoolDump(TRUE);
120
121      return MM_ERROR_NONE;
122 }
123
124 int MMSoundThreadPoolRun(void *param, void (*func)(void*))
125 {
126         GError* error = NULL;
127
128         /* Dump current thread pool */
129         MMSoundThreadPoolDump(FALSE);
130
131         /* Create thread info structure. 
132            This thread info data will be free in __ThreadWork(), after use. */
133         THREAD_INFO* thread_info = (THREAD_INFO*)malloc (sizeof(THREAD_INFO));
134         if (thread_info) {
135                 thread_info->func = func;
136                 thread_info->param = param;
137                 debug_msg ("alloc thread_info = %p\n", thread_info);
138         
139                 /* Add thread to queue of thread pool */
140                 g_thread_pool_push (g_pool, thread_info, &error);
141                 if (error) {
142                         debug_error ("g_thread_pool_push failed : %s\n", error->message);
143                         g_error_free (error);
144                         return MM_ERROR_SOUND_INTERNAL;
145                 }
146         } else {
147                 debug_error("failed to alloc thread info\n");
148                 return MM_ERROR_SOUND_INTERNAL;
149         }
150
151     return MM_ERROR_NONE;
152 }
153
154 int MMSoundThreadPoolFini(void)
155 {
156         /* If immediate is TRUE, no new task is processed for pool. 
157         Otherwise pool is not freed before the last task is processed. 
158         Note however, that no thread of this pool is interrupted, while processing a task. 
159         Instead at least all still running threads can finish their tasks before the pool is freed.
160
161         If wait_ is TRUE, the functions does not return before all tasks to be processed 
162         (dependent on immediate, whether all or only the currently running) are ready. 
163         Otherwise the function returns immediately.     */
164         debug_msg ("thread pool will be free\n");
165         g_thread_pool_free (g_pool, TRUE, FALSE);
166     
167         return MM_ERROR_NONE;
168 }
169
170 #else // USE_G_THREAD_POOL
171
172 #define THREAD_POOL_MAX 10
173
174 struct __control_t
175 {
176         pthread_t threadid;
177         pthread_cond_t condition;
178         int stopflag;
179         void *param;
180         void (*func)(void *);
181 };
182
183 static int threadmap[THREAD_POOL_MAX];
184 static struct __control_t control[THREAD_POOL_MAX];
185
186 static pthread_cond_t startcond = PTHREAD_COND_INITIALIZER;
187 static pthread_mutex_t startsync = PTHREAD_MUTEX_INITIALIZER;
188 static pthread_mutex_t controlsync = PTHREAD_MUTEX_INITIALIZER;
189 static pthread_mutex_t funcsync = PTHREAD_MUTEX_INITIALIZER;
190
191 static int __GetEmptyPool(void)
192 {
193         int count = 0;
194         while ((count < THREAD_POOL_MAX) && threadmap[count] == 1)
195                 ++count;
196         return count == THREAD_POOL_MAX ? -1 : count;
197 }
198
199 static void __SetPool(int n)
200 {
201     threadmap[n] = 1;
202 }
203
204 static void __ResetPool(int n)
205 {
206     threadmap[n] = 0;
207 }
208
209 static void __InitPool(void)
210 {
211     int count = 0;
212     for (count = 0; count < THREAD_POOL_MAX; count++)
213     {
214         threadmap[count] = 0;
215         if (pthread_cond_init(&control[count].condition, NULL) != 0)
216             perror("Make Thread Condition");
217     }
218 }
219
220 static void __DestroyPool(void)
221 {
222     int count = 0;
223     for (count = 0; count < THREAD_POOL_MAX; count++)
224     {
225         if (pthread_cond_destroy(&control[count].condition) != 0)
226         {
227             perror("Remove Thread Condition");
228             exit(0);
229         }
230     }
231 }
232
233 static void* __ThreadWork(void *param)
234 {
235     int myid = -1;
236
237     myid = (int)param;
238
239     pthread_mutex_lock(&startsync);
240     pthread_cond_signal(&startcond);
241     pthread_mutex_unlock(&startsync);
242
243     while(1)
244     {
245         pthread_mutex_lock(&controlsync);
246         pthread_cond_wait(&control[myid].condition, &controlsync);
247         pthread_mutex_unlock(&controlsync);
248
249         if (control[myid].func != NULL)
250             control[myid].func(control[myid].param);
251 /*        if (control[myid].param != NULL)
252             free(control[myid].param);*/
253
254         control[myid].func = NULL;
255         control[myid].param = NULL;
256         pthread_mutex_lock(&startsync);
257         __ResetPool(myid);
258         pthread_mutex_unlock(&startsync);
259
260         if (control[myid].stopflag) {
261             pthread_exit(0);
262         }
263     }
264 }
265
266 int MMSoundThreadPoolDump(void)
267 {
268         int count = 0;
269         int ret = MM_ERROR_NONE;
270
271         fprintf(stdout, "================================================================================\n");
272         fprintf(stdout, "                              Thread States                                     \n");
273         fprintf(stdout, "--------------------------------------------------------------------------------\n");
274         for (count = 0; count < THREAD_POOL_MAX; count ++)
275         {
276                 fprintf(stdout, "Thread %d\n", control[count].threadid);
277                 fprintf(stdout, "Current State is \"%s\"\n", threadmap[count] ? "Running" : "Ready");
278                 if (threadmap[count])
279                 {
280                         fprintf(stdout, "Running function address %p\n", control[count].func);
281                 }
282                 else
283                 {
284                         fprintf(stdout, "Threadmap is NULL\n");
285                         ret = MM_ERROR_SOUND_INTERNAL;
286                 }
287         }
288         fprintf(stdout, "================================================================================\n");
289         return ret;
290 }
291
292 int MMSoundThreadPoolInit(void)
293 {
294     volatile int count = 0;
295     pthread_mutex_lock(&funcsync);
296
297     __InitPool();
298
299     for (count = 0; count < THREAD_POOL_MAX; count++)
300     {
301         control[count].stopflag = 0;
302         pthread_mutex_lock(&startsync);
303         if (pthread_create(&control[count].threadid, NULL, __ThreadWork, (void*)count) < 0)
304         {
305             perror("Make Thread Fail");
306             exit(0);
307         }
308         pthread_cond_wait(&startcond, &startsync);
309         pthread_mutex_unlock(&startsync);
310         usleep(100); /* Delay for thread init */
311     }
312     pthread_mutex_unlock(&funcsync);
313     return MM_ERROR_NONE;
314 }
315
316 int MMSoundThreadPoolRun(void *param, void (*func)(void*))
317 {
318     int poolnum = -1;
319
320     pthread_mutex_lock(&funcsync);
321     pthread_mutex_lock(&startsync);
322     poolnum = __GetEmptyPool();
323     if (poolnum < 0) {
324         pthread_mutex_unlock(&startsync);
325         pthread_mutex_unlock(&funcsync);
326         return MM_ERROR_COMMON_NO_FREE_SPACE;
327     }
328     __SetPool(poolnum);
329     pthread_mutex_unlock(&startsync);
330     control[poolnum].param = param;
331     control[poolnum].func = func;
332     pthread_cond_signal(&control[poolnum].condition);
333     pthread_mutex_unlock(&funcsync);
334
335     return MM_ERROR_NONE;
336 }
337
338 int MMSoundThreadPoolFini(void)
339 {
340     int count = 0;
341
342     pthread_mutex_lock(&funcsync);
343     for (count = 0; count < THREAD_POOL_MAX; count++)
344     {
345         control[count].stopflag = 1;
346         pthread_cond_signal(&control[count].condition);
347         if (pthread_join(control[count].threadid, NULL) < 0)
348         {
349             perror("Join Fail");
350             exit(0);
351         }
352     }
353     __DestroyPool();
354     pthread_mutex_unlock(&funcsync);
355
356     return MM_ERROR_NONE;
357 }
358 #endif // USE_G_THREAD_POOL
359