1 /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are
5  * met:
6  *     * Redistributions of source code must retain the above copyright
7  *       notice, this list of conditions and the following disclaimer.
8  *     * Redistributions in binary form must reproduce the above
9  *       copyright notice, this list of conditions and the following
10  *       disclaimer in the documentation and/or other materials provided
11  *       with the distribution.
12  *     * Neither the name of The Linux Foundation, nor the names of its
13  *       contributors may be used to endorse or promote products derived
14  *       from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "msg_q.h"
30 
31 #define LOG_TAG "LocSvc_utils_q"
32 #include "log_util.h"
33 #include "platform_lib_includes.h"
34 #include "linked_list.h"
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <pthread.h>
38 
39 typedef struct msg_q {
40    void* msg_list;                  /* Linked list to store information */
41    pthread_cond_t  list_cond;       /* Condition variable for waiting on msg queue */
42    pthread_mutex_t list_mutex;      /* Mutex for exclusive access to message queue */
43    int unblocked;                   /* Has this message queue been unblocked? */
44 } msg_q;
45 
46 /*===========================================================================
47 FUNCTION    convert_linked_list_err_type
48 
49 DESCRIPTION
50    Converts from one set of enum values to another.
51 
52    linked_list_val: Value to convert to msg_q_enum_type
53 
54 DEPENDENCIES
55    N/A
56 
57 RETURN VALUE
58    Corresponding linked_list_enum_type in msg_q_enum_type
59 
60 SIDE EFFECTS
61    N/A
62 
63 ===========================================================================*/
64 static msq_q_err_type convert_linked_list_err_type(linked_list_err_type linked_list_val)
65 {
66    switch( linked_list_val )
67    {
68    case eLINKED_LIST_SUCCESS:
69       return eMSG_Q_SUCCESS;
70    case eLINKED_LIST_INVALID_PARAMETER:
71       return eMSG_Q_INVALID_PARAMETER;
72    case eLINKED_LIST_INVALID_HANDLE:
73       return eMSG_Q_INVALID_HANDLE;
74    case eLINKED_LIST_UNAVAILABLE_RESOURCE:
75       return eMSG_Q_UNAVAILABLE_RESOURCE;
76    case eLINKED_LIST_INSUFFICIENT_BUFFER:
77       return eMSG_Q_INSUFFICIENT_BUFFER;
78 
79    case eLINKED_LIST_FAILURE_GENERAL:
80    default:
81       return eMSG_Q_FAILURE_GENERAL;
82    }
83 }
84 
85 /* ----------------------- END INTERNAL FUNCTIONS ---------------------------------------- */
86 
87 /*===========================================================================
88 
89   FUNCTION:   msg_q_init
90 
91   ===========================================================================*/
92 msq_q_err_type msg_q_init(void** msg_q_data)
93 {
94    if( msg_q_data == NULL )
95    {
96       LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
97       return eMSG_Q_INVALID_PARAMETER;
98    }
99 
100    msg_q* tmp_msg_q;
101    tmp_msg_q = (msg_q*)calloc(1, sizeof(msg_q));
102    if( tmp_msg_q == NULL )
103    {
104       LOC_LOGE("%s: Unable to allocate space for message queue!\n", __FUNCTION__);
105       return eMSG_Q_FAILURE_GENERAL;
106    }
107 
108    if( linked_list_init(&tmp_msg_q->msg_list) != 0 )
109    {
110       LOC_LOGE("%s: Unable to initialize storage list!\n", __FUNCTION__);
111       free(tmp_msg_q);
112       return eMSG_Q_FAILURE_GENERAL;
113    }
114 
115    if( pthread_mutex_init(&tmp_msg_q->list_mutex, NULL) != 0 )
116    {
117       LOC_LOGE("%s: Unable to initialize list mutex!\n", __FUNCTION__);
118       linked_list_destroy(&tmp_msg_q->msg_list);
119       free(tmp_msg_q);
120       return eMSG_Q_FAILURE_GENERAL;
121    }
122 
123    if( pthread_cond_init(&tmp_msg_q->list_cond, NULL) != 0 )
124    {
125       LOC_LOGE("%s: Unable to initialize msg q cond var!\n", __FUNCTION__);
126       linked_list_destroy(&tmp_msg_q->msg_list);
127       pthread_mutex_destroy(&tmp_msg_q->list_mutex);
128       free(tmp_msg_q);
129       return eMSG_Q_FAILURE_GENERAL;
130    }
131 
132    tmp_msg_q->unblocked = 0;
133 
134    *msg_q_data = tmp_msg_q;
135 
136    return eMSG_Q_SUCCESS;
137 }
138 
139 /*===========================================================================
140 
141   FUNCTION:   msg_q_init2
142 
143   ===========================================================================*/
144 const void* msg_q_init2()
145 {
146   void* q = NULL;
147   if (eMSG_Q_SUCCESS != msg_q_init(&q)) {
148     q = NULL;
149   }
150   return q;
151 }
152 
153 /*===========================================================================
154 
155   FUNCTION:   msg_q_destroy
156 
157   ===========================================================================*/
158 msq_q_err_type msg_q_destroy(void** msg_q_data)
159 {
160    if( msg_q_data == NULL )
161    {
162       LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
163       return eMSG_Q_INVALID_HANDLE;
164    }
165 
166    msg_q* p_msg_q = (msg_q*)*msg_q_data;
167 
168    linked_list_destroy(&p_msg_q->msg_list);
169    pthread_mutex_destroy(&p_msg_q->list_mutex);
170    pthread_cond_destroy(&p_msg_q->list_cond);
171 
172    p_msg_q->unblocked = 0;
173 
174    free(*msg_q_data);
175    *msg_q_data = NULL;
176 
177    return eMSG_Q_SUCCESS;
178 }
179 
180 /*===========================================================================
181 
182   FUNCTION:   msg_q_snd
183 
184   ===========================================================================*/
185 msq_q_err_type msg_q_snd(void* msg_q_data, void* msg_obj, void (*dealloc)(void*))
186 {
187    msq_q_err_type rv;
188    if( msg_q_data == NULL )
189    {
190       LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
191       return eMSG_Q_INVALID_HANDLE;
192    }
193    if( msg_obj == NULL )
194    {
195       LOC_LOGE("%s: Invalid msg_obj parameter!\n", __FUNCTION__);
196       return eMSG_Q_INVALID_PARAMETER;
197    }
198 
199    msg_q* p_msg_q = (msg_q*)msg_q_data;
200 
201    pthread_mutex_lock(&p_msg_q->list_mutex);
202    LOC_LOGD("%s: Sending message with handle = 0x%p\n", __FUNCTION__, msg_obj);
203 
204    if( p_msg_q->unblocked )
205    {
206       LOC_LOGE("%s: Message queue has been unblocked.\n", __FUNCTION__);
207       pthread_mutex_unlock(&p_msg_q->list_mutex);
208       return eMSG_Q_UNAVAILABLE_RESOURCE;
209    }
210 
211    rv = convert_linked_list_err_type(linked_list_add(p_msg_q->msg_list, msg_obj, dealloc));
212 
213    /* Show data is in the message queue. */
214    pthread_cond_signal(&p_msg_q->list_cond);
215 
216    pthread_mutex_unlock(&p_msg_q->list_mutex);
217 
218    LOC_LOGD("%s: Finished Sending message with handle = 0x%p\n", __FUNCTION__, msg_obj);
219 
220    return rv;
221 }
222 
223 /*===========================================================================
224 
225   FUNCTION:   msg_q_rcv
226 
227   ===========================================================================*/
228 msq_q_err_type msg_q_rcv(void* msg_q_data, void** msg_obj)
229 {
230    msq_q_err_type rv;
231    if( msg_q_data == NULL )
232    {
233       LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
234       return eMSG_Q_INVALID_HANDLE;
235    }
236 
237    if( msg_obj == NULL )
238    {
239       LOC_LOGE("%s: Invalid msg_obj parameter!\n", __FUNCTION__);
240       return eMSG_Q_INVALID_PARAMETER;
241    }
242 
243    msg_q* p_msg_q = (msg_q*)msg_q_data;
244 
245    LOC_LOGD("%s: Waiting on message\n", __FUNCTION__);
246 
247    pthread_mutex_lock(&p_msg_q->list_mutex);
248 
249    if( p_msg_q->unblocked )
250    {
251       LOC_LOGE("%s: Message queue has been unblocked.\n", __FUNCTION__);
252       pthread_mutex_unlock(&p_msg_q->list_mutex);
253       return eMSG_Q_UNAVAILABLE_RESOURCE;
254    }
255 
256    /* Wait for data in the message queue */
257    while( linked_list_empty(p_msg_q->msg_list) && !p_msg_q->unblocked )
258    {
259       pthread_cond_wait(&p_msg_q->list_cond, &p_msg_q->list_mutex);
260    }
261 
262    rv = convert_linked_list_err_type(linked_list_remove(p_msg_q->msg_list, msg_obj));
263 
264    pthread_mutex_unlock(&p_msg_q->list_mutex);
265 
266    LOC_LOGD("%s: Received message 0x%p rv = %d\n", __FUNCTION__, *msg_obj, rv);
267 
268    return rv;
269 }
270 
271 /*===========================================================================
272 
273   FUNCTION:   msg_q_flush
274 
275   ===========================================================================*/
276 msq_q_err_type msg_q_flush(void* msg_q_data)
277 {
278    msq_q_err_type rv;
279    if ( msg_q_data == NULL )
280    {
281       LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
282       return eMSG_Q_INVALID_HANDLE;
283    }
284 
285    msg_q* p_msg_q = (msg_q*)msg_q_data;
286 
287    LOC_LOGD("%s: Flushing Message Queue\n", __FUNCTION__);
288 
289    pthread_mutex_lock(&p_msg_q->list_mutex);
290 
291    /* Remove all elements from the list */
292    rv = convert_linked_list_err_type(linked_list_flush(p_msg_q->msg_list));
293 
294    pthread_mutex_unlock(&p_msg_q->list_mutex);
295 
296    LOC_LOGD("%s: Message Queue flushed\n", __FUNCTION__);
297 
298    return rv;
299 }
300 
301 /*===========================================================================
302 
303   FUNCTION:   msg_q_unblock
304 
305   ===========================================================================*/
306 msq_q_err_type msg_q_unblock(void* msg_q_data)
307 {
308    if ( msg_q_data == NULL )
309    {
310       LOC_LOGE("%s: Invalid msg_q_data parameter!\n", __FUNCTION__);
311       return eMSG_Q_INVALID_HANDLE;
312    }
313 
314    msg_q* p_msg_q = (msg_q*)msg_q_data;
315    pthread_mutex_lock(&p_msg_q->list_mutex);
316 
317    if( p_msg_q->unblocked )
318    {
319       LOC_LOGE("%s: Message queue has been unblocked.\n", __FUNCTION__);
320       pthread_mutex_unlock(&p_msg_q->list_mutex);
321       return eMSG_Q_UNAVAILABLE_RESOURCE;
322    }
323 
324    LOC_LOGD("%s: Unblocking Message Queue\n", __FUNCTION__);
325    /* Unblocking message queue */
326    p_msg_q->unblocked = 1;
327 
328    /* Allow all the waiters to wake up */
329    pthread_cond_broadcast(&p_msg_q->list_cond);
330 
331    pthread_mutex_unlock(&p_msg_q->list_mutex);
332 
333    LOC_LOGD("%s: Message Queue unblocked\n", __FUNCTION__);
334 
335    return eMSG_Q_SUCCESS;
336 }
337