886e70d1e61d30e4f34dfbcf46dded7d45f79069
[platform/core/api/sound-pool.git] / src / stream_cb_manager.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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 /**
18  * @file stream_cb_manager.c
19  * @brief This file include implementation of protected API for the stream
20  * callback manager as part of SoundPool.
21  */
22
23 #include "internal/stream_cb_manager.h"
24
25 static gpointer __sound_pool_callback_isolator(gpointer user_data);
26 static void __thread_cancel_cleanup();
27 static void __queue_destroy_item(gpointer data);
28
29 static void __queue_destroy_item(gpointer data)
30 {
31         SP_DEBUG_FENTER();
32
33         SP_SAFE_GFREE(data);
34
35         SP_DEBUG_FLEAVE();
36 }
37
38 static void __thread_cancel_cleanup(void *user_data)
39 {
40         SP_DEBUG_FENTER();
41
42         stream_cb_manager_t *cbmgr = (stream_cb_manager_t *)user_data;
43         g_async_queue_unref(cbmgr->isolator_callback_queue);
44
45         pthread_cond_broadcast(&cbmgr->isolator_data_cond);
46         pthread_cond_destroy(&cbmgr->isolator_data_cond);
47         pthread_mutex_destroy(&cbmgr->isolator_data_mutex);
48
49         SP_SAFE_GFREE(cbmgr);
50
51         SP_DEBUG_FLEAVE();
52 }
53
54 sound_pool_error_e _stream_cb_manager_process_pending_events(stream_cb_manager_t *cbmgr)
55 {
56         SP_DEBUG_FENTER();
57         SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't handle callback "
58                         "manager, it is NULL.");
59
60         pthread_mutex_lock(&cbmgr->isolator_data_mutex);
61         if (cbmgr->isolator_state_changed) {
62                 cbmgr->isolator_force_run = TRUE;
63                 SP_INFO("waiting for isolator callback thread completion");
64                 pthread_cond_wait(&cbmgr->isolator_force_run_cond, &cbmgr->isolator_data_mutex);
65         }
66         pthread_mutex_unlock(&cbmgr->isolator_data_mutex);
67
68         SP_DEBUG_FLEAVE();
69         return SOUND_POOL_ERROR_NONE;
70 }
71
72 sound_pool_error_e _stream_cb_manager_signal_completed_events(stream_cb_manager_t *cbmgr)
73 {
74         SP_DEBUG_FENTER();
75         SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't handle callback "
76                         "manager, it is NULL.");
77
78         pthread_mutex_lock(&cbmgr->isolator_data_mutex);
79         cbmgr->isolator_state_changed = FALSE;
80         if (cbmgr->isolator_force_run) {
81                 SP_INFO("Signal indicating isolator callback completed the task");
82                 pthread_cond_signal(&cbmgr->isolator_force_run_cond);
83         }
84         cbmgr->isolator_force_run = FALSE;
85         pthread_mutex_unlock(&cbmgr->isolator_data_mutex);
86
87         SP_DEBUG_FLEAVE();
88         return SOUND_POOL_ERROR_NONE;
89 }
90
91 static gpointer __sound_pool_callback_isolator(gpointer user_data)
92 {
93         SP_DEBUG_FENTER();
94         int err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
95         if (err != 0)
96                 SP_INFO("Can't setup cancel type for isolation thread with error [%d].", err);
97         err = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
98         if (err != 0)
99                 SP_INFO("Can't setup cancel state for isolation thread with error [%d].", err);
100         SP_RETVM_IF(!user_data, NULL, "User data is NULL. Terminate callback thread");
101
102         stream_cb_manager_t *cbmgr = (stream_cb_manager_t *)user_data;
103         pthread_cleanup_push(__thread_cancel_cleanup,  user_data);
104         pthread_mutex_lock(&cbmgr->isolator_data_mutex);
105         gboolean runing = cbmgr->isolator_loop_run;
106         pthread_mutex_unlock(&cbmgr->isolator_data_mutex);
107
108         while (runing) {
109                 pthread_mutex_lock(&cbmgr->isolator_data_mutex);
110                 while (!cbmgr->isolator_state_changed)
111                         pthread_cond_wait(&cbmgr->isolator_data_cond, &cbmgr->isolator_data_mutex);
112                 pthread_mutex_unlock(&cbmgr->isolator_data_mutex);
113
114                 /* Iterate streams have been pushed to the queue and call for each
115                    state changed callback for each stream */
116                 stream_cb_manager_event_data_t *event_data = NULL;
117                 while ((event_data = (stream_cb_manager_event_data_t *)
118                                 g_async_queue_try_pop(cbmgr->isolator_callback_queue))) {
119
120                         sound_pool_t* _pool = cbmgr->pool;
121                         if (NULL != event_data->callback) {
122                                 event_data->callback(_pool,
123                                                 event_data->stream_id, event_data->state_previous,
124                                                 event_data->state,
125                                                 event_data->user_data);
126                         }
127                         SP_SAFE_GFREE(event_data);
128                 }
129                 /* Signal indicating isolator callback thread completed the events */
130                 _stream_cb_manager_signal_completed_events(cbmgr);
131
132                 pthread_mutex_lock(&cbmgr->isolator_data_mutex);
133                 runing = cbmgr->isolator_loop_run;
134                 pthread_mutex_unlock(&cbmgr->isolator_data_mutex);
135         }
136
137         pthread_cleanup_pop(0);
138         SP_DEBUG_FLEAVE();
139         return NULL;
140 }
141
142 sound_pool_error_e _stream_cb_manager_create(sound_pool_t *pool,
143                 stream_cb_manager_t **cbmgr)
144 {
145         SP_DEBUG_FENTER();
146
147         int err;
148         sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
149         SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, "Stream callback "
150                         "manager can't be created for NULL sound pool");
151         SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create "
152                         "stream state changing callback manager. Manager pointer is NULL");
153
154         stream_cb_manager_t *_cbmgr = NULL;
155         SP_RETVM_IF(!(_cbmgr = g_try_malloc0(sizeof(*_cbmgr))),
156                         SOUND_POOL_ERROR_OUT_OF_MEMORY,
157                         "Memory alloc failure. Can't create stream callback manager");
158
159         _cbmgr->isolator_callback_queue = g_async_queue_new_full(__queue_destroy_item);
160
161         err = pthread_mutex_init(&_cbmgr->isolator_data_mutex, NULL);
162
163         if (0 != err) {
164                 SP_ERROR("Error while initialize mutex for isolation thread with error[%d].", err);
165                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
166                 GOTO_FAIL("", creturn);
167         }
168
169         _cbmgr->isolator_state_changed = FALSE;
170         _cbmgr->isolator_loop_run = TRUE;
171         _cbmgr->isolator_force_run = FALSE;
172
173         err = pthread_cond_init(&_cbmgr->isolator_data_cond, NULL);
174
175         if (0 != err) {
176                 SP_ERROR("Error while initialize condition for isolation thread with error[%d].", err);
177                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
178                 GOTO_FAIL("", creturn);
179         }
180         err = pthread_cond_init(&_cbmgr->isolator_force_run_cond, NULL);
181
182         if (0 != err) {
183                 SP_ERROR("Error while initialize condition for isolation thread with error[%d].", err);
184                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
185                 GOTO_FAIL("", creturn);
186         }
187         err = pthread_create(&_cbmgr->isolator_thread, NULL, &__sound_pool_callback_isolator, (void*)_cbmgr);
188
189         if (0 != err) {
190                 SP_ERROR("Error while thread creating for isolation thread with error[%d].", err);
191                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
192                 GOTO_FAIL("", creturn);
193         }
194
195         _cbmgr->pool = pool;
196         pool->cbmgr = _cbmgr;
197         *cbmgr = _cbmgr;
198
199         SP_DEBUG_FLEAVE();
200
201         return SOUND_POOL_ERROR_NONE;
202
203 creturn:
204         if (_cbmgr->isolator_callback_queue)
205                 g_async_queue_unref(_cbmgr->isolator_callback_queue);
206         if (&_cbmgr->isolator_data_mutex)
207                 pthread_mutex_destroy(&_cbmgr->isolator_data_mutex);
208         if (&_cbmgr->isolator_data_cond)
209                 pthread_cond_destroy(&_cbmgr->isolator_data_cond);
210         SP_SAFE_GFREE(_cbmgr);
211
212         SP_DEBUG_FLEAVE();
213         return ret;
214 }
215
216 sound_pool_error_e _stream_cb_manager_destroy(stream_cb_manager_t *cbmgr)
217 {
218         SP_DEBUG_FENTER();
219         void *return_val;
220         int err;
221         SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't handle callback "
222                         "manager, it is NULL.");
223         sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
224
225         pthread_t thread = cbmgr->isolator_thread;
226
227         /* Wait for completing the isolator callback thread events */
228         _stream_cb_manager_process_pending_events(cbmgr);
229         err = pthread_kill(thread, 0);
230         if (0 == err) {
231                 err = pthread_cancel(thread);
232                 if (0 != err) {
233                         SP_ERROR("Error while cancelling of isolation thread[%d].", err);
234                         ret = SOUND_POOL_ERROR_INVALID_OPERATION;
235                         GOTO_FAIL("", creturn);
236                 }
237         } else {
238                 SP_ERROR("Invalid isolation thread[%d].", err);
239                 ret = SOUND_POOL_ERROR_INVALID_OPERATION;
240                 GOTO_FAIL("", creturn);
241         }
242
243         err = pthread_join(thread, &return_val);
244         if (0 != err)
245                 SP_ERROR("Error while joining of isolation thread[%d].", err);
246         if (return_val == PTHREAD_CANCELED)
247                 SP_INFO("Isolation thread canceled.");
248         else {
249                 ret = SOUND_POOL_ERROR_NONE;
250                 GOTO_FAIL("Routine joining of isolation thread.", creturn);
251         }
252
253         SP_DEBUG_FLEAVE();
254         return ret;
255
256 creturn:
257         g_async_queue_unref(cbmgr->isolator_callback_queue);
258         pthread_mutex_destroy(&cbmgr->isolator_data_mutex);
259         pthread_cond_destroy(&cbmgr->isolator_data_cond);
260         SP_SAFE_GFREE(cbmgr);
261         SP_DEBUG_FLEAVE();
262         return ret;
263 }
264
265 sound_pool_error_e _stream_cb_manager_register_event(stream_cb_manager_t *cbmgr,
266                 sound_stream_t *stream)
267 {
268         SP_DEBUG_FENTER();
269         SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER,
270                         "Can't handle callback manager, it is NULL.");
271         SP_RETVM_IF(!stream, SOUND_POOL_ERROR_INVALID_PARAMETER,
272                         "Can't handle stream in callback manager, it is NULL.");
273
274         stream_cb_manager_event_data_t *event_data = NULL;
275         SP_RETVM_IF(!(event_data = g_try_malloc0(sizeof(stream_cb_manager_event_data_t))),
276                         SOUND_POOL_ERROR_OUT_OF_MEMORY,
277                         "Memory alloc failure. Can't create stream callback event data structure.");
278         event_data->stream_id = stream->id;
279         event_data->callback = stream->state_cb_info.callback;
280         event_data->user_data = stream->state_cb_info.user_data;
281         event_data->state = stream->state;
282         event_data->state_previous = stream->state_previous;
283
284         g_async_queue_push(cbmgr->isolator_callback_queue, (gpointer)(event_data));
285         pthread_mutex_lock(&cbmgr->isolator_data_mutex);
286         cbmgr->isolator_state_changed = TRUE;
287         pthread_cond_signal(&cbmgr->isolator_data_cond);
288         pthread_mutex_unlock(&cbmgr->isolator_data_mutex);
289
290         SP_DEBUG_FLEAVE();
291         return SOUND_POOL_ERROR_NONE;
292 }