Apply tizen coding rule
[platform/framework/web/download-provider.git] / provider / download-provider-queue-manager.c
1 /*
2  * Copyright (c) 2013 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 #include <stdlib.h>
18 #include <signal.h> // pthread_kill
19 #include <errno.h> // ESRCH
20 #include <sys/time.h>
21
22 #include <download-provider-log.h>
23 #include <download-provider-pthread.h>
24
25 #include <download-provider-db-defs.h>
26 #include <download-provider-db.h>
27 #include <download-provider-queue.h>
28 #include <download-provider-network.h>
29 #include <download-provider-notify.h>
30 #include <download-provider-client.h>
31 #include <download-provider-client-manager.h>
32 #include <download-provider-plugin-download-agent.h>
33
34 pthread_mutex_t g_dp_queue_manager_mutex = PTHREAD_MUTEX_INITIALIZER;
35 pthread_cond_t g_dp_queue_manager_cond = PTHREAD_COND_INITIALIZER;
36 pthread_t g_dp_queue_manager_tid = 0;
37
38 static dp_queue_fmt *g_dp_queue_network_all = NULL;
39 static dp_queue_fmt *g_dp_queue_network_wifi = NULL;
40 static dp_queue_fmt *g_dp_queue_network_data_network = NULL;
41 static dp_queue_fmt *g_dp_queue_network_wifi_direct = NULL;
42
43 static dp_queue_fmt **__dp_queue_manager_get_queue(int network)
44 {
45         switch (network) {
46         case DP_NETWORK_ALL:
47                 //TRACE_DEBUG("network all");
48                 return &g_dp_queue_network_all;
49         case DP_NETWORK_WIFI:
50                 //TRACE_DEBUG("network wifi only");
51                 return &g_dp_queue_network_wifi;
52         case DP_NETWORK_DATA_NETWORK:
53                 //TRACE_DEBUG("network data network only");
54                 return &g_dp_queue_network_data_network;
55         case DP_NETWORK_WIFI_DIRECT:
56                 //TRACE_DEBUG("network wifi-direct");
57                 return &g_dp_queue_network_wifi_direct;
58         default:
59                 break;
60         }
61         return NULL;
62 }
63 int dp_queue_manager_push_queue(void *slot, void *request)
64 {
65         dp_request_fmt *requestp = request;
66         if (slot == NULL || request == NULL) {
67                 TRACE_DEBUG("check address client:%p request:%p id:%d", slot,
68                                 request, (request == NULL ? 0 : requestp->id));
69                 return -1;
70         }
71         dp_queue_fmt **queue = __dp_queue_manager_get_queue(requestp->network_type);
72         if (requestp->state != DP_STATE_QUEUED) {
73                 TRACE_ERROR("check id:%d state:%s", requestp->id, dp_print_state(requestp->state));
74                 return -1;
75         }
76         if (dp_queue_push(queue, slot, request) < 0) {
77                 TRACE_ERROR("failed to push to queue id:%d", requestp->id);
78                 return -1;
79         }
80         return 0;
81 }
82
83 void dp_queue_manager_clear_queue(void *request)
84 {
85         dp_request_fmt *requestp = request;
86         if (request == NULL) {
87                 TRACE_DEBUG("check address request:%p id:%d",
88                                 request, (request == NULL ? 0 : requestp->id));
89                 return ;
90         }
91         dp_queue_fmt **queue = __dp_queue_manager_get_queue(requestp->network_type);
92         dp_queue_clear(queue, request);
93 }
94
95 // if return negative, queue-manager try again.
96 static int __dp_queue_manager_try_download(dp_client_slots_fmt *slot, dp_request_fmt *request)
97 {
98         int errorcode = DP_ERROR_NONE;
99         int result = 0;
100
101         if (slot == NULL || request == NULL) {
102                 TRACE_DEBUG("check address client:%p request:%p id:%d", slot,
103                                 request, (request == NULL ? 0 : request->id));
104                 // return 0 to ignore this call.
105                 return 0;
106         }
107
108         if (request->state != DP_STATE_QUEUED) {
109                 TRACE_ERROR("check id %d state:%d", request->id, request->state);
110                 return 0;
111         }
112
113         // check startcount
114
115         request->startcount++;
116         request->access_time = (int)time(NULL);
117
118         if (dp_db_replace_property(slot->client.dbhandle, request->id,
119                         DP_TABLE_LOGGING, DP_DB_COL_STARTCOUNT,
120                         (void *)&request->startcount, 0, 0, &errorcode) < 0) {
121                 TRACE_ERROR("failed to set startcount");
122                 return -1;
123         }
124
125         errorcode = DP_ERROR_NONE;
126
127         if (dp_is_alive_download(request->agent_id) > 0)
128                 errorcode = dp_resume_agent_download(request->agent_id);
129         else
130                 // call agent start function
131                 errorcode = dp_start_agent_download(slot, request);
132
133         if (errorcode == DP_ERROR_NONE) {
134                 request->state = DP_STATE_CONNECTING;
135         } else if (errorcode == DP_ERROR_TOO_MANY_DOWNLOADS ||
136                         errorcode == DP_ERROR_DISK_BUSY ||
137                         errorcode == DP_ERROR_OUT_OF_MEMORY) {
138                 TRACE_ERROR("push queue id:%d error:%s", request->id, dp_print_errorcode(errorcode));
139                 // PENDED
140                 request->state = DP_STATE_QUEUED;
141                 result = -1; // try again.
142         } else if (errorcode == DP_ERROR_INVALID_STATE) { // by resume
143                 // ignore this request
144                 result = -1;
145                 TRACE_ERROR("failed to resume id:%d", request->id);
146                 request->agent_id = -1; // workaround. da_agent will an object for this agent_id later
147         } else if (errorcode != DP_ERROR_NONE) {
148                 request->state = DP_STATE_FAILED;
149         }
150
151         request->error = errorcode;
152
153         if (result == 0) { // it's not for retrying
154                 int sqlerror = DP_ERROR_NONE;
155                 if (dp_db_update_logging(slot->client.dbhandle, request->id,
156                                 request->state, request->error, &sqlerror) < 0) {
157                         TRACE_ERROR("logging failure id:%d error:%d", request->id, sqlerror);
158                 }
159                 if (errorcode != DP_ERROR_NONE && request->state_cb == 1) { // announce state
160
161                         TRACE_ERROR("notify id:%d error:%s", request->id, dp_print_errorcode(errorcode));
162                         if (dp_notify_feedback(slot->client.notify, slot,
163                                         request->id, request->state, request->error, 0) < 0) {
164                                 TRACE_ERROR("disable state callback by IO_ERROR id:%d", request->id);
165                                 request->state_cb = 0;
166                         }
167                 }
168         }
169
170         return result;
171
172 }
173
174 static int __dp_queue_manager_check_queue(dp_queue_fmt **queue)
175 {
176         dp_client_slots_fmt *slot = NULL;
177         dp_request_fmt *request = NULL;
178         while (dp_queue_pop(queue, (void *)&slot, (void *)&request) == 0) { // pop a request from queue.
179                 TRACE_DEBUG("queue-manager pop a request");
180                 if (slot == NULL || request == NULL) {
181                         TRACE_DEBUG("queue error client:%p request:%p id:%d", slot, request, (request == NULL ? 0 : request->id));
182                         continue;
183                 }
184
185                 CLIENT_MUTEX_LOCK(&slot->mutex);
186
187                 TRACE_DEBUG("queue info slot:%p request:%p id:%d", slot, request, (request == NULL ? 0 : request->id));
188
189                 int errorcode = DP_ERROR_NONE;
190                 int download_state = DP_STATE_NONE;
191                 if (slot != NULL && request != NULL &&
192                                 dp_db_get_property_int(slot->client.dbhandle, request->id, DP_TABLE_LOGGING, DP_DB_COL_STATE, &download_state, &errorcode) < 0) {
193                         TRACE_ERROR("deny checking id:%d db state:%s memory:%s", request->id, dp_print_state(download_state), dp_print_state(request->state));
194                         errorcode = DP_ERROR_ID_NOT_FOUND;
195                 }
196
197                 if (download_state == DP_STATE_QUEUED && __dp_queue_manager_try_download(slot, request) < 0) {
198                         // if failed to start, push at the tail of queue. try again.
199                         if (dp_queue_push(queue, slot, request) < 0) {
200                                 TRACE_ERROR("failed to push to queue id:%d", request->id);
201                                 int errorcode = DP_ERROR_NONE;
202                                 if (dp_db_update_logging(slot->client.dbhandle, request->id, DP_STATE_FAILED, DP_ERROR_QUEUE_FULL, &errorcode) < 0)
203                                         TRACE_ERROR("failed to update log id:%d", request->id);
204                                 request->state = DP_STATE_FAILED;
205                                 request->error = DP_ERROR_QUEUE_FULL;
206                         }
207                         CLIENT_MUTEX_UNLOCK(&slot->mutex);
208                         return -1; // return negative for taking a break
209                 }
210
211                 CLIENT_MUTEX_UNLOCK(&slot->mutex);
212
213                 slot = NULL;
214                 request = NULL;
215         }
216         return 0;
217 }
218
219 static void *__dp_queue_manager(void *arg)
220 {
221         pthread_cond_init(&g_dp_queue_manager_cond, NULL);
222
223         if (dp_init_agent() != DP_ERROR_NONE) {
224                 TRACE_ERROR("failed to init agent");
225                 pthread_cond_destroy(&g_dp_queue_manager_cond);
226                 pthread_exit(NULL);
227                 return 0;
228         }
229
230         do {
231
232                 if (g_dp_queue_manager_tid <= 0) {
233                         TRACE_INFO("queue-manager is closed by other thread");
234                         break;
235                 }
236
237                 // check wifi_direct first
238                 if (dp_network_is_wifi_direct() == 1 && __dp_queue_manager_check_queue(&g_dp_queue_network_wifi_direct) < 0) {
239                         TRACE_ERROR("download-agent is busy, try again after 15 seconds");
240                 } else { // enter here if disable wifi-direct or download-agent is available
241                         int network_status = dp_network_get_status();
242                         if (network_status != DP_NETWORK_OFF) {
243                                 TRACE_INFO("queue-manager try to check queue network:%d", network_status);
244                                 if (g_dp_queue_network_all != NULL && __dp_queue_manager_check_queue(&g_dp_queue_network_all) < 0) {
245                                         TRACE_ERROR("download-agent is busy, try again after 15 seconds");
246                                 } else {
247                                         dp_queue_fmt **queue = __dp_queue_manager_get_queue(network_status);
248                                         if (__dp_queue_manager_check_queue(queue) < 0)
249                                                 TRACE_ERROR("download-agent is busy, try again after 15 seconds");
250                                 }
251                         }
252                 }
253
254                 struct timeval now;
255                 struct timespec ts;
256                 gettimeofday(&now, NULL);
257                 ts.tv_sec = now.tv_sec + 5;
258                 ts.tv_nsec = now.tv_usec * 1000;
259                 CLIENT_MUTEX_LOCK(&g_dp_queue_manager_mutex);
260                 pthread_cond_timedwait(&g_dp_queue_manager_cond, &g_dp_queue_manager_mutex, &ts);
261                 CLIENT_MUTEX_UNLOCK(&g_dp_queue_manager_mutex);
262
263         } while (g_dp_queue_manager_tid > 0);
264
265         TRACE_DEBUG("queue-manager's working is done");
266         dp_deinit_agent();
267         dp_queue_clear_all(&g_dp_queue_network_all);
268         pthread_cond_destroy(&g_dp_queue_manager_cond);
269         pthread_exit(NULL);
270         return 0;
271 }
272
273 static int __dp_queue_manager_start()
274 {
275         if (g_dp_queue_manager_tid == 0 ||
276                         pthread_kill(g_dp_queue_manager_tid, 0) == ESRCH) {
277                 TRACE_DEBUG("try to create queue-manager");
278                 if (pthread_create(&g_dp_queue_manager_tid, NULL,
279                                 __dp_queue_manager, NULL) != 0) {
280                         TRACE_ERROR("failed to create queue-manager");
281                         return -1;
282                 }
283         }
284         return 0;
285 }
286
287 void dp_queue_manager_wake_up()
288 {
289         if (g_dp_queue_manager_tid > 0 &&
290                         pthread_kill(g_dp_queue_manager_tid, 0) != ESRCH) {
291                 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_queue_manager_mutex);
292                 if (locked == 0) {
293                         pthread_cond_signal(&g_dp_queue_manager_cond);
294                         CLIENT_MUTEX_UNLOCK(&g_dp_queue_manager_mutex);
295                 }
296         } else {
297                 __dp_queue_manager_start();
298         }
299 }
300
301 void dp_queue_manager_kill()
302 {
303         if (g_dp_queue_manager_tid > 0 &&
304                         pthread_kill(g_dp_queue_manager_tid, 0) != ESRCH) {
305                 //send signal to queue thread
306                 int status;
307                 pthread_t tid;
308                 tid = g_dp_queue_manager_tid;
309                 CLIENT_MUTEX_LOCK(&g_dp_queue_manager_mutex);
310                 g_dp_queue_manager_tid = 0;
311                 pthread_cond_signal(&g_dp_queue_manager_cond);
312                 CLIENT_MUTEX_UNLOCK(&g_dp_queue_manager_mutex);
313                 pthread_cancel(tid);
314                 pthread_join(tid, (void **)&status);
315         }
316 }