summaryrefslogtreecommitdiff
path: root/legacy/evas/src
diff options
context:
space:
mode:
authorIván Briano <sachieru@gmail.com>2012-05-03 21:01:31 +0000
committerIván Briano <sachieru@gmail.com>2012-05-03 21:01:31 +0000
commit15328efb85bcf413025d787d1d58d812a407f25a (patch)
tree37c09205c159d8b9758ffdada5dff2248e632613 /legacy/evas/src
parentb8ade6a7cfa99ca19c054bd6b0bf488e63accbf4 (diff)
evas/cserve2: fix typo that kept cserve2 disabled
now seriously... Introducing Cache Serve 2. This cache server will initially load images for clients connected to it. It starts slave processes to load these images, and share the loaded images through shm with the clients. All the connection done between clients and the server goes through sockets. The cserve2 build option is turned on by default, while the old cserve was disabled, but in order to make clients use it, the environment variable EVAS_CSERVE2 must be set, and a server must be running. Clients will try to find the socket on a specified location using the environment variable EVAS_CSERVE2_SOCKET. If it's not defined, then the XDG_RUNTIME_DIR path should be used, and finally HOME, TMPDIR and /tmp. SVN revision: 70699
Diffstat (limited to 'legacy/evas/src')
-rw-r--r--legacy/evas/src/bin/Makefile.am50
-rw-r--r--legacy/evas/src/bin/dummy_slave.c205
-rw-r--r--legacy/evas/src/bin/evas_cserve2.h180
-rw-r--r--legacy/evas/src/bin/evas_cserve2_cache.c1261
-rw-r--r--legacy/evas/src/bin/evas_cserve2_client.c429
-rw-r--r--legacy/evas/src/bin/evas_cserve2_main.c456
-rw-r--r--legacy/evas/src/bin/evas_cserve2_main_loop_linux.c779
-rw-r--r--legacy/evas/src/bin/evas_cserve2_messages.c189
-rw-r--r--legacy/evas/src/bin/evas_cserve2_shm.c147
-rw-r--r--legacy/evas/src/bin/evas_cserve2_slave.c480
-rw-r--r--legacy/evas/src/bin/evas_cserve2_slave.h86
-rw-r--r--legacy/evas/src/bin/evas_cserve2_slaves.c395
-rw-r--r--legacy/evas/src/bin/evas_cserve2_utils.c58
-rw-r--r--legacy/evas/src/bin/loaders/Makefile.am47
-rw-r--r--legacy/evas/src/bin/loaders/bmp/Makefile.am32
-rw-r--r--legacy/evas/src/bin/loaders/bmp/evas_image_load_bmp.c1479
-rw-r--r--legacy/evas/src/bin/loaders/eet/Makefile.am34
-rw-r--r--legacy/evas/src/bin/loaders/eet/evas_image_load_eet.c158
-rw-r--r--legacy/evas/src/bin/loaders/ico/Makefile.am32
-rw-r--r--legacy/evas/src/bin/loaders/ico/evas_image_load_ico.c789
-rw-r--r--legacy/evas/src/bin/loaders/jpeg/Makefile.am33
-rw-r--r--legacy/evas/src/bin/loaders/jpeg/evas_image_load_jpeg.c1118
-rw-r--r--legacy/evas/src/bin/loaders/pmaps/Makefile.am33
-rw-r--r--legacy/evas/src/bin/loaders/pmaps/evas_image_load_pmaps.c573
-rw-r--r--legacy/evas/src/bin/loaders/png/Makefile.am33
-rw-r--r--legacy/evas/src/bin/loaders/png/evas_image_load_png.c310
-rw-r--r--legacy/evas/src/bin/loaders/psd/Makefile.am32
-rw-r--r--legacy/evas/src/bin/loaders/psd/evas_image_load_psd.c981
-rw-r--r--legacy/evas/src/bin/loaders/tga/Makefile.am32
-rw-r--r--legacy/evas/src/bin/loaders/tga/evas_image_load_tga.c574
-rw-r--r--legacy/evas/src/bin/loaders/tiff/Makefile.am34
-rw-r--r--legacy/evas/src/bin/loaders/tiff/evas_image_load_tiff.c282
-rw-r--r--legacy/evas/src/bin/loaders/wbmp/Makefile.am32
-rw-r--r--legacy/evas/src/bin/loaders/wbmp/evas_image_load_wbmp.c189
-rw-r--r--legacy/evas/src/bin/loaders/xpm/Makefile.am34
-rw-r--r--legacy/evas/src/bin/loaders/xpm/evas_image_load_xpm.c650
-rw-r--r--legacy/evas/src/examples/Makefile.am6
-rw-r--r--legacy/evas/src/examples/evas-images3.c196
-rw-r--r--legacy/evas/src/lib/Makefile.am22
-rw-r--r--legacy/evas/src/lib/cache/evas_cache_image.c4
-rw-r--r--legacy/evas/src/lib/cache2/Makefile.am29
-rw-r--r--legacy/evas/src/lib/cache2/evas_cache2.c932
-rw-r--r--legacy/evas/src/lib/cache2/evas_cache2.h87
-rw-r--r--legacy/evas/src/lib/canvas/Makefile.am1
-rw-r--r--legacy/evas/src/lib/canvas/evas_main.c6
-rw-r--r--legacy/evas/src/lib/canvas/evas_render.c4
-rw-r--r--legacy/evas/src/lib/cserve2/Makefile.am27
-rw-r--r--legacy/evas/src/lib/cserve2/evas_cs2.h133
-rw-r--r--legacy/evas/src/lib/cserve2/evas_cs2_client.c742
-rw-r--r--legacy/evas/src/lib/cserve2/evas_cs2_image_data.c23
-rw-r--r--legacy/evas/src/lib/cserve2/evas_cs2_private.h34
-rw-r--r--legacy/evas/src/lib/engines/common/Makefile.am1
-rw-r--r--legacy/evas/src/lib/engines/common/evas_image.h5
-rw-r--r--legacy/evas/src/lib/engines/common/evas_image_load.c39
-rw-r--r--legacy/evas/src/lib/engines/common/evas_image_main.c96
-rw-r--r--legacy/evas/src/lib/engines/common/evas_image_private.h2
-rw-r--r--legacy/evas/src/lib/engines/common/evas_image_scalecache.c58
-rw-r--r--legacy/evas/src/lib/include/evas_common.h9
-rw-r--r--legacy/evas/src/modules/engines/buffer/Makefile.am1
-rw-r--r--legacy/evas/src/modules/engines/buffer/evas_outbuf.c75
-rw-r--r--legacy/evas/src/modules/engines/software_generic/Makefile.am1
-rw-r--r--legacy/evas/src/modules/engines/software_generic/evas_engine.c109
-rw-r--r--legacy/evas/src/modules/engines/software_x11/Makefile.am1
-rw-r--r--legacy/evas/src/modules/engines/software_x11/evas_xlib_outbuf.c155
64 files changed, 14977 insertions, 47 deletions
diff --git a/legacy/evas/src/bin/Makefile.am b/legacy/evas/src/bin/Makefile.am
index 7159eaa9f8..0abb3bbe51 100644
--- a/legacy/evas/src/bin/Makefile.am
+++ b/legacy/evas/src/bin/Makefile.am
@@ -39,3 +39,53 @@ $(top_builddir)/src/lib/libevas.la \
39@EINA_LIBS@ 39@EINA_LIBS@
40 40
41endif 41endif
42
43if EVAS_CSERVE2
44
45SUBDIRS = loaders
46
47libexec_PROGRAMS = evas_cserve2 evas_cserve2_slave dummy_slave
48bin_PROGRAMS = evas_cserve2_client
49
50AM_CPPFLAGS = \
51-I. \
52-I$(top_srcdir)/src/lib \
53-I$(top_srcdir)/src/lib/include \
54-I$(top_srcdir)/src/lib/cserve2 \
55-DPACKAGE_BIN_DIR=\"$(bindir)\" \
56-DPACKAGE_LIB_DIR=\"$(libdir)\" \
57-DPACKAGE_LIBEXEC_DIR=\"$(libexecdir)\" \
58@EINA_CFLAGS@
59
60evas_cserve2_SOURCES = \
61evas_cserve2.h \
62evas_cserve2_slave.h \
63evas_cserve2_main.c \
64evas_cserve2_slaves.c \
65evas_cserve2_messages.c \
66evas_cserve2_shm.c \
67evas_cserve2_cache.c \
68evas_cserve2_main_loop_linux.c
69
70evas_cserve2_LDADD = \
71@EINA_LIBS@
72
73evas_cserve2_client_SOURCES = \
74evas_cserve2_client.c
75
76evas_cserve2_slave_SOURCES = \
77evas_cserve2_slave.c \
78evas_cserve2_utils.c
79
80evas_cserve2_slave_LDFLAGS = -export-dynamic
81
82evas_cserve2_slave_LDADD = \
83@EINA_LIBS@
84
85dummy_slave_SOURCES = \
86dummy_slave.c
87
88dummy_slave_LDADD = \
89@EINA_LIBS@
90
91endif
diff --git a/legacy/evas/src/bin/dummy_slave.c b/legacy/evas/src/bin/dummy_slave.c
new file mode 100644
index 0000000000..a150d67e1f
--- /dev/null
+++ b/legacy/evas/src/bin/dummy_slave.c
@@ -0,0 +1,205 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include <errno.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <time.h>
9#include <sys/mman.h>
10#include <fcntl.h>
11#include <unistd.h>
12
13#include "evas_cserve2.h"
14
15static Eina_Bool
16command_read(int fd, Slave_Command *cmd, void **params)
17{
18 ssize_t ret;
19 int ints[2], size, got = 0;
20 char *buf;
21
22 ret = read(fd, ints, sizeof(int) * 2);
23 if (ret < (int)sizeof(int) * 2)
24 return EINA_FALSE;
25
26 size = ints[0];
27 buf = malloc(size);
28 if (!buf) return EINA_FALSE;
29
30 do {
31 ret = read(fd, buf + got, size - got);
32 if (ret < 0)
33 {
34 /* EINTR means we were interrupted by a signal before anything
35 * was sent, and if we are back here it means that signal was
36 * not meant for us to die. Any other error here is fatal and
37 * should result in the slave terminating.
38 */
39 if (errno == EINTR)
40 continue;
41 free(buf);
42 return EINA_FALSE;
43 }
44 got += ret;
45 } while (got < size);
46
47 *cmd = ints[1];
48 *params = buf;
49
50 return EINA_TRUE;
51}
52
53static Eina_Bool
54response_send(int fd, Slave_Command cmd, void *resp, int size)
55{
56 int sent = 0, ints[2];
57 const char *data = resp;
58 ssize_t ret;
59
60 ints[0] = size;
61 ints[1] = cmd;
62 ret = write(fd, ints, sizeof(int) * 2);
63 if (ret < 0)
64 return EINA_FALSE;
65 if (!size)
66 return EINA_TRUE;
67 do {
68 ret = write(fd, data + sent, size - sent);
69 if (ret < 0)
70 {
71 /* EINTR means we were interrupted by a signal before anything
72 * was sent, and if we are back here it means that signal was
73 * not meant for us to die. Any other error here is fatal and
74 * should result in the slave terminating.
75 */
76 if (errno == EINTR)
77 continue;
78 return EINA_FALSE;
79 }
80 sent += ret;
81 } while (sent < size);
82
83 return EINA_TRUE;
84}
85
86static Eina_Bool
87error_send(int fd, Error_Type err)
88{
89 return response_send(fd, ERROR, &err, sizeof(Error_Type));
90}
91
92void *
93cserve2_shm_map(const char *name, size_t length, off_t offset)
94{
95 void *map;
96 int fd;
97
98 fd = shm_open(name, O_RDWR, 0);
99 if (fd == -1)
100 return MAP_FAILED;
101
102 map = mmap(NULL, length, PROT_WRITE, MAP_SHARED, fd, offset);
103
104 close(fd);
105
106 return map;
107}
108
109void
110cserve2_shm_unmap(void *map, size_t length)
111{
112 munmap(map, length);
113}
114
115static Error_Type
116image_open(const char *file __UNUSED__, const char *key __UNUSED__, Slave_Msg_Image_Opened *result)
117{
118 memset(result, 0, sizeof(*result));
119 result->w = 32;
120 result->h = 32;
121 result->frame_count = 1;
122 result->loop_count = 0;
123 result->loop_hint = 0;
124 result->alpha = EINA_TRUE;
125 return CSERVE2_NONE;
126}
127
128static Error_Type
129image_load(const char *shmfile, Slave_Msg_Image_Load *params)
130{
131 char *map = cserve2_shm_map(shmfile, params->shm.mmap_size,
132 params->shm.mmap_offset);
133 if (map == MAP_FAILED)
134 return CSERVE2_RESOURCE_ALLOCATION_FAILED;
135
136 memset(map + params->shm.image_offset, 'A', params->shm.image_size);
137
138 return CSERVE2_NONE;
139}
140
141int main(int c, char **v)
142{
143 int wfd, rfd;
144 Slave_Command cmd;
145 void *params = NULL;;
146 Eina_Bool quit = EINA_FALSE;
147
148 if (c < 3)
149 return 1;
150
151 wfd = atoi(v[1]);
152 rfd = atoi(v[2]);
153
154 while (!quit)
155 {
156 if (!command_read(rfd, &cmd, &params))
157 {
158 error_send(wfd, CSERVE2_INVALID_COMMAND);
159 continue;
160 }
161
162 switch (cmd)
163 {
164 case IMAGE_OPEN:
165 {
166 Slave_Msg_Image_Opened result;
167 Slave_Msg_Image_Open *p;
168 Error_Type err;
169 const char *file, *key;
170 p = params;
171 file = (const char *)(p + sizeof(*p));
172 key = file + strlen(file) + 1;
173 if ((err = image_open(file, key, &result)) != CSERVE2_NONE)
174 error_send(wfd, err);
175 else
176 response_send(wfd, IMAGE_OPEN, &result,
177 sizeof(Slave_Msg_Image_Opened));
178 break;
179 }
180 case IMAGE_LOAD:
181 {
182 Slave_Msg_Image_Load *load_args = params;
183 Error_Type err;
184 const char *shmfile = ((const char *)params) +
185 sizeof(Slave_Msg_Image_Load);
186 if ((err = image_load(shmfile, load_args)) != CSERVE2_NONE)
187 error_send(wfd, err);
188 else
189 response_send(wfd, IMAGE_LOAD, NULL, 0);
190 break;
191 }
192 case SLAVE_QUIT:
193 {
194 quit = EINA_TRUE;
195 break;
196 }
197
198 default:
199 error_send(wfd, CSERVE2_INVALID_COMMAND);
200 }
201 free(params);
202 }
203
204 return 0;
205}
diff --git a/legacy/evas/src/bin/evas_cserve2.h b/legacy/evas/src/bin/evas_cserve2.h
new file mode 100644
index 0000000000..92cfe627ed
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2.h
@@ -0,0 +1,180 @@
1#ifndef _EVAS_CSERVE2_H
2#define _EVAS_CSERVE2_H
3
4#include <Eina.h>
5#include "evas_cs2.h"
6
7#ifdef ERR
8#undef ERR
9#endif
10#define ERR(...) EINA_LOG_DOM_ERR(_evas_cserve2_bin_log_dom, __VA_ARGS__)
11#ifdef DBG
12#undef DBG
13#endif
14#define DBG(...) EINA_LOG_DOM_DBG(_evas_cserve2_bin_log_dom, __VA_ARGS__)
15#ifdef WRN
16#undef WRN
17#endif
18#define WRN(...) EINA_LOG_DOM_WARN(_evas_cserve2_bin_log_dom, __VA_ARGS__)
19#ifdef INF
20#undef INF
21#endif
22#define INF(...) EINA_LOG_DOM_INFO(_evas_cserve2_bin_log_dom, __VA_ARGS__)
23
24extern int _evas_cserve2_bin_log_dom;
25
26typedef struct _Slave_Proc Slave_Proc;
27typedef struct _Shm_Handle Shm_Handle;
28
29typedef enum {
30 FD_READ = 1,
31 FD_WRITE = 2,
32 FD_ERROR = 4
33} Fd_Flags;
34
35struct _Client {
36 unsigned int id;
37 int socket;
38 struct {
39 Eina_Bool reading;
40 char *buf; // buffer of data being read
41 int done, size;
42 Eina_Binbuf *pending; // pending data to send
43 } msg;
44 struct {
45 Eina_Hash *referencing; // indexed by client file id
46 } files;
47 struct {
48 Eina_Hash *referencing; // indexed by client image id
49 } images;
50};
51
52typedef struct _Client Client;
53
54struct _Image_Load_Opts {
55 unsigned int w, h;
56 unsigned int rx, ry, rw, rh;
57 unsigned int scale_down_by;
58 double dpi;
59 Eina_Bool orientation;
60};
61
62typedef struct _Image_Load_Opts Image_Load_Opts;
63
64typedef enum {
65 IMAGE_OPEN,
66 IMAGE_LOAD,
67 SLAVE_QUIT,
68 ERROR
69} Slave_Command;
70
71struct _Slave_Msg_Image_Open {
72 Eina_Bool has_opts : 1;
73 Eina_Bool has_loader_data : 1;
74};
75
76struct _Slave_Msg_Image_Opened {
77 int w, h;
78 int degree;
79 int scale; /* used by jpeg when loading in smaller sizes */
80 int frame_count;
81 int loop_count;
82 int loop_hint; /* include Evas.h? Copy the enum around? */
83 Eina_Bool alpha : 1;
84 Eina_Bool animated : 1;
85 Eina_Bool rotated : 1;
86
87 Eina_Bool has_loader_data : 1;
88};
89
90struct _Slave_Msg_Image_Load {
91 int w, h;
92 Image_Load_Opts opts;
93 struct {
94 int mmap_offset;
95 int image_offset;
96 int mmap_size;
97 int image_size;
98 } shm;
99 Eina_Bool alpha : 1;
100 Eina_Bool has_loader_data : 1;
101};
102
103struct _Slave_Msg_Image_Loaded {
104 Eina_Bool alpha_sparse : 1;
105};
106
107typedef struct _Slave_Msg_Image_Open Slave_Msg_Image_Open;
108typedef struct _Slave_Msg_Image_Opened Slave_Msg_Image_Opened;
109typedef struct _Slave_Msg_Image_Load Slave_Msg_Image_Load;
110typedef struct _Slave_Msg_Image_Loaded Slave_Msg_Image_Loaded;
111
112typedef void (*Fd_Watch_Cb)(int fd, Fd_Flags flags, void *data);
113typedef void (*Timeout_Cb)(void); /* void* for compat? */
114typedef void (*Main_Loop_Child_Dead_Cb)(int pid, int status); /* void* for compat? */
115typedef void (*Slave_Dead_Cb)(Slave_Proc *slave, void *data);
116typedef void (*Slave_Read_Cb)(Slave_Proc *slave, Slave_Command cmd, void *msg, void *data);
117typedef void (*File_Change_Cb)(const char *path, Eina_Bool deleted, void *data);
118
119void cserve2_client_accept(int fd);
120ssize_t cserve2_client_read(Client *client, void *buf, size_t len);
121ssize_t cserve2_client_write(Client *client, const void *buf, size_t len);
122void cserve2_client_del(Client *client);
123void cserve2_client_deliver(Client *client);
124void cserve2_client_error_send(Client *client, unsigned int rid, int error_code);
125ssize_t cserve2_client_send(Client *client, const void *data, size_t size);
126
127Eina_Bool cserve2_fd_watch_add(int fd, Fd_Flags flags, Fd_Watch_Cb cb, const void *data);
128Eina_Bool cserve2_fd_watch_del(int fd);
129Eina_Bool cserve2_fd_watch_flags_set(int fd, Fd_Flags flags);
130Eina_Bool cserve2_fd_watch_flags_get(int fd, Fd_Flags *flags);
131
132Eina_Bool cserve2_file_change_watch_add(const char *path, File_Change_Cb cb, const void *data);
133Eina_Bool cserve2_file_change_watch_del(const char *path);
134
135void cserve2_on_child_dead_set(Main_Loop_Child_Dead_Cb cb);
136
137void cserve2_timeout_cb_set(int t, Timeout_Cb cb);
138
139Eina_Bool cserve2_main_loop_setup(void);
140void cserve2_main_loop_run(void);
141void cserve2_main_loop_finish(void);
142
143Eina_Bool cserve2_slaves_init(void);
144void cserve2_slaves_shutdown(void);
145int cserve2_slave_available_get(void);
146Eina_Bool cserve2_slave_cmd_dispatch(void *data, Slave_Command cmd, const void *msg, int size);
147Slave_Proc *cserve2_slave_run(const char *exe, Slave_Read_Cb read_cb, Slave_Dead_Cb dead_cb, const void *data);
148void cserve2_slave_send(Slave_Proc *s, Slave_Command cmd, const char *data, size_t size);
149void cserve2_slave_kill(Slave_Proc *s);
150
151void cserve2_message_handler(int fd, Fd_Flags flags, void *data);
152
153Shm_Handle *cserve2_shm_request(size_t size);
154void cserve2_shm_unref(Shm_Handle *shm);
155const char *cserve2_shm_name_get(const Shm_Handle *shm);
156off_t cserve2_shm_map_offset_get(const Shm_Handle *shm);
157off_t cserve2_shm_offset_get(const Shm_Handle *shm);
158size_t cserve2_shm_map_size_get(const Shm_Handle *shm);
159size_t cserve2_shm_size_get(const Shm_Handle *shm);
160
161void cserve2_command_run(Client *client, Message_Type type);
162
163void cserve2_cache_init(void);
164void cserve2_cache_shutdown(void);
165void cserve2_cache_client_new(Client *client);
166void cserve2_cache_client_del(Client *client);
167int cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid);
168void cserve2_cache_file_close(Client *client, unsigned int client_file_id);
169int cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg);
170void cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid);
171void cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid);
172void cserve2_cache_image_unload(Client *client, unsigned int client_image_id);
173
174void cserve2_cache_requests_process(void);
175
176void cserve2_cache_request_opened(Slave_Msg_Image_Opened *resp, void *data);
177void cserve2_cache_request_loaded(Slave_Msg_Image_Loaded *resp, void *data);
178void cserve2_cache_request_failed(void *data, Error_Type error);
179
180#endif /* _EVAS_CSERVE2_H */
diff --git a/legacy/evas/src/bin/evas_cserve2_cache.c b/legacy/evas/src/bin/evas_cserve2_cache.c
new file mode 100644
index 0000000000..3cd45474ed
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2_cache.c
@@ -0,0 +1,1261 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include <string.h>
6
7#include "evas_cserve2.h"
8
9typedef struct _Request Request;
10typedef struct _Entry Entry;
11typedef struct _Reference Reference;
12typedef struct _Waiter Waiter;
13typedef struct _File_Data File_Data;
14typedef struct _Image_Data Image_Data;
15typedef struct _File_Watch File_Watch;
16
17typedef enum {
18 CSERVE2_IMAGE_FILE,
19 CSERVE2_IMAGE_DATA
20} Entry_Type;
21
22struct _Request {
23 Entry *entry;
24 Eina_List *waiters;
25 Eina_Bool processing;
26};
27
28struct _File_Data {
29 char *path;
30 char *key;
31 int w, h;
32 int frame_count;
33 int loop_count;
34 int loop_hint;
35 const char *loader_data;
36 File_Watch *watcher;
37 Eina_List *images;
38 Eina_Bool alpha : 1;
39 Eina_Bool invalid : 1;
40};
41
42// Default values for load options commented below
43struct _Image_Data {
44 unsigned int file_id;
45 Entry *file;
46 struct {
47 double dpi; // dpi < -1
48 int w, h; // w and h < -1
49 int scale_down; // scale_down < -1
50 int rx, ry, rw, rh; // rx, ry, rw, rh < -1
51 Eina_Bool orientation; // orientation == 0
52 } opts;
53 Shm_Handle *shm;
54 Eina_Bool alpha_sparse : 1;
55 Eina_Bool unused : 1;
56 Eina_Bool doload : 1;
57};
58
59struct _Entry {
60 unsigned int id;
61 Eina_List *references;
62 Request *request;
63 Entry_Type type;
64 union {
65 File_Data file;
66 Image_Data image;
67 };
68};
69
70struct _Reference {
71 Client *client;
72 Entry *entry;
73 unsigned int client_entry_id; // for reverse lookup
74 int count;
75};
76
77struct _Waiter {
78 Reference *ref;
79 unsigned int rid;
80 Message_Type type;
81};
82
83struct _File_Watch {
84 const char *path;
85 Eina_List *entries;
86};
87
88static unsigned int _file_id = 0; // id unique number
89static unsigned int _image_id = 0; // id unique number
90static Eina_Hash *file_ids = NULL; // maps path + key --> file_id
91static Eina_Hash *file_entries = NULL; // maps file_id --> entry
92static Eina_List *open_requests = NULL;
93
94static Eina_Hash *image_ids = NULL; // maps file id + load opts --> image id
95static Eina_Hash *image_entries = NULL; // maps image_id --> entry
96static Eina_List *load_requests = NULL;
97static Eina_List *spload_requests = NULL; // speculative preload requests
98
99static Eina_Hash *file_watch = NULL;
100
101static Eina_List *image_entries_lru = NULL;
102
103static int max_unused_mem_usage = 5 * 1024; /* in kbytes */
104static int unused_mem_usage = 0;
105
106static unsigned int
107_img_opts_id_get(Image_Data *im, char *buf, int size)
108{
109 uintptr_t image_id;
110
111 snprintf(buf, size, "%u:%0.3f:%dx%d:%d:%d,%d+%dx%d:%d",
112 im->file_id, im->opts.dpi, im->opts.w, im->opts.h,
113 im->opts.scale_down, im->opts.rx, im->opts.ry,
114 im->opts.rw, im->opts.rh, im->opts.orientation);
115
116 image_id = (uintptr_t)eina_hash_find(image_ids, buf);
117
118 return image_id;
119}
120
121static int
122_image_entry_size_get(Entry *e)
123{
124 int size = sizeof(Entry);
125 /* XXX: get the overhead of the shm handler too */
126 if (e->image.shm)
127 size += cserve2_shm_size_get(e->image.shm);
128 return size / 1024;
129}
130
131static void
132_file_id_free(Entry *entry)
133{
134 char buf[4096];
135
136 DBG("Removing entry file id: %d, file: \"%s:%s\"",
137 entry->id, entry->file.path, entry->file.key);
138 snprintf(buf, sizeof(buf), "%s:%s", entry->file.path, entry->file.key);
139 eina_hash_del_by_key(file_ids, buf);
140}
141
142static void
143_image_id_free(Entry *entry)
144{
145 char buf[4096];
146
147 DBG("Removing entry image id: %d", entry->id);
148
149 _img_opts_id_get(&entry->image, buf, sizeof(buf));
150 eina_hash_del_by_key(image_ids, buf);
151}
152
153static void
154_image_entry_free(Entry *entry)
155{
156 Entry *fentry = entry->image.file;
157
158 if (entry->request)
159 {
160 if (entry->request->processing)
161 entry->request->entry = NULL;
162 else if (!entry->request->waiters)
163 {
164 if (entry->image.doload)
165 load_requests = eina_list_remove(load_requests, entry->request);
166 else
167 spload_requests = eina_list_remove(spload_requests,
168 entry->request);
169 }
170 }
171
172 if (entry->image.unused)
173 {
174 image_entries_lru = eina_list_remove(image_entries_lru, entry);
175 unused_mem_usage -= _image_entry_size_get(entry);
176 }
177
178 if (fentry)
179 fentry->file.images = eina_list_remove(fentry->file.images, entry);
180 if (entry->image.shm)
181 cserve2_shm_unref(entry->image.shm);
182 free(entry);
183}
184
185static void
186_hash_image_entry_free(void *data)
187{
188 Entry *entry = data;
189
190 _image_id_free(entry);
191 _image_entry_free(entry);
192}
193
194static void
195_file_entry_free(Entry *entry)
196{
197 File_Watch *fw;
198
199 // Should we call free for each of the images too?
200 // If everything goes fine, it's not necessary.
201 if (entry->file.images)
202 {
203 ERR("Freeing file %d (\"%s:%s\") image data still referenced.",
204 entry->id, entry->file.path, entry->file.key);
205 eina_list_free(entry->file.images);
206 }
207
208 if (entry->request)
209 {
210 if (entry->request->processing)
211 entry->request->entry = NULL;
212 else if (!entry->request->waiters)
213 {
214 open_requests = eina_list_remove(open_requests, entry->request);
215 free(entry->request);
216 }
217 }
218
219 if ((fw = entry->file.watcher))
220 {
221 fw->entries = eina_list_remove(fw->entries, entry);
222 if (!fw->entries)
223 eina_hash_del_by_key(file_watch, fw->path);
224 }
225
226 free(entry->file.key);
227 free(entry->file.path);
228 eina_stringshare_del(entry->file.loader_data);
229 free(entry);
230}
231
232static void
233_hash_file_entry_free(void *data)
234{
235 Entry *entry = data;
236 // TODO: Add some checks to make sure that we are freeing an
237 // unused entry.
238
239 _file_id_free(entry);
240 _file_entry_free(entry);
241}
242
243static void
244_file_watch_free(void *data)
245{
246 File_Watch *fw = data;
247 cserve2_file_change_watch_del(fw->path);
248 eina_stringshare_del(fw->path);
249 eina_list_free(fw->entries);
250 free(fw);
251}
252
253void
254cserve2_cache_init(void)
255{
256 file_ids = eina_hash_string_superfast_new(NULL);
257 file_entries = eina_hash_int32_new(_hash_file_entry_free);
258 image_ids = eina_hash_string_superfast_new(NULL);
259 image_entries = eina_hash_string_superfast_new(_hash_image_entry_free);
260 file_watch = eina_hash_string_superfast_new(_file_watch_free);
261}
262
263void
264cserve2_cache_shutdown(void)
265{
266 eina_hash_free(image_entries);
267 eina_hash_free(image_ids);
268 eina_hash_free(file_entries);
269 eina_hash_free(file_ids);
270 eina_hash_free(file_watch);
271}
272
273static void
274_request_answer_del(Eina_List **requests, Request *req, Client *client, Error_Type err)
275{
276 Eina_List *l, *l_next;
277 Waiter *it;
278
279 DBG("Removing answer requests from entry: %d, client: %d",
280 req->entry->id, client->id);
281
282 EINA_LIST_FOREACH_SAFE(req->waiters, l, l_next, it)
283 {
284 if (it->ref->client->id == client->id)
285 {
286 cserve2_client_error_send(client, it->rid, err);
287 req->waiters = eina_list_remove_list(req->waiters, l);
288 free(it);
289 }
290 }
291
292 // FIXME: Should this be really here? I guess that it should be in the
293 // entry_free_cb function, or entry_reference_del, when there are no more
294 // references
295 if (!req->waiters && !req->entry)
296 {
297 *requests = eina_list_remove(*requests, req);
298 free(req);
299 }
300}
301
302static void
303_request_answer_all_del(Eina_List **requests, Request *req, Error_Type err)
304{
305 Waiter *it;
306
307 DBG("Removing all answer requests from entry: %d", req->entry->id);
308
309 EINA_LIST_FREE(req->waiters, it)
310 {
311 cserve2_client_error_send(it->ref->client, it->rid, err);
312 free(it);
313 }
314
315 *requests = eina_list_remove(*requests, req);
316 free(req);
317}
318
319/*
320static void
321_open_request_del(Request *req)
322{
323 Waiter *it;
324
325 EINA_LIST_FREE(req->waiters, it)
326 free(it);
327
328 req->entry->request = NULL;
329
330 open_requests = eina_list_remove(open_requests, req);
331 free(req);
332}
333*/
334
335static void
336_request_answer_add(Request *req, Reference *ref, unsigned int rid, Message_Type type)
337{
338 Waiter *w = malloc(sizeof(*w));
339
340 w->ref = ref;
341 w->rid = rid;
342 w->type = type;
343
344 DBG("Add answer request for entry id: %d, client: %d, rid: %d",
345 req->entry->id, ref->client->id, rid);
346 req->waiters = eina_list_append(req->waiters, w);
347}
348
349static void
350_request_add(Eina_List **requests, Entry *entry, Reference *ref, unsigned int rid, Message_Type type)
351{
352 Request *req;
353
354 // add the request if it doesn't exist yet
355 if (!entry->request)
356 {
357 req = malloc(sizeof(*req));
358 req->entry = entry;
359 req->waiters = NULL;
360 req->processing = EINA_FALSE;
361 entry->request = req;
362 *requests = eina_list_append(*requests, req);
363 DBG("Add request for entry id: %d, client: %d, rid: %d",
364 req->entry->id, ref->client->id, rid);
365 }
366 else
367 req = entry->request;
368
369 if (type != CSERVE2_SETOPTS)
370 _request_answer_add(req, ref, rid, type);
371 else
372 DBG("Adding entry for speculative preload: id=%d", req->entry->id);
373}
374
375static Reference *
376_entry_reference_add(Entry *entry, Client *client, unsigned int client_entry_id)
377{
378 Reference *ref;
379
380 // increase reference for this file
381 ref = malloc(sizeof(*ref));
382 ref->client = client;
383 ref->entry = entry;
384 ref->client_entry_id = client_entry_id;
385 ref->count = 1;
386 entry->references = eina_list_append(entry->references, ref);
387
388 return ref;
389}
390
391static int
392_cserve2_cache_open_requests_process(int nloaders)
393{
394 Request *req;
395 Slave_Msg_Image_Open msg;
396 Entry *entry;
397 char slave_cmd_data[4096];
398 int slave_cmd_size;
399 int path_len, key_len;
400
401 while ((nloaders > 0) && (open_requests))
402 {
403
404 // remove the first element from the list and process this element
405 req = eina_list_data_get(open_requests);
406 open_requests = eina_list_remove_list(open_requests, open_requests);
407
408 entry = req->entry;
409 DBG("Processing OPEN request for file entry: %d", entry->id);
410
411 memset(&msg, 0, sizeof(msg));
412 memcpy(slave_cmd_data, &msg, sizeof(msg));
413
414 path_len = strlen(entry->file.path) + 1;
415 key_len = strlen(entry->file.key) + 1;
416 slave_cmd_size = sizeof(msg) + path_len + key_len;
417 memcpy(slave_cmd_data + sizeof(msg), entry->file.path, path_len);
418 memcpy(slave_cmd_data + sizeof(msg) + path_len, entry->file.key,
419 key_len);
420 cserve2_slave_cmd_dispatch(req, IMAGE_OPEN, slave_cmd_data,
421 slave_cmd_size);
422
423 req->processing = EINA_TRUE;
424 nloaders--;
425 }
426
427 return nloaders;
428}
429
430static void
431_image_preloaded_send(Client *client, unsigned int rid)
432{
433 int size;
434 Msg_Preloaded msg;
435
436 DBG("Sending PRELOADED reply for RID: %d.", rid);
437 memset(&msg, 0, sizeof(msg));
438 msg.base.rid = rid;
439 msg.base.type = CSERVE2_PRELOADED;
440
441 size = sizeof(msg);
442 cserve2_client_send(client, &size, sizeof(size));
443 cserve2_client_send(client, &msg, size);
444}
445
446static void
447_image_loaded_send(Client *client, Entry *entry, unsigned int rid)
448{
449 int size;
450 const char *shmpath = cserve2_shm_name_get(entry->image.shm);
451 Msg_Loaded msg;
452 int path_len;
453 char *buf;
454
455 DBG("Sending LOADED reply for entry %d and RID: %d.", entry->id, rid);
456 path_len = strlen(shmpath) + 1;
457
458 memset(&msg, 0, sizeof(msg));
459 msg.base.rid = rid;
460 msg.base.type = CSERVE2_LOADED;
461
462 msg.shm.mmap_offset = cserve2_shm_map_offset_get(entry->image.shm);
463 msg.shm.use_offset = cserve2_shm_offset_get(entry->image.shm);
464 msg.shm.mmap_size = cserve2_shm_map_size_get(entry->image.shm);
465 msg.shm.image_size = cserve2_shm_size_get(entry->image.shm);
466 msg.alpha_sparse = entry->image.alpha_sparse;
467
468 buf = malloc(sizeof(msg) + path_len);
469
470 memcpy(buf, &msg, sizeof(msg));
471 memcpy(buf + sizeof(msg), shmpath, path_len);
472
473 size = sizeof(msg) + path_len;
474
475 cserve2_client_send(client, &size, sizeof(size));
476 cserve2_client_send(client, buf, size);
477
478 free(buf);
479}
480
481static void
482_cserve2_cache_load_request_run(Request *req)
483{
484 Entry *fentry;
485 Shm_Handle *shm;
486 const char *shmpath, *file, *key, *loader;
487 int shmlen, filelen, keylen, loaderlen;
488 Slave_Msg_Image_Load msg;
489 char *buf, *cur;
490 Entry *ientry;
491 size_t size;
492
493 ientry = req->entry;
494
495 fentry = ientry->image.file;
496 // opening shm for this file
497 shm = cserve2_shm_request(fentry->file.w * fentry->file.h * 4);
498 shmpath = cserve2_shm_name_get(shm);
499 shmlen = strlen(shmpath) + 1;
500
501 file = fentry->file.path;
502 filelen = strlen(file) + 1;
503
504 key = fentry->file.key;
505 keylen = strlen(key) + 1;
506
507 loader = fentry->file.loader_data;
508 if (!loader)
509 loaderlen = 0;
510 else
511 loaderlen = strlen(loader) + 1;
512
513 memset(&msg, 0, sizeof(msg));
514 msg.w = ientry->image.file->file.w;
515 msg.h = ientry->image.file->file.h;
516 msg.alpha = ientry->image.file->file.alpha;
517 msg.opts.w = ientry->image.opts.w;
518 msg.opts.h = ientry->image.opts.h;
519 msg.opts.rx = ientry->image.opts.rx;
520 msg.opts.ry = ientry->image.opts.ry;
521 msg.opts.rw = ientry->image.opts.rw;
522 msg.opts.rh = ientry->image.opts.rh;
523 msg.opts.scale_down_by = ientry->image.opts.scale_down;
524 msg.opts.dpi = ientry->image.opts.dpi;
525 msg.opts.orientation = ientry->image.opts.orientation;
526
527 msg.shm.mmap_offset = cserve2_shm_map_offset_get(shm);
528 msg.shm.image_offset = cserve2_shm_offset_get(shm);
529 msg.shm.mmap_size = cserve2_shm_map_size_get(shm);
530 msg.shm.image_size = cserve2_shm_size_get(shm);
531
532 msg.has_loader_data = !!loaderlen;
533
534 size = sizeof(msg) + shmlen + filelen + keylen + loaderlen;
535
536 buf = calloc(1, size);
537
538 memcpy(buf, &msg, sizeof(msg));
539 cur = buf + sizeof(msg);
540 memcpy(cur, shmpath, shmlen);
541
542 cur += shmlen;
543 memcpy(cur, file, filelen);
544
545 cur += filelen;
546 memcpy(cur, key, keylen);
547
548 cur += keylen;
549 memcpy(cur, loader, loaderlen);
550
551 ientry->image.shm = shm;
552
553 cserve2_slave_cmd_dispatch(req, IMAGE_LOAD, buf, size);
554
555 free(buf);
556}
557
558static int
559_cserve2_cache_load_requests_list_process(Eina_List **queue, int nloaders)
560{
561 Eina_List *skipped = NULL;
562 Request *req;
563
564 while ((nloaders > 0) && (*queue))
565 {
566 // remove the first element from the list and process this element
567 req = eina_list_data_get(*queue);
568 *queue = eina_list_remove_list(*queue, *queue);
569
570 if (!req->entry->image.file)
571 {
572 ERR("File entry doesn't exist for entry id %d", req->entry->id);
573 cserve2_cache_request_failed(req, CSERVE2_INVALID_CACHE);
574 continue;
575 }
576
577 if (req->entry->image.file->request)
578 {
579 /* OPEN still pending, skip this request */
580 skipped = eina_list_append(skipped, req);
581 continue;
582 }
583
584 DBG("Processing LOAD request for image entry: %d", req->entry->id);
585
586 _cserve2_cache_load_request_run(req);
587
588 req->processing = EINA_TRUE;
589
590 nloaders--;
591 }
592
593 EINA_LIST_FREE(skipped, req)
594 *queue = eina_list_append(*queue, req);
595
596 return nloaders;
597}
598
599static void
600_cserve2_cache_load_requests_process(int nloaders)
601{
602 nloaders = _cserve2_cache_load_requests_list_process(&load_requests,
603 nloaders);
604 _cserve2_cache_load_requests_list_process(&spload_requests, nloaders - 1);
605}
606
607
608void
609cserve2_cache_requests_process(void)
610{
611 int avail_loaders;
612
613 avail_loaders = cserve2_slave_available_get();
614 avail_loaders = _cserve2_cache_open_requests_process(avail_loaders);
615 _cserve2_cache_load_requests_process(avail_loaders);
616}
617
618static void
619_entry_unused_push(Entry *e)
620{
621 int size = _image_entry_size_get(e);
622
623 if ((size > max_unused_mem_usage) || !(e->image.doload))
624 {
625 eina_hash_del_by_key(image_entries, &e->id);
626 return;
627 }
628 while (size > (max_unused_mem_usage - unused_mem_usage))
629 {
630 Entry *ie = eina_list_data_get(eina_list_last(image_entries_lru));
631 eina_hash_del_by_key(image_entries, &ie->id);
632 }
633 image_entries_lru = eina_list_append(image_entries_lru, e);
634 e->image.unused = EINA_TRUE;
635 unused_mem_usage += size;
636}
637
638static void
639_entry_reference_del(Entry *entry, Reference *ref)
640{
641 entry->references = eina_list_remove(entry->references, ref);
642
643 if (entry->references)
644 goto free_ref;
645
646 if (entry->type == CSERVE2_IMAGE_FILE)
647 {
648 if (entry->file.invalid)
649 _file_entry_free(entry);
650 else
651 {
652 if (entry->file.images)
653 {
654 Entry *ie;
655 EINA_LIST_FREE(entry->file.images, ie)
656 ie->image.file = NULL;
657 entry->file.images = NULL;
658 }
659 eina_hash_del_by_key(file_entries, &entry->id);
660 }
661 }
662 else if (entry->type == CSERVE2_IMAGE_DATA)
663 {
664 if (!entry->image.file)
665 eina_hash_del_by_key(image_entries, &entry->id);
666 else if (entry->image.file->file.invalid)
667 _image_entry_free(entry);
668 else
669 _entry_unused_push(entry);
670 }
671 else
672 ERR("Wrong type of entry.");
673
674free_ref:
675 free(ref);
676}
677
678static void
679_entry_free_cb(void *data)
680{
681 Reference *ref = data;
682 Entry *entry;
683
684 DBG("Removing client reference for entry id: %d, client: %d",
685 ref->entry->id, ref->client->id);
686
687 entry = ref->entry;
688
689 if (entry->request && !entry->request->processing)
690 {
691 if (entry->type == CSERVE2_IMAGE_FILE)
692 _request_answer_del(&open_requests, entry->request, ref->client,
693 CSERVE2_REQUEST_CANCEL);
694 else
695 {
696 if (entry->image.doload)
697 _request_answer_del(&load_requests, entry->request,
698 ref->client, CSERVE2_REQUEST_CANCEL);
699 else
700 _request_answer_del(&spload_requests, entry->request,
701 ref->client, CSERVE2_REQUEST_CANCEL);
702 }
703 }
704
705 _entry_reference_del(entry, ref);
706}
707
708void
709cserve2_cache_client_new(Client *client)
710{
711 client->files.referencing = eina_hash_int32_new(_entry_free_cb);
712 client->images.referencing = eina_hash_int32_new(_entry_free_cb);
713}
714
715void
716cserve2_cache_client_del(Client *client)
717{
718 // will call _entry_free_cb() for every entry
719 eina_hash_free(client->images.referencing);
720 // will call _entry_free_cb() for every entry
721 eina_hash_free(client->files.referencing);
722}
723
724void
725_image_opened_send(Client *client, Entry *entry, unsigned int rid)
726{
727 int size;
728 Msg_Opened msg;
729
730 DBG("Sending OPENED reply for entry: %d and RID: %d.", entry->id, rid);
731 // clear the struct with possible paddings, since it is not aligned.
732 memset(&msg, 0, sizeof(msg));
733 msg.base.rid = rid;
734 msg.base.type = CSERVE2_OPENED;
735 msg.image.w = entry->file.w;
736 msg.image.h = entry->file.h;
737 msg.image.frame_count = entry->file.frame_count;
738 msg.image.loop_count = entry->file.loop_count;
739 msg.image.loop_hint = entry->file.loop_hint;
740 msg.image.alpha = entry->file.alpha;
741
742 size = sizeof(msg);
743 cserve2_client_send(client, &size, sizeof(size));
744 cserve2_client_send(client, &msg, sizeof(msg));
745 // _cserve2_cache_load_requests_process();
746}
747
748static Entry *
749_image_msg_new(Client *client, Msg_Setopts *msg)
750{
751 Reference *ref;
752 Entry *im_entry;
753
754 ref = eina_hash_find(client->files.referencing, &msg->file_id);
755 if (!ref)
756 {
757 ERR("Couldn't find file id: %d, for image id: %d",
758 msg->file_id, msg->image_id);
759 cserve2_client_error_send(client, msg->base.rid,
760 CSERVE2_INVALID_CACHE);
761 return NULL;
762 }
763 if (ref->entry->file.invalid)
764 {
765 cserve2_client_error_send(client, msg->base.rid,
766 CSERVE2_FILE_CHANGED);
767 return NULL;
768 }
769
770 im_entry = calloc(1, sizeof(*im_entry));
771 im_entry->type = CSERVE2_IMAGE_DATA;
772 im_entry->image.file_id = ref->entry->id;
773 im_entry->image.file = ref->entry;
774 im_entry->image.opts.dpi = msg->opts.dpi;
775 im_entry->image.opts.w = msg->opts.w;
776 im_entry->image.opts.h = msg->opts.h;
777 im_entry->image.opts.scale_down = msg->opts.scale_down;
778 im_entry->image.opts.rx = msg->opts.rx;
779 im_entry->image.opts.ry = msg->opts.ry;
780 im_entry->image.opts.rw = msg->opts.rw;
781 im_entry->image.opts.rh = msg->opts.rh;
782 im_entry->image.opts.orientation = msg->opts.orientation;
783
784 return im_entry;
785}
786
787/*
788static Entry *
789_image_default_new(Entry *file_entry)
790{
791 Entry *im_entry = calloc(1, sizeof(*im_entry));
792 im_entry->image.file_id = file_entry->id;
793 im_entry->image.opts.dpi = -1;
794 im_entry->image.opts.w = -1;
795 im_entry->image.opts.h = -1;
796 im_entry->image.opts.scale_down = -1;
797 im_entry->image.opts.rx = -1;
798 im_entry->image.opts.ry = -1;
799 im_entry->image.opts.rw = -1;
800 im_entry->image.opts.rh = -1;
801 im_entry->image.opts.orientation = 0;
802
803 return im_entry;
804}
805*/
806
807static void
808_file_changed_cb(const char *path __UNUSED__, Eina_Bool deleted __UNUSED__, void *data)
809{
810 File_Watch *fw = data;
811 Entry *e;
812 Eina_List *l;
813
814 EINA_LIST_FOREACH(fw->entries, l, e)
815 {
816 Eina_List *ll;
817 Entry *ie;
818
819 e->file.invalid = EINA_TRUE;
820 e->file.watcher = NULL;
821
822 EINA_LIST_FOREACH(e->file.images, ll, ie)
823 {
824 _image_id_free(ie);
825 eina_hash_set(image_entries, &ie->id, NULL);
826 if (ie->request && !ie->request->processing)
827 {
828 if (ie->image.doload)
829 _request_answer_all_del(&load_requests, ie->request,
830 CSERVE2_FILE_CHANGED);
831 else
832 _request_answer_all_del(&spload_requests, ie->request,
833 CSERVE2_FILE_CHANGED);
834 }
835 ie->request = NULL;
836 if (ie->image.unused)
837 _image_entry_free(ie);
838 }
839
840 _file_id_free(e);
841 eina_hash_set(file_entries, &e->id, NULL);
842 if (e->request && !e->request->processing)
843 _request_answer_all_del(&open_requests, ie->request,
844 CSERVE2_FILE_CHANGED);
845 e->request = NULL;
846 if (!e->file.images && !e->references)
847 _file_entry_free(e);
848 }
849
850 eina_hash_del_by_key(file_watch, fw->path);
851}
852
853int
854cserve2_cache_file_open(Client *client, unsigned int client_file_id, const char *path, const char *key, unsigned int rid)
855{
856 uintptr_t file_id;
857 Entry *entry;
858 Reference *ref;
859 File_Watch *fw;
860 char buf[4906];
861
862 // look for this file on client references
863 ref = eina_hash_find(client->files.referencing, &client_file_id);
864 if (ref)
865 {
866 entry = ref->entry;
867
868 if (entry->file.invalid)
869 {
870 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
871 return -1;
872 }
873
874 DBG("found client file id: %d", client_file_id);
875 ref->count++;
876
877 // File already being loaded, just add the request to be replied
878 if (entry->request)
879 _request_answer_add(entry->request, ref, rid, CSERVE2_OPEN);
880 else
881 _image_opened_send(client, entry, rid);
882 return 0;
883 }
884
885 // search whether the file is already opened by another client
886 snprintf(buf, sizeof(buf), "%s:%s", path, key);
887 file_id = (uintptr_t)eina_hash_find(file_ids, buf);
888 if (file_id)
889 {
890 DBG("found file_id %d for client file id %d",
891 file_id, client_file_id);
892 entry = eina_hash_find(file_entries, &file_id);
893 if (!entry)
894 {
895 ERR("file \"%s\" is in file_ids hash but not in entries hash.",
896 buf);
897 cserve2_client_error_send(client, rid, CSERVE2_INVALID_CACHE);
898 return -1;
899 }
900 ref = _entry_reference_add(entry, client, client_file_id);
901 eina_hash_add(client->files.referencing, &client_file_id, ref);
902 if (entry->request)
903 _request_answer_add(entry->request, ref, rid, CSERVE2_OPEN);
904 else // File already loaded, otherwise there would be a request
905 _image_opened_send(client, entry, rid);
906 return 0;
907 }
908
909 file_id = _file_id++;
910 while ((file_id == 0) || (eina_hash_find(file_entries, &file_id)))
911 file_id = _file_id++;
912
913 DBG("Creating new entry with file_id: %d for file \"%s:%s\"",
914 file_id, path, key);
915 entry = calloc(1, sizeof(*entry));
916 entry->type = CSERVE2_IMAGE_FILE;
917 entry->file.path = strdup(path);
918 entry->file.key = strdup(key);
919 entry->id = file_id;
920 eina_hash_add(file_entries, &file_id, entry);
921 eina_hash_add(file_ids, buf, (void *)file_id);
922 ref = _entry_reference_add(entry, client, client_file_id);
923 eina_hash_add(client->files.referencing, &client_file_id, ref);
924
925 fw = eina_hash_find(file_watch, entry->file.path);
926 if (!fw)
927 {
928 fw = calloc(1, sizeof(File_Watch));
929 fw->path = eina_stringshare_add(entry->file.path);
930 cserve2_file_change_watch_add(fw->path, _file_changed_cb, fw);
931 eina_hash_direct_add(file_watch, fw->path, fw);
932 }
933 fw->entries = eina_list_append(fw->entries, entry);
934 entry->file.watcher = fw;
935
936 _request_add(&open_requests, entry, ref, rid, CSERVE2_OPEN);
937
938 // _open_image_default_set(entry);
939
940 return 0;
941}
942
943void
944cserve2_cache_file_close(Client *client, unsigned int client_file_id)
945{
946 Reference *ref = eina_hash_find(client->files.referencing,
947 &client_file_id);
948 if (!ref)
949 {
950 ERR("Couldn't find file %d in client hash.", client_file_id);
951 return;
952 }
953
954 ref->count--;
955 if (ref->count <= 0)
956 // will call _entry_free_cb() for this entry
957 eina_hash_del_by_key(client->files.referencing, &client_file_id);
958}
959
960int
961cserve2_cache_image_opts_set(Client *client, Msg_Setopts *msg)
962{
963 Entry *entry;
964 Entry *fentry = NULL;
965 Reference *ref, *oldref;
966 unsigned int image_id;
967 char buf[4096];
968
969 oldref = eina_hash_find(client->images.referencing, &msg->image_id);
970
971 // search whether the image is already loaded by another client
972 entry = _image_msg_new(client, msg);
973 if (!entry)
974 return -1;
975 image_id = _img_opts_id_get(&entry->image, buf, sizeof(buf));
976 if (image_id)
977 { // if so, just update the references
978 free(entry);
979 DBG("found image_id %d for client image id %d",
980 image_id, msg->image_id);
981 entry = eina_hash_find(image_entries, &image_id);
982 if (!entry)
983 {
984 ERR("image id %d is in file_ids hash, but not in entries hash"
985 "with entry id %d.", msg->image_id, image_id);
986 cserve2_client_error_send(client, msg->base.rid,
987 CSERVE2_INVALID_CACHE);
988 return -1;
989 }
990
991 if (entry->image.unused)
992 {
993 DBG("Re-using old image entry (id: %d) from the LRU list.",
994 entry->id);
995 entry->image.unused = EINA_FALSE;
996 image_entries_lru = eina_list_remove(image_entries_lru, entry);
997 unused_mem_usage -= _image_entry_size_get(entry);
998 }
999
1000 if (oldref && (oldref->entry->id == image_id))
1001 return 0;
1002
1003 ref = _entry_reference_add(entry, client, msg->image_id);
1004
1005 if (oldref)
1006 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
1007
1008 eina_hash_add(client->images.referencing, &msg->image_id, ref);
1009
1010 return 0;
1011 }
1012
1013 image_id = _image_id++;
1014 while ((image_id == 0) || (eina_hash_find(image_entries, &image_id)))
1015 image_id = _image_id++;
1016
1017 entry->id = image_id;
1018 eina_hash_add(image_entries, &image_id, entry);
1019 eina_hash_add(image_ids, buf, (void *)image_id);
1020 ref = _entry_reference_add(entry, client, msg->image_id);
1021
1022 if (oldref)
1023 eina_hash_del_by_key(client->images.referencing, &msg->image_id);
1024 eina_hash_add(client->images.referencing, &msg->image_id, ref);
1025
1026 fentry = entry->image.file;
1027 fentry->file.images = eina_list_append(fentry->file.images, entry);
1028
1029 _request_add(&spload_requests, entry, ref, msg->base.rid, CSERVE2_SETOPTS);
1030 return 0;
1031}
1032
1033void
1034cserve2_cache_image_load(Client *client, unsigned int client_image_id, unsigned int rid)
1035{
1036 Entry *entry;
1037 Reference *ref;
1038
1039 ref = eina_hash_find(client->images.referencing, &client_image_id);
1040 if (!ref)
1041 {
1042 ERR("Can't load: client %d has no image id %d",
1043 client->id, client_image_id);
1044 return;
1045 }
1046 if (ref->entry->image.file->file.invalid)
1047 {
1048 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
1049 return;
1050 }
1051
1052 DBG("Loading image id: %d", ref->entry->id);
1053
1054 entry = ref->entry;
1055
1056 // File already being loaded, just add the request to be replied
1057 if (entry->request)
1058 {
1059 _request_answer_add(entry->request, ref, rid, CSERVE2_LOAD);
1060 if ((!entry->request->processing) && (!entry->image.doload))
1061 {
1062 DBG("Removing entry %d from speculative preload and adding "
1063 "to normal load queue.", entry->id);
1064 spload_requests = eina_list_remove(spload_requests,
1065 entry->request);
1066 load_requests = eina_list_append(load_requests, entry->request);
1067 }
1068 }
1069 else if (entry->image.shm)
1070 _image_loaded_send(client, entry, rid);
1071 else
1072 _request_add(&load_requests, entry, ref, rid, CSERVE2_LOAD);
1073
1074 entry->image.doload = EINA_TRUE;
1075}
1076
1077void
1078cserve2_cache_image_preload(Client *client, unsigned int client_image_id, unsigned int rid)
1079{
1080 Entry *entry;
1081 Reference *ref;
1082
1083 ref = eina_hash_find(client->images.referencing, &client_image_id);
1084 if (!ref)
1085 {
1086 ERR("Can't load: client %d has no image id %d",
1087 client->id, client_image_id);
1088 return;
1089 }
1090 if (ref->entry->image.file->file.invalid)
1091 {
1092 cserve2_client_error_send(client, rid, CSERVE2_FILE_CHANGED);
1093 return;
1094 }
1095
1096 DBG("Loading image id: %d", ref->entry->id);
1097
1098 entry = ref->entry;
1099
1100 // File already being loaded, just add the request to be replied
1101 if (entry->request)
1102 {
1103 _request_answer_add(entry->request, ref, rid, CSERVE2_PRELOAD);
1104 if ((!entry->request->processing) && (!entry->image.doload))
1105 {
1106 DBG("Removing entry %d from speculative preload and adding "
1107 "to normal (pre)load queue.", entry->id);
1108 spload_requests = eina_list_remove(spload_requests,
1109 entry->request);
1110 load_requests = eina_list_append(load_requests, entry->request);
1111 }
1112 }
1113 else if (entry->image.shm)
1114 _image_preloaded_send(client, rid);
1115 else
1116 _request_add(&load_requests, entry, ref, rid, CSERVE2_PRELOAD);
1117
1118 entry->image.doload = EINA_TRUE;
1119}
1120
1121void
1122cserve2_cache_image_unload(Client *client, unsigned int client_image_id)
1123{
1124 Reference *ref = eina_hash_find(client->images.referencing,
1125 &client_image_id);
1126 if (!ref)
1127 {
1128 ERR("Couldn't find file %d in client hash.", client_image_id);
1129 return;
1130 }
1131
1132 ref->count--;
1133 if (ref->count <= 0)
1134 // will call _entry_free_cb() for this entry
1135 eina_hash_del_by_key(client->images.referencing, &client_image_id);
1136}
1137
1138void
1139cserve2_cache_request_opened(Slave_Msg_Image_Opened *resp, void *data)
1140{
1141 Waiter *w;
1142 Request *req = data;
1143 Entry *entry;
1144
1145 entry = req->entry;
1146 if (!entry)
1147 {
1148 Error_Type err = entry->file.invalid
1149 ? CSERVE2_FILE_CHANGED
1150 : CSERVE2_REQUEST_CANCEL;
1151 DBG("File entry for request doesn't exist anymore.");
1152 cserve2_cache_request_failed(req, err);
1153 entry->request = NULL;
1154 return;
1155 }
1156
1157 entry->request = NULL;
1158
1159 entry->file.w = resp->w;
1160 entry->file.h = resp->h;
1161 entry->file.frame_count = resp->frame_count;
1162 entry->file.loop_count = resp->loop_count;
1163 entry->file.loop_hint = resp->loop_hint;
1164 entry->file.alpha = resp->alpha;
1165 if (resp->has_loader_data)
1166 {
1167 const char *ldata = (const char *)resp +
1168 sizeof(Slave_Msg_Image_Opened);
1169 entry->file.loader_data = eina_stringshare_add(ldata);
1170 }
1171
1172 DBG("Finished opening file %d. Notifying %d waiters.", entry->id,
1173 req->waiters ? eina_list_count(req->waiters) : 0);
1174 EINA_LIST_FREE(req->waiters, w)
1175 {
1176 _image_opened_send(w->ref->client, entry, w->rid);
1177 free(w);
1178 }
1179
1180 free(req);
1181}
1182
1183void
1184cserve2_cache_request_loaded(Slave_Msg_Image_Loaded *resp, void *data)
1185{
1186 Waiter *w;
1187 Request *req = data;
1188 Entry *entry;
1189
1190 entry = req->entry;
1191 if (!entry)
1192 {
1193 // FIXME: Wouldn't we keep the entry alive when the file changed
1194 // until we send the errors needed and just then delete it? Right now
1195 // the check below just makes no sense.
1196 // Error_Type err = entry->image.file->file.invalid
1197 // ? CSERVE2_FILE_CHANGED
1198 // : CSERVE2_REQUEST_CANCEL;
1199 DBG("Image entry for request doesn't exist anymore.");
1200 cserve2_cache_request_failed(req, CSERVE2_REQUEST_CANCEL);
1201 entry->request = NULL;
1202 return;
1203 }
1204
1205 entry->request = NULL;
1206 entry->image.alpha_sparse = resp->alpha_sparse;
1207 if (!entry->image.doload)
1208 DBG("Entry %d loaded by speculative preload.", entry->id);
1209
1210 DBG("Finished loading image %d. Notifying %d waiters.", entry->id,
1211 req->waiters ? eina_list_count(req->waiters) : 0);
1212 EINA_LIST_FREE(req->waiters, w)
1213 {
1214 if (w->type == CSERVE2_LOAD)
1215 _image_loaded_send(w->ref->client, entry, w->rid);
1216 else if (w->type == CSERVE2_PRELOAD)
1217 _image_preloaded_send(w->ref->client, w->rid);
1218 // else w->type == CSERVE2_SETOPTS --> do nothing
1219
1220 free(w);
1221 }
1222
1223 free(req);
1224}
1225
1226void
1227cserve2_cache_request_failed(void *data, Error_Type type)
1228{
1229 Waiter *w;
1230 Request *req = data;
1231 Entry *entry;
1232 Eina_List *l;
1233 Reference *ref;
1234
1235 DBG("Request for entry %p failed with error %d", req->entry, type);
1236 EINA_LIST_FREE(req->waiters, w)
1237 {
1238 cserve2_client_error_send(w->ref->client, w->rid, type);
1239
1240 w->ref->count--;
1241 free(w);
1242 }
1243
1244 entry = req->entry;
1245 if (!entry)
1246 goto free_req;
1247
1248 EINA_LIST_FOREACH(entry->references, l, ref)
1249 {
1250 Eina_Hash *hash;
1251 if (entry->type == CSERVE2_IMAGE_FILE)
1252 hash = ref->client->files.referencing;
1253 else if (entry->type == CSERVE2_IMAGE_DATA)
1254 hash = ref->client->images.referencing;
1255
1256 eina_hash_del_by_key(hash, &(ref->client_entry_id));
1257 }
1258
1259free_req:
1260 free(req);
1261}
diff --git a/legacy/evas/src/bin/evas_cserve2_client.c b/legacy/evas/src/bin/evas_cserve2_client.c
new file mode 100644
index 0000000000..9863dc8f5a
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2_client.c
@@ -0,0 +1,429 @@
1#include "config.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <errno.h>
6#include <string.h>
7#include <sys/types.h>
8#include <sys/socket.h>
9#include <sys/un.h>
10#include <unistd.h>
11
12#include "evas_cserve2.h"
13
14static const char *SOCK_PATH = "/tmp/cserve2.socket";
15static unsigned int _rid_count = 0;
16
17static void
18debug_msg(const void *buf, int size)
19{
20 const char *str = buf;
21 int i;
22
23 printf("message: ");
24 for (i = 0; i < size; i++)
25 printf("%x ", str[i]);
26
27 printf("\n");
28}
29
30static int
31_read_line(char *buf, int size)
32{
33 int len;
34 char *c;
35
36 fgets(buf, size, stdin);
37 c = strchr(buf, '#');
38 if (c)
39 *c = '\0';
40 else
41 {
42 c = strchr(buf, '\n');
43 if (c)
44 *c = '\0';
45 }
46 len = strlen(buf);
47
48 return len + 1;
49}
50
51static void *
52parse_input_open(int *size)
53{
54 char path[4096];
55 char key[4096];
56 char line[4096];
57 int path_len, key_len;
58 Msg_Open msg;
59 char *buf;
60 int file_id;
61
62 _read_line(line, sizeof(line));
63 path_len = _read_line(path, sizeof(path));
64 key_len = _read_line(key, sizeof(key));
65
66 sscanf(line, "%d", &file_id);
67
68 buf = malloc(sizeof(msg) + path_len + key_len);
69
70 msg.base.rid = _rid_count++;
71 msg.base.type = CSERVE2_OPEN;
72 msg.file_id = file_id;
73 msg.path_offset = 0;
74 msg.key_offset = path_len;
75
76 memcpy(buf, &msg, sizeof(msg));
77 memcpy(buf + sizeof(msg), path, path_len);
78 memcpy(buf + sizeof(msg) + path_len, key, key_len);
79
80 *size = sizeof(msg) + path_len + key_len;
81
82 return buf;
83}
84
85static void *
86parse_input_setopts(int *size)
87{
88 Msg_Setopts *msg;
89 char line[4096];
90 int file_id, image_id;
91 double dpi;
92 int w, h;
93 int scale;
94 int rx, ry, rw, rh;
95 int orientation;
96
97 // reading file_id, image_id
98 _read_line(line, sizeof(line));
99 sscanf(line, "%d %d", &file_id, &image_id);
100
101 // reading load dpi
102 _read_line(line, sizeof(line));
103 dpi = atof(line);
104
105 // reading load size
106 _read_line(line, sizeof(line));
107 sscanf(line, "%d %d", &w, &h);
108
109 // reading load scale down
110 _read_line(line, sizeof(line));
111 sscanf(line, "%d", &scale);
112
113 // reading load region
114 _read_line(line, sizeof(line));
115 sscanf(line, "%d %d %d %d", &rx, &ry, &rw, &rh);
116
117 // reading orientation
118 _read_line(line, sizeof(line));
119 sscanf(line, "%d", &orientation);
120
121
122 msg = calloc(1, sizeof(*msg));
123
124 msg->base.rid = _rid_count++;
125 msg->base.type = CSERVE2_SETOPTS;
126 msg->file_id = file_id;
127 msg->image_id = image_id;
128 msg->opts.dpi = dpi;
129 msg->opts.w = w;
130 msg->opts.h = h;
131 msg->opts.scale_down = scale;
132 msg->opts.rx = rx;
133 msg->opts.ry = ry;
134 msg->opts.rw = rw;
135 msg->opts.rh = rh;
136 msg->opts.orientation = !!orientation;
137
138 *size = sizeof(*msg);
139
140 return msg;
141}
142
143static void *
144parse_input_load(int *size)
145{
146 Msg_Load *msg;
147 char line[4096];
148 int image_id;
149
150 // read image_id
151 _read_line(line, sizeof(line));
152 sscanf(line, "%d", &image_id);
153
154 msg = calloc(1, sizeof(*msg));
155
156 msg->base.rid = _rid_count++;
157 msg->base.type = CSERVE2_LOAD;
158 msg->image_id = image_id;
159
160 *size = sizeof(*msg);
161
162 return msg;
163}
164
165static void *
166parse_input_preload(int *size)
167{
168 Msg_Preload *msg;
169 char line[4096];
170 int image_id;
171
172 // read image_id
173 _read_line(line, sizeof(line));
174 sscanf(line, "%d", &image_id);
175
176 msg = calloc(1, sizeof(*msg));
177
178 msg->base.rid = _rid_count++;
179 msg->base.type = CSERVE2_PRELOAD;
180 msg->image_id = image_id;
181
182 *size = sizeof(*msg);
183
184 return msg;
185}
186
187static void *
188parse_input_unload(int *size)
189{
190 Msg_Unload *msg;
191 char line[4096];
192 int image_id;
193
194 // read image_id
195 _read_line(line, sizeof(line));
196 sscanf(line, "%d", &image_id);
197
198 msg = calloc(1, sizeof(*msg));
199
200 msg->base.rid = _rid_count++;
201 msg->base.type = CSERVE2_UNLOAD;
202 msg->image_id = image_id;
203
204 *size = sizeof(*msg);
205
206 return msg;
207}
208
209static void *
210parse_input_close(int *size)
211{
212 Msg_Close *msg;
213 char line[4096];
214 int file_id;
215
216 // read file_id
217 _read_line(line, sizeof(line));
218 sscanf(line, "%d", &file_id);
219
220 msg = calloc(1, sizeof(*msg));
221
222 msg->base.rid = _rid_count++;
223 msg->base.type = CSERVE2_CLOSE;
224 msg->file_id = file_id;
225
226 *size = sizeof(*msg);
227
228 return msg;
229}
230
231static void
232parse_answer_opened(const void *buf)
233{
234 const Msg_Opened *msg = buf;
235 printf("OPENED rid = %d\n", msg->base.rid);
236 printf("size: %dx%d, alpha: %d\n\n",
237 msg->image.w, msg->image.h, msg->image.alpha);
238}
239
240static void
241parse_answer_setoptsed(const void *buf)
242{
243 const Msg_Setoptsed *msg = buf;
244 printf("SETOPTSED rid = %d\n", msg->base.rid);
245}
246
247static void
248parse_answer_loaded(const void *buf)
249{
250 const Msg_Loaded *msg = buf;
251 const char *path;
252
253 path = ((const char *)msg) + sizeof(*msg);
254
255 printf("LOADED rid = %d\n", msg->base.rid);
256 printf("shm mmap_offset = 0x%x, use_offset = 0x%x, mmap size = %d bytes\n",
257 msg->shm.mmap_offset, msg->shm.use_offset, msg->shm.mmap_size);
258 printf("shm path: \"%s\"\n\n", path);
259}
260
261static void
262parse_answer_preloaded(const void *buf)
263{
264 const Msg_Preloaded *msg = buf;
265
266 printf("PRELOADED rid = %d\n", msg->base.rid);
267}
268
269static void
270parse_answer_error(const void *buf)
271{
272 const Msg_Error *msg = buf;
273
274 printf("ERROR rid = %d, error = %d\n", msg->base.rid, msg->error);
275}
276
277static void
278parse_answer(const void *buf)
279{
280 const Msg_Base *msg = buf;
281
282 switch (msg->type)
283 {
284 case CSERVE2_OPENED:
285 parse_answer_opened(buf);
286 break;
287 case CSERVE2_SETOPTSED:
288 parse_answer_setoptsed(buf);
289 break;
290 case CSERVE2_LOADED:
291 parse_answer_loaded(buf);
292 break;
293 case CSERVE2_PRELOADED:
294 parse_answer_preloaded(buf);
295 break;
296 case CSERVE2_ERROR:
297 parse_answer_error(buf);
298 break;
299 default:
300 printf("unhandled answer: %d\n", msg->type);
301 }
302}
303
304static struct {
305 const char *name;
306 Message_Type type;
307 void *(*parse_func)(int *size);
308} _msg_types[] = {
309 { "OPEN", CSERVE2_OPEN, parse_input_open },
310 { "OPENED", CSERVE2_OPENED, NULL },
311 { "SETOPTS", CSERVE2_SETOPTS, parse_input_setopts },
312 { "SETOPTSED", CSERVE2_SETOPTSED, NULL },
313 { "LOAD", CSERVE2_LOAD, parse_input_load },
314 { "LOADED", CSERVE2_LOADED, NULL },
315 { "PRELOAD", CSERVE2_PRELOAD, parse_input_preload },
316 { "PRELOADED", CSERVE2_PRELOADED, NULL },
317 { "UNLOAD", CSERVE2_UNLOAD, parse_input_unload },
318 { "CLOSE", CSERVE2_CLOSE, parse_input_close },
319 { NULL, 0, NULL }
320};
321
322int main(void)
323{
324 int s, t, len, skip_cmd = 0;
325 struct sockaddr_un remote;
326 char msgbuf[4096];
327
328 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
329 {
330 perror("socket");
331 exit(1);
332 }
333
334 printf("Trying to connect...\n");
335
336 remote.sun_family = AF_UNIX;
337 strcpy(remote.sun_path, SOCK_PATH);
338 len = strlen(remote.sun_path) + sizeof(remote.sun_family);
339 if (connect(s, (struct sockaddr *)&remote, len) == -1)
340 {
341 perror("connect");
342 exit(1);
343 }
344
345 printf("Connected.\n");
346
347 while(!feof(stdin))
348 {
349 char cmd[1024];
350 int i;
351 int size;
352 void *msg;
353
354 if (skip_cmd)
355 skip_cmd = 0;
356 else
357 printf("\n> ");
358 fgets(cmd, sizeof(cmd), stdin);
359 len = strlen(cmd) - 1;
360 cmd[len] = '\0';
361
362 if (!len)
363 {
364 skip_cmd = 1;
365 continue;
366 }
367
368 for (i = 0; _msg_types[i].name; i++)
369 {
370 if (!strcmp(cmd, _msg_types[i].name))
371 break;
372 }
373
374 // discards the end of the message if we can't parse it
375 if (!_msg_types[i].name)
376 {
377 printf("Invalid command.\n");
378 continue;
379 }
380
381 if (!_msg_types[i].parse_func)
382 {
383 printf("Command %s still unhandled.\n", _msg_types[i].name);
384 continue;
385 }
386
387 msg = _msg_types[i].parse_func(&size);
388
389 if (send(s, &size, sizeof(size), MSG_NOSIGNAL) == -1)
390 {
391 perror("send size");
392 exit(1);
393 }
394 if (send(s, msg, size, MSG_NOSIGNAL) == -1)
395 {
396 perror("send");
397 exit(1);
398 }
399
400 free(msg);
401
402 usleep(100000);
403
404 if ((t=recv(s, &size, sizeof(size), MSG_DONTWAIT)) > 0)
405 {
406 int len = recv(s, msgbuf, size, 0);
407 printf("size of received message: %d\n", len);
408 if (len != size)
409 {
410 printf("couldn't read entire message.\n");
411 continue;
412 }
413 debug_msg(&size, sizeof(size));
414 debug_msg(msgbuf, size);
415 parse_answer(msgbuf);
416 }
417 else
418 {
419 if (t < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
420 continue;
421 else fprintf(stderr, "Server closed connection\n");
422 exit(1);
423 }
424 }
425
426 close(s);
427
428 return 0;
429}
diff --git a/legacy/evas/src/bin/evas_cserve2_main.c b/legacy/evas/src/bin/evas_cserve2_main.c
new file mode 100644
index 0000000000..47f120e5e2
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2_main.c
@@ -0,0 +1,456 @@
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
18struct _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
28typedef struct _Slave_Worker Slave_Worker;
29
30int _evas_cserve2_bin_log_dom = -1;
31static unsigned int _client_id = 0;
32static Eina_Hash *client_list = NULL;
33static Eina_Inlist *slaves_idle = NULL;
34static Eina_Inlist *slaves_working = NULL;
35
36void
37cserve2_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
53static 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
68static 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_request_failed(sw->data, CSERVE2_LOADER_DIED);
91 if (sw->ret)
92 eina_binbuf_free(sw->ret);
93 free(sw);
94}
95
96static void
97_slave_read_error(Slave_Worker *sw, void *msg)
98{
99 Error_Type *error = msg;
100
101 cserve2_cache_request_failed(sw->data, *error);
102}
103
104static void
105_slave_read_cb(Slave_Proc *s __UNUSED__, Slave_Command cmd, void *msg, void *data)
106{
107 Slave_Worker *sw = data;
108
109 DBG("Received reply command '%d' from slave '%p'", cmd, sw->slave);
110 switch (cmd)
111 {
112 case IMAGE_OPEN:
113 cserve2_cache_request_opened(msg, sw->data);
114 sw->done = EINA_TRUE;
115 break;
116 case IMAGE_LOAD:
117 cserve2_cache_request_loaded(msg, sw->data);
118 sw->done = EINA_TRUE;
119 break;
120 case ERROR:
121 _slave_read_error(sw, msg);
122 break;
123 default:
124 ERR("Unrecognized command received from slave: %d", cmd);
125 }
126 free(msg);
127
128 // slave finishes its work, put it back to idle list
129 sw->data = NULL;
130 slaves_working = eina_inlist_remove(slaves_working, EINA_INLIST_GET(sw));
131
132 if (!sw->delete_me) // if it is being deleted, it shouldn't be in any list
133 slaves_idle = eina_inlist_append(slaves_idle, EINA_INLIST_GET(sw));
134
135 cserve2_cache_requests_process();
136}
137
138int
139cserve2_slave_available_get(void)
140{
141 return MAX_SLAVES - eina_inlist_count(slaves_working);
142}
143
144Eina_Bool
145cserve2_slave_cmd_dispatch(void *data, Slave_Command cmd, const void *msg, int size)
146{
147 Slave_Worker *sw;
148 char *exe;
149
150 DBG("Dispatching command to slave. %d idle slaves, %d working slaves.",
151 eina_inlist_count(slaves_idle), eina_inlist_count(slaves_working));
152
153 // first check if there's an available slave
154 if (slaves_idle)
155 {
156 sw = EINA_INLIST_CONTAINER_GET(slaves_idle, Slave_Worker);
157 slaves_idle = eina_inlist_remove(slaves_idle, slaves_idle);
158 slaves_working = eina_inlist_append(slaves_working,
159 EINA_INLIST_GET(sw));
160
161 sw->data = data;
162 DBG("Dispatching command '%d' to slave '%p'", cmd, sw->slave);
163 cserve2_slave_send(sw->slave, cmd, msg, size);
164 return EINA_TRUE;
165 }
166
167 // no available slave, start a new one
168 sw = calloc(1, sizeof(Slave_Worker));
169 if (!sw) return EINA_FALSE;
170
171 sw->data = data;
172 exe = getenv("EVAS_CSERVE2_SLAVE");
173 if (!exe) exe = "evas_cserve2_slave";
174 sw->slave = cserve2_slave_run(exe, _slave_read_cb, _slave_dead_cb, sw);
175 if (!sw->slave)
176 {
177 ERR("Could not launch slave process");
178 cserve2_cache_request_failed(data, CSERVE2_LOADER_EXEC_ERR);
179 free(sw);
180 return EINA_FALSE;
181 }
182 DBG("Dispatching command '%d' to slave '%p'", cmd, sw->slave);
183 cserve2_slave_send(sw->slave, cmd, msg, size);
184
185 slaves_working = eina_inlist_append(slaves_working, EINA_INLIST_GET(sw));
186
187 return EINA_TRUE;
188}
189
190static void
191_cserve2_client_close(Client *client)
192{
193 Msg_Close *msg = (Msg_Close *)client->msg.buf;
194
195 INF("Received CLOSE command: RID=%d", msg->base.rid);
196 INF("File_ID: %d\n", msg->file_id);
197
198 cserve2_cache_file_close(client, msg->file_id);
199}
200
201static void
202_cserve2_client_unload(Client *client)
203{
204 Msg_Unload *msg = (Msg_Unload *)client->msg.buf;
205
206 INF("Received UNLOAD command: RID=%d", msg->base.rid);
207 INF("Image_ID: %d\n", msg->image_id);
208
209 cserve2_cache_image_unload(client, msg->image_id);
210}
211
212static void
213_cserve2_client_preload(Client *client)
214{
215 Msg_Preload *msg = (Msg_Preload *)client->msg.buf;
216
217 INF("Received PRELOAD command: RID=%d", msg->base.rid);
218 INF("Image_ID: %d\n", msg->image_id);
219
220 cserve2_cache_image_preload(client, msg->image_id, msg->base.rid);
221 cserve2_cache_requests_process();
222}
223
224static void
225_cserve2_client_load(Client *client)
226{
227 Msg_Load *msg = (Msg_Load *)client->msg.buf;
228
229 INF("Received LOAD command: RID=%d", msg->base.rid);
230 INF("Image_ID: %d\n", msg->image_id);
231
232 cserve2_cache_image_load(client, msg->image_id, msg->base.rid);
233 cserve2_cache_requests_process();
234}
235
236static void
237_cserve2_client_setopts(Client *client)
238{
239 Msg_Setopts *msg = (Msg_Setopts *)client->msg.buf;
240
241 INF("Received SETOPTS command: RID=%d", msg->base.rid);
242 INF("File_ID: %d, Image_ID: %d", msg->file_id, msg->image_id);
243 INF("Load Options:");
244 INF("\tdpi: %03.1f", msg->opts.dpi);
245 INF("\tsize: %dx%d", msg->opts.w, msg->opts.h);
246 INF("\tscale down: %d", msg->opts.scale_down);
247 INF("\tregion: %d,%d + %dx%d",
248 msg->opts.rx, msg->opts.ry, msg->opts.rw, msg->opts.rh);
249 INF("\torientation: %d\n", msg->opts.orientation);
250
251 if (cserve2_cache_image_opts_set(client, msg) != 0)
252 return;
253
254 _cserve2_client_image_setoptsed(client, msg->base.rid);
255}
256
257static void
258_cserve2_client_open(Client *client)
259{
260 Msg_Open *msg = (Msg_Open *)client->msg.buf;
261 const char *path, *key;
262
263 path = ((const char *)msg) + sizeof(*msg) + msg->path_offset;
264 key = ((const char *)msg) + sizeof(*msg) + msg->key_offset;
265
266 INF("Received OPEN command: RID=%d", msg->base.rid);
267 INF("File_ID: %d, path=\"%s\", key=\"%s\"\n",
268 msg->file_id, path, key);
269
270 cserve2_cache_file_open(client, msg->file_id, path, key, msg->base.rid);
271 cserve2_cache_requests_process();
272}
273
274void
275cserve2_command_run(Client *client, Message_Type type)
276{
277 switch (type)
278 {
279 case CSERVE2_OPEN:
280 _cserve2_client_open(client);
281 break;
282 case CSERVE2_SETOPTS:
283 _cserve2_client_setopts(client);
284 break;
285 case CSERVE2_LOAD:
286 _cserve2_client_load(client);
287 break;
288 case CSERVE2_PRELOAD:
289 _cserve2_client_preload(client);
290 break;
291 case CSERVE2_UNLOAD:
292 _cserve2_client_unload(client);
293 break;
294 case CSERVE2_CLOSE:
295 _cserve2_client_close(client);
296 break;
297 default:
298 WRN("Unhandled message");
299 }
300}
301
302static void
303_slave_quit_send(Slave_Worker *sw)
304{
305 cserve2_slave_send(sw->slave, SLAVE_QUIT, NULL, 0);
306}
307
308static void
309_slaves_restart(void)
310{
311 Slave_Worker *list, *sw;
312
313 while (slaves_idle) // remove idle workers from idle list
314 {
315 sw = EINA_INLIST_CONTAINER_GET(slaves_idle, Slave_Worker);
316 slaves_idle = eina_inlist_remove(slaves_idle, slaves_idle);
317 sw->delete_me = EINA_TRUE;
318 _slave_quit_send(sw);
319 }
320
321 // working workers will be removed from the working list when they
322 // finish processing their jobs
323 list = EINA_INLIST_CONTAINER_GET(slaves_working, Slave_Worker);
324 EINA_INLIST_FOREACH(list, sw)
325 {
326 sw->delete_me = EINA_TRUE;
327 _slave_quit_send(sw);
328 }
329}
330
331static void
332_timeout_cb(void)
333{
334 static unsigned int slaves_restart = 0;
335
336 slaves_restart++;
337
338 if (slaves_restart == 10)
339 {
340 DBG("kill slaves");
341 _slaves_restart();
342 slaves_restart = 0;
343 }
344
345 cserve2_timeout_cb_set(3000, _timeout_cb);
346}
347
348void
349cserve2_client_accept(int fd)
350{
351 Client *client = calloc(1, sizeof(*client));
352
353 client->socket = fd;
354 client->id = _client_id++;
355
356 while (eina_hash_find(client_list, &client->id))
357 client->id = _client_id++;
358
359 if (!eina_hash_add(client_list, &client->id, client))
360 {
361 Eina_Error err = eina_error_get();
362 ERR("Could not add client to the list: \"%s\"",
363 eina_error_msg_get(err));
364 free(client);
365 close(fd);
366 }
367
368 cserve2_fd_watch_add(fd, FD_READ | FD_ERROR, cserve2_message_handler,
369 client);
370 INF("Client %d connection accepted.", client->id);
371
372 cserve2_cache_client_new(client);
373}
374
375void
376cserve2_client_del(Client *client)
377{
378 eina_hash_del_by_key(client_list, &client->id);
379}
380
381static void
382_client_free(void *data)
383{
384 Client *client = data;
385 cserve2_cache_client_del(client);
386 if (client->msg.pending)
387 eina_binbuf_free(client->msg.pending);
388 cserve2_fd_watch_del(client->socket);
389 close(client->socket);
390 free(data);
391}
392
393static void
394_clients_setup(void)
395{
396 client_list = eina_hash_int32_new(_client_free);
397}
398
399static void
400_clients_finish(void)
401{
402 eina_hash_free(client_list);
403}
404
405int
406main(int argc __UNUSED__, const char *argv[] __UNUSED__)
407{
408 eina_init();
409
410 _evas_cserve2_bin_log_dom = eina_log_domain_register
411 ("evas_cserve2_bin", CSERVE2_BIN_DEFAULT_COLOR);
412 if (_evas_cserve2_bin_log_dom < 0)
413 {
414 EINA_LOG_ERR("impossible to create a log domain.");
415 eina_shutdown();
416 exit(1);
417 }
418
419 if (!cserve2_main_loop_setup())
420 {
421 ERR("could not setup main loop.");
422 goto error;
423 }
424
425 if (!cserve2_slaves_init())
426 {
427 ERR("Could not init slaves subsystem.");
428 goto error;
429 }
430
431 cserve2_cache_init();
432
433 _clients_setup();
434
435 cserve2_timeout_cb_set(3000, _timeout_cb);
436
437 cserve2_main_loop_run();
438
439 _clients_finish();
440
441 cserve2_cache_shutdown();
442
443 _slaves_restart();
444 cserve2_slaves_shutdown();
445
446 cserve2_main_loop_finish();
447
448 eina_log_domain_unregister(_evas_cserve2_bin_log_dom);
449 eina_shutdown();
450 return 0;
451
452error:
453 eina_log_domain_unregister(_evas_cserve2_bin_log_dom);
454 eina_shutdown();
455 exit(1);
456}
diff --git a/legacy/evas/src/bin/evas_cserve2_main_loop_linux.c b/legacy/evas/src/bin/evas_cserve2_main_loop_linux.c
new file mode 100644
index 0000000000..b092cc2b77
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2_main_loop_linux.c
@@ -0,0 +1,779 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include "evas_cserve2.h"
6
7#include <errno.h>
8#include <string.h>
9#include <sys/epoll.h>
10#include <sys/inotify.h>
11#include <sys/signalfd.h>
12#include <sys/socket.h>
13#include <sys/time.h>
14#include <sys/types.h>
15#include <sys/un.h>
16#include <sys/wait.h>
17#include <signal.h>
18#include <unistd.h>
19
20#define MAX_EPOLL_EVENTS 10
21#define MAX_INCOMING_CONN 10
22
23struct _Watch_Data
24{
25 int fd;
26 Fd_Flags flags;
27 Fd_Watch_Cb callback;
28 const void *user_data;
29};
30
31typedef struct _Watch_Data Watch_Data;
32
33struct _Inotify_Data
34{
35 EINA_INLIST;
36 const char *path;
37 int watchid;
38 File_Change_Cb cb;
39 const void *data;
40};
41
42typedef struct _Inotify_Data Inotify_Data;
43
44static int epoll_fd = -1;
45static int signal_fd = -1;
46static int socket_fd = -1;
47static int inotify_fd = -1;
48static struct sockaddr_un socket_local;
49static Eina_Hash *watch_list;
50static Eina_Hash *inotify_path_hash;
51static Eina_Hash *inotify_id_hash;
52static Eina_Bool running;
53static Eina_Bool terminate;
54static int timeout = -1; // in miliseconds
55static long timeout_time = 0; // in miliseconds
56
57static Timeout_Cb timeout_func = NULL;
58static Main_Loop_Child_Dead_Cb reap_children_func = NULL;
59
60#ifndef UNIX_PATH_MAX
61#define UNIX_PATH_MAX sizeof(socket_local.sun_path)
62#endif
63
64static void
65_signal_handle_child(struct signalfd_siginfo *sinfo __UNUSED__)
66{
67 int status;
68 pid_t pid;
69
70 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
71 {
72 if (reap_children_func)
73 {
74 reap_children_func(pid, status);
75 continue;
76 }
77
78 DBG("Received SIGCHLD and no handler is set.");
79
80 if (WIFEXITED(status))
81 DBG("Child '%d' exited with status '%d'.", pid,
82 WEXITSTATUS(status));
83 else if (WIFSIGNALED(status))
84 DBG("Child '%d' exited with signal '%d'.", pid,
85 WTERMSIG(status));
86 }
87}
88
89static void
90_signal_handle_int(struct signalfd_siginfo *sinfo __UNUSED__)
91{
92 DBG("Received SIGINT. Honoring request.");
93 terminate = EINA_TRUE;
94}
95
96static void
97_signal_handle_term(struct signalfd_siginfo *sinfo __UNUSED__)
98{
99 DBG("Received SIGTERM. Honoring request.");
100 terminate = EINA_TRUE;
101}
102
103static void
104_signalfd_handler(int fd, Fd_Flags flags __UNUSED__, void *data __UNUSED__)
105{
106 struct signalfd_siginfo sinfo;
107 ssize_t ret;
108
109 for (;;)
110 {
111 ret = read(fd, &sinfo, sizeof(struct signalfd_siginfo));
112 if (ret == -1)
113 {
114 if (errno == EAGAIN)
115 break;
116 ERR("Error reading from signal fd: %m.");
117 return;
118 }
119
120 switch(sinfo.ssi_signo)
121 {
122 case SIGCHLD:
123 _signal_handle_child(&sinfo);
124 break;
125 case SIGINT:
126 _signal_handle_int(&sinfo);
127 break;
128 case SIGTERM:
129 _signal_handle_term(&sinfo);
130 break;
131 default:
132 ERR("Caught unexpected signal '%d'.", sinfo.ssi_signo);
133 }
134 }
135}
136
137static int
138_signalfd_setup(void)
139{
140 sigset_t mask;
141
142 sigemptyset(&mask);
143 sigaddset(&mask, SIGCHLD);
144 sigaddset(&mask, SIGINT);
145 sigaddset(&mask, SIGTERM);
146
147 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
148 {
149 ERR("Could not set mask of handled signals.");
150 return -1;
151 }
152
153 signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
154 if (signal_fd == -1)
155 ERR("Could not create file descriptor from signalfd.");
156
157 /* ignore SIGPIPE so it's handled by write() and send() as needed */
158 signal(SIGPIPE, SIG_IGN);
159
160 return signal_fd;
161}
162
163static void
164_signalfd_finish(void)
165{
166 sigset_t mask;
167
168 cserve2_fd_watch_del(signal_fd);
169
170 sigemptyset(&mask);
171 sigprocmask(SIG_BLOCK, NULL, &mask);
172
173 close(signal_fd);
174 sigprocmask(SIG_UNBLOCK, &mask, NULL);
175
176 signal(SIGPIPE, SIG_DFL);
177}
178
179static void
180_socketfd_handler(int fd __UNUSED__, Fd_Flags flags __UNUSED__, void *data __UNUSED__)
181{
182 struct sockaddr_un remote;
183 unsigned int len;
184 int s;
185
186 len = sizeof(struct sockaddr_un);
187 s = accept4(socket_fd, &remote, &len, SOCK_CLOEXEC);
188 if (s == -1)
189 {
190 ERR("Could not accept socket: \"%s\"", strerror(errno));
191 return;
192 }
193
194 cserve2_client_accept(s);
195}
196
197static void
198_socket_path_set(char *path)
199{
200 char *env;
201 char buf[UNIX_PATH_MAX];
202
203 env = getenv("EVAS_CSERVE2_SOCKET");
204 if (env && env[0])
205 {
206 strncpy(path, env, UNIX_PATH_MAX - 1);
207 return;
208 }
209
210 env = getenv("XDG_RUNTIME_DIR");
211 if (!env || !env[0])
212 {
213 env = getenv("HOME");
214 if (!env || !env[0])
215 {
216 env = getenv("TMPDIR");
217 if (!env || !env[0])
218 env = "/tmp";
219 }
220 }
221
222 snprintf(buf, sizeof(buf), "%s/evas-cserve2-%x.socket", env, getuid());
223 /* FIXME: check we can actually create this socket */
224 strcpy(path, buf);
225}
226
227static int
228_socketfd_setup(void)
229{
230 int s;
231 int len;
232
233 s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
234 if (s == -1)
235 {
236 ERR("Could not create socketfd: \"%s\"", strerror(errno));
237 return -1;
238 }
239
240 socket_local.sun_family = AF_UNIX;
241 _socket_path_set(socket_local.sun_path);
242 DBG("Using '%s' as server socket.", socket_local.sun_path);
243 unlink(socket_local.sun_path);
244 len = strlen(socket_local.sun_path) + sizeof(socket_local.sun_family);
245 if (bind(s, (struct sockaddr *)&socket_local, len) == -1)
246 {
247 ERR("Could not bind socketfd: \"%s\"", strerror(errno));
248 close(s);
249 return -1;
250 }
251
252 if (listen(s, MAX_INCOMING_CONN) == -1)
253 {
254 ERR("Could not listen on socketfd: \"%s\"", strerror(errno));
255 close(s);
256 unlink(socket_local.sun_path);
257 return -1;
258 }
259
260 socket_fd = s;
261
262 return s;
263}
264
265static void
266_socketfd_finish(void)
267{
268 close(socket_fd);
269 unlink(socket_local.sun_path);
270}
271
272static void
273_inotifyfd_handler(int fd, Fd_Flags flags, void *data __UNUSED__)
274{
275 char buffer[16384];
276 int i = 0;
277 ssize_t size;
278
279 if (flags & FD_ERROR)
280 {
281 ERR("Error on inotify file handler, what to do?");
282 return;
283 }
284
285 size = read(fd, buffer, sizeof(buffer));
286 while (i < size)
287 {
288 struct inotify_event *event;
289 int event_size;
290 Eina_Inlist *ids, *itr;
291 Inotify_Data *id;
292 Eina_Bool deleted;
293
294 event = (struct inotify_event *)&buffer[i];
295 event_size = sizeof(struct inotify_event) + event->len;
296 i += event_size;
297
298 ids = eina_hash_find(inotify_id_hash, &event->wd);
299 if (!ids) continue;
300
301 deleted = !!(event->mask
302 & (IN_DELETE_SELF | IN_MOVE_SELF
303 | IN_IGNORED | IN_UNMOUNT));
304 EINA_INLIST_FOREACH_SAFE(ids, itr, id)
305 id->cb(id->path, deleted, (void *)id->data);
306 }
307}
308
309static void
310_inotify_id_hash_free_cb(void *data)
311{
312 Eina_Inlist *list = data;
313
314 while (list)
315 {
316 Inotify_Data *id;
317 id = EINA_INLIST_CONTAINER_GET(list, Inotify_Data);
318 list = eina_inlist_remove(list, list);
319 eina_stringshare_del(id->path);
320 free(id);
321 }
322}
323
324static int
325_inotifyfd_setup(void)
326{
327 inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
328 if (inotify_fd == -1)
329 {
330 ERR("Could not create inotifyfd: \"%s\"", strerror(errno));
331 return -1;
332 }
333 inotify_path_hash = eina_hash_string_superfast_new(NULL);
334 inotify_id_hash = eina_hash_int32_new(_inotify_id_hash_free_cb);
335
336 return inotify_fd;
337}
338
339static void
340_inotifyfd_finish(void)
341{
342 close(inotify_fd);
343
344 eina_hash_free(inotify_path_hash);
345 eina_hash_free(inotify_id_hash);
346}
347
348static void
349_watch_data_free_cb(void *data)
350{
351 free(data);
352}
353
354Eina_Bool
355cserve2_main_loop_setup(void)
356{
357 int sfd;
358 int socket;
359 int ifd;
360
361 epoll_fd = epoll_create1(EPOLL_CLOEXEC);
362
363 if (epoll_fd < 0)
364 {
365 ERR("Could not create epoll fd.");
366 return EINA_FALSE;
367 }
368
369 watch_list = eina_hash_int32_new(_watch_data_free_cb);
370 if (!watch_list)
371 {
372 ERR("Could not create watch list hash struct.");
373 close(epoll_fd);
374 return EINA_FALSE;
375 }
376
377 sfd = _signalfd_setup();
378 if (sfd == -1)
379 {
380 ERR("Could not setup signalfd.");
381 close(epoll_fd);
382 eina_hash_free(watch_list);
383 return EINA_FALSE;
384 }
385
386 DBG("Add watch for signal fd: %d", sfd);
387 if (!cserve2_fd_watch_add(sfd, FD_READ, _signalfd_handler, NULL))
388 {
389 ERR("Could not add watch for signalfd.");
390 close(sfd);
391 close(epoll_fd);
392 eina_hash_free(watch_list);
393 return EINA_FALSE;
394 }
395
396 socket = _socketfd_setup();
397 if (socket == -1)
398 {
399 ERR("Could not setup socketfd.");
400 goto error_socket;
401 }
402
403 DBG("Add watch for socket fd: %d", socket);
404 if (!cserve2_fd_watch_add(socket, FD_READ, _socketfd_handler, NULL))
405 {
406 ERR("Could not add watch for socketf.");
407 close(socket);
408 goto error_socket;
409 }
410
411 ifd = _inotifyfd_setup();
412 if (ifd == -1)
413 {
414 ERR("Could not setup inotify.");
415 goto error_inotify;
416 }
417
418 DBG("Add watch for inotify fd: %d", ifd);
419 if (!cserve2_fd_watch_add(ifd, FD_READ, _inotifyfd_handler, NULL))
420 {
421 ERR("Could not add watch for inotifyfd.");
422 close(ifd);
423 goto error_inotify;
424 }
425
426 return EINA_TRUE;
427
428error_inotify:
429 _socketfd_finish();
430
431error_socket:
432 close(sfd);
433 close(epoll_fd);
434 eina_hash_free(watch_list);
435 return EINA_FALSE;
436}
437
438void
439cserve2_main_loop_finish(void)
440{
441 _socketfd_finish();
442
443 _signalfd_finish();
444
445 _inotifyfd_finish();
446
447 eina_hash_free(watch_list);
448
449 close(epoll_fd);
450}
451
452Eina_Bool
453cserve2_fd_watch_add(int fd, Fd_Flags flags, Fd_Watch_Cb cb, const void *data)
454{
455 Watch_Data *w_data;
456 struct epoll_event ev;
457 int err;
458
459 DBG("Add watch for fd %d, flags = 0x%x", fd, flags);
460
461 if ((fd < 0) || (!cb))
462 {
463 ERR("Can't add watch: fd = %d, callback = %p", fd, cb);
464 return EINA_FALSE;
465 }
466
467 w_data = calloc(1, sizeof(*w_data));
468 w_data->fd = fd;
469 w_data->flags = flags;
470 w_data->callback = cb;
471 w_data->user_data = data;
472
473 if (!eina_hash_add(watch_list, &fd, w_data))
474 {
475 ERR("Could not add watch for fd %d to the hash.", fd);
476 free(w_data);
477 return EINA_FALSE;
478 }
479
480 memset(&ev, 0, sizeof(ev));
481 if (flags & FD_READ)
482 ev.events |= EPOLLIN;
483 if (flags & FD_WRITE)
484 ev.events |= EPOLLOUT;
485 ev.data.ptr = w_data;
486
487 err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
488 if (!err < 0)
489 {
490 ERR("Could not create epoll watch for fd %d.", fd);
491 eina_hash_del(watch_list, &fd, NULL);
492 return EINA_FALSE;
493 }
494
495 return EINA_TRUE;
496}
497
498Eina_Bool
499cserve2_fd_watch_flags_set(int fd, Fd_Flags flags)
500{
501 Watch_Data *w_data;
502 struct epoll_event ev;
503 int err;
504
505 DBG("Set watch flags for fd %d, flags = 0x%x", fd, flags);
506
507 if (fd < 0)
508 {
509 ERR("Can't modify watch: fd = %d", fd);
510 return EINA_FALSE;
511 }
512
513 w_data = eina_hash_find(watch_list, &fd);
514 if (!w_data)
515 {
516 ERR("Couldn't find data for fd %d: not being watched.", fd);
517 return EINA_FALSE;
518 }
519
520 if (flags == w_data->flags)
521 return EINA_TRUE;
522
523 memset(&ev, 0, sizeof(ev));
524 if (flags & FD_READ)
525 ev.events |= EPOLLIN;
526 if (flags & FD_WRITE)
527 ev.events |= EPOLLOUT;
528 ev.data.ptr = w_data;
529
530 err = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev);
531 if (err < 0)
532 {
533 ERR("Could not modify epoll watch for fd: %d", fd);
534 return EINA_FALSE;
535 }
536
537 return EINA_TRUE;
538}
539
540Eina_Bool
541cserve2_fd_watch_flags_get(int fd, Fd_Flags *flags)
542{
543 Watch_Data *w_data;
544 if (fd < 0)
545 {
546 ERR("Can't get flags for watch: fd = %d", fd);
547 return EINA_FALSE;
548 }
549
550 w_data = eina_hash_find(watch_list, &fd);
551 if (!w_data)
552 {
553 ERR("Couldn't find data for fd: %d. Is it really being watched?", fd);
554 return EINA_FALSE;
555 }
556
557 *flags = w_data->flags;
558
559 return EINA_TRUE;
560}
561
562Eina_Bool
563cserve2_fd_watch_del(int fd)
564{
565 int err;
566
567 DBG("Remove watch for fd %d", fd);
568
569 if (fd < 0)
570 return EINA_FALSE;
571
572 if (!eina_hash_del(watch_list, &fd, NULL))
573 ERR("Could not remove watch for fd %d from watch list hash.", fd);
574
575 err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
576 if (err < 0)
577 {
578 ERR("Could not remove epoll watch for fd %d.", fd);
579 return EINA_FALSE;
580 }
581
582 return EINA_TRUE;
583}
584
585Eina_Bool
586cserve2_file_change_watch_add(const char *path, File_Change_Cb cb, const void *data)
587{
588 Inotify_Data *id;
589 Eina_Inlist *ids;
590 unsigned int mask = IN_ATTRIB | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF;
591
592 id = eina_hash_find(inotify_path_hash, path);
593 if (id)
594 {
595 ERR("Attempt to watch changes for path '%s', which is already "
596 "being watched.", path);
597 return EINA_FALSE;
598 }
599
600 id = calloc(1, sizeof(Inotify_Data));
601 if (!id)
602 {
603 ERR("Could not alloc Inotify_Data instance.");
604 return EINA_FALSE;
605 }
606
607 id->watchid = inotify_add_watch(inotify_fd, path, mask);
608 if (id->watchid == -1)
609 {
610 ERR("Could not add inotify watch for %s: %m.", path);
611 free(id);
612 return EINA_FALSE;
613 }
614
615 id->path = eina_stringshare_add(path);
616 id->cb = cb;
617 id->data = data;
618
619 eina_hash_direct_add(inotify_path_hash, id->path, id);
620
621 ids = eina_hash_find(inotify_id_hash, &id->watchid);
622 ids = eina_inlist_append(ids, EINA_INLIST_GET(id));
623 eina_hash_set(inotify_id_hash, &id->watchid, ids);
624
625 return EINA_TRUE;
626}
627
628Eina_Bool
629cserve2_file_change_watch_del(const char *path)
630{
631 Inotify_Data *id;
632 Eina_Inlist *ids;
633 int wd;
634
635 id = eina_hash_set(inotify_path_hash, path, NULL);
636 if (!id)
637 {
638 ERR("Requested to remove change watch for %s, but it's not being "
639 "watched.", path);
640 return EINA_FALSE;
641 }
642
643 ids = eina_hash_find(inotify_id_hash, &id->watchid);
644 ids = eina_inlist_remove(ids, EINA_INLIST_GET(id));
645 eina_hash_set(inotify_id_hash, &id->watchid, ids);
646
647 wd = id->watchid;
648 eina_stringshare_del(id->path);
649 free(id);
650
651 if (!ids)
652 {
653 if (inotify_rm_watch(inotify_fd, wd) == -1)
654 {
655 ERR("Could not remove change watch for %s: %m", path);
656 return EINA_FALSE;
657 }
658 }
659
660 return EINA_TRUE;
661}
662
663static void
664_update_timeout(void)
665{
666 struct timeval timev;
667 long cur_time;
668
669 if (timeout <= 0)
670 return;
671
672 gettimeofday(&timev, NULL);
673 cur_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
674 timeout -= cur_time - timeout_time;
675 timeout_time = cur_time;
676
677 if (timeout < 0)
678 timeout = 0;
679}
680
681void
682cserve2_timeout_cb_set(int t, Timeout_Cb cb)
683{
684 struct timeval timev;
685 if (cb && t <= 0)
686 {
687 ERR("timeout must be a value greater than 0 to set a callback."
688 " given timeout: %d miliseconds", t);
689 return;
690 }
691
692 if (!cb)
693 {
694 DBG("Removing timeout callback.");
695 timeout = -1;
696 cb = NULL;
697 return;
698 }
699
700 //DBG("Setting timeout to: %d miliseconds", t);
701 gettimeofday(&timev, NULL);
702 timeout_time = timev.tv_sec * 1000 + timev.tv_usec / 1000;
703 timeout = t;
704 timeout_func = cb;
705}
706
707void
708cserve2_on_child_dead_set(Main_Loop_Child_Dead_Cb cb)
709{
710 reap_children_func = cb;
711}
712
713void
714cserve2_main_loop_run(void)
715{
716 running = EINA_TRUE;
717 terminate = EINA_FALSE;
718
719 for (;;)
720 {
721 struct epoll_event events[MAX_EPOLL_EVENTS];
722 int n, nfds;
723
724 if (terminate)
725 break;
726
727 nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout);
728 if (nfds < 0)
729 {
730 ERR("An error occurred when reading the epoll fd.");
731 ERR("%s", strerror(errno));
732 break;
733 }
734 if (nfds == 0) // timeout occurred
735 {
736 timeout = -1;
737 if (!timeout_func)
738 ERR("Timeout expired, but no timeout function set.");
739 else
740 timeout_func();
741 }
742
743 for (n = 0; n < nfds; n++)
744 {
745 Watch_Data *data = events[n].data.ptr;
746 Fd_Flags flags = 0;
747
748 if (!data)
749 continue;
750
751 if (!data->callback)
752 continue;
753
754 if (events[n].events & EPOLLIN)
755 flags |= FD_READ;
756 if (events[n].events & EPOLLOUT)
757 flags |= FD_WRITE;
758 if (events[n].events & EPOLLERR)
759 flags |= FD_ERROR;
760 data->callback(data->fd, flags, (void *)data->user_data);
761 }
762
763 _update_timeout();
764 }
765
766 running = EINA_FALSE;
767}
768
769ssize_t
770cserve2_client_read(Client *client, void *buf, size_t len)
771{
772 return recv(client->socket, buf, len, MSG_DONTWAIT);
773}
774
775ssize_t
776cserve2_client_write(Client *client, const void *data, size_t size)
777{
778 return send(client->socket, data, size, MSG_NOSIGNAL | MSG_DONTWAIT);
779}
diff --git a/legacy/evas/src/bin/evas_cserve2_messages.c b/legacy/evas/src/bin/evas_cserve2_messages.c
new file mode 100644
index 0000000000..51868969dd
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2_messages.c
@@ -0,0 +1,189 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include <errno.h>
6#include <string.h>
7
8#include "evas_cserve2.h"
9
10// #define DEBUG_MSG 1
11
12static void
13debug_msg(const char *typestr, const void *buf, int size)
14{
15#ifdef DEBUG_MSG
16 const char *str = buf;
17 int i;
18
19 printf("message %s: ", typestr);
20 for (i = 0; i < size; i++)
21 printf("%x ", str[i]);
22
23 printf("\n");
24#endif
25}
26
27static void
28_client_msg_allocate_buf(Client *client, int msgsize)
29{
30 client->msg.reading = EINA_TRUE;
31 client->msg.buf = malloc(msgsize + 1);
32 client->msg.size = msgsize;
33 client->msg.done = 0;
34}
35
36static void
37_client_msg_free(Client *client)
38{
39 client->msg.reading = EINA_FALSE;
40 free(client->msg.buf);
41}
42
43static void
44_client_msg_parse(Client *client)
45{
46 Msg_Base *msg = (Msg_Base *)client->msg.buf;
47 DBG("Message received. Size: %d; type = %d",
48 client->msg.size, msg->type);
49
50 cserve2_command_run(client, msg->type);
51}
52
53static void
54_client_msg_read(Client *client, int done)
55{
56 client->msg.done += done;
57 if (client->msg.done == client->msg.size)
58 {
59 debug_msg("received", client->msg.buf, client->msg.size);
60 _client_msg_parse(client);
61 _client_msg_free(client);
62 }
63}
64
65void
66cserve2_message_handler(int fd __UNUSED__, Fd_Flags flags, void *data)
67{
68 Client *client = data;
69 int len;
70 int msgsize;
71
72 if (flags & FD_ERROR)
73 {
74 ERR("Error on socket for client: %d", client->id);
75 goto client_close;
76 }
77
78 if (flags & FD_WRITE)
79 cserve2_client_deliver(client);
80
81 if (!(flags & FD_READ))
82 return;
83
84 if (!client->msg.reading)
85 len = cserve2_client_read(client, &msgsize, sizeof(msgsize));
86 else
87 len = cserve2_client_read(client, &client->msg.buf[client->msg.done],
88 client->msg.size - client->msg.done);
89
90 if (!len)
91 {
92 INF("Client %d connection closed.", client->id);
93 goto client_close;
94 }
95
96 if (len < 0)
97 {
98 if (errno == EAGAIN || errno == EWOULDBLOCK)
99 {
100 WRN("No data to read but the message handler was called.");
101 return;
102 }
103 WRN("Error when reading message from client: \"%s\"",
104 strerror(errno));
105 // FIXME: Should we close the connection, or just send an ERROR
106 // message?
107 goto client_close;
108 }
109
110 if (!client->msg.reading)
111 _client_msg_allocate_buf(client, msgsize);
112 else
113 _client_msg_read(client, len);
114
115 return;
116
117client_close:
118 if (client->msg.reading)
119 _client_msg_free(client);
120 cserve2_client_del(client);
121}
122
123void
124cserve2_client_deliver(Client *client)
125{
126 size_t sent, size;
127 const char *str;
128
129 if (!client->msg.pending)
130 {
131 Fd_Flags cur_flags;
132 cserve2_fd_watch_flags_get(client->socket, &cur_flags);
133 cur_flags ^= FD_WRITE;
134 cserve2_fd_watch_flags_set(client->socket, cur_flags);
135 return;
136 }
137
138 size = eina_binbuf_length_get(client->msg.pending);
139 str = (const char *)eina_binbuf_string_get(client->msg.pending);
140 sent = cserve2_client_write(client, str, size);
141 if (sent == size)
142 {
143 eina_binbuf_free(client->msg.pending);
144 client->msg.pending = NULL;
145 return;
146 }
147
148 eina_binbuf_remove(client->msg.pending, 0, sent);
149}
150
151ssize_t
152cserve2_client_send(Client *client, const void *data, size_t size)
153{
154 ssize_t sent;
155
156 debug_msg("sent", data, size);
157 if (client->msg.pending)
158 {
159 eina_binbuf_append_length
160 (client->msg.pending, (unsigned char *)data, size);
161 return size;
162 }
163
164 sent = cserve2_client_write(client, data, size);
165 if ((sent < 0) && ((errno != EAGAIN) && (errno != EWOULDBLOCK)))
166 {
167 // FIXME: Big error when writing on the socket to the client,
168 // so we must close the connection to the client and remove
169 // its references inside our cache.
170 WRN("Error on socket with client %d: %s", client->id, strerror(errno));
171 if (client->msg.reading)
172 _client_msg_free(client);
173 cserve2_client_del(client);
174 return sent;
175 }
176 if (sent < 0)
177 sent = 0;
178 if (sent < (int)size)
179 {
180 Fd_Flags cur_flags;
181 client->msg.pending = eina_binbuf_new();
182 eina_binbuf_append_length
183 (client->msg.pending, (unsigned char *)data + sent, size - sent);
184 cserve2_fd_watch_flags_get(client->socket, &cur_flags);
185 cur_flags |= FD_WRITE;
186 cserve2_fd_watch_flags_set(client->socket, cur_flags);
187 }
188 return size;
189}
diff --git a/legacy/evas/src/bin/evas_cserve2_shm.c b/legacy/evas/src/bin/evas_cserve2_shm.c
new file mode 100644
index 0000000000..36291f947c
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2_shm.c
@@ -0,0 +1,147 @@
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/mman.h>
9#include <sys/stat.h>
10#include <fcntl.h>
11#include <unistd.h>
12
13struct _Shm_Mapping
14{
15 const char *name;
16 size_t length;
17 Eina_Inlist *segments;
18};
19
20typedef struct _Shm_Mapping Shm_Mapping;
21
22struct _Shm_Handle
23{
24 EINA_INLIST;
25 Shm_Mapping *mapping;
26 off_t map_offset;
27 off_t image_offset;
28 size_t map_size;
29 size_t image_size;
30};
31
32static int id = 0;
33
34Shm_Handle *
35cserve2_shm_request(size_t size)
36{
37 Shm_Mapping *map;
38 Shm_Handle *shm;
39 char shmname[NAME_MAX];
40 size_t map_size;
41 long pagesize;
42 int fd;
43
44 map = calloc(1, sizeof(Shm_Mapping));
45 if (!map)
46 {
47 ERR("Failed to allocate mapping handler.");
48 return NULL;
49 }
50
51 do {
52 snprintf(shmname, sizeof(shmname), "/evas-shm-img-%d", id++);
53 fd = shm_open(shmname, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
54 if (fd == -1 && errno != EEXIST)
55 {
56 ERR("Failed to create shared memory object '%s': %m", shmname);
57 free(map);
58 return NULL;
59 }
60 } while (fd == -1);
61
62 pagesize = sysconf(_SC_PAGESIZE);
63 if (pagesize < 1)
64 {
65 ERR("sysconf() reported weird value for PAGESIZE, assuming 4096.");
66 pagesize = 4096;
67 }
68
69 map_size = ((size + pagesize - 1) / pagesize) * pagesize;
70
71 if (ftruncate(fd, map_size) == -1)
72 {
73 ERR("Failed to set size of shared file: %m");
74 close(fd);
75 free(map);
76 return NULL;
77 }
78 close(fd);
79
80 map->name = eina_stringshare_add(shmname);
81 map->length = map_size;
82
83 shm = calloc(1, sizeof(Shm_Handle));
84 if (!shm)
85 {
86 ERR("Failed to allocate shared memory handler.");
87 eina_stringshare_del(map->name);
88 free(map);
89 return NULL;
90 }
91
92 map->segments = eina_inlist_append(map->segments, EINA_INLIST_GET(shm));
93 shm->mapping = map;
94 shm->map_offset = 0;
95 shm->image_offset = 0;
96
97 shm->image_size = size;
98 shm->map_size = map_size;
99
100 return shm;
101}
102
103void
104cserve2_shm_unref(Shm_Handle *shm)
105{
106 Shm_Mapping *map = shm->mapping;
107
108 map->segments = eina_inlist_remove(map->segments, EINA_INLIST_GET(shm));
109 free(shm);
110
111 if (map->segments)
112 return;
113
114 shm_unlink(map->name);
115 eina_stringshare_del(map->name);
116 free(map);
117}
118
119const char *
120cserve2_shm_name_get(const Shm_Handle *shm)
121{
122 return shm->mapping->name;
123}
124
125off_t
126cserve2_shm_map_offset_get(const Shm_Handle *shm)
127{
128 return shm->map_offset;
129}
130
131off_t
132cserve2_shm_offset_get(const Shm_Handle *shm)
133{
134 return shm->image_offset;
135}
136
137size_t
138cserve2_shm_map_size_get(const Shm_Handle *shm)
139{
140 return shm->map_size;
141}
142
143size_t
144cserve2_shm_size_get(const Shm_Handle *shm)
145{
146 return shm->image_size;
147}
diff --git a/legacy/evas/src/bin/evas_cserve2_slave.c b/legacy/evas/src/bin/evas_cserve2_slave.c
new file mode 100644
index 0000000000..b6e46101e5
--- /dev/null
+++ b/legacy/evas/src/bin/evas_cserve2_slave.c
@@ -0,0 +1,480 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include <errno.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <time.h>
9#include <sys/mman.h>
10#include <fcntl.h>
11#include <unistd.h>
12
13#include "evas_cserve2.h"
14#include "evas_cserve2_slave.h"
15
16static Eina_Hash *loaders = NULL;
17static Eina_List *modules = NULL;
18
19struct ext_loader_s
20{
21 unsigned int length;
22 const char *extension;
23 const char *loader;
24};
25
26#define MATCHING(Ext, Module) \
27 { sizeof (Ext), Ext, Module }
28
29static const struct ext_loader_s map_loaders[] =
30{ /* map extensions to loaders to use for good first-guess tries */
31 MATCHING(".png", "png"),
32 MATCHING(".jpg", "jpeg"),
33 MATCHING(".jpeg", "jpeg"),
34 MATCHING(".jfif", "jpeg"),
35 MATCHING(".eet", "eet"),
36 MATCHING(".edj", "eet"),
37 MATCHING(".eap", "eet"),
38 MATCHING(".edb", "edb"),
39 MATCHING(".xpm", "xpm"),
40 MATCHING(".tiff", "tiff"),
41 MATCHING(".tif", "tiff"),
42 MATCHING(".svg", "svg"),
43 MATCHING(".svgz", "svg"),
44 MATCHING(".svg.gz", "svg"),
45 MATCHING(".gif", "gif"),
46 MATCHING(".pbm", "pmaps"),
47 MATCHING(".pgm", "pmaps"),
48 MATCHING(".ppm", "pmaps"),
49 MATCHING(".pnm", "pmaps"),
50 MATCHING(".bmp", "bmp"),
51 MATCHING(".tga", "tga"),
52 MATCHING(".wbmp", "wbmp"),
53 MATCHING(".ico", "ico"),
54 MATCHING(".cur", "ico"),
55 MATCHING(".psd", "psd")
56};
57
58static const char *loaders_name[] =
59{ /* in order of most likely needed */
60 "png", "jpeg", "eet", "xpm", "tiff", "gif", "svg", "pmaps", "bmp", "tga", "wbmp", "ico", "psd", "edb"
61};
62
63Eina_Bool
64evas_cserve2_loader_register(Evas_Loader_Module_Api *api)
65{
66