Remove the right element from the list
[profile/ivi/evas.git] / src / bin / evas_cserve2_requests.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include "evas_cserve2.h"
6
7 #define MAX_SLAVES 1
8
9 typedef enum
10 {
11    SLAVE_IMAGE,
12    SLAVE_FONT,
13    SLAVE_NONE
14 } Slave_Type;
15
16 struct _Slave_Worker
17 {
18    Slave_Type type;
19    void *data;
20    Slave *slave;
21    Eina_Binbuf *ret;
22    int ret_size;
23    Eina_Bool done;
24    Eina_Bool delete_me;
25 };
26
27 typedef struct _Slave_Worker Slave_Worker;
28
29 /* This struct is used to match font request types to the respective slave
30  * type, and the message type that will be used for that request. The order
31  * of the request types on it is the order in which these requests will
32  * be processed.
33  */
34 static struct _Request_Match
35 {
36    Font_Request_Type rtype;
37    Slave_Type stype;
38    Slave_Command ctype;
39 } _request_match[] =
40 {
41    { CSERVE2_REQ_FONT_LOAD, SLAVE_FONT, FONT_LOAD },
42    { CSERVE2_REQ_FONT_GLYPHS_LOAD, SLAVE_FONT, FONT_GLYPHS_LOAD },
43    { CSERVE2_REQ_LAST, 0, 0 }
44 };
45
46 static Slave *_create_image_slave(void *data);
47 static Slave *_create_font_slave(void *data);
48
49 static struct _Worker
50 {
51    Slave_Type type;
52    unsigned int max;
53    Eina_List *idle;
54    Eina_List *working;
55    Slave *(*create_slave)(void *data);
56 } _workers[] =
57 {
58    { SLAVE_IMAGE, 3, NULL, NULL, _create_image_slave },
59    { SLAVE_FONT, 1, NULL, NULL, _create_font_slave },
60 };
61
62 struct _Font_Request
63 {
64    EINA_INLIST;
65    Font_Request_Type type;
66    void *data;
67    void *msg;
68    Eina_List *waiters;
69    Eina_Bool processing;
70    Font_Request_Funcs *funcs;
71    Font_Request *dependency;
72    Eina_List *dependents; /* list of requests that depend on this one finishing */
73    Eina_Bool locked : 1; /* locked waiting for a dependency request to finish */
74 };
75
76 struct _Waiter
77 {
78    unsigned int rid;
79    Client *client;
80 };
81
82 typedef struct _Waiter Waiter;
83
84 struct _Request_Queue
85 {
86    Eina_Inlist *waiting;
87    Eina_Inlist *processing; // TODO: Check if is there any use for this list.
88 };
89
90 typedef struct _Request_Queue Request_Queue;
91
92 static Request_Queue *requests = NULL;
93 // static Eina_List *processing = NULL;
94
95 static void _cserve2_requests_process(void);
96
97 static void
98 _request_waiter_add(Font_Request *req, Client *client, unsigned int rid)
99 {
100    Waiter *w = malloc(sizeof(*w));
101
102    DBG("Add waiter to request. Client: %d, rid: %d", client->id, rid);
103
104    w->client = client;
105    w->rid = rid;
106
107    req->waiters = eina_list_append(req->waiters, w);
108 }
109
110 Font_Request *
111 cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Font_Request *dep, Font_Request_Funcs *funcs, void *data)
112 {
113    Font_Request *req, *r;
114
115    req = NULL;
116
117    /* Check if this request was already being processed. */
118    EINA_INLIST_FOREACH(requests[type].processing, r)
119      {
120         if (r->data != data)
121           continue;
122
123         req = r;
124         break;
125      }
126
127    /* Check if this request was already waiting to be processed. */
128    if (!req)
129      {
130         EINA_INLIST_FOREACH(requests[type].waiting, r)
131           {
132              if (r->data != data)
133                continue;
134
135              req = r;
136              break;
137           }
138      }
139
140    /* create new request */
141    if (!req)
142      {
143         DBG("Add request for rid: %d", rid);
144         req = calloc(1, sizeof(*req));
145         req->type = type;
146         req->data = data;
147         req->waiters = NULL;
148         req->processing = EINA_FALSE;
149         req->funcs = funcs;
150         requests[type].waiting = eina_inlist_append(requests[type].waiting,
151                                                     EINA_INLIST_GET(req));
152      }
153
154    if (dep && !req->dependency)
155      {
156         req->locked = EINA_TRUE;
157         dep->dependents = eina_list_append(dep->dependents, req);
158         req->dependency = dep;
159      }
160
161    _request_waiter_add(req, client, rid);
162
163    _cserve2_requests_process();
164
165    return req;
166 }
167
168 void
169 cserve2_request_waiter_add(Font_Request *req, unsigned int rid, Client *client)
170 {
171    _request_waiter_add(req, client, rid);
172 }
173
174 static void
175 _request_dependents_cancel(Font_Request *req, Error_Type err)
176 {
177    Font_Request *dep;
178
179    EINA_LIST_FREE(req->dependents, dep)
180      {
181         dep->locked = EINA_FALSE;
182         dep->dependency = NULL;
183         /* Maybe we need a better way to inform the creator of the request
184          * that it was cancelled because its dependency failed? */
185         cserve2_request_cancel_all(dep, err);
186      }
187 }
188
189 void
190 cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err)
191 {
192    Eina_List *l, *l_next;
193    Waiter *w;
194
195    EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, w)
196      {
197         if (w->client->id == client->id)
198           {
199              DBG("Removing answer from waiter client: %d, rid: %d",
200                  client->id, w->rid);
201              if (req->funcs && req->funcs->error)
202                req->funcs->error(client, req->data, err, w->rid);
203              req->waiters = eina_list_remove_list(req->waiters, l);
204              free(w);
205           }
206      }
207
208    if (req->dependency)
209      req->dependency->dependents = eina_list_remove(
210         req->dependency->dependents, req);
211
212    _request_dependents_cancel(req, err);
213
214    // TODO: When we have speculative preload, there may be no waiters,
215    // so we need a flag or something else to make things still load.
216    if ((!req->waiters) && (!req->processing))
217      {
218         Eina_Inlist **reqlist = &requests[req->type].waiting;
219         *reqlist = eina_inlist_remove(*reqlist, EINA_INLIST_GET(req));
220         // TODO: If the request is being processed, it can't be deleted. Must
221         // be marked as delete_me instead.
222         req->funcs->msg_free(req->msg, req->data);
223         free(req);
224      }
225
226 }
227
228 void
229 cserve2_request_cancel_all(Font_Request *req, Error_Type err)
230 {
231    Waiter *w;
232
233    DBG("Removing all answers.");
234
235    EINA_LIST_FREE(req->waiters, w)
236      {
237         DBG("Removing answer from waiter client: %d, rid: %d",
238             w->client->id, w->rid);
239         if (req->funcs && req->funcs->error)
240           req->funcs->error(w->client, req->data, err, w->rid);
241         free(w);
242      }
243
244    _request_dependents_cancel(req, err);
245
246    if (req->processing)
247      return;
248
249    if (req->dependency)
250      req->dependency->dependents = eina_list_remove(
251         req->dependency->dependents, req);
252
253    requests[req->type].waiting = eina_inlist_remove(
254       requests[req->type].waiting, EINA_INLIST_GET(req));
255    req->funcs->msg_free(req->msg, req->data);
256    free(req);
257 }
258
259 void
260 cserve2_requests_init(void)
261 {
262    DBG("Initializing requests.");
263    requests = calloc(CSERVE2_REQ_LAST, sizeof(*requests));
264 }
265
266 void
267 cserve2_requests_shutdown(void)
268 {
269    DBG("Shutting down requests.");
270    free(requests);
271 }
272
273 static void
274 _cserve2_request_failed(Font_Request *req, Error_Type type)
275 {
276    Waiter *w;
277
278    EINA_LIST_FREE(req->waiters, w)
279      {
280         req->funcs->error(w->client, req->data, type, w->rid);
281         free(w);
282      }
283
284    req->funcs->msg_free(req->msg, req->data);
285    requests[req->type].processing = eina_inlist_remove(
286       requests[req->type].processing, EINA_INLIST_GET(req));
287
288    _request_dependents_cancel(req, type);
289
290    free(req);
291 }
292
293 static void
294 _slave_read_cb(Slave *s __UNUSED__, Slave_Command cmd, void *msg, void *data)
295 {
296    Slave_Worker *sw = data;
297    Font_Request *dep, *req = sw->data;
298    Eina_List **working, **idle;
299    Waiter *w;
300
301    EINA_LIST_FREE(req->waiters, w)
302      {
303         if (cmd == ERROR)
304           {
305              Error_Type *err = msg;
306              req->funcs->error(w->client, req->data, *err, w->rid);
307           }
308         else
309           req->funcs->response(w->client, req->data, msg, w->rid);
310         free(w);
311      }
312
313    req->funcs->msg_free(req->msg, req->data);
314    // FIXME: We shouldn't free this message directly, it must be freed by a
315    // callback.
316    free(msg);
317    requests[req->type].processing = eina_inlist_remove(
318       requests[req->type].processing, EINA_INLIST_GET(req));
319
320    EINA_LIST_FREE(req->dependents, dep)
321      {
322         dep->locked = EINA_FALSE;
323         dep->dependency = NULL;
324      }
325
326    free(req);
327    sw->data = NULL;
328
329    working = &_workers[sw->type].working;
330    idle = &_workers[sw->type].idle;
331    *working = eina_list_remove(*working, sw);
332    *idle = eina_list_append(*idle, sw);
333
334    _cserve2_requests_process();
335 }
336
337 static void
338 _slave_dead_cb(Slave *s __UNUSED__, void *data)
339 {
340    Slave_Worker *sw = data;
341    Font_Request *req = sw->data;
342    Eina_List **working = &_workers[sw->type].working;
343
344    if (req)
345      _cserve2_request_failed(req, CSERVE2_LOADER_DIED);
346
347    *working = eina_list_remove(*working, sw);
348    free(sw);
349 }
350
351 static Slave *
352 _create_image_slave(void *data)
353 {
354    char *exe;
355    Slave *slave;
356
357    exe = getenv("EVAS_CSERVE2_SLAVE");
358    if (!exe) exe = "evas_cserve2_slave";
359
360    slave = cserve2_slave_run(exe, _slave_read_cb,
361                              _slave_dead_cb, data);
362
363    return slave;
364 }
365
366 static Slave *
367 _create_font_slave(void *data)
368 {
369    Slave *slave;
370
371    slave = cserve2_slave_thread_run(cserve2_font_slave_cb, NULL,
372                                     _slave_read_cb, _slave_dead_cb,
373                                     data);
374
375    return slave;
376 }
377
378 static Slave_Worker *
379 _slave_for_request_create(Slave_Type type)
380 {
381    Slave_Worker *sw;
382    Slave *slave;
383
384    sw = calloc(1, sizeof(Slave_Worker));
385    if (!sw) return NULL;
386
387    slave = _workers[type].create_slave(sw);
388    if (!slave)
389      {
390         ERR("Could not launch slave process");
391         free(sw);
392         return NULL;
393      }
394
395    sw->slave = slave;
396    sw->type = type;
397    _workers[type].idle = eina_list_append(_workers[type].idle, sw);
398
399    return sw;
400 }
401
402 static Eina_Bool
403 _cserve2_request_dispatch(Slave_Worker *sw, Slave_Command ctype, Font_Request *req)
404 {
405    int size;
406    char *slave_msg = req->funcs->msg_create(req->data, &size);
407
408
409    DBG("dispatching message of type %d to slave.", req->type);
410    if (!slave_msg)
411      {
412         ERR("Could not create slave message for request type %d.", req->type);
413         return EINA_FALSE;
414      }
415
416    req->msg = slave_msg;
417    sw->data = req;
418    cserve2_slave_send(sw->slave, ctype, slave_msg, size);
419    req->processing = EINA_TRUE;
420
421    return EINA_TRUE;
422 }
423
424 static void
425 _cserve2_requests_process(void)
426 {
427     unsigned int rtype, j;
428
429     for (rtype = 0; rtype < CSERVE2_REQ_LAST; rtype++)
430       {
431          Slave_Type type = SLAVE_NONE;
432          Slave_Command ctype;
433          unsigned int max_workers;
434          Eina_List **idle, **working;
435          Eina_Inlist *itr;
436          Font_Request *req;
437
438          for (j = 0; _request_match[j].rtype != CSERVE2_REQ_LAST; j++)
439            {
440               if (_request_match[j].rtype == rtype)
441                 {
442                    type = _request_match[j].stype;
443                    ctype = _request_match[j].ctype;
444                    break;
445                 }
446            }
447
448          if (type == SLAVE_NONE)
449            continue;
450
451          if (!requests[rtype].waiting)
452            continue;
453
454          /* Now we have the worker type to use (image or font), and the list
455           * of requests to process. Just process as many requests as we can.
456           */
457          max_workers = _workers[type].max;
458          idle = &_workers[type].idle;
459          working = &_workers[type].working;
460
461          EINA_INLIST_FOREACH_SAFE(requests[rtype].waiting, itr, req)
462            {
463               Slave_Worker *sw;
464
465               if (eina_list_count(*working) >= max_workers)
466                 break;
467
468               if (req->locked)
469                 continue;
470
471               requests[rtype].waiting = eina_inlist_remove(
472                  requests[rtype].waiting, EINA_INLIST_GET(req));
473               requests[rtype].processing = eina_inlist_append(
474                  requests[rtype].processing, EINA_INLIST_GET(req));
475
476               if (!(*idle))
477                 sw = _slave_for_request_create(type);
478
479               if (!(*idle))
480                 {
481                    ERR("No idle slave available to process request type %d.",
482                        rtype);
483                    _cserve2_request_failed(req, CSERVE2_GENERIC);
484                    continue;
485                 }
486
487               sw = eina_list_data_get(*idle);
488               if (!_cserve2_request_dispatch(sw, ctype, req))
489                 {
490                    ERR("Could not dispatch request.");
491                    _cserve2_request_failed(req, CSERVE2_GENERIC);
492                    continue;
493                 }
494
495               *idle = eina_list_remove_list(*idle, *idle);
496               *working = eina_list_append(*working, sw);
497            }
498       }
499 }