Merge branch 'master' into notification-service
[platform/upstream/iotivity.git] / resource / csdk / connectivity / common / src / cathreadpool_pthreads.c
1 /* ****************************************************************
2  *
3  * Copyright 2014 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************/
20
21 /**
22  * @file
23  *
24  * This file provides APIs related to thread pool.
25  */
26
27 #ifndef _GNU_SOURCE
28 #define _GNU_SOURCE
29 #endif
30 #include <errno.h>
31 #include <pthread.h>
32 #include "cathreadpool.h"
33 #include "logger.h"
34 #include "oic_malloc.h"
35 #include "uarraylist.h"
36 #include "camutex.h"
37
38 #define TAG PCF("UTHREADPOOL")
39
40 /**
41  * empty struct to represent the details.  This implementation has no data
42  * that it needs to keep track of, so it only uses NULL for the internal value.
43  */
44 typedef struct ca_thread_pool_details_t
45 {
46     u_arraylist_t* threads_list;
47     ca_mutex list_lock;
48 } ca_thread_pool_details_t;
49
50 /**
51  * struct to wrap the pthreads callback properly.  The function pointer for
52  * pthreads requires a void* return value, however u_thread_func is a void.
53  */
54 typedef struct ca_thread_pool_callback_info_t
55 {
56     ca_thread_func func;
57     void* data;
58 } ca_thread_pool_callback_info_t;
59
60 // passthrough function to convert the pthreads call to a u_thread_func call
61 void* ca_thread_pool_pthreads_delegate(void* data)
62 {
63     ca_thread_pool_callback_info_t* info = (ca_thread_pool_callback_info_t*)data;
64     info->func(info->data);
65     OICFree(info);
66     return NULL;
67 }
68
69 // this implementation doesn't do a thread pool, so this function is essentially
70 // a no-op besides creating a valid ca_thread_pool_t object.  It was determined after
71 // reading through the existing implementation that the thread-pooling was unnecessary
72 // for the posix platforms.  Behavior shouldn't be changed since previously num_of_threads
73 // was greater than the number of requested threads.
74 CAResult_t ca_thread_pool_init(int32_t num_of_threads, ca_thread_pool_t *thread_pool)
75 {
76     OIC_LOG(DEBUG, TAG, "IN");
77
78     if(!thread_pool)
79     {
80         OIC_LOG(ERROR, TAG, "Parameter thread_pool was null!");
81         return CA_STATUS_INVALID_PARAM;
82     }
83
84     if(num_of_threads <= 0)
85     {
86         OIC_LOG(ERROR, TAG, "num_of_threads must be positive and non-zero");
87         return CA_STATUS_INVALID_PARAM;
88     }
89
90     *thread_pool = OICMalloc(sizeof(struct ca_thread_pool));
91
92     if(!*thread_pool)
93     {
94         OIC_LOG(ERROR, TAG, "Failed to allocate for thread-pool");
95         return CA_MEMORY_ALLOC_FAILED;
96     }
97
98     (*thread_pool)->details = OICMalloc(sizeof(struct ca_thread_pool_details_t));
99     if(!(*thread_pool)->details)
100     {
101         OIC_LOG(ERROR, TAG, "Failed to allocate for thread-pool details");
102         OICFree(*thread_pool);
103         *thread_pool=NULL;
104         return CA_MEMORY_ALLOC_FAILED;
105     }
106
107     (*thread_pool)->details->list_lock = ca_mutex_new();
108
109     if(!(*thread_pool)->details->list_lock)
110     {
111         OIC_LOG(ERROR, TAG, "Failed to create thread-pool mutex");
112         goto exit;
113     }
114
115     (*thread_pool)->details->threads_list = u_arraylist_create();
116
117     if(!(*thread_pool)->details->threads_list)
118     {
119         OIC_LOG(ERROR, TAG, "Failed to create thread-pool list");
120         if(!ca_mutex_free((*thread_pool)->details->list_lock))
121         {
122             OIC_LOG(ERROR, TAG, "Failed to free thread-pool mutex");
123         }
124         goto exit;
125     }
126
127     OIC_LOG(DEBUG, TAG, "OUT");
128     return CA_STATUS_OK;
129
130 exit:
131     OICFree((*thread_pool)->details);
132     OICFree(*thread_pool);
133     *thread_pool = NULL;
134     return CA_STATUS_FAILED;
135 }
136
137 CAResult_t ca_thread_pool_add_task(ca_thread_pool_t thread_pool, ca_thread_func method,
138                                     void *data)
139 {
140     OIC_LOG(DEBUG, TAG, "IN");
141
142     if(NULL == thread_pool || NULL == method)
143     {
144         OIC_LOG(ERROR, TAG, "thread_pool or method was NULL");
145         return CA_STATUS_INVALID_PARAM;
146     }
147
148     ca_thread_pool_callback_info_t* info = OICMalloc(sizeof(ca_thread_pool_callback_info_t));
149     if(!info)
150     {
151         OIC_LOG(ERROR, TAG, "Failed to allocate for memory wrapper");
152         return CA_MEMORY_ALLOC_FAILED;
153     }
154
155     info->func = method;
156     info->data = data;
157
158     pthread_t threadHandle;
159
160     int result = pthread_create(&threadHandle, NULL, ca_thread_pool_pthreads_delegate, info);
161
162     if(result != 0)
163     {
164         OIC_LOG_V(ERROR, TAG, "Thread start failed with error %d", result);
165         return CA_STATUS_FAILED;
166     }
167
168     ca_mutex_lock(thread_pool->details->list_lock);
169     bool addResult = u_arraylist_add(thread_pool->details->threads_list, (void*)threadHandle);
170     ca_mutex_unlock(thread_pool->details->list_lock);
171
172     if(!addResult)
173     {
174         OIC_LOG_V(ERROR, TAG, "Arraylist Add failed, may not be properly joined: %d", addResult);
175         return CA_STATUS_FAILED;
176     }
177
178     OIC_LOG(DEBUG, TAG, "OUT");
179     return CA_STATUS_OK;
180 }
181
182 void ca_thread_pool_free(ca_thread_pool_t thread_pool)
183 {
184     OIC_LOG(DEBUG, TAG, "IN");
185
186     if(!thread_pool)
187     {
188         OIC_LOG(ERROR, TAG, "Invalid parameter thread_pool was NULL");
189         return;
190     }
191
192     ca_mutex_lock(thread_pool->details->list_lock);
193
194     for(uint32_t i = 0; i<u_arraylist_length(thread_pool->details->threads_list); ++i)
195     {
196         pthread_t tid = (pthread_t)u_arraylist_get(thread_pool->details->threads_list, i);
197         int joinres = pthread_join(tid, NULL);
198         if(0 != joinres)
199         {
200             OIC_LOG_V(ERROR, TAG, "Failed to join thread at index %u with error %d", i, joinres);
201         }
202     }
203
204     u_arraylist_free(&(thread_pool->details->threads_list));
205
206     ca_mutex_unlock(thread_pool->details->list_lock);
207     ca_mutex_free(thread_pool->details->list_lock);
208
209     OICFree(thread_pool->details);
210     OICFree(thread_pool);
211
212     OIC_LOG(DEBUG, TAG, "OUT");
213 }