746d5122ee77bac6c87aeb302e5c81a806fef3bb
[platform/framework/web/download-provider.git] / src / download-provider-thread-queue.c
1 /*
2  * Copyright (c) 2012 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 <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20
21 #include <time.h>
22 #include <sys/time.h>
23
24 #include <sys/types.h>
25 #include <sys/socket.h>
26
27 #include <signal.h>
28
29 #include "download-provider.h"
30 #include "download-provider-log.h"
31 #include "download-provider-config.h"
32 #include "download-provider-slots.h"
33 #include "download-provider-socket.h"
34 #include "download-provider-pthread.h"
35 #include "download-provider-db.h"
36 #include "download-provider-queue.h"
37 #include "download-provider-network.h"
38 #include "download-provider-da-interface.h"
39
40 void dp_terminate(int signo);
41
42 pthread_mutex_t g_dp_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
43 pthread_cond_t g_dp_queue_cond = PTHREAD_COND_INITIALIZER;
44
45
46 //////////////////////////////////////////////////////////////////////////
47 /// @brief              check network status is matched with the type setted by user
48 /// @return     matched : 0 mispatch : -1
49 static int __is_matched_network(dp_network_type now_state, dp_network_type setted_state)
50 {
51         if (now_state == setted_state
52                 || now_state == DP_NETWORK_TYPE_ETHERNET
53                 || setted_state == DP_NETWORK_TYPE_ALL)
54                 return 0;
55         #if 0
56         if (setted_state == DP_NETWORK_TYPE_ALL
57                 || setted_state == DP_NETWORK_TYPE_DATA_NETWORK
58                 || now_state == DP_NETWORK_TYPE_WIFI
59                 || now_state == DP_NETWORK_TYPE_ETHERNET
60                 || (setted_state == DP_NETWORK_TYPE_WIFI
61                         && (now_state == DP_NETWORK_TYPE_WIFI
62                                 || now_state == DP_NETWORK_TYPE_ETHERNET))
63                 )
64                 return 0;
65         #endif
66         return -1;
67 }
68
69 //////////////////////////////////////////////////////////////////////////
70 /// @brief              the count of slot downloading currently
71 static unsigned __get_active_count(dp_request_slots *requests)
72 {
73         unsigned count = 0;
74         unsigned i = 0;
75
76         if (!requests)
77                 return 0;
78
79         for (i = 0; i < DP_MAX_REQUEST; i++) {
80                 if (requests[i].request) {
81                         if (requests[i].request->state == DP_STATE_CONNECTING ||
82                                 requests[i].request->state == DP_STATE_DOWNLOADING)
83                                 count++;
84                 }
85         }
86         return count;
87 }
88
89 //////////////////////////////////////////////////////////////////////////
90 /// @brief              index of slot which last time is oldest
91 static int __get_oldest_request_with_network(dp_request_slots *requests, dp_state_type state, dp_network_type now_state)
92 {
93         int i = 0;
94         int oldest_time = (int)time(NULL);
95         oldest_time++; // most last time
96         int oldest_index = -1;
97
98         if (!requests)
99                 return -1;
100
101         for (i = 0; i < DP_MAX_REQUEST; i++) {
102                 if (requests[i].request) {
103                         if (requests[i].request->state == state &&
104                                 requests[i].request->start_time > 0 &&
105                                 requests[i].request->start_time < oldest_time &&
106                                 __is_matched_network
107                                         (now_state, requests[i].request->network_type)
108                                 == 0) {
109                                 oldest_time = requests[i].request->start_time;
110                                 oldest_index = i;
111                         }
112                 }
113         }
114         return oldest_index;
115 }
116
117 //////////////////////////////////////////////////////////////////////////
118 /// @brief THREAD function for calling da_start_download_with_extension.
119 /// @warning da_start_download_with_extension can take long time
120 /// @param the pointer of memory slot allocated for this request.
121 /// @todo simplify da_start_download_with_extension
122 static void *__request_download_start_agent(void *args)
123 {
124         dp_error_type errcode = DP_ERROR_NONE;
125
126         dp_request *request = (dp_request *) args;
127         if (!request) {
128                 TRACE_ERROR("[NULL-CHECK] download_clientinfo_slot");
129                 pthread_exit(NULL);
130                 return 0;
131         }
132
133         if (dp_is_alive_download(request->agent_id)) {
134                 errcode = dp_resume_agent_download(request->agent_id);
135         } else {
136                 // call agent start function
137                 errcode = dp_start_agent_download(request);
138         }
139
140         CLIENT_MUTEX_LOCK(&(request->mutex));
141         // send to state callback.
142         if (errcode == DP_ERROR_NONE) {
143                 // CONNECTING
144                 request->state = DP_STATE_CONNECTING;
145                 request->error = DP_ERROR_NONE;
146                 request->startcount++;
147                 if (dp_db_set_column
148                                 (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STARTCOUNT,
149                                 DP_DB_COL_TYPE_INT, &request->startcount) < 0)
150                         TRACE_ERROR("[ERROR][%d][SQL]", request->id);
151         } else if (errcode == DP_ERROR_TOO_MANY_DOWNLOADS) {
152                 // PENDED
153                 request->state = DP_STATE_QUEUED;
154                 request->error = DP_ERROR_TOO_MANY_DOWNLOADS;
155         } else if (errcode == DP_ERROR_CONNECTION_FAILED) {
156                 // FAILED
157                 request->state = DP_STATE_FAILED;
158                 request->error = DP_ERROR_CONNECTION_FAILED;
159                 dp_ipc_send_event(request->group->event_socket,
160                         request->id, request->state, request->error, 0);
161                 dp_thread_queue_manager_wake_up();
162         } else {
163                 request->state = DP_STATE_FAILED;
164                 request->error = errcode;
165                 dp_ipc_send_event(request->group->event_socket,
166                         request->id, request->state, request->error, 0);
167                 dp_thread_queue_manager_wake_up();
168         }
169         if (dp_db_set_column
170                         (request->id, DP_DB_TABLE_LOG, DP_DB_COL_STATE,
171                         DP_DB_COL_TYPE_INT, &request->state) < 0)
172                 TRACE_ERROR("[ERROR][%d][SQL]", request->id);
173
174         CLIENT_MUTEX_UNLOCK(&(request->mutex));
175         pthread_exit(NULL);
176         return 0;
177 }
178
179 //////////////////////////////////////////////////////////////////////////
180 /// @brief create thread.
181 /// @warning if failed to create thread, change to PENED.
182 /// @param the pointer of memory slot allocated for this request.
183 static int __request_download_start_thread(dp_request *request)
184 {
185         // declare all resources
186         pthread_t thread_pid;
187         pthread_attr_t thread_attr;
188
189         TRACE_INFO("");
190         if (!request) {
191                 TRACE_ERROR("[CRITICAL] Invalid Address");
192                 return -1;
193         }
194
195         // initialize
196         if (pthread_attr_init(&thread_attr) != 0) {
197                 TRACE_STRERROR("[ERROR][%d] pthread_attr_init", request->id);
198                 return -1;
199         }
200         if (pthread_attr_setdetachstate(&thread_attr,
201                                                                         PTHREAD_CREATE_DETACHED) != 0) {
202                 TRACE_STRERROR
203                         ("[ERROR][%d] pthread_attr_setdetachstate", request->id);
204                 return -1;
205         }
206
207         request->state = DP_STATE_CONNECTING;
208         if (pthread_create(&thread_pid, &thread_attr,
209                                         __request_download_start_agent, request) != 0) {
210                 TRACE_STRERROR("[ERROR][%d] pthread_create", request->id);
211                 pthread_attr_destroy(&thread_attr);
212                 request->state = DP_STATE_QUEUED;
213                 return -1;
214         }
215         pthread_attr_destroy(&thread_attr);
216         return 0;
217 }
218
219
220 void dp_thread_queue_manager_wake_up()
221 {
222         TRACE_INFO("");
223         CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex));
224         pthread_cond_signal(&g_dp_queue_cond);
225         CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex));
226 }
227
228
229 // Main role : start download, check status of queue.
230 // No timeout. Wake up by Signal be sent from other thread
231 void *dp_thread_queue_manager(void *arg)
232 {
233         int i;
234         int active_count;
235         dp_client_group *group = NULL;
236         dp_request *request = NULL;
237
238         TRACE_INFO("Start Queue Thread");
239
240         dp_privates *privates = (dp_privates*)arg;
241         if (!privates) {
242                 TRACE_ERROR("[CRITICAL] Invalid Address");
243                 dp_terminate(SIGTERM);
244                 pthread_exit(NULL);
245                 return 0;
246         }
247
248         pthread_cond_init(&g_dp_queue_cond, NULL);
249         while (privates != NULL && privates->listen_fd >= 0) {
250
251                 CLIENT_MUTEX_LOCK(&(g_dp_queue_mutex));
252                 pthread_cond_wait(&g_dp_queue_cond, &g_dp_queue_mutex);
253
254                 if (privates == NULL || privates->requests == NULL ||
255                         privates->listen_fd < 0) {
256                         TRACE_INFO("Terminate Thread");
257                         CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex));
258                         break;
259                 }
260
261                 if (!privates->connection)
262                         privates->network_status =
263                                 dp_get_network_connection_instant_status();
264
265                 if (privates->network_status == DP_NETWORK_TYPE_OFF) {
266                         TRACE_INFO("[CHECK NETWORK STATE]");
267                         CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex));
268                         continue;
269                 }
270
271                 active_count = __get_active_count(privates->requests);
272
273                 // Start Conditions
274                 // 1. state is QUEUED
275                 // 2. 1 QUEUED per 1 Group : need not to check max limitation.!!
276                 //    if no group, it will be started later.
277                 // 3. most old last time : below conditions need max limitation.
278                 // 4. match network connection type
279
280                 // search group having 1 queued_count
281                 // guarantee 1 instant download per 1 group
282                 if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) {
283                         for (i = 0; i < DP_MAX_REQUEST; i++) {
284                                 request = privates->requests[i].request;
285                                 if (request && request->state == DP_STATE_QUEUED) {
286                                         group = privates->requests[i].request->group;
287                                         if (group && group->queued_count == 1 &&
288                                                         __is_matched_network(privates->network_status,
289                                                                         request->network_type) == 0 &&
290                                                                         __request_download_start_thread(request) ==
291                                                                                         0) {
292                                                 TRACE_INFO
293                                                         ("[Guarantee Intant Download] Group [%s]",
294                                                         group->pkgname);
295                                                 active_count++;
296                                         }
297                                 }
298                         }
299                 }
300
301                 if (active_count >= DP_MAX_DOWNLOAD_AT_ONCE) {
302                         TRACE_INFO("[BUSY] Active[%d] Max[%d]",
303                                 active_count, DP_MAX_DOWNLOAD_AT_ONCE);
304                         CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex));
305                         continue;
306                 }
307
308                 // can start download more.
309                 // search oldest request
310                 while(active_count < DP_MAX_DOWNLOAD_AT_ONCE) {
311                         i = __get_oldest_request_with_network(privates->requests,
312                                         DP_STATE_QUEUED, privates->network_status);
313                         if (i < 0) {
314                                 TRACE_INFO
315                                         ("No Request to can start now Active[%d] Max[%d]",
316                                         active_count, DP_MAX_DOWNLOAD_AT_ONCE);
317                                 break;
318                         }
319                         TRACE_INFO
320                                 ("QUEUE Status now %d active %d/%d", i, active_count,
321                                 DP_MAX_DOWNLOAD_AT_ONCE);
322                         request = privates->requests[i].request;
323                         __request_download_start_thread(request);
324                         active_count++;
325                 }
326                 CLIENT_MUTEX_UNLOCK(&(g_dp_queue_mutex));
327         }
328         pthread_cond_destroy(&g_dp_queue_cond);
329         pthread_exit(NULL);
330         return 0;
331 }