317f84a04bdffacaa2b5b4ca6976a5917089cb9a
[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                                 }
205                                 request->state = DP_STATE_FAILED;
206                                 request->error = DP_ERROR_QUEUE_FULL;
207                         }
208                         CLIENT_MUTEX_UNLOCK(&slot->mutex);
209                         return -1; // return negative for taking a break
210                 }
211
212                 CLIENT_MUTEX_UNLOCK(&slot->mutex);
213
214                 slot = NULL;
215                 request = NULL;
216         }
217         return 0;
218 }
219
220 static void *__dp_queue_manager(void *arg)
221 {
222         pthread_cond_init(&g_dp_queue_manager_cond, NULL);
223
224         if (dp_init_agent() != DP_ERROR_NONE) {
225                 TRACE_ERROR("failed to init agent");
226                 pthread_cond_destroy(&g_dp_queue_manager_cond);
227                 pthread_exit(NULL);
228                 return 0;
229         }
230
231         do {
232
233                 if (g_dp_queue_manager_tid <= 0) {
234                         TRACE_INFO("queue-manager is closed by other thread");
235                         break;
236                 }
237
238                 // check wifi_direct first
239                 if (dp_network_is_wifi_direct() == 1 && __dp_queue_manager_check_queue(&g_dp_queue_network_wifi_direct) < 0) {
240                         TRACE_ERROR("download-agent is busy, try again after 15 seconds");
241                 } else { // enter here if disable wifi-direct or download-agent is available
242                         int network_status = dp_network_get_status();
243                         if (network_status != DP_NETWORK_OFF) {
244                                 TRACE_INFO("queue-manager try to check queue network:%d", network_status);
245                                 if (g_dp_queue_network_all != NULL && __dp_queue_manager_check_queue(&g_dp_queue_network_all) < 0) {
246                                         TRACE_ERROR("download-agent is busy, try again after 15 seconds");
247                                 } else {
248                                         dp_queue_fmt **queue = __dp_queue_manager_get_queue(network_status);
249                                         if (__dp_queue_manager_check_queue(queue) < 0) {
250                                                 TRACE_ERROR("download-agent is busy, try again after 15 seconds");
251                                         }
252                                 }
253                         }
254                 }
255
256                 struct timeval now;
257                 struct timespec ts;
258                 gettimeofday(&now, NULL);
259                 ts.tv_sec = now.tv_sec + 5;
260                 ts.tv_nsec = now.tv_usec * 1000;
261                 CLIENT_MUTEX_LOCK(&g_dp_queue_manager_mutex);
262                 pthread_cond_timedwait(&g_dp_queue_manager_cond, &g_dp_queue_manager_mutex, &ts);
263                 CLIENT_MUTEX_UNLOCK(&g_dp_queue_manager_mutex);
264
265         } while (g_dp_queue_manager_tid > 0);
266
267         TRACE_DEBUG("queue-manager's working is done");
268         dp_deinit_agent();
269         dp_queue_clear_all(&g_dp_queue_network_all);
270         pthread_cond_destroy(&g_dp_queue_manager_cond);
271         pthread_exit(NULL);
272         return 0;
273 }
274
275 static int __dp_queue_manager_start()
276 {
277         if (g_dp_queue_manager_tid == 0 ||
278                         pthread_kill(g_dp_queue_manager_tid, 0) == ESRCH) {
279                 TRACE_DEBUG("try to create queue-manager");
280                 if (pthread_create(&g_dp_queue_manager_tid, NULL,
281                                 __dp_queue_manager, NULL) != 0) {
282                         TRACE_ERROR("failed to create queue-manager");
283                         return -1;
284                 }
285         }
286         return 0;
287 }
288
289 void dp_queue_manager_wake_up()
290 {
291         if (g_dp_queue_manager_tid > 0 &&
292                         pthread_kill(g_dp_queue_manager_tid, 0) != ESRCH) {
293                 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_queue_manager_mutex);
294                 if (locked == 0) {
295                         pthread_cond_signal(&g_dp_queue_manager_cond);
296                         CLIENT_MUTEX_UNLOCK(&g_dp_queue_manager_mutex);
297                 }
298         } else {
299                 __dp_queue_manager_start();
300         }
301 }
302
303 void dp_queue_manager_kill()
304 {
305         if (g_dp_queue_manager_tid > 0 &&
306                         pthread_kill(g_dp_queue_manager_tid, 0) != ESRCH) {
307                 //send signal to queue thread
308                 int status;
309                 pthread_t tid;
310                 tid = g_dp_queue_manager_tid;
311                 CLIENT_MUTEX_LOCK(&g_dp_queue_manager_mutex);
312                 g_dp_queue_manager_tid = 0;
313                 pthread_cond_signal(&g_dp_queue_manager_cond);
314                 CLIENT_MUTEX_UNLOCK(&g_dp_queue_manager_mutex);
315                 pthread_cancel(tid);
316                 pthread_join(tid, (void **)&status);
317         }
318 }