summaryrefslogtreecommitdiff
path: root/src/bin/evas/evas_cserve2_slaves.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_slaves.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_slaves.c')
-rw-r--r--src/bin/evas/evas_cserve2_slaves.c642
1 files changed, 642 insertions, 0 deletions
diff --git a/src/bin/evas/evas_cserve2_slaves.c b/src/bin/evas/evas_cserve2_slaves.c
new file mode 100644
index 0000000000..92f04e29e0
--- /dev/null
+++ b/src/bin/evas/evas_cserve2_slaves.c
@@ -0,0 +1,642 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include "evas_cserve2.h"
6
7#include <errno.h>
8#include <sys/wait.h>
9#include <signal.h>
10#include <fcntl.h>
11#include <unistd.h>
12
13typedef enum
14{
15 SLAVE_PROCESS,
16 SLAVE_THREAD
17} Slave_Type;
18
19struct _Slave
20{
21 Slave_Type type;
22 int write_fd;
23 int read_fd;
24 Slave_Read_Cb read_cb;
25 Slave_Dead_Cb dead_cb;
26 const void *data;
27 Eina_Binbuf *pending;
28
29 struct {
30 int size;
31 int read_size;
32 Slave_Command cmd;
33 char *buf;
34 } read;
35};
36
37struct _Slave_Proc
38{
39 Slave base;
40 pid_t pid;
41 const char *name;
42 Eina_Bool killed : 1;
43};
44
45typedef struct _Slave_Thread Slave_Thread;
46typedef struct _Slave_Proc Slave_Proc;
47
48struct _Slave_Thread
49{
50 Slave base;
51 pthread_t tid;
52 Slave_Thread_Data *tdata;
53};
54
55struct _Slave_Thread_Data {
56 int write_fd;
57 int read_fd;
58 Slave_Thread_Cb cb;
59 void *cb_data;
60 const void *cmddata;
61 void *cmdanswer;
62};
63
64static Eina_List *slave_procs;
65static Eina_List *slave_threads;
66static pthread_attr_t slave_thread_attr;
67
68static Slave_Proc *
69_slave_proc_find(pid_t pid)
70{
71 Eina_List *l;
72 Slave_Proc *s;
73
74 EINA_LIST_FOREACH(slave_procs, l, s)
75 if (s->pid == pid)
76 return s;
77 return NULL;
78}
79
80static void
81_slave_free(Slave *s)
82{
83 if (s->write_fd)
84 close(s->write_fd);
85 if (s->read_fd)
86 {
87 cserve2_fd_watch_del(s->read_fd);
88 close(s->read_fd);
89 }
90
91 free(s->read.buf);
92
93 if (s->pending)
94 eina_binbuf_free(s->pending);
95
96 if (s->dead_cb)
97 s->dead_cb(s, (void *)s->data);
98
99}
100
101static void
102_slave_proc_free(Slave_Proc *s)
103{
104 _slave_free((Slave *)s);
105
106 eina_stringshare_del(s->name);
107
108 free(s);
109}
110
111static void
112_slave_thread_free(Slave_Thread *s)
113{
114 Slave_Thread_Data *sd = s->tdata;
115
116 close(sd->write_fd);
117 close(sd->read_fd);
118
119 free(sd);
120
121 _slave_free((Slave *)s);
122
123 free(s);
124}
125
126static void
127_slave_proc_dead_cb(int pid, int status EINA_UNUSED)
128{
129 Slave_Proc *s;
130
131 DBG("Child dead with pid '%d'.", pid);
132 s = _slave_proc_find(pid);
133 if (!s)
134 {
135 ERR("Unknown child dead '%d'.", pid);
136 return;
137 }
138
139 slave_procs = eina_list_remove(slave_procs, s);
140 _slave_proc_free(s);
141}
142
143static size_t
144_slave_write(Slave *s, const char *data, size_t size)
145{
146 size_t sent = 0;
147
148 do
149 {
150 ssize_t ret;
151 ret = write(s->write_fd, data + sent, size - sent);
152 if (ret == -1)
153 {
154 if (errno == EAGAIN)
155 break;
156 if (errno == EPIPE)
157 {
158 WRN("Slave unexpectedly gone.");
159 /* handle dead? */
160 break;
161 }
162 }
163 sent += ret;
164 } while(sent < size);
165
166 return sent;
167}
168
169static void
170_slave_write_cb(int fd EINA_UNUSED, Fd_Flags flags EINA_UNUSED, void *data)
171{
172 Slave *s = data;
173 size_t sent;
174 size_t size;
175 const char *str;
176
177 size = eina_binbuf_length_get(s->pending);
178 str = (const char *)eina_binbuf_string_get(s->pending);
179 sent = _slave_write(s, str, size);
180 if (sent == size)
181 {
182 eina_binbuf_free(s->pending);
183 s->pending = NULL;
184 cserve2_fd_watch_del(s->write_fd);
185 return;
186 }
187
188 eina_binbuf_remove(s->pending, 0, sent);
189}
190
191static void
192_slave_read_clear(Slave *s)
193{
194 s->read.buf = NULL;
195 s->read.cmd = 0;
196 s->read.read_size = s->read.size = 0;
197}
198
199static void
200_slave_proc_read_cb(int fd, Fd_Flags flags, void *data)
201{
202 Slave *s = data;
203 Eina_Bool done = EINA_FALSE;
204
205 /* handle error */
206 if (!(flags & FD_READ))
207 return;
208
209 if (!s->read.size)
210 {
211 int ints[2];
212 ssize_t ret;
213
214 ret = read(fd, ints, sizeof(int) * 2);
215 if (ret < (int)sizeof(int) * 2)
216 {
217 return;
218 }
219 s->read.size = ints[0];
220 s->read.cmd = ints[1];
221 if (s->read.size)
222 s->read.buf = malloc(s->read.size);
223 else
224 done = EINA_TRUE;
225 }
226
227 if (s->read.buf)
228 {
229 ssize_t ret;
230 do {
231 char *p = s->read.buf + s->read.read_size;
232 int sz = s->read.size - s->read.read_size;
233 ret = read(fd, p, sz);
234 if (ret < 0)
235 {
236 if (errno == EAGAIN)
237 break;
238 }
239 s->read.read_size += ret;
240 } while(s->read.read_size < s->read.size);
241
242 if (s->read.read_size == s->read.size)
243 done = EINA_TRUE;
244 }
245
246 if (done)
247 {
248 s->read_cb(s, s->read.cmd, s->read.buf, (void *)s->data);
249 _slave_read_clear(s);
250 }
251}
252
253static void
254_slave_thread_read_cb(int fd, Fd_Flags flags, void *data)
255{
256 Slave_Thread *s = data;
257 Slave_Thread_Data *sd = s->tdata;
258
259 if (!(flags & FD_READ))
260 return;
261
262 Slave_Command cmd;
263 ssize_t ret;
264
265 ret = read(fd, (char *)&cmd, sizeof(cmd));
266 if (ret < (int)sizeof(int))
267 {
268 return;
269 }
270
271 s->base.read_cb((Slave *)s, cmd, sd->cmdanswer, (void *)s->base.data);
272}
273
274Eina_Bool
275cserve2_slaves_init(void)
276{
277 cserve2_on_child_dead_set(_slave_proc_dead_cb);
278
279 if (pthread_attr_init(&slave_thread_attr))
280 {
281 ERR("Could not initialize attributes for thread.");
282 cserve2_on_child_dead_set(NULL);
283 return EINA_FALSE;
284 }
285 return EINA_TRUE;
286}
287
288void
289cserve2_slaves_shutdown(void)
290{
291 Slave_Proc *sp;
292 Slave_Thread *st;
293 Eina_List *l;
294
295 cserve2_on_child_dead_set(NULL);
296
297 if (!slave_procs && !slave_threads)
298 return;
299
300 DBG("Shutting down slaves subsystem with %d slaves alive!",
301 eina_list_count(slave_procs));
302
303 EINA_LIST_FREE(slave_procs, sp)
304 {
305 kill(sp->pid, SIGKILL);
306 _slave_proc_free(sp);
307 }
308
309 EINA_LIST_FOREACH(slave_threads, l, st)
310 pthread_cancel(st->tid);
311
312 EINA_LIST_FREE(slave_threads, st)
313 {
314 pthread_join(st->tid, NULL);
315 _slave_thread_free(st);
316 }
317}
318
319static const char *
320_slave_proc_path_get(const char *name)
321{
322 char buf[PATH_MAX], cwd[PATH_MAX];
323 char *ret;
324
325 if (name[0] == '/')
326 {
327 if (access(name, X_OK))
328 return NULL;
329 return eina_stringshare_add(name);
330 }
331
332 ret = getcwd(cwd, sizeof(cwd));
333 snprintf(buf, sizeof(buf), "%s/%s", cwd, name);
334 if (!access(buf, X_OK))
335 return eina_stringshare_add(buf);
336
337 snprintf(buf, sizeof(buf), PACKAGE_LIBEXEC_DIR"/%s", name);
338 if (!access(buf, X_OK))
339 return eina_stringshare_add(buf);
340
341 return NULL;
342}
343
344static Slave *
345_cserve2_slave_proc_run(const char *exe, Slave_Read_Cb read_cb, Slave_Dead_Cb dead_cb, const void *data)
346{
347 Slave_Proc *s;
348 Slave *sb;
349 pid_t pid;
350 int child[2], parent[2];
351 int flags;
352 const char *name;
353
354 name = _slave_proc_path_get(exe);
355 if (!name)
356 {
357 ERR("Cannot execute slave '%s'. Not found or not executable.", exe);
358 return NULL;
359 }
360 DBG("Running slave '%s', resolved to path: %s", exe, name);
361
362 s = calloc(1, sizeof(Slave_Proc));
363 if (!s)
364 {
365 ERR("Could not create Slave_Proc handler.");
366 eina_stringshare_del(name);
367 return NULL;
368 }
369
370 sb = (Slave *)s;
371
372 if (pipe(child))
373 {
374 ERR("Could not create pipes for child.");
375 eina_stringshare_del(name);
376 free(s);
377 return NULL;
378 }
379
380 if (pipe(parent))
381 {
382 ERR("Could not create pipes for parent.");
383 eina_stringshare_del(name);
384 free(s);
385 close(child[0]);
386 close(child[1]);
387 return NULL;
388 }
389
390 pid = fork();
391 if (pid < 0)
392 {
393 ERR("Could not create sub process.");
394 eina_stringshare_del(name);
395 close(child[0]);
396 close(child[1]);
397 close(parent[0]);
398 close(parent[1]);
399 free(s);
400 return NULL;
401 }
402
403 if (!pid)
404 {
405 char *args[4], readfd[12], writefd[12];
406
407 close(child[1]);
408 close(parent[0]);
409
410 sprintf(readfd, "%d", child[0]);
411 sprintf(writefd, "%d", parent[1]);
412 args[0] = (char *)name;
413 args[1] = writefd;
414 args[2] = readfd;
415 args[3] = NULL;
416 execvp(name, args);
417 /* we only get here if execvp fails, which should not
418 * happen and if it does, it's baaaaaaaaad */
419 ERR("execvp() for slave at: '%s' failed! '%m'", name);
420 exit(1);
421 }
422
423 s->pid = pid;
424 s->name = name;
425 sb->type = SLAVE_PROCESS;
426 sb->write_fd = child[1];
427 flags = fcntl(sb->write_fd, F_GETFL);
428 flags |= O_NONBLOCK;
429 fcntl(sb->write_fd, F_SETFL, flags);
430 sb->read_fd = parent[0];
431 flags = fcntl(sb->read_fd, F_GETFL);
432 flags |= O_NONBLOCK;
433 fcntl(sb->read_fd, F_SETFL, flags);
434 sb->read_cb = read_cb;
435 sb->dead_cb = dead_cb;
436 sb->data = data;
437 cserve2_fd_watch_add(sb->read_fd, FD_READ, _slave_proc_read_cb, sb);
438
439 close(child[0]);
440 close(parent[1]);
441
442 slave_procs = eina_list_append(slave_procs, s);
443
444 return sb;
445}
446
447Slave *
448cserve2_slave_run(const char *name, Slave_Read_Cb read_cb, Slave_Dead_Cb dead_cb, const void *data)
449{
450 return _cserve2_slave_proc_run(name, read_cb, dead_cb, data);
451}
452
453static void *
454_slave_thread_cb(void *data)
455{
456 ssize_t n;
457 int ret;
458 Slave_Command cmd;
459
460 Slave_Thread_Data *sd = data;
461
462 n = read(sd->read_fd, &cmd, sizeof(cmd));
463 while (n != 0)
464 {
465 /* EINTR means we were interrupted by a signal before anything
466 * was sent, and if we are back here it means that signal was
467 * not meant for us to die. Any other error here is fatal and
468 * should result in the slave terminating.
469 */
470 if (errno == EINTR)
471 continue;
472
473 if (n != sizeof(cmd))
474 {
475 ERR("Slave thread read invalid size of command from server: %zu",
476 n);
477 continue;
478 }
479 sd->cmdanswer = sd->cb(sd, &cmd, sd->cmddata, sd->cb_data);
480 ret = write(sd->write_fd, &cmd, sizeof(cmd));
481
482 n = read(sd->read_fd, &cmd, sizeof(cmd));
483 }
484
485 ERR("Pipe was closed on the side. Slave thread exiting...");
486
487 return NULL;
488}
489
490Slave *
491cserve2_slave_thread_run(Slave_Thread_Cb thread_cb, void *thread_data, Slave_Read_Cb read_cb, Slave_Dead_Cb dead_cb, const void *data)
492{
493 Slave_Thread_Data *sd;
494 Slave_Thread *s;
495 Slave *sb;
496 pthread_t tid;
497 int child[2], parent[2];
498 int flags;
499
500 s = calloc(1, sizeof(Slave_Thread));
501 if (!s)
502 {
503 ERR("Could not create Slave_Thread handler.");
504 return NULL;
505 }
506
507 sb = (Slave *)s;
508
509 sd = calloc(1, sizeof(Slave_Thread_Data));
510 if (!sd)
511 {
512 ERR("Could not create Slave_Thread_Data.");
513 return NULL;
514 }
515
516 if (pipe(child))
517 {
518 ERR("Could not create pipes for child.");
519 free(s);
520 free(sd);
521 return NULL;
522 }
523
524 if (pipe(parent))
525 {
526 ERR("Could not create pipes for parent.");
527 free(s);
528 free(sd);
529 close(child[0]);
530 close(child[1]);
531 return NULL;
532 }
533
534 /* Setting data for slave thread */
535 sd->read_fd = child[0];
536 sd->write_fd = parent[1];
537
538 sd->cb = thread_cb;
539 sd->cb_data = thread_data;
540
541 if (pthread_create(&tid, &slave_thread_attr, _slave_thread_cb, sd))
542 {
543 ERR("Could not start slave thread.");
544 free(s);
545 free(sd);
546 close(child[0]);
547 close(child[1]);
548 close(parent[0]);
549 close(parent[1]);
550 return NULL;
551 }
552
553 s->tid = tid;
554 s->tdata = sd;
555 sb->type = SLAVE_THREAD;
556 sb->write_fd = child[1];
557 flags = fcntl(sb->write_fd, F_GETFL);
558 flags |= O_NONBLOCK;
559 fcntl(sb->write_fd, F_SETFL, flags);
560 sb->read_fd = parent[0];
561 flags = fcntl(sb->read_fd, F_GETFL);
562 flags |= O_NONBLOCK;
563 fcntl(sb->read_fd, F_SETFL, flags);
564 sb->read_cb = read_cb;
565 sb->dead_cb = dead_cb;
566 sb->data = data;
567 cserve2_fd_watch_add(sb->read_fd, FD_READ, _slave_thread_read_cb, sb);
568
569 slave_threads = eina_list_append(slave_threads, s);
570
571 return sb;
572}
573
574static void
575_slave_send_aux(Slave *s, const char *data, size_t size)
576{
577 size_t sent;
578
579 if (s->pending)
580 {
581 eina_binbuf_append_length(s->pending, (unsigned char *)data, size);
582 return;
583 }
584
585 sent = _slave_write(s, data, size);
586 if (sent < size)
587 {
588 s->pending = eina_binbuf_new();
589 eina_binbuf_append_length(s->pending, (unsigned char *)data + sent,
590 size - sent);
591 cserve2_fd_watch_add(s->write_fd, FD_WRITE, _slave_write_cb, s);
592 }
593}
594
595void
596_cserve2_slave_proc_send(Slave *s, Slave_Command cmd, const char *data, size_t size)
597{
598 int ints[2];
599
600 ints[0] = size;
601 ints[1] = cmd;
602 _slave_send_aux(s, (char *)ints, sizeof(int) * 2);
603 if (size)
604 _slave_send_aux(s, (char *)data, size);
605}
606
607void
608_cserve2_slave_thread_send(Slave_Thread *s, Slave_Command cmd, const char *data)
609{
610 s->tdata->cmddata = data;
611
612 _slave_send_aux((Slave *)s, (char *)&cmd, sizeof(cmd));
613}
614
615void
616cserve2_slave_send(Slave *s, Slave_Command cmd, const char *data, size_t size)
617{
618 if (s->type == SLAVE_PROCESS)
619 _cserve2_slave_proc_send(s, cmd, data, size);
620 else
621 _cserve2_slave_thread_send((Slave_Thread *)s, cmd, data);
622}
623
624static void
625_cserve2_slave_proc_kill(Slave_Proc *s)
626{
627 if (s->killed)
628 {
629 if (!kill(s->pid, 0))
630 DBG("Slave %p(%d) requested to kill, but it's still alive.",
631 s, s->pid);
632 }
633
634 s->killed = EINA_TRUE;
635 kill(s->pid, SIGTERM);
636}
637
638void
639cserve2_slave_kill(Slave *s)
640{
641 _cserve2_slave_proc_kill((Slave_Proc *)s);
642}