summaryrefslogtreecommitdiff
path: root/src/bin/evas/evas_cserve2_requests.c
diff options
context:
space:
mode:
authorVincent Torri <vincent.torri@gmail.com>2012-11-04 11:51:42 +0000
committerVincent Torri <vincent.torri@gmail.com>2012-11-04 11:51:42 +0000
commitc15e9c6575c3b5f39ded167dda5259de3de96151 (patch)
tree5115d7ae3620af24c2bc094cd062575af7adeda9 /src/bin/evas/evas_cserve2_requests.c
parenta5ac6a987caec5a7f7596a25d0a065b9cc94c50c (diff)
merge: and now Evas
I've tested make -j 3 install and it works nicely I've tested expedite with software and opengl xlib, and it works. Not tested other engines, so please report any problems (engines or other) on the ML. TODO: examples and tests, I'll add them later ISSUE: Eina_Unicode size check. It indirectly depends on eina_config.h, which is created at the end of the configure script. So its size is always 0. I don't know how that size is used, so I can't do a lot, for now. SVN revision: 78895
Diffstat (limited to 'src/bin/evas/evas_cserve2_requests.c')
-rw-r--r--src/bin/evas/evas_cserve2_requests.c560
1 files changed, 560 insertions, 0 deletions
diff --git a/src/bin/evas/evas_cserve2_requests.c b/src/bin/evas/evas_cserve2_requests.c
new file mode 100644
index 0000000000..2fe540d1fa
--- /dev/null
+++ b/src/bin/evas/evas_cserve2_requests.c
@@ -0,0 +1,560 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include "evas_cserve2.h"
6
7#define MAX_SLAVES 1
8
9typedef enum
10{
11 SLAVE_IMAGE,
12 SLAVE_FONT,
13 SLAVE_NONE
14} Slave_Type;
15
16struct _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
27typedef 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 */
34static struct _Request_Match
35{
36 Slave_Request_Type rtype;
37 Slave_Type stype;
38 Slave_Command ctype;
39 int require_spares; /* for speculative operations, will require to leave at
40 least this number of slaves always available */
41} _request_match[] =
42{
43 { CSERVE2_REQ_IMAGE_OPEN, SLAVE_IMAGE, IMAGE_OPEN, 0 },
44 { CSERVE2_REQ_IMAGE_LOAD, SLAVE_IMAGE, IMAGE_LOAD, 0 },
45 { CSERVE2_REQ_IMAGE_SPEC_LOAD, SLAVE_IMAGE, IMAGE_LOAD, 1 },
46 { CSERVE2_REQ_FONT_LOAD, SLAVE_FONT, FONT_LOAD, 0 },
47 { CSERVE2_REQ_FONT_GLYPHS_LOAD, SLAVE_FONT, FONT_GLYPHS_LOAD, 0 },
48 { CSERVE2_REQ_LAST, 0, 0, 0 }
49};
50
51static Slave *_create_image_slave(void *data);
52static Slave *_create_font_slave(void *data);
53
54static struct _Worker
55{
56 Slave_Type type;
57 unsigned int max;
58 Eina_List *idle;
59 Eina_List *working;
60 Slave *(*create_slave)(void *data);
61} _workers[] =
62{
63 { SLAVE_IMAGE, 3, NULL, NULL, _create_image_slave },
64 { SLAVE_FONT, 1, NULL, NULL, _create_font_slave },
65};
66
67struct _Slave_Request
68{
69 EINA_INLIST;
70 Slave_Request_Type type;
71 void *data;
72 void *msg;
73 Eina_List *waiters;
74 Eina_Bool processing;
75 Slave_Request_Funcs *funcs;
76 Slave_Request *dependency;
77 Eina_List *dependents; /* list of requests that depend on this one finishing */
78 Eina_Bool locked : 1; /* locked waiting for a dependency request to finish */
79 Eina_Bool cancelled : 1;
80};
81
82struct _Waiter
83{
84 unsigned int rid;
85 Client *client;
86};
87
88typedef struct _Waiter Waiter;
89
90struct _Request_Queue
91{
92 Eina_Inlist *waiting;
93 Eina_Inlist *processing; // TODO: Check if is there any use for this list.
94};
95
96typedef struct _Request_Queue Request_Queue;
97
98static Request_Queue *requests = NULL;
99// static Eina_List *processing = NULL;
100
101static void _cserve2_requests_process(void);
102
103static void
104_request_waiter_add(Slave_Request *req, Client *client, unsigned int rid)
105{
106 Waiter *w = malloc(sizeof(*w));
107
108 DBG("Add waiter to request. Client: %d, rid: %d", client->id, rid);
109
110 w->client = client;
111 w->rid = rid;
112
113 req->waiters = eina_list_append(req->waiters, w);
114}
115
116Slave_Request *
117cserve2_request_add(Slave_Request_Type type, unsigned int rid, Client *client, Slave_Request *dep, Slave_Request_Funcs *funcs, void *data)
118{
119 Slave_Request *req, *r;
120
121 req = NULL;
122
123 /* Check if this request was already being processed. */
124 EINA_INLIST_FOREACH(requests[type].processing, r)
125 {
126 if (r->data != data)
127 continue;
128
129 req = r;
130 break;
131 }
132
133 /* Check if this request was already waiting to be processed. */
134 if (!req)
135 {
136 EINA_INLIST_FOREACH(requests[type].waiting, r)
137 {
138 if (r->data != data)
139 continue;
140
141 req = r;
142 break;
143 }
144 }
145
146 /* create new request */
147 if (!req)
148 {
149 DBG("Add request for rid: %d", rid);
150 req = calloc(1, sizeof(*req));
151 req->type = type;
152 req->data = data;
153 req->waiters = NULL;
154 req->processing = EINA_FALSE;
155 req->funcs = funcs;
156 requests[type].waiting = eina_inlist_append(requests[type].waiting,
157 EINA_INLIST_GET(req));
158 }
159
160 if (dep && !req->dependency)
161 {
162 req->locked = EINA_TRUE;
163 dep->dependents = eina_list_append(dep->dependents, req);
164 req->dependency = dep;
165 }
166
167 if (client && rid)
168 _request_waiter_add(req, client, rid);
169
170 _cserve2_requests_process();
171
172 return req;
173}
174
175void
176cserve2_request_waiter_add(Slave_Request *req, unsigned int rid, Client *client)
177{
178 _request_waiter_add(req, client, rid);
179}
180
181void
182cserve2_request_type_set(Slave_Request *req, Slave_Request_Type type)
183{
184 Eina_Inlist **from, **to;
185
186 if (req->processing || (type == req->type))
187 return;
188
189 from = &requests[req->type].waiting;
190 to = &requests[type].waiting;
191
192 req->type = type;
193 *from = eina_inlist_remove(*from, EINA_INLIST_GET(req));
194 *to = eina_inlist_append(*to, EINA_INLIST_GET(req));
195}
196
197static void
198_request_dependents_cancel(Slave_Request *req, Error_Type err)
199{
200 Slave_Request *dep;
201
202 EINA_LIST_FREE(req->dependents, dep)
203 {
204 dep->locked = EINA_FALSE;
205 dep->dependency = NULL;
206 /* Maybe we need a better way to inform the creator of the request
207 * that it was cancelled because its dependency failed? */
208 cserve2_request_cancel_all(dep, err);
209 }
210}
211
212void
213cserve2_request_cancel(Slave_Request *req, Client *client, Error_Type err)
214{
215 Eina_List *l, *l_next;
216 Waiter *w;
217
218 if (req->funcs && req->funcs->error)
219 req->funcs->error(req->data, err);
220
221 EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, w)
222 {
223 if (w->client->id == client->id)
224 {
225 DBG("Removing answer from waiter client: %d, rid: %d",
226 client->id, w->rid);
227 cserve2_client_error_send(w->client, w->rid, err);
228 req->waiters = eina_list_remove_list(req->waiters, l);
229 free(w);
230 }
231 }
232
233 // TODO: When we have speculative preload, there may be no waiters,
234 // so we need a flag or something else to make things still load.
235 if ((!req->waiters) && (!req->processing))
236 {
237 Eina_Inlist **reqlist = &requests[req->type].waiting;
238 *reqlist = eina_inlist_remove(*reqlist, EINA_INLIST_GET(req));
239 // TODO: If the request is being processed, it can't be deleted. Must
240 // be marked as delete_me instead.
241 req->funcs->msg_free(req->msg, req->data);
242
243 if (req->dependency)
244 req->dependency->dependents = eina_list_remove(
245 req->dependency->dependents, req);
246
247 _request_dependents_cancel(req, err);
248
249 free(req);
250 }
251 else if (!req->waiters)
252 req->cancelled = EINA_TRUE;
253}
254
255void
256cserve2_request_cancel_all(Slave_Request *req, Error_Type err)
257{
258 Waiter *w;
259
260 DBG("Removing all answers.");
261
262 if (req->funcs && req->funcs->error)
263 req->funcs->error(req->data, err);
264
265 EINA_LIST_FREE(req->waiters, w)
266 {
267 DBG("Removing answer from waiter client: %d, rid: %d",
268 w->client->id, w->rid);
269 cserve2_client_error_send(w->client, w->rid, err);
270 free(w);
271 }
272
273 _request_dependents_cancel(req, err);
274
275 if (req->processing)
276 {
277 req->cancelled = EINA_TRUE;
278 return;
279 }
280
281 if (req->dependency)
282 req->dependency->dependents = eina_list_remove(
283 req->dependency->dependents, req);
284
285 requests[req->type].waiting = eina_inlist_remove(
286 requests[req->type].waiting, EINA_INLIST_GET(req));
287 req->funcs->msg_free(req->msg, req->data);
288 free(req);
289}
290
291void
292cserve2_requests_init(void)
293{
294 DBG("Initializing requests.");
295 requests = calloc(CSERVE2_REQ_LAST, sizeof(*requests));
296}
297
298void
299cserve2_requests_shutdown(void)
300{
301 DBG("Shutting down requests.");
302 free(requests);
303}
304
305static void
306_cserve2_request_failed(Slave_Request *req, Error_Type type)
307{
308 Waiter *w;
309
310 req->funcs->error(req->data, type);
311
312 EINA_LIST_FREE(req->waiters, w)
313 {
314 cserve2_client_error_send(w->client, w->rid, type);
315 free(w);
316 }
317
318 req->funcs->msg_free(req->msg, req->data);
319 requests[req->type].processing = eina_inlist_remove(
320 requests[req->type].processing, EINA_INLIST_GET(req));
321
322 _request_dependents_cancel(req, type);
323
324 free(req);
325}
326
327static void
328_slave_read_cb(Slave *s EINA_UNUSED, Slave_Command cmd, void *msg, void *data)
329{
330 Slave_Worker *sw = data;
331 Slave_Request *dep, *req = sw->data;
332 Eina_List **working, **idle;
333 Waiter *w;
334 Msg_Base *resp = NULL;
335 int resp_size;
336
337 if (req->cancelled)
338 goto free_it;
339
340 if (cmd == ERROR)
341 {
342 Error_Type *err = msg;
343 WRN("Received error %d from slave, for request type %d.",
344 *err, req->type);
345 req->funcs->error(req->data, *err);
346 }
347 else
348 {
349 DBG("Received response from slave for message type %d.", req->type);
350 resp = req->funcs->response(req->data, msg, &resp_size);
351 }
352
353 EINA_LIST_FREE(req->waiters, w)
354 {
355 if (cmd == ERROR)
356 {
357 Error_Type *err = msg;
358 cserve2_client_error_send(w->client, w->rid, *err);
359 }
360 else
361 {
362 resp->rid = w->rid;
363 cserve2_client_send(w->client, &resp_size, sizeof(resp_size));
364 cserve2_client_send(w->client, resp, resp_size);
365 }
366 free(w);
367 }
368
369 free(resp);
370
371free_it:
372 req->funcs->msg_free(req->msg, req->data);
373
374 // FIXME: We shouldn't free this message directly, it must be freed by a
375 // callback.
376 free(msg);
377 requests[req->type].processing = eina_inlist_remove(
378 requests[req->type].processing, EINA_INLIST_GET(req));
379
380 EINA_LIST_FREE(req->dependents, dep)
381 {
382 dep->locked = EINA_FALSE;
383 dep->dependency = NULL;
384 }
385
386 free(req);
387 sw->data = NULL;
388
389 working = &_workers[sw->type].working;
390 idle = &_workers[sw->type].idle;
391 *working = eina_list_remove(*working, sw);
392 *idle = eina_list_append(*idle, sw);
393
394 _cserve2_requests_process();
395}
396
397static void
398_slave_dead_cb(Slave *s EINA_UNUSED, void *data)
399{
400 Slave_Worker *sw = data;
401 Slave_Request *req = sw->data;
402 Eina_List **working = &_workers[sw->type].working;
403
404 if (req)
405 _cserve2_request_failed(req, CSERVE2_LOADER_DIED);
406
407 *working = eina_list_remove(*working, sw);
408 free(sw);
409}
410
411static Slave *
412_create_image_slave(void *data)
413{
414 char *exe;
415 Slave *slave;
416
417 exe = getenv("EVAS_CSERVE2_SLAVE");
418 if (!exe) exe = "evas_cserve2_slave";
419
420 slave = cserve2_slave_run(exe, _slave_read_cb,
421 _slave_dead_cb, data);
422
423 return slave;
424}
425
426static Slave *
427_create_font_slave(void *data)
428{
429 Slave *slave;
430
431 slave = cserve2_slave_thread_run(cserve2_font_slave_cb, NULL,
432 _slave_read_cb, _slave_dead_cb,
433 data);
434
435 return slave;
436}
437
438static Slave_Worker *
439_slave_for_request_create(Slave_Type type)
440{
441 Slave_Worker *sw;
442 Slave *slave;
443
444 sw = calloc(1, sizeof(Slave_Worker));
445 if (!sw) return NULL;
446
447 slave = _workers[type].create_slave(sw);
448 if (!slave)
449 {
450 ERR("Could not launch slave process");
451 free(sw);
452 return NULL;
453 }
454
455 sw->slave = slave;
456 sw->type = type;
457 _workers[type].idle = eina_list_append(_workers[type].idle, sw);
458
459 return sw;
460}
461
462static Eina_Bool
463_cserve2_request_dispatch(Slave_Worker *sw, Slave_Command ctype, Slave_Request *req)
464{
465 int size;
466 char *slave_msg = req->funcs->msg_create(req->data, &size);
467
468
469 DBG("dispatching message of type %d to slave.", req->type);
470 if (!slave_msg)
471 {
472 ERR("Could not create slave message for request type %d.", req->type);
473 return EINA_FALSE;
474 }
475
476 req->msg = slave_msg;
477 sw->data = req;
478 cserve2_slave_send(sw->slave, ctype, slave_msg, size);
479 req->processing = EINA_TRUE;
480
481 return EINA_TRUE;
482}
483
484static void
485_cserve2_requests_process(void)
486{
487 unsigned int rtype, j;
488
489 for (rtype = 0; rtype < CSERVE2_REQ_LAST; rtype++)
490 {
491 Slave_Type type = SLAVE_NONE;
492 Slave_Command ctype;
493 unsigned int max_workers;
494 Eina_List **idle, **working;
495 Eina_Inlist *itr;
496 Slave_Request *req;
497
498 for (j = 0; _request_match[j].rtype != CSERVE2_REQ_LAST; j++)
499 {
500 if (_request_match[j].rtype == rtype)
501 {
502 type = _request_match[j].stype;
503 ctype = _request_match[j].ctype;
504 break;
505 }
506 }
507
508 if (type == SLAVE_NONE)
509 continue;
510
511 if (!requests[rtype].waiting)
512 continue;
513
514 /* Now we have the worker type to use (image or font), and the list
515 * of requests to process. Just process as many requests as we can.
516 */
517 max_workers = _workers[type].max;
518 idle = &_workers[type].idle;
519 working = &_workers[type].working;
520
521 EINA_INLIST_FOREACH_SAFE(requests[rtype].waiting, itr, req)
522 {
523 Slave_Worker *sw;
524
525 if (eina_list_count(*working) >=
526 (max_workers - _request_match[j].require_spares))
527 break;
528
529 if (req->locked)
530 continue;
531
532 requests[rtype].waiting = eina_inlist_remove(
533 requests[rtype].waiting, EINA_INLIST_GET(req));
534 requests[rtype].processing = eina_inlist_append(
535 requests[rtype].processing, EINA_INLIST_GET(req));
536
537 if (!(*idle))
538 sw = _slave_for_request_create(type);
539
540 if (!(*idle))
541 {
542 ERR("No idle slave available to process request type %d.",
543 rtype);
544 _cserve2_request_failed(req, CSERVE2_GENERIC);
545 continue;
546 }
547
548 sw = eina_list_data_get(*idle);
549 if (!_cserve2_request_dispatch(sw, ctype, req))
550 {
551 ERR("Could not dispatch request.");
552 _cserve2_request_failed(req, CSERVE2_GENERIC);
553 continue;
554 }
555
556 *idle = eina_list_remove_list(*idle, *idle);
557 *working = eina_list_append(*working, sw);
558 }
559 }
560}