5 #include "evas_cserve2.h"
27 typedef struct _Slave_Worker Slave_Worker;
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
34 static struct _Request_Match
36 Font_Request_Type rtype;
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 }
46 static Slave *_create_image_slave(void *data);
47 static Slave *_create_font_slave(void *data);
55 Slave *(*create_slave)(void *data);
58 { SLAVE_IMAGE, 3, NULL, NULL, _create_image_slave },
59 { SLAVE_FONT, 1, NULL, NULL, _create_font_slave },
65 Font_Request_Type type;
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 */
82 typedef struct _Waiter Waiter;
87 Eina_Inlist *processing; // TODO: Check if is there any use for this list.
90 typedef struct _Request_Queue Request_Queue;
92 static Request_Queue *requests = NULL;
93 // static Eina_List *processing = NULL;
95 static void _cserve2_requests_process(void);
98 _request_waiter_add(Font_Request *req, Client *client, unsigned int rid)
100 Waiter *w = malloc(sizeof(*w));
102 DBG("Add waiter to request. Client: %d, rid: %d", client->id, rid);
107 req->waiters = eina_list_append(req->waiters, w);
111 cserve2_request_add(Font_Request_Type type, unsigned int rid, Client *client, Font_Request *dep, Font_Request_Funcs *funcs, void *data)
113 Font_Request *req, *r;
117 /* Check if this request was already being processed. */
118 EINA_INLIST_FOREACH(requests[type].processing, r)
127 /* Check if this request was already waiting to be processed. */
130 EINA_INLIST_FOREACH(requests[type].waiting, r)
140 /* create new request */
143 DBG("Add request for rid: %d", rid);
144 req = calloc(1, sizeof(*req));
148 req->processing = EINA_FALSE;
150 requests[type].waiting = eina_inlist_append(requests[type].waiting,
151 EINA_INLIST_GET(req));
154 if (dep && !req->dependency)
156 req->locked = EINA_TRUE;
157 dep->dependents = eina_list_append(dep->dependents, req);
158 req->dependency = dep;
161 _request_waiter_add(req, client, rid);
163 _cserve2_requests_process();
169 cserve2_request_waiter_add(Font_Request *req, unsigned int rid, Client *client)
171 _request_waiter_add(req, client, rid);
175 _request_dependents_cancel(Font_Request *req, Error_Type err)
179 EINA_LIST_FREE(req->dependents, dep)
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);
190 cserve2_request_cancel(Font_Request *req, Client *client, Error_Type err)
192 Eina_List *l, *l_next;
195 EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, w)
197 if (w->client->id == client->id)
199 DBG("Removing answer from waiter client: %d, rid: %d",
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);
209 req->dependency->dependents = eina_list_remove(
210 req->dependency->dependents, req);
212 _request_dependents_cancel(req, err);
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))
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);
229 cserve2_request_cancel_all(Font_Request *req, Error_Type err)
233 DBG("Removing all answers.");
235 EINA_LIST_FREE(req->waiters, w)
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);
244 _request_dependents_cancel(req, err);
250 req->dependency->dependents = eina_list_remove(
251 req->dependency->dependents, req);
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);
260 cserve2_requests_init(void)
262 DBG("Initializing requests.");
263 requests = calloc(CSERVE2_REQ_LAST, sizeof(*requests));
267 cserve2_requests_shutdown(void)
269 DBG("Shutting down requests.");
274 _cserve2_request_failed(Font_Request *req, Error_Type type)
278 EINA_LIST_FREE(req->waiters, w)
280 req->funcs->error(w->client, req->data, type, w->rid);
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));
288 _request_dependents_cancel(req, type);
294 _slave_read_cb(Slave *s __UNUSED__, Slave_Command cmd, void *msg, void *data)
296 Slave_Worker *sw = data;
297 Font_Request *dep, *req = sw->data;
298 Eina_List **working, **idle;
301 EINA_LIST_FREE(req->waiters, w)
305 Error_Type *err = msg;
306 req->funcs->error(w->client, req->data, *err, w->rid);
309 req->funcs->response(w->client, req->data, msg, w->rid);
313 req->funcs->msg_free(req->msg, req->data);
314 // FIXME: We shouldn't free this message directly, it must be freed by a
317 requests[req->type].processing = eina_inlist_remove(
318 requests[req->type].processing, EINA_INLIST_GET(req));
320 EINA_LIST_FREE(req->dependents, dep)
322 dep->locked = EINA_FALSE;
323 dep->dependency = NULL;
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);
334 _cserve2_requests_process();
338 _slave_dead_cb(Slave *s __UNUSED__, void *data)
340 Slave_Worker *sw = data;
341 Font_Request *req = sw->data;
342 Eina_List **working = &_workers[sw->type].working;
345 _cserve2_request_failed(req, CSERVE2_LOADER_DIED);
347 *working = eina_list_remove(*working, sw);
352 _create_image_slave(void *data)
357 exe = getenv("EVAS_CSERVE2_SLAVE");
358 if (!exe) exe = "evas_cserve2_slave";
360 slave = cserve2_slave_run(exe, _slave_read_cb,
361 _slave_dead_cb, data);
367 _create_font_slave(void *data)
371 slave = cserve2_slave_thread_run(cserve2_font_slave_cb, NULL,
372 _slave_read_cb, _slave_dead_cb,
378 static Slave_Worker *
379 _slave_for_request_create(Slave_Type type)
384 sw = calloc(1, sizeof(Slave_Worker));
385 if (!sw) return NULL;
387 slave = _workers[type].create_slave(sw);
390 ERR("Could not launch slave process");
397 _workers[type].idle = eina_list_append(_workers[type].idle, sw);
403 _cserve2_request_dispatch(Slave_Worker *sw, Slave_Command ctype, Font_Request *req)
406 char *slave_msg = req->funcs->msg_create(req->data, &size);
409 DBG("dispatching message of type %d to slave.", req->type);
412 ERR("Could not create slave message for request type %d.", req->type);
416 req->msg = slave_msg;
418 cserve2_slave_send(sw->slave, ctype, slave_msg, size);
419 req->processing = EINA_TRUE;
425 _cserve2_requests_process(void)
427 unsigned int rtype, j;
429 for (rtype = 0; rtype < CSERVE2_REQ_LAST; rtype++)
431 Slave_Type type = SLAVE_NONE;
433 unsigned int max_workers;
434 Eina_List **idle, **working;
438 for (j = 0; _request_match[j].rtype != CSERVE2_REQ_LAST; j++)
440 if (_request_match[j].rtype == rtype)
442 type = _request_match[j].stype;
443 ctype = _request_match[j].ctype;
448 if (type == SLAVE_NONE)
451 if (!requests[rtype].waiting)
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.
457 max_workers = _workers[type].max;
458 idle = &_workers[type].idle;
459 working = &_workers[type].working;
461 EINA_INLIST_FOREACH_SAFE(requests[rtype].waiting, itr, req)
465 if (eina_list_count(*working) >= max_workers)
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));
477 sw = _slave_for_request_create(type);
481 ERR("No idle slave available to process request type %d.",
483 _cserve2_request_failed(req, CSERVE2_GENERIC);
487 sw = eina_list_data_get(*idle);
488 if (!_cserve2_request_dispatch(sw, ctype, req))
490 ERR("Could not dispatch request.");
491 _cserve2_request_failed(req, CSERVE2_GENERIC);
495 *idle = eina_list_remove_list(*idle, *idle);
496 *working = eina_list_append(*working, sw);