63c3a1c368a08f98dee738c1d0217b0a17ce1541
[profile/ivi/evas.git] / src / bin / evas_cserve2_main.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include <errno.h>
6 #include <string.h>
7 #include <unistd.h>
8
9 #include "evas_cserve2.h"
10
11 #ifdef CSERVE2_BIN_DEFAULT_COLOR
12 #undef CSERVE2_BIN_DEFAULT_COLOR
13 #endif
14 #define CSERVE2_BIN_DEFAULT_COLOR EINA_COLOR_BLUE
15
16 #define MAX_SLAVES 3
17
18 struct _Slave_Worker {
19    EINA_INLIST;
20    void *data;
21    Slave_Proc *slave;
22    Eina_Binbuf *ret;
23    int ret_size;
24    Eina_Bool done;
25    Eina_Bool delete_me;
26 };
27
28 typedef struct _Slave_Worker Slave_Worker;
29
30 int _evas_cserve2_bin_log_dom = -1;
31 static unsigned int _client_id = 0;
32 static Eina_Hash *client_list = NULL;
33 static Eina_Inlist *slaves_idle = NULL;
34 static Eina_Inlist *slaves_working = NULL;
35
36 void
37 cserve2_client_error_send(Client *client, unsigned int rid, int error_code)
38 {
39    int size;
40    Msg_Error msg;
41
42     // clear the struct with possible paddings, since it is not aligned.
43     memset(&msg, 0, sizeof(msg));
44     msg.base.rid = rid;
45     msg.base.type = CSERVE2_ERROR;
46     msg.error = error_code;
47
48     size = sizeof(msg);
49     cserve2_client_send(client, &size, sizeof(size));
50     cserve2_client_send(client, &msg, sizeof(msg));
51 }
52
53 static void
54 _cserve2_client_image_setoptsed(Client *client, unsigned int rid)
55 {
56    int size;
57    Msg_Setoptsed msg;
58
59    memset(&msg, 0, sizeof(msg));
60    msg.base.rid = rid;
61    msg.base.type = CSERVE2_SETOPTSED;
62
63    size = sizeof(msg);
64    cserve2_client_send(client, &size, sizeof(size));
65    cserve2_client_send(client, &msg, size);
66 }
67
68 static void
69 _slave_dead_cb(Slave_Proc *s __UNUSED__, void *data)
70 {
71    Slave_Worker *sw = data;
72
73    if (sw->delete_me)
74      {
75         DBG("Slave killed by cserve2. Restart routine.");
76         free(sw);
77         return;
78      }
79
80    if (!sw->data)
81      {
82         WRN("Slave died with no pending job, but not requested.");
83         slaves_idle = eina_inlist_remove(slaves_idle, EINA_INLIST_GET(sw));
84         free(sw);
85         return;
86      }
87
88    slaves_working = eina_inlist_remove(slaves_working, EINA_INLIST_GET(sw));
89    if (!sw->done)
90      cserve2_cache_requests_response(ERROR, (Error_Type[]){ CSERVE2_LOADER_DIED }, sw->data);
91    if (sw->ret)
92      eina_binbuf_free(sw->ret);
93    free(sw);
94 }
95
96 static void
97 _slave_read_cb(Slave_Proc *s __UNUSED__, Slave_Command cmd, void *msg, void *data)
98 {
99    Slave_Worker *sw = data;
100
101    DBG("Received reply command '%d' from slave '%p'", cmd, sw->slave);
102    switch (cmd)
103      {
104       case IMAGE_OPEN:
105       case IMAGE_LOAD:
106          sw->done = EINA_TRUE;
107          break;
108       case ERROR:
109          break;
110       default:
111          ERR("Unrecognized command received from slave: %d", cmd);
112      }
113    cserve2_cache_requests_response(cmd, msg, sw->data);
114    free(msg);
115
116    // slave finishes its work, put it back to idle list
117    sw->data = NULL;
118    slaves_working = eina_inlist_remove(slaves_working, EINA_INLIST_GET(sw));
119
120    if (!sw->delete_me) // if it is being deleted, it shouldn't be in any list
121      slaves_idle = eina_inlist_append(slaves_idle, EINA_INLIST_GET(sw));
122
123    cserve2_cache_requests_process();
124 }
125
126 int
127 cserve2_slave_available_get(void)
128 {
129     return MAX_SLAVES - eina_inlist_count(slaves_working);
130 }
131
132 Eina_Bool
133 cserve2_slave_cmd_dispatch(void *data, Slave_Command cmd, const void *msg, int size)
134 {
135    Slave_Worker *sw;
136    char *exe;
137
138    DBG("Dispatching command to slave. %d idle slaves, %d working slaves.",
139        eina_inlist_count(slaves_idle), eina_inlist_count(slaves_working));
140
141    // first check if there's an available slave
142    if (slaves_idle)
143      {
144         sw = EINA_INLIST_CONTAINER_GET(slaves_idle, Slave_Worker);
145         slaves_idle = eina_inlist_remove(slaves_idle, slaves_idle);
146         slaves_working = eina_inlist_append(slaves_working,
147                                             EINA_INLIST_GET(sw));
148
149         sw->data = data;
150         DBG("Dispatching command '%d' to slave '%p'", cmd, sw->slave);
151         cserve2_slave_send(sw->slave, cmd, msg, size);
152         return EINA_TRUE;
153      }
154
155    // no available slave, start a new one
156    sw = calloc(1, sizeof(Slave_Worker));
157    if (!sw) return EINA_FALSE;
158
159    sw->data = data;
160    exe = getenv("EVAS_CSERVE2_SLAVE");
161    if (!exe) exe = "evas_cserve2_slave";
162    sw->slave = cserve2_slave_run(exe, _slave_read_cb, _slave_dead_cb, sw);
163    if (!sw->slave)
164      {
165         ERR("Could not launch slave process");
166         cserve2_cache_requests_response(ERROR, (Error_Type[]){ CSERVE2_LOADER_EXEC_ERR }, sw->data);
167         free(sw);
168         return EINA_FALSE;
169      }
170    DBG("Dispatching command '%d' to slave '%p'", cmd, sw->slave);
171    cserve2_slave_send(sw->slave, cmd, msg, size);
172
173    slaves_working = eina_inlist_append(slaves_working, EINA_INLIST_GET(sw));
174
175    return EINA_TRUE;
176 }
177
178 static void
179 _cserve2_client_close(Client *client)
180 {
181    Msg_Close *msg = (Msg_Close *)client->msg.buf;
182
183    INF("Received CLOSE command: RID=%d", msg->base.rid);
184    INF("File_ID: %d\n", msg->file_id);
185
186    cserve2_cache_file_close(client, msg->file_id);
187 }
188
189 static void
190 _cserve2_client_unload(Client *client)
191 {
192    Msg_Unload *msg = (Msg_Unload *)client->msg.buf;
193
194    INF("Received UNLOAD command: RID=%d", msg->base.rid);
195    INF("Image_ID: %d\n", msg->image_id);
196
197    cserve2_cache_image_unload(client, msg->image_id);
198 }
199
200 static void
201 _cserve2_client_preload(Client *client)
202 {
203    Msg_Preload *msg = (Msg_Preload *)client->msg.buf;
204
205    INF("Received PRELOAD command: RID=%d", msg->base.rid);
206    INF("Image_ID: %d\n", msg->image_id);
207
208    cserve2_cache_image_preload(client, msg->image_id, msg->base.rid);
209    cserve2_cache_requests_process();
210 }
211
212 static void
213 _cserve2_client_load(Client *client)
214 {
215    Msg_Load *msg = (Msg_Load *)client->msg.buf;
216
217    INF("Received LOAD command: RID=%d", msg->base.rid);
218    INF("Image_ID: %d\n", msg->image_id);
219
220    cserve2_cache_image_load(client, msg->image_id, msg->base.rid);
221    cserve2_cache_requests_process();
222 }
223
224 static void
225 _cserve2_client_setopts(Client *client)
226 {
227    Msg_Setopts *msg = (Msg_Setopts *)client->msg.buf;
228
229    INF("Received SETOPTS command: RID=%d", msg->base.rid);
230    INF("File_ID: %d, Image_ID: %d", msg->file_id, msg->image_id);
231    INF("Load Options:");
232    INF("\tdpi: %03.1f", msg->opts.dpi);
233    INF("\tsize: %dx%d", msg->opts.w, msg->opts.h);
234    INF("\tscale down: %d", msg->opts.scale_down);
235    INF("\tregion: %d,%d + %dx%d",
236           msg->opts.rx, msg->opts.ry, msg->opts.rw, msg->opts.rh);
237    INF("\torientation: %d\n", msg->opts.orientation);
238
239    if (cserve2_cache_image_opts_set(client, msg) != 0)
240      return;
241
242    _cserve2_client_image_setoptsed(client, msg->base.rid);
243 }
244
245 static void
246 _cserve2_client_open(Client *client)
247 {
248    Msg_Open *msg = (Msg_Open *)client->msg.buf;
249    const char *path, *key;
250
251    path = ((const char *)msg) + sizeof(*msg) + msg->path_offset;
252    key = ((const char *)msg) + sizeof(*msg) + msg->key_offset;
253
254    INF("Received OPEN command: RID=%d", msg->base.rid);
255    INF("File_ID: %d, path=\"%s\", key=\"%s\"\n",
256           msg->file_id, path, key);
257
258    cserve2_cache_file_open(client, msg->file_id, path, key, msg->base.rid);
259    cserve2_cache_requests_process();
260 }
261
262 void
263 cserve2_command_run(Client *client, Message_Type type)
264 {
265    switch (type)
266      {
267       case CSERVE2_OPEN:
268          _cserve2_client_open(client);
269          break;
270       case CSERVE2_SETOPTS:
271          _cserve2_client_setopts(client);
272          break;
273       case CSERVE2_LOAD:
274          _cserve2_client_load(client);
275          break;
276       case CSERVE2_PRELOAD:
277          _cserve2_client_preload(client);
278          break;
279       case CSERVE2_UNLOAD:
280          _cserve2_client_unload(client);
281          break;
282       case CSERVE2_CLOSE:
283          _cserve2_client_close(client);
284          break;
285       default:
286          WRN("Unhandled message");
287      }
288 }
289
290 static void
291 _slave_quit_send(Slave_Worker *sw)
292 {
293    cserve2_slave_send(sw->slave, SLAVE_QUIT, NULL, 0);
294 }
295
296 static void
297 _slaves_restart(void)
298 {
299    Slave_Worker *list, *sw;
300
301    while (slaves_idle) // remove idle workers from idle list
302      {
303         sw = EINA_INLIST_CONTAINER_GET(slaves_idle, Slave_Worker);
304         slaves_idle = eina_inlist_remove(slaves_idle, slaves_idle);
305         sw->delete_me = EINA_TRUE;
306         _slave_quit_send(sw);
307      }
308
309    // working workers will be removed from the working list when they
310    // finish processing their jobs
311    list = EINA_INLIST_CONTAINER_GET(slaves_working, Slave_Worker);
312    EINA_INLIST_FOREACH(list, sw)
313      {
314         sw->delete_me = EINA_TRUE;
315         _slave_quit_send(sw);
316      }
317 }
318
319 static void
320 _timeout_cb(void)
321 {
322    static unsigned int slaves_restart = 0;
323
324    slaves_restart++;
325
326    if (slaves_restart == 10)
327      {
328         DBG("kill slaves");
329         _slaves_restart();
330         slaves_restart = 0;
331      }
332
333    cserve2_timeout_cb_set(3000, _timeout_cb);
334 }
335
336 void
337 cserve2_client_accept(int fd)
338 {
339    Client *client = calloc(1, sizeof(*client));
340
341    client->socket = fd;
342    client->id = _client_id++;
343
344    while (eina_hash_find(client_list, &client->id))
345      client->id = _client_id++;
346
347    if (!eina_hash_add(client_list, &client->id, client))
348      {
349         Eina_Error err = eina_error_get();
350         ERR("Could not add client to the list: \"%s\"",
351             eina_error_msg_get(err));
352         free(client);
353         close(fd);
354      }
355
356    cserve2_fd_watch_add(fd, FD_READ | FD_ERROR, cserve2_message_handler,
357                         client);
358    INF("Client %d connection accepted.", client->id);
359
360    cserve2_cache_client_new(client);
361 }
362
363 void
364 cserve2_client_del(Client *client)
365 {
366    eina_hash_del_by_key(client_list, &client->id);
367 }
368
369 static void
370 _client_free(void *data)
371 {
372    Client *client = data;
373    cserve2_cache_client_del(client);
374    if (client->msg.pending)
375        eina_binbuf_free(client->msg.pending);
376    cserve2_fd_watch_del(client->socket);
377    close(client->socket);
378    free(data);
379 }
380
381 static void
382 _clients_setup(void)
383 {
384    client_list = eina_hash_int32_new(_client_free);
385 }
386
387 static void
388 _clients_finish(void)
389 {
390    eina_hash_free(client_list);
391 }
392
393 int
394 main(int argc __UNUSED__, const char *argv[] __UNUSED__)
395 {
396    eina_init();
397
398    _evas_cserve2_bin_log_dom = eina_log_domain_register
399       ("evas_cserve2_bin", CSERVE2_BIN_DEFAULT_COLOR);
400    if (_evas_cserve2_bin_log_dom < 0)
401      {
402         EINA_LOG_ERR("impossible to create a log domain.");
403         eina_shutdown();
404         exit(1);
405      }
406
407    if (!cserve2_main_loop_setup())
408      {
409         ERR("could not setup main loop.");
410         goto error;
411      }
412
413    if (!cserve2_slaves_init())
414      {
415         ERR("Could not init slaves subsystem.");
416         goto error;
417      }
418
419    cserve2_cache_init();
420
421    _clients_setup();
422
423    cserve2_timeout_cb_set(3000, _timeout_cb);
424
425    cserve2_main_loop_run();
426
427    _clients_finish();
428
429    cserve2_cache_shutdown();
430
431    _slaves_restart();
432    cserve2_slaves_shutdown();
433
434    cserve2_main_loop_finish();
435
436    eina_log_domain_unregister(_evas_cserve2_bin_log_dom);
437    eina_shutdown();
438    return 0;
439
440 error:
441    eina_log_domain_unregister(_evas_cserve2_bin_log_dom);
442    eina_shutdown();
443    exit(1);
444 }