shared cache server++
authorraster <raster@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 1 May 2009 07:11:07 +0000 (07:11 +0000)
committerraster <raster@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Fri, 1 May 2009 07:11:07 +0000 (07:11 +0000)
is it ok?

1. it can be --disabled in evas's configure, but i think it works WITHOUT
disabling it (runtime) as it falls back to the old way of loading
2. it may cause build problems on some platforms - without it being enabled
we won't find out, so enable.
3. it needs enabling runtime to make use of it so it should be safe for now
until you enable it.

what is it?

it is a SHARED cache server - that means images loaded are loaded BY the
cache server (not by the actual process using evas). images are shared via
shared memory segments (shm_open + mmap). this means only 1 copy is in all
ram at any time - no matter how many processes need it , and its only loaded
once. also if another app has already loaded the same data - and its in the
cache or active hash, then another process needing the same stuff will avoid
the loads as it will just get instant replies from the cache of "image already
there". as it runs in its own process it can also time-out images from the
cache too.

right now you enable it by doing 2 things

1. run evas_cserve (it has cmd-line options to configure cache etc.
2. export EVAS_CSERVE=1 (im the environment of apps that should use the cache
server).

it works (for me) without crashes or problems. except for the following:

1. preloading doesnt work so its disabled if cserve is enabled. thisis
because the load threads interfere withthe unix comms socket causing
problems. this need to really change and have the cserve know about/do
preload and let the select() on the evas async events fd listen for the
unsolicited reply "load done". but it's not broken - simple preloads are
syncronous and forced if cserve is enabled (at build time).
2. if cserve is killed/crashes every app using it will have a bad day. baaad
day. so dont do it. also cserve may be vulnerable to apps crashing on it - it
may also exit with sigpipe. this needs fixing.
3. if the apps load using relative paths - this will break as it doesnt
account for the CWD of the client currently. will be fixed.
4. no way to change cache config runtime (yet)
5. no way to get internal cache state (yet).
6. if cache server exist - it wont clean up the shmem file nodes in /dev/shm
- it will clean on restart (remove the old junk). this needs fixing.

if you fine other issues - let me know.

things for the future:

1. now its a separate server.. the server could do async http etc. loads too
2. as a server it could monitor history of usage of files and images and
auto-pre-load files it knows historically are loaded then whose data is
immediately accessed.
3. the same infra could be used to share font loads (freetype and/or
fontconfig data).
4. ultimately being able to share rendered font glyphs will help a lot too.
5. it could, on its own, monitor "free memory" and when free memory runs
load, reduce cache size dynamically. (improving low memory situations).
6. it should get a gui to query cache state/contents and display visually.
this would be awesome to have a list of thumbnails that show whats in the
cache, how many referencesa they have, last active timestamps etc.

blah blah.

please let me know if the build is broken asap though as i will vanish
offline for a bit in about 24hrs...

git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/evas@40478 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

19 files changed:
configure.ac
src/Makefile.am
src/bin/Makefile.am [new file with mode: 0644]
src/bin/evas_cserve_main.c [new file with mode: 0644]
src/bin/evas_cserve_test_main.c [new file with mode: 0644]
src/lib/Makefile.am
src/lib/cache/evas_cache_image.c
src/lib/canvas/Makefile.am
src/lib/canvas/evas_main.c
src/lib/cserve/Makefile.am [new file with mode: 0644]
src/lib/cserve/evas_cs.h [new file with mode: 0644]
src/lib/cserve/evas_cs_client.c [new file with mode: 0644]
src/lib/cserve/evas_cs_main.c [new file with mode: 0644]
src/lib/cserve/evas_cs_mem.c [new file with mode: 0644]
src/lib/cserve/evas_cs_server.c [new file with mode: 0644]
src/lib/engines/common/Makefile.am
src/lib/engines/common/evas_image_load.c
src/lib/engines/common/evas_image_main.c
src/lib/include/evas_common.h

index 6edfe01..fb66fcd 100644 (file)
@@ -137,11 +137,30 @@ dnl when used, that option makes configure script fails when
 dnl a requirement is selected, but not met.
 AC_ARG_ENABLE(strict,
   AC_HELP_STRING(
-    [enable strict mode]),
+    [--enable-strict],
+    [enable strict checking mode. [[default==disabled]]]),
   [use_strict="yes"],
   [use_strict="no"]
 )
 
+#######################################
+## CACHE SERVER (CSERVE / CS)
+want_evas_cserve="yes"
+AC_MSG_CHECKING(whether to build shared cache server and support)
+AC_ARG_ENABLE(evas-cserve,
+  AC_HELP_STRING(
+    [--disable-evas-cserve],
+    [disable shared cache server support. [[default=enabled]]]
+  ),
+  [ want_evas_cserve="$enableval" ]
+)
+AC_MSG_RESULT($want_evas_cserve)
+
+AM_CONDITIONAL(EVAS_CSERVE, test "x$want_evas_cserve" = "xyes")
+if test "x$want_evas_cserve" = "xyes"; then
+  AC_DEFINE(EVAS_CSERVE, 1, [Shared caceh server.])
+fi
+
 ##################################################################
 #fribidi support - OPTIONAL!
 PKG_CHECK_MODULES([FRIBIDI], [fribidi], ,HAS_BIDI=0)
@@ -1044,12 +1063,14 @@ evas.pc
 doc/evas.dox
 doc/Makefile
 src/Makefile
+src/bin/Makefile
 src/lib/Makefile
 src/lib/canvas/Makefile
 src/lib/data/Makefile
 src/lib/file/Makefile
 src/lib/imaging/Makefile
 src/lib/cache/Makefile
+src/lib/cserve/Makefile
 src/lib/engines/Makefile
 src/lib/engines/common/Makefile
 src/lib/engines/common/evas_op_add/Makefile
@@ -1172,11 +1193,13 @@ echo "  SSE.....................: $build_cpu_sse"
 echo "  ALTIVEC.................: $build_cpu_altivec"
 echo "  NEON....................: $build_cpu_neon"
 echo "  Thread Support..........: $build_pthreads"
+echo "Features:"
 echo "  MAGIC_DEBUG.............: $want_evas_magic_debug"
+echo "  Cache Server............: $want_evas_cserve"
 echo
-echo "Threaded Pipe Rendering...: $build_pipe_render"
-echo "Async Events..............: $build_async_events"
-echo "Async Image Preload.......: $build_async_preload"
+echo "  Threaded Pipe Rendering.: $build_pipe_render"
+echo "  Async Events............: $build_async_events"
+echo "  Async Image Preload.....: $build_async_preload"
 echo
 echo "ARGB Software Engine Options:"
 echo "  Sampling Scaler.........: $scaler_sample"
index 75f3f70..1f52208 100644 (file)
@@ -1,3 +1,3 @@
 MAINTAINERCLEANFILES = Makefile.in
 
-SUBDIRS = lib modules
+SUBDIRS = lib bin modules
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
new file mode 100644 (file)
index 0000000..76c7aa8
--- /dev/null
@@ -0,0 +1,40 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+if EVAS_CSERVE
+
+AM_CPPFLAGS = \
+-I. \
+-I$(top_srcdir)/src/lib \
+-I$(top_srcdir)/src/lib/include \
+-I$(top_srcdir)/src/lib/cserve \
+-DPACKAGE_BIN_DIR=\"$(bindir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+@EINA_CFLAGS@ \
+@FREETYPE_CFLAGS@ \
+@FRIBIDI_CFLAGS@ \
+@EET_CFLAGS@ \
+@FONTCONFIG_CFLAGS@ \
+@pthread_cflags@
+
+AM_CFLAGS = @WIN32_CFLAGS@
+
+bin_PROGRAMS = evas_cserve evas_cserve_test
+
+evas_cserve_SOURCES = \
+evas_cserve_main.c
+
+evas_cserve_LDADD = \
+$(top_builddir)/src/lib/libevas.la
+
+evas_cserve_test_LDFLAGS =
+
+evas_cserve_test_SOURCES = \
+evas_cserve_test_main.c
+
+evas_cserve_test_LDADD = \
+$(top_builddir)/src/lib/libevas.la
+
+evas_cserve_test_LDFLAGS =
+
+endif
diff --git a/src/bin/evas_cserve_main.c b/src/bin/evas_cserve_main.c
new file mode 100644 (file)
index 0000000..68bb726
--- /dev/null
@@ -0,0 +1,733 @@
+#include "Evas.h"
+#include "evas_cs.h"
+
+// fixme:'s
+// 
+// sigint/term - catch and shut down cleanly (server)
+// sigpipe - catch and ignore (both)
+// cwd for loading files needs to be put into a full path (client)
+// add ops to get/set cache size, check time and cache time (both)
+// add ops to get internal state (both)
+// preload - make it work (both)
+// monitor /proc/meminfo and if mem low - free items until cache empty (server)
+// 
+// pants!
+
+typedef struct _Img Img;
+typedef struct _Lopt Lopt;
+
+struct _Lopt
+{
+   int    scale_down_by; // if > 1 then use this
+   double dpi; // if > 0.0 use this
+   int    w, h; // if > 0 use this
+};
+
+struct _Img
+{
+   Image_Entry ie;
+   int ref;
+   int usage;
+   Mem *mem;
+   const char *key;
+   time_t cached;
+   struct {
+      const char *file;
+      const char *key;
+      time_t modtime;
+      time_t last_stat;
+   } file;
+   Lopt load_opts;
+   struct {
+      int w, h;
+      void *data;
+      Eina_Bool alpha : 1;
+   } image;
+   Eina_Bool dead : 1;
+   Eina_Bool active : 1;
+};
+
+// config
+static int stat_res_interval = 2;
+
+static time_t t_now = 0;
+
+static Evas_Cache_Image *cache = NULL;
+
+static Eina_Hash *active_images = NULL;
+static Eina_List *cache_images = NULL;
+static int cache_usage = 0;
+static int cache_max_usage = 1 * 1024 * 1024;
+static int cache_item_timeout = -1;
+static int cache_item_timeout_check = 10;
+static Mem *stat_mem = NULL;
+
+static int stat_mem_num = 0;
+static Eina_List *stat_mems = NULL;
+
+static void
+stat_clean(Mem *m)
+{
+   int *ints;
+   int size, pid, *ids, count, i;
+   
+   ints = (int *)m->data;
+   size = ints[0];
+   if (!evas_cserve_mem_resize(m, size)) return;
+   ints = (int *)m->data;
+   pid =  ints[1];
+   count =  (size - (2 * sizeof(int))) / sizeof(int);
+   ids = ints + 2;
+   for (i = 0; i < count; i++)
+     evas_cserve_mem_del(pid, ids[i]);
+}
+
+static int
+stat_init(Mem *m)
+{
+   int *ints;
+
+   ints = (int *)m->data;
+   
+   if (!evas_cserve_mem_resize(m, 2 * sizeof(int))) return 0;
+   ints[0] = 2 * sizeof(int);
+   ints[1] = getpid();
+   msync(m->data, 2 * sizeof(int), MS_SYNC | MS_INVALIDATE);
+   return 1;
+}
+
+static int
+stat_update(Mem *m)
+{
+   Eina_List *l;
+   Mem *m2;
+   int *ints, *ids, i;
+   
+   ints = (int *)m->data;
+   ints[0] = (2 * sizeof(int)) + (eina_list_count(stat_mems) * sizeof(int));
+   if (!evas_cserve_mem_resize(m, ints[0])) return 0;
+   ints = (int *)m->data;
+   ids = ints + 2;
+   i = 0;
+   EINA_LIST_FOREACH(stat_mems, l, m2)
+     {
+        ids[i] = m2->id;
+        i++;
+     }
+   msync(m->data, ints[0], MS_SYNC | MS_INVALIDATE);
+   return 1;
+}
+
+static Image_Entry *
+_img_alloc(void)
+{
+   Img *img;
+   
+   img = calloc(1, sizeof(Img));
+   return (Image_Entry *)img;
+}
+
+static void
+_img_dealloc(Image_Entry *ie)
+{
+   Img *img = (Img *)ie;
+   free(img);
+}
+
+static int
+_img_surface_alloc(Image_Entry *ie, int w, int h)
+{
+   Img *img = (Img *)ie;
+
+   img->mem = evas_cserve_mem_new(w * h * sizeof(DATA32), NULL);
+   if (!img->mem) return -1;
+   img->image.data = img->mem->data + img->mem->offset;
+   
+   stat_mems = eina_list_append(stat_mems, img->mem);
+   stat_update(stat_mem);
+   return 0;
+}
+
+static void
+_img_surface_delete(Image_Entry *ie)
+{
+   Img *img = (Img *)ie;
+
+   if (!img->mem) return;
+   
+   stat_mems = eina_list_remove(stat_mems, img->mem);
+   stat_update(stat_mem);
+   
+   evas_cserve_mem_free(img->mem);
+   img->mem = NULL;
+   img->image.data = NULL;
+}
+
+static DATA32 *
+_img_surface_pixels(Image_Entry *ie)
+{
+   Img *img = (Img *)ie;
+   
+   return img->image.data;
+}
+
+static int
+_img_load(Image_Entry *ie)
+{
+   return evas_common_load_rgba_image_module_from_file(ie);
+}
+
+static void
+_img_unload(Image_Entry *ie)
+{
+}
+
+static void
+_img_dirty_region(Image_Entry *ie, int x, int y, int w, int h)
+{
+}
+
+static int
+_img_dirty(Image_Entry *dst, const Image_Entry *src)
+{
+   return 0;
+}
+
+static int
+_img_size_set(Image_Entry *dst, const Image_Entry *src, int w, int h)
+{
+   return 0;
+}
+
+static int
+_img_copied_data(Image_Entry *ie, int w, int h, DATA32 *image_data, int alpha, int cspace)
+{
+   return 0;
+}
+
+static int
+_img_data(Image_Entry *ie, int w, int h, DATA32 *image_data, int alpha, int cspace)
+{
+   return 0;
+}
+
+static int
+_img_color_space(Image_Entry *ie, int cspace)
+{
+   return 0;
+}
+
+static int
+_img_load_data(Image_Entry *ie)
+{
+   return evas_common_load_rgba_image_data_from_file(ie);
+}
+
+static int
+_img_mem_size_get(Image_Entry *ie)
+{
+   return 1;
+}
+
+static void
+img_init(void)
+{
+   const Evas_Cache_Image_Func cache_funcs = 
+     {
+        _img_alloc,//Image_Entry *(*alloc)(void);
+          _img_dealloc,//void         (*dealloc)(Image_Entry *im);
+        /* The cache provide some helpers for surface manipulation. */
+          _img_surface_alloc,//int          (*surface_alloc)(Image_Entry *im, int w, int h);
+          _img_surface_delete,//void         (*surface_delete)(Image_Entry *im);
+          _img_surface_pixels,//DATA32      *(*surface_pixels)(Image_Entry *im);
+        /* The cache is doing the allocation and deallocation, you must just do the rest. */
+          _img_load,//int          (*constructor)(Image_Entry *im);
+          _img_unload,//void         (*destructor)(Image_Entry *im);
+          _img_dirty_region,//void         (*dirty_region)(Image_Entry *im, int x, int y, int w, int h);
+        /* Only called when references > 0. Need to provide a fresh copie of im. */
+        /* The destination surface does have a surface, but no allocated pixel data. */
+          _img_dirty,//int          (*dirty)(Image_Entry *dst, const Image_Entry *src);
+        /* Only called when references == 1. We will call drop on im'. */
+        /* The destination surface does not have any surface. */
+          _img_size_set,//int          (*size_set)(Image_Entry *dst, const Image_Entry *src, int w, int h);
+        /* The destination surface does not have any surface. */
+          _img_copied_data,//int          (*copied_data)(Image_Entry *dst, int w, int h, DATA32 *image_data, int alpha, int cspace);
+        /* The destination surface does not have any surface. */
+          _img_data,//int          (*data)(Image_Entry *dst, int w, int h, DATA32 *image_data, int alpha, int cspace);
+          _img_color_space,//int          (*color_space)(Image_Entry *dst, int cspace);
+        /* This function need to update im->w and im->h. */
+          _img_load_data,//int          (*load)(Image_Entry *im);
+          _img_mem_size_get,//int          (*mem_size_get)(Image_Entry *im);
+          NULL,//void         (*debug)(const char *context, Image_Entry *im);
+     };
+   
+   active_images = eina_hash_string_superfast_new(NULL);
+   cache = evas_cache_image_init(&cache_funcs);
+}
+
+static void
+img_shutdown(void)
+{
+   evas_cache_image_shutdown(cache);
+   cache = NULL;
+   // FIXME: shutdown properly
+}
+
+static Img *
+img_new(const char *file, const char *key, RGBA_Image_Loadopts *load_opts, const char *bufkey)
+{
+   Img *img;
+   struct stat st;
+   int ret;
+   Image_Entry *ie;
+   int err = 0;
+   
+   ret = stat(file, &st);
+   if (ret < 0) return NULL;
+   ie = evas_cache_image_request(cache, file, key, load_opts, &err);
+   if (!ie) return NULL;
+   img = (Img *)ie;
+   img->key = eina_stringshare_add(bufkey);
+   img->file.modtime = st.st_mtime;
+   img->file.file = eina_stringshare_add(file);
+   if (key) img->file.key = eina_stringshare_add(key);
+   img->load_opts.scale_down_by = load_opts->scale_down_by;
+   img->load_opts.dpi = load_opts->dpi;
+   img->load_opts.w = load_opts->w;
+   img->load_opts.h = load_opts->h;
+   img->image.w = ie->w;
+   img->image.h = ie->h;
+   img->image.alpha = ie->flags.alpha;
+   img->ref = 1;
+   img->active = 1;
+
+   img->usage = sizeof(Img) + strlen(img->key) + 1 + strlen(img->file.file) + 1;
+   if (img->file.key) img->usage += strlen(img->file.key) + 1;
+   // fixme: load img, get header
+   eina_hash_direct_add(active_images, img->key, img);
+   return img;
+}
+
+static void
+img_loaddata(Img *img)
+{
+   // fixme: load img data
+   evas_cache_image_load_data((Image_Entry *)img);
+   if (img->image.data)
+     msync(img->image.data, img->image.w * img->image.h * sizeof(DATA32), MS_SYNC | MS_INVALIDATE);
+   img->usage += 
+     (4096 * (((img->image.w * img->image.h * sizeof(DATA32)) + 4095) / 4096)) +
+     sizeof(Mem);
+}
+
+static void
+img_free(Img *img)
+{
+   eina_stringshare_del(img->key);
+   eina_stringshare_del(img->file.file);
+   eina_stringshare_del(img->file.key);
+   evas_cache_image_drop((Image_Entry *)img);
+}
+
+static void
+cache_clean(void)
+{
+   while ((cache_usage > cache_max_usage) && (cache_images))
+     {
+        Img *img;
+        Eina_List *l;
+        
+        l = eina_list_last(cache_images);
+        if (!l) break;
+        img = l->data;
+        if (!img) break;
+        cache_images = eina_list_remove_list(cache_images, l);
+        cache_usage -= img->usage;
+        img_free(img);
+     }
+}
+
+static void
+cache_timeout(time_t t)
+{
+   Eina_List *l, *l_next;
+   Img *img;
+   
+   if (cache_item_timeout < 0) return;
+   EINA_LIST_FOREACH_SAFE(cache_images, l, l_next, img)
+     {
+        if ((t - img->cached) > cache_item_timeout)
+          {
+             cache_images = eina_list_remove_list(cache_images, l);
+             cache_usage -= img->usage;
+             img_free(img);
+          }
+     }
+}
+
+static void
+img_cache(Img *img)
+{
+   eina_hash_del(active_images, img->key, img);
+   img->active = 0;
+   if (img->dead)
+     {
+        img_free(img);
+        return;
+     }
+   cache_images = eina_list_prepend(cache_images, img);
+   img->cached = t_now;
+   cache_usage += img->usage;
+   if (cache_usage > cache_max_usage)
+     cache_clean();
+}
+
+static void
+img_dead(Img *img)
+{
+   if (img->active) return;
+   cache_images = eina_list_remove(cache_images, img);
+   img_free(img);
+}
+
+static Eina_Bool
+img_ok(Img *img)
+{
+   struct stat st;
+   int ret;
+   
+   if (img->dead) return 0;
+   if ((t_now > img->file.last_stat) &&
+       ((t_now - img->file.last_stat) < stat_res_interval)) return 1;
+   img->file.last_stat = t_now;
+   ret = stat(img->file.file, &st);
+   if (ret < 0)
+     {
+        img->dead = 1;
+        img_dead(img);
+        return 0;
+     }
+   if (st.st_mtime != img->file.modtime)
+     {
+        img->dead = 1;
+        img_dead(img);
+        return 0;
+     }
+   return 1;
+}
+
+static Img *
+img_load(const char *file, const char *key, RGBA_Image_Loadopts *load_opts)
+{
+   Img *img;
+   char buf[8192];
+   Eina_List *l, *l_next;
+   
+   if (!file) return NULL;
+   if (key)
+     snprintf(buf, sizeof(buf), "%s///::/%s/\001/%i/%1.8f/%ix%i",
+              file, key, 
+              load_opts->scale_down_by, 
+              load_opts->dpi,
+              load_opts->w, load_opts->h);
+   else
+     snprintf(buf, sizeof(buf), "%s///\001/%i/%1.8f/%ix%i",
+              file, 
+              load_opts->scale_down_by, 
+              load_opts->dpi,
+              load_opts->w, load_opts->h);
+   img = eina_hash_find(active_images, buf);
+   if ((img) && (img_ok(img)))
+     {
+        img->ref++;
+        return img;
+     }
+   
+   // FIXME: keep hash of cached images too 
+   EINA_LIST_FOREACH_SAFE(cache_images, l, l_next, img)
+    {
+        if (!strcmp(img->key, buf))
+          {
+             if (img_ok(img))
+               {
+                  img->ref++;
+                  cache_images = eina_list_remove_list(cache_images, l);
+                  eina_hash_direct_add(active_images, img->key, img);
+                  return img;
+               }
+          }
+     }
+   return img_new(file, key, load_opts, buf);
+}
+
+static void
+img_unload(Img *img)
+{
+   img->ref--;
+   if (img->ref <= 0)
+     {
+        img_cache(img);
+     }
+}
+
+static void
+img_forcedunload(Img *img)
+{
+   img->dead = 1;
+   img_unload(img);
+}
+
+static void
+img_preload(Img *img)
+{
+   printf("preload '%s'\n", img->file.file);
+}
+
+static void
+client_del(void *data, Client *c)
+{
+   Eina_List *images;
+   Img *img;
+   
+   images = data;
+   EINA_LIST_FREE(images, img)
+     {
+        img_unload(img);
+     }
+}
+
+static int
+message(void *fdata, Server *s, Client *c, int opcode, int size, unsigned char *data)
+{
+   t_now = time(NULL);
+   switch (opcode)
+     {
+     case OP_INIT:
+          {
+             Op_Init *rep;
+             Op_Init msg;
+             
+             memset(&msg, 0, sizeof(msg));
+             msg.pid = getpid();
+             rep = (Op_Init *)data;
+             c->pid = rep->pid;
+             c->func = client_del;
+             c->data = NULL;
+             evas_cserve_client_send(c, OP_INIT, sizeof(msg), (unsigned char *)(&msg));
+          }
+        break;
+     case OP_LOAD:
+          {
+             Op_Load *rep;
+             Op_Load_Reply msg;
+             Img *img;
+             RGBA_Image_Loadopts lopt = {0, 0.0, 0, 0};
+             char *file = NULL, *key = NULL;
+             
+             rep = (Op_Load *)data;
+             file = data + sizeof(Op_Load);
+             key = file + strlen(file) + 1;
+             if (key[0] == 0) key = NULL;
+             lopt.scale_down_by = rep->lopt.scale_down_by;
+             lopt.dpi = rep->lopt.dpi;
+             lopt.w = rep->lopt.w;
+             lopt.h = rep->lopt.h;
+             img = img_load(file, key, &lopt);
+             if (img)
+               {
+                  Eina_List *list;
+                  
+                  list = c->data;
+                  list = eina_list_append(list, img);
+                  c->data = list;
+               }
+             memset(&msg, 0, sizeof(msg));
+             msg.handle = img;
+             if ((img) && (img->mem))
+               {
+                  msg.mem.id = img->mem->id;
+                  msg.mem.offset = img->mem->offset;
+                  msg.mem.size = img->mem->size;
+               }
+             else
+               msg.mem.id = msg.mem.offset = msg.mem.size = 0;
+             if (img)
+               {
+                  msg.image.w = img->image.w;
+                  msg.image.h = img->image.h;
+                  msg.image.alpha = img->image.alpha;
+               }
+             evas_cserve_client_send(c, OP_LOAD, sizeof(msg), (unsigned char *)(&msg)); 
+         } 
+        break;
+     case OP_UNLOAD:
+          {
+             Op_Unload *rep;
+             Img *img;
+             
+             rep = (Op_Unload *)data;
+             img = rep->handle;
+             c->data = eina_list_remove(c->data, img);
+             img_unload(img);
+          } 
+        break;
+     case OP_LOADDATA:
+          {
+             Op_Loaddata *rep;
+             Op_Loaddata_Reply msg;
+             Img *img;
+// fixme: handle loadopts on loaddata             
+//             RGBA_Image_Loadopts lopt = {0, 0.0, 0, 0};
+             
+             rep = (Op_Loaddata *)data;
+             img = rep->handle;
+             img_loaddata(img);
+             memset(&msg, 0, sizeof(msg));
+             if (img->mem)
+               {
+                  msg.mem.id = img->mem->id;
+                  msg.mem.offset = img->mem->offset;
+                  msg.mem.size = img->mem->size;
+               }
+             else
+               msg.mem.id = msg.mem.offset = msg.mem.size = 0;
+             evas_cserve_client_send(c, OP_LOADDATA, sizeof(msg), (unsigned char *)(&msg));
+          }
+        break;
+     case OP_PRELOAD:
+          {
+             Op_Preload *rep;
+             Img *img;
+             
+             rep = (Op_Preload *)data;
+             img = rep->handle;
+             c->data = eina_list_remove(c->data, img);
+             printf("preload %p\n", img);
+             img_preload(img);
+          }
+     case OP_FORCEDUNLOAD:
+          {
+             Op_Forcedunload *rep;
+             Img *img;
+             
+             rep = (Op_Forcedunload *)data;
+             img = rep->handle;
+             c->data = eina_list_remove(c->data, img);
+             img_forcedunload(img);
+          } 
+        break;
+     default:
+        break;
+     }
+}
+
+static void
+parse_args(int argc, char **argv)
+{
+   int i;
+   
+   for (i = 1; i < argc; i++)
+     {
+        if ((!strcmp(argv[i], "-h")) ||
+            (!strcmp(argv[i], "-help")) ||
+            (!strcmp(argv[i], "--help")))
+          {
+             printf("Options:\n"
+                    "\t-h          This help\n"
+                    "\t-csize      Size of speculative cache (Kb)\n"
+                    "\t-ctime      Maximum life of a cached image (seconds)\n"
+                    "\t-ctimecheck Time between checking the cache for timeouts (seconds)\n"
+                    "\n");
+             exit(0);
+          }
+        else if ((!strcmp(argv[i], "-csize")) && (i < (argc - 1)))
+          {
+             i++;
+             cache_max_usage = atoi(argv[i]) * 1024;
+          }
+        else if ((!strcmp(argv[i], "-ctime")) && (i < (argc - 1)))
+          {
+             i++;
+             cache_item_timeout = atoi(argv[i]);
+          }
+        else if ((!strcmp(argv[i], "-ctimecheck")) && (i < (argc - 1)))
+          {
+             i++;
+             cache_item_timeout_check = atoi(argv[i]);
+          }
+     }
+}
+
+int
+main(int argc, char **argv)
+{
+   Server *s;
+   time_t last_check, t, t_next;
+
+   parse_args(argc, argv);
+
+   eina_init();
+   evas_init();
+
+   img_init();
+   s = evas_cserve_server_add();
+   if (!s)
+     {
+        printf("ERROR: server socket init fail. abort.\n");
+        goto error;
+     }
+   stat_mem = evas_cserve_mem_open(0, 0, "status", sizeof(int), 0);
+   if (stat_mem)
+     {
+        printf("WARNING: previous evas_cserve left garbage. cleaning up.\n");
+        stat_clean(stat_mem);
+        evas_cserve_mem_close(stat_mem);
+        stat_mem = NULL;
+     }
+   stat_mem = evas_cserve_mem_new(sizeof(int), "status");
+   if (!stat_mem)
+     {
+        printf("ERROR: cannot create status shmseg. abort.\n");
+        goto error;
+     }
+   if (!stat_init(stat_mem))
+     {
+        printf("ERROR: cannot init status shmseg. abort.\n");
+        evas_cserve_mem_free(stat_mem);
+        stat_mem = NULL;
+        goto error;
+     }
+   
+   evas_cserve_server_message_handler_set(s, message, NULL);
+   last_check = time(NULL);
+   t_next = 0; 
+   if (cache_item_timeout_check > 0) t_next = cache_item_timeout_check;
+   for (;;)
+     {
+        /* fixme: timeout 0 only her - future use timeouts for timed
+         * housekeping */
+        evas_cserve_server_wait(s, t_next * 1000000);
+        t = time(NULL);
+        t_next = t - last_check;
+        if ((t_next) > cache_item_timeout_check)
+          {
+             t_next = cache_item_timeout_check;
+             
+             last_check = t;
+             cache_timeout(t);
+          }
+        if ((t_next <= 0) && (cache_item_timeout_check > 0))
+          t_next = 1;
+     }
+   error:
+   img_shutdown();
+   if (stat_mem)
+     {
+        evas_cserve_mem_free(stat_mem);
+        stat_mem = NULL;
+     }
+   
+   evas_shutdown();
+   eina_shutdown();
+   return 0;
+}
diff --git a/src/bin/evas_cserve_test_main.c b/src/bin/evas_cserve_test_main.c
new file mode 100644 (file)
index 0000000..8a72705
--- /dev/null
@@ -0,0 +1,33 @@
+#include "evas_cs.h"
+
+int
+main(int argc, char **argv)
+{
+   evas_init();
+   
+   printf("evas_cserve_init = %i\n", evas_cserve_init());
+   
+     {
+        Image_Entry *ie;
+        RGBA_Image_Loadopts lopt = { 0, 0.0, 0, 0};
+        
+        ie = malloc(sizeof(Image_Entry));
+        if (evas_cserve_image_load(ie, argv[1], NULL, &lopt))
+          {
+             printf("load ok\n");
+             if (evas_cserve_image_data_load(ie))
+               {
+                  Mem *m;
+                  
+                  m = ie->data2;
+                  printf("first pixel: %08x\n", *((int *)m->data));
+                  printf("load data ok\n");
+//                  evas_cserve_image_free(ie);
+               }
+          }
+     }
+   
+   evas_cserve_shutdown();
+   evas_shutdown();
+   return 0;
+}
index fd50028..4e30556 100644 (file)
@@ -1,6 +1,6 @@
 MAINTAINERCLEANFILES = Makefile.in
 
-SUBDIRS = canvas data cache file engines imaging include
+SUBDIRS = canvas data cache cserve file engines imaging include
 
 AM_CPPFLAGS = \
 -I. \
@@ -30,6 +30,7 @@ data/libevas_data.la \
 file/libevas_file.la \
 cache/libevas_cache.la \
 imaging/libevas_imaging.la \
+cserve/libevas_cserve.la \
 engines/common/libevas_engine_common.la \
 @dlopen_libs@ \
 @FREETYPE_LIBS@ \
@@ -47,6 +48,7 @@ data/libevas_data.la \
 file/libevas_file.la \
 cache/libevas_cache.la \
 imaging/libevas_imaging.la \
+cserve/libevas_cserve.la \
 engines/common/libevas_engine_common.la
 
 if BUILD_ENGINE_SOFTWARE_16
index cffa4ca..3c304fe 100644 (file)
 #include "evas_common.h"
 #include "evas_private.h"
 
+#ifdef EVAS_CSERVE
+// FIXME: cache server and threaded preload clash badly atm - disable
+#undef BUILD_ASYNC_PRELOAD
+#endif
+
 #ifdef BUILD_ASYNC_PRELOAD
 #include <pthread.h>
 
@@ -801,8 +806,11 @@ evas_cache_image_dirty(Image_Entry *im, int x, int y, int w, int h)
    cache = im->cache;
    if (!(im->flags.dirty))
      {
+#ifndef EVAS_CSERVE
+// if ref 1 also copy if using shared cache as its read-only
         if (im->references == 1) im_dirty = im;
         else
+#endif
           {
              int        error;
 
index 637ddae..96d614c 100644 (file)
@@ -1,10 +1,10 @@
-
 MAINTAINERCLEANFILES = Makefile.in
 
 AM_CPPFLAGS = \
 -I. \
 -I$(top_srcdir)/src/lib \
 -I$(top_srcdir)/src/lib/include \
+-I$(top_srcdir)/src/lib/cserve \
 -DPACKAGE_BIN_DIR=\"$(bindir)\" \
 -DPACKAGE_LIB_DIR=\"$(libdir)\" \
 -DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
index fe2d2c2..2d2065b 100644 (file)
@@ -1,5 +1,6 @@
 #include "evas_common.h"
 #include "evas_private.h"
+#include "evas_cs.h"
 
 extern Eina_List *evas_modules;
 static int initcount = 0;
@@ -28,6 +29,9 @@ evas_init(void)
 
        evas_module_init();
        evas_async_events_init();
+#ifdef EVAS_CSERVE
+        if (getenv("EVAS_CSERVE")) evas_cserve_init();
+#endif        
      }
    return ++initcount;
 }
@@ -38,6 +42,9 @@ evas_shutdown(void)
    initcount--;
    if (initcount == 0)
      {
+#ifdef EVAS_CSERVE
+        if (getenv("EVAS_CSERVE")) evas_cserve_shutdown();
+#endif        
        evas_async_events_shutdown();
        evas_font_dir_cache_free();
        evas_common_shutdown();
diff --git a/src/lib/cserve/Makefile.am b/src/lib/cserve/Makefile.am
new file mode 100644 (file)
index 0000000..117ee4d
--- /dev/null
@@ -0,0 +1,29 @@
+MAINTAINERCLEANFILES = Makefile.in
+
+AM_CPPFLAGS = \
+-I. \
+-I$(top_srcdir)/src/lib \
+-I$(top_srcdir)/src/lib/include \
+-I$(top_srcdir)/src/lib/cserve \
+-DPACKAGE_BIN_DIR=\"$(bindir)\" \
+-DPACKAGE_LIB_DIR=\"$(libdir)\" \
+-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \
+@EINA_CFLAGS@ \
+@FREETYPE_CFLAGS@ \
+@FRIBIDI_CFLAGS@ \
+@EET_CFLAGS@ \
+@FONTCONFIG_CFLAGS@ \
+@pthread_cflags@
+
+noinst_LTLIBRARIES = libevas_cserve.la
+libevas_cserve_la_SOURCES = \
+evas_cs.h \
+evas_cs_main.c \
+evas_cs_server.c \
+evas_cs_client.c \
+evas_cs_mem.c
+
+libevas_cserve_la_LIBADD = @EINA_LIBS@ -lrt
+
+libevas_la_DEPENDENCIES = \
+$(top_builddir)/config.h
diff --git a/src/lib/cserve/evas_cs.h b/src/lib/cserve/evas_cs.h
new file mode 100644 (file)
index 0000000..bc9ab39
--- /dev/null
@@ -0,0 +1,199 @@
+#ifndef EVAS_CS_H
+#define EVAS_CS_H 1
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "evas_common.h"
+#include "evas_private.h"
+
+#ifdef EAPI
+# undef EAPI
+#endif
+
+#ifdef _WIN32
+# ifdef EFL_EVAS_BUILD
+#  ifdef DLL_EXPORT
+#   define EAPI __declspec(dllexport)
+#  else
+#   define EAPI
+#  endif /* ! DLL_EXPORT */
+# else
+#  define EAPI __declspec(dllimport)
+# endif /* ! EFL_EVAS_BUILD */
+#else
+# ifdef __GNUC__
+#  if __GNUC__ >= 4
+#   define EAPI __attribute__ ((visibility("default")))
+#  else
+#   define EAPI
+#  endif
+# else
+#  define EAPI
+# endif
+#endif /* ! _WIN32 */
+
+#ifdef EVAS_CSERVE
+
+#define LENGTH_OF_SOCKADDR_UN(s) (strlen((s)->sun_path) + (size_t)(((struct sockaddr_un *)NULL)->sun_path))
+
+typedef struct _Server Server;
+typedef struct _Client Client;
+typedef struct _Mem Mem;
+
+struct _Server
+{
+   char *socket_path;
+   int fd;
+   Eina_List *clients;
+   int (*func) (void *fdata, Server *s, Client *c, int opcode, int size, unsigned char *data);
+   void *data;
+   pid_t pid;
+};
+
+struct _Client
+{
+   Server *server;
+   unsigned char *buf;
+   int bufsize, bufalloc;
+   int fd;
+   unsigned char *inbuf;
+   int inbufsize, inbufalloc;
+   unsigned char dead : 1;
+   void (*func) (void *fdata, Client *c);
+   void *data;
+   pid_t pid;
+};
+
+struct _Mem
+{
+   unsigned char *data;
+   char *name;
+   int fd;
+   int id;
+   int offset;
+   int size;
+   int ref;
+   Eina_Bool write : 1;
+};
+
+//// for comms
+// for clients to connect to cserve
+EAPI Eina_Bool evas_cserve_init(void);
+EAPI int       evas_cserve_use_get(void);
+EAPI void      evas_cserve_shutdown(void);
+EAPI Eina_Bool evas_cserve_image_load(Image_Entry *ie, const char *file, const char *key, RGBA_Image_Loadopts *lopt);
+EAPI Eina_Bool evas_cserve_image_data_load(Image_Entry *ie);
+EAPI void      evas_cserve_image_free(Image_Entry *ie);
+    
+// for the server
+EAPI Server *evas_cserve_server_add(void);
+EAPI void evas_cserve_server_del(Server *s);
+EAPI void evas_cserve_client_send(Client *c, int opcode, int size, unsigned char *data);
+EAPI void evas_cserve_server_message_handler_set(Server *s, int (*func) (void *fdata, Server *s, Client *c, int opcode, int size, unsigned char *data), void *data);
+EAPI void evas_cserve_server_wait(Server *s, int timeout);
+    
+//// for memory
+// for server
+EAPI Mem *evas_cserve_mem_new(int size, const char *name);
+EAPI void evas_cserve_mem_free(Mem *m);
+    
+// for client
+EAPI Mem *evas_cserve_mem_open(int pid, int id, const char *name, int size, int write);
+EAPI void evas_cserve_mem_close(Mem *m);
+
+// for both
+EAPI Eina_Bool evas_cserve_mem_resize(Mem *m, int size);
+EAPI void      evas_cserve_mem_del(int pid, int id);
+    
+
+enum
+{
+   OP_NOP, // 0
+     
+     OP_INIT, // 1
+     OP_LOAD, // 2
+     OP_UNLOAD, // 3
+     OP_LOADDATA, // 4
+     OP_PRELOAD, // 5
+     OP_FORCEDUNLOAD, // 6
+     
+   OP_INVALID // 6
+};
+
+typedef struct
+{
+   pid_t pid;
+} Op_Init;
+typedef struct
+{
+   struct {
+      int    scale_down_by;
+      double dpi;
+      int    w, h;
+   } lopt;
+} Op_Load; // +"file""key"
+typedef struct
+{
+   void *handle;
+   struct {
+      int id;
+      int offset;
+      int size;
+   } mem;
+   struct {
+      int w, h;
+      Eina_Bool alpha : 1;
+   } image;
+} Op_Load_Reply;
+typedef struct
+{
+   void *handle;
+} Op_Unload;
+typedef struct
+{
+   void *handle;
+} Op_Loaddata;
+typedef struct
+{
+   struct {
+      int id;
+      int offset;
+      int size;
+   } mem;
+} Op_Loaddata_Reply;
+typedef struct
+{
+   void *handle;
+} Op_Preload;
+typedef struct
+{
+   void *handle;
+   struct {
+      int id;
+      int offset;
+      int size;
+   } mem;
+} Op_Preload_Reply;
+typedef struct
+{
+   void *handle;
+} Op_Forcedunload;
+
+#endif
+
+#endif
diff --git a/src/lib/cserve/evas_cs_client.c b/src/lib/cserve/evas_cs_client.c
new file mode 100644 (file)
index 0000000..d9b9248
--- /dev/null
@@ -0,0 +1,245 @@
+#include "evas_cs.h"
+
+#ifdef EVAS_CSERVE
+
+static Server *
+server_connect(void)
+{
+   Server *s;
+   char buf[PATH_MAX];
+   int curstate = 0;
+   struct sockaddr_un socket_unix;
+   int socket_unix_len;
+   
+   s = calloc(1, sizeof(Server));
+   if (!s) return NULL;
+   s->fd = -1;
+   snprintf(buf, sizeof(buf), "/tmp/.evas-cserve-%x", getuid());
+   s->socket_path = strdup(buf);
+   if (!s->socket_path)
+     {
+        free(s);
+        return NULL;
+     }
+   s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+   if (s->fd < 0) goto error;
+   if (fcntl(s->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
+   if (setsockopt(s->fd, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate)) < 0)
+     goto error;
+   socket_unix.sun_family = AF_UNIX;
+   strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
+   socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
+   if (connect(s->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) goto error;
+   return s;
+   error:
+   if (s->fd >= 0) close(s->fd);
+   free(s->socket_path);
+   free(s);
+   return NULL;
+}
+
+static void
+server_disconnect(Server *s)
+{
+   close(s->fd);
+   free(s->socket_path);
+   free(s);
+}
+
+static int
+server_send(Server *s, int opcode, int size, unsigned char *data)
+{
+   int ints[2];
+   int num;
+   
+   ints[0] = size;
+   ints[1] = opcode;
+   num = write(s->fd, ints, (sizeof(int) * 2));
+   if (num < 0) return 0;
+   num = write(s->fd, data, size);
+   if (num < 0) return 0;
+   return 1;
+}
+
+static unsigned char *
+server_read(Server *s, int *opcode, int *size)
+{
+   int ints[2], num;
+   unsigned char *data;
+   
+   num = read(s->fd, ints, sizeof(int) * 2);
+   if (num != (sizeof(int) * 2)) return NULL;
+   *size = ints[0];
+   *opcode = ints[1];
+   if ((*size < 0) || (*size > (1024 * 1024))) return NULL;
+   data = malloc(*size);
+   if (!data) return NULL;
+   num = read(s->fd, data, *size);
+   if (num != *size)
+     {
+        free(data);
+        return NULL;
+     }
+   return data;
+}
+
+static int
+server_init(Server *s)
+{
+   Op_Init msg, *rep;
+   int opcode;
+   int size;
+   
+   msg.pid = getpid();
+   if (!server_send(s, OP_INIT, sizeof(msg), (unsigned char *)(&msg)))
+     return 0;
+   rep = (Op_Init *)server_read(s, &opcode, &size);
+   if (rep)
+     {
+        s->pid = rep->pid;
+        free(rep);
+     }
+   return 1;
+}
+
+static Server *cserve = NULL;
+static int csrve_init = 0;
+
+EAPI Eina_Bool
+evas_cserve_init(void)
+{
+   csrve_init++;
+   if (cserve) return 1;
+   cserve = server_connect();
+   if (!cserve) return 0;
+   if (!server_init(cserve))
+     {
+        server_disconnect(cserve);
+        cserve = NULL;
+        return 0;
+     }
+   return 1;
+}
+
+EAPI int
+evas_cserve_use_get(void)
+{
+   return csrve_init;
+}
+
+EAPI void
+evas_cserve_shutdown(void)
+{
+   csrve_init--;
+   if (csrve_init > 0) return;
+   if (!cserve) return;
+   server_disconnect(cserve);
+   cserve = NULL;
+}
+
+static void
+server_reinit(void)
+{
+   if (cserve) return;
+   cserve = server_connect();
+   if (cserve)
+     {
+        if (!server_init(cserve))
+          {
+             server_disconnect(cserve);
+             cserve = NULL;
+          }
+     }
+}
+
+EAPI Eina_Bool
+evas_cserve_image_load(Image_Entry *ie, const char *file, const char *key, RGBA_Image_Loadopts *lopt)
+{
+   Op_Load msg;
+   Op_Load_Reply *rep;
+   unsigned char *buf;
+   int flen, klen;
+   int opcode;
+   int size;
+   
+   if (csrve_init > 0) server_reinit();
+   else return 0;
+   if (!cserve) return 0;
+   if (!key) key = "";
+   memset(&msg, 0, sizeof(msg));
+   msg.lopt.scale_down_by = lopt->scale_down_by;
+   msg.lopt.dpi = lopt->dpi;
+   msg.lopt.w = lopt->w;
+   msg.lopt.h = lopt->h;
+   flen = strlen(file) + 1;
+   klen = strlen(key) + 1;
+   buf = malloc(sizeof(msg) + flen + klen);
+   memcpy(buf, &msg, sizeof(msg));
+   strcpy(buf + sizeof(msg), file);
+   strcpy(buf + sizeof(msg) + flen, key);
+   if (!buf) return 0;
+   if (!server_send(cserve, OP_LOAD, 
+                    sizeof(msg) + flen + klen,
+                    buf))
+     {
+        free(buf);
+        return 0;
+     }
+   free(buf);
+   if (!cserve) return 0;
+   rep = (Op_Load_Reply *)server_read(cserve, &opcode, &size);
+   if ((rep) && (opcode == OP_LOAD) && (size == sizeof(Op_Load_Reply)))
+     {
+        ie->w = rep->image.w;
+        ie->h = rep->image.h;
+        ie->flags.alpha = rep->image.alpha;
+        ie->data1 = rep->handle;
+     }
+   if (ie->data1 == NULL) return 0;
+   return 1;
+}
+
+EAPI Eina_Bool
+evas_cserve_image_data_load(Image_Entry *ie)
+{
+   Op_Loaddata msg;
+   Op_Loaddata_Reply *rep;
+   int opcode;
+   int size;
+   if (csrve_init > 0) server_reinit();
+   else return 0;
+   if (!cserve) return 0;
+   if (ie->data1 == NULL) return 0;
+   memset(&msg, 0, sizeof(msg));
+   msg.handle = ie->data1;
+   if (!server_send(cserve, OP_LOADDATA, sizeof(msg), (unsigned char *)(&msg)))
+     return 0;
+   if (!cserve) return 0;
+   rep = (Op_Loaddata_Reply *)server_read(cserve, &opcode, &size);
+   if ((rep) && (opcode == OP_LOADDATA) && (size == sizeof(Op_Loaddata_Reply)))
+     {
+        if (rep->mem.size <= 0) return 0;
+        ie->data2 = evas_cserve_mem_open(cserve->pid, rep->mem.id, NULL, rep->mem.size, 0);
+        return 1;
+     }
+   return 0;
+}
+
+EAPI void
+evas_cserve_image_free(Image_Entry *ie)
+{
+   Op_Unload msg;
+   
+   if (csrve_init > 0) server_reinit();
+   else return;
+   if (!cserve) return;
+   if (ie->data1 == NULL) return;
+   memset(&msg, 0, sizeof(msg));
+   msg.handle = ie->data1;
+   if (ie->data2) evas_cserve_mem_close(ie->data2);
+   ie->data2 = NULL;
+   server_send(cserve, OP_UNLOAD, sizeof(msg), (unsigned char *)(&msg));
+   ie->data1 = NULL;
+}
+
+#endif
diff --git a/src/lib/cserve/evas_cs_main.c b/src/lib/cserve/evas_cs_main.c
new file mode 100644 (file)
index 0000000..7bb2978
--- /dev/null
@@ -0,0 +1,5 @@
+#include "evas_cs.h"
+
+#ifdef EVAS_CSERVE
+
+#endif
diff --git a/src/lib/cserve/evas_cs_mem.c b/src/lib/cserve/evas_cs_mem.c
new file mode 100644 (file)
index 0000000..3e7a944
--- /dev/null
@@ -0,0 +1,155 @@
+#include "evas_cs.h"
+
+#ifdef EVAS_CSERVE
+
+EAPI Mem *
+evas_cserve_mem_new(int size, const char *name)
+{
+   Mem *m;
+   static int id = 0;
+   char buf[PATH_MAX];
+   
+   m = calloc(1, sizeof(Mem));
+   if (!m) return NULL;
+   if (name)
+     snprintf(buf, sizeof(buf), "/evas-shm-%x.%s", getuid(), name);
+   else
+     {
+        id++;
+        snprintf(buf, sizeof(buf), "/evas-shm-%x.%x.%x", getuid(), getpid(), id);
+     }
+   m->id = id;
+   m->offset = 0;
+   m->name = strdup(buf);
+   if (!m->name)
+     {
+        free(m);
+        return NULL;
+     }
+   m->size = size;
+   m->fd = shm_open(m->name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+   if (m->fd < 0)
+     {
+        free(m->name);
+        free(m);
+        return NULL;
+     }
+   if (ftruncate(m->fd, m->size) < 0)
+     {
+        shm_unlink(m->name);
+        close(m->fd);
+        free(m->name);
+        free(m);
+        return NULL;
+     }
+   m->data = mmap(NULL, m->size, PROT_READ | PROT_WRITE, MAP_SHARED, m->fd, 0);
+   if (m->data == MAP_FAILED)
+     {
+        shm_unlink(m->name);
+        close(m->fd);
+        free(m->name);
+        free(m);
+        return NULL;
+     }
+   m->ref = 1;
+   m->write = 1;
+   return m;
+}
+
+EAPI void
+evas_cserve_mem_free(Mem *m)
+{
+   shm_unlink(m->name);
+   munmap(m->data, m->size);
+   close(m->fd);
+   free(m->name);
+   free(m);
+}
+
+EAPI Mem *
+evas_cserve_mem_open(int pid, int id, const char *name, int size, int write)
+{
+   Mem *m;
+   char buf[PATH_MAX];
+   
+   m = calloc(1, sizeof(Mem));
+   if (!m) return NULL;
+   if (name)
+     snprintf(buf, sizeof(buf), "/evas-shm-%x.%s", getuid(), name);
+   else
+     snprintf(buf, sizeof(buf), "/evas-shm-%x.%x.%x", getuid(), pid, id);
+   m->name = strdup(buf);
+   if (!m->name)
+     {
+        free(m);
+        return NULL;
+     }
+   m->size = size;
+   if (write)
+     m->fd = shm_open(m->name, O_RDWR, S_IRUSR | S_IWUSR);
+   else
+     m->fd = shm_open(m->name, O_RDONLY, S_IRUSR);
+   if (m->fd < 0)
+     {
+        free(m->name);
+        free(m);
+        return NULL;
+     }
+   m->write = write;
+   if (write)
+     m->data = mmap(NULL, m->size, PROT_READ | PROT_WRITE, MAP_SHARED, m->fd, 0);
+   else
+     m->data = mmap(NULL, m->size, PROT_READ, MAP_SHARED, m->fd, 0);
+   if (m->data == MAP_FAILED)
+     {
+        close(m->fd);
+        free(m->name);
+        free(m);
+        return NULL;
+     }
+   m->ref = 1;
+   return m;
+}
+
+EAPI void
+evas_cserve_mem_close(Mem *m)
+{
+   munmap(m->data, m->size);
+   close(m->fd);
+   free(m->name);
+   free(m);
+}
+
+EAPI Eina_Bool
+evas_cserve_mem_resize(Mem *m, int size)
+{
+   if (m->write)
+     {
+        if (ftruncate(m->fd, size) < 0) return 0;
+        munmap(m->data, m->size);
+        m->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, m->fd, 0);
+     }
+   else
+     {
+        munmap(m->data, m->size);
+        m->data = mmap(NULL, size, PROT_READ, MAP_SHARED, m->fd, 0);
+     }
+   if (m->data == MAP_FAILED)
+     {
+        m->data = NULL;
+        return 0;
+     }
+   m->size = size;
+   return 1;
+}
+
+EAPI void
+evas_cserve_mem_del(int pid, int id)
+{
+   char buf[PATH_MAX];
+   
+   snprintf(buf, sizeof(buf), "/evas-shm-%x.%x.%x", getuid(), pid, id);
+   shm_unlink(buf);
+}
+
+#endif
diff --git a/src/lib/cserve/evas_cs_server.c b/src/lib/cserve/evas_cs_server.c
new file mode 100644 (file)
index 0000000..14e53cc
--- /dev/null
@@ -0,0 +1,343 @@
+#include "evas_cs.h"
+
+#ifdef EVAS_CSERVE
+
+EAPI Server *
+evas_cserve_server_add(void)
+{
+   Server *s;
+   char buf[PATH_MAX];
+   struct sockaddr_un socket_unix;
+   struct linger lin;
+   mode_t pmode;
+   int socket_unix_len;
+   
+   s = calloc(1, sizeof(Server));
+   if (!s) return NULL;
+   s->fd = -1;
+   snprintf(buf, sizeof(buf), "/tmp/.evas-cserve-%x", getuid());
+   s->socket_path = strdup(buf);
+   if (!s->socket_path)
+     {
+        free(s);
+        return NULL;
+     }
+   pmode = umask(~(S_IRUSR | S_IWUSR));
+   start:
+   s->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+   if (s->fd < 0) goto error;
+   if (fcntl(s->fd, F_SETFL, O_NONBLOCK) < 0) goto error;
+   if (fcntl(s->fd, F_SETFD, FD_CLOEXEC) < 0) goto error;
+   lin.l_onoff = 1;
+   lin.l_linger = 0;
+   if (setsockopt(s->fd, SOL_SOCKET, SO_LINGER, &lin, sizeof(struct linger)) < 0)
+     goto error;
+   socket_unix.sun_family = AF_UNIX;
+   strncpy(socket_unix.sun_path, buf, sizeof(socket_unix.sun_path));
+   socket_unix_len = LENGTH_OF_SOCKADDR_UN(&socket_unix);
+   if (bind(s->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0)
+     {
+        if ((connect(s->fd, (struct sockaddr *)&socket_unix, socket_unix_len) < 0) &&
+            (unlink(s->socket_path) >= 0))
+          {
+             close(s->fd);
+             goto start;
+          }
+        else
+          goto error;
+     }
+   if (listen(s->fd, 4096) < 0) goto error;
+   umask(pmode);
+   return s;
+   error:
+   umask(pmode);
+   if (s->fd >= 0) close(s->fd);
+   free(s->socket_path);
+   free(s);
+   return NULL;
+}
+
+EAPI void
+evas_cserve_server_del(Server *s)
+{
+   /* FIXME: del clients! */
+   close(s->fd);
+   unlink(s->socket_path);
+   free(s->socket_path);
+   free(s);
+}
+
+static void
+server_accept(Server *s)
+{
+   Client *c;
+   int new_fd;
+   struct sockaddr_in incoming;
+   size_t size_in;
+   
+   size_in = sizeof(struct sockaddr_in);
+   new_fd = accept(s->fd, (struct sockaddr *)&incoming, (socklen_t *)&size_in);
+   if (new_fd < 0) return;
+   fcntl(new_fd, F_SETFL, O_NONBLOCK);
+   fcntl(new_fd, F_SETFD, FD_CLOEXEC);
+   c = calloc(1, sizeof(Client));
+   if (!c)
+     {
+        close(new_fd);
+        return;
+     }
+   c->server = s;
+   c->fd = new_fd;
+   s->clients = eina_list_append(s->clients, c);
+}
+
+static void
+client_flush(Client *c)
+{
+   int num;
+   
+   num = write(c->fd, c->buf, c->bufsize);
+   if (num < 0)
+     {
+        c->dead = 1;
+        return;
+     }
+   if (num != c->bufsize)
+     {
+        unsigned char *buf;
+        
+        buf = malloc(c->bufsize - num);
+        if (buf)
+          {
+             memcpy(buf, c->buf + num, c->bufsize - num);
+             free(c->buf);
+             c->bufsize = c->bufsize - num;
+             c->bufalloc = c->bufsize;
+             c->buf = buf;
+          }
+     }
+   else
+     {
+        free(c->buf);
+        c->buf = NULL;
+        c->bufsize = 0;
+        c->bufalloc = 0;
+     }
+}
+
+static void
+client_buf_add(Client *c, unsigned char *data, int size)
+{
+   int newsize;
+   unsigned char *buf;
+   
+   newsize = c->bufalloc + size;
+   if (newsize > c->bufalloc)
+     {
+        c->bufalloc + newsize + 1024;
+        buf = realloc(c->buf, c->bufalloc);
+        if (buf) c->buf = buf;
+        else return;
+     }
+   memcpy(c->buf + c->bufsize, data, size);
+   c->bufsize += size;
+}
+
+static void
+client_write(Client *c, unsigned char *data, int size)
+{
+   int num;
+   
+   num = write(c->fd, data, size);
+   if (num != size)
+     client_buf_add(c, data + num, size - num);
+}
+
+EAPI void
+evas_cserve_client_send(Client *c, int opcode, int size, unsigned char *data)
+{
+   unsigned char *data2;
+   int *ints;
+   
+   data2 = malloc(size + (sizeof(int) * 2));
+   if (!data2) return;
+   ints = (int *)data2;
+   ints[0] = size;
+   ints[1] = opcode;
+   memcpy(data2 + (sizeof(int) * 2), data, size);
+   client_write(c, data2, size + (sizeof(int) * 2));
+   free(data2);
+}
+
+static void
+server_message_handle(Server *s, Client *c, int opcode, int size, unsigned char *data)
+{
+   if (s->func) s->func(s->data, s, c, opcode, size, data);
+}
+
+EAPI void
+evas_cserve_server_message_handler_set(Server *s, int (*func) (void *fdata, Server *s, Client *c, int opcode, int size, unsigned char *data), void *data)
+{
+   s->func = func;
+   s->data = data;
+}
+
+static int
+server_parse(Server *s, Client *c)
+{
+   int *ints;
+   unsigned char *data, *newbuf;
+   
+   if (c->inbufsize < sizeof(int)) return;
+   ints = (int *)((c->inbuf));
+   if ((ints[0] < 0) || (ints[0] > (1024 * 1024)))
+     return;
+   if (c->inbufsize < (ints[0] + (sizeof(int) * 2)))
+     {
+        return 0;
+     }
+   data = c->inbuf + (sizeof(int) * 2);
+   server_message_handle(s, c, ints[1], ints[0], data);
+   c->inbufalloc -= ints[0] + (sizeof(int) * 2);
+   if (c->inbufalloc == 0)
+     {
+        free(c->inbuf);
+        c->inbuf = NULL;
+        c->inbufsize = 0;
+        return 0;
+     }
+   newbuf = malloc(c->inbufalloc);
+   if (!newbuf)
+     {
+        c->inbufalloc += ints[0] + (sizeof(int) * 2);
+        /* fixme - bad situation */
+        return 0;
+     }
+   memcpy(newbuf, c->inbuf + ints[0] + (sizeof(int) * 2), c->inbufalloc);
+   c->inbufsize -= ints[0] + (sizeof(int) * 2);
+   free(c->inbuf);
+   c->inbuf = newbuf;
+   return 1;
+}
+
+static void
+server_data(Server *s, Client *c, unsigned char *data, int size)
+{
+   if (!c->inbuf)
+     {
+        c->inbuf = malloc(size);
+        if (c->inbuf)
+          {
+             memcpy(c->inbuf, data, size);
+             c->inbufalloc = size;
+             c->inbufsize = size;
+          }
+        else
+          {
+             /* fixme - bad situation */
+             return;
+          }
+     }
+   else
+     {
+        int size2;
+        
+        size2 = c->inbufsize + size;
+        if (size2 > c->inbufalloc)
+          {
+             unsigned char *newbuf;
+             
+             c->inbufalloc = size2;
+             newbuf = realloc(c->inbuf, c->inbufalloc);
+             if (newbuf) c->inbuf = newbuf;
+             else size2 = 0;
+          }
+        if (size2 > 0)
+          {
+             memcpy(c->inbuf + c->inbufsize, data, size);
+             c->inbufsize = size2;
+          }
+        else
+          {
+             /* fixme - bad situation */
+             return;
+          }
+     }
+   while (server_parse(s, c));
+}
+
+EAPI void
+evas_cserve_server_wait(Server *s, int timeout)
+{
+   fd_set rset, wset, xset;
+   int maxfd;
+   int ret;
+   struct timeval to;
+   Eina_List *l, *dead = NULL;
+   Client *c;
+   
+   maxfd = 0;
+   FD_ZERO(&rset);
+   FD_ZERO(&wset);
+   FD_ZERO(&xset);
+   FD_SET(s->fd, &rset);
+   if (s->fd > maxfd) maxfd = s->fd;
+   EINA_LIST_FOREACH(s->clients, l, c)
+     {
+        FD_SET(c->fd, &rset);
+        if (c->buf)
+          FD_SET(c->fd, &wset);
+        if (c->fd > maxfd) maxfd = c->fd;
+     }
+   if (timeout > 0)
+     {
+        to.tv_sec = timeout / 1000000;
+        to.tv_usec = timeout % 1000000;
+        ret = select(maxfd + 1, &rset, &wset, &xset, &to);
+     }
+   else
+     ret = select(maxfd + 1, &rset, &wset, &xset, NULL);
+   if (ret < 1) return;
+   
+   EINA_LIST_FOREACH(s->clients, l, c)
+     {
+        if (c->dead) continue;
+        if (FD_ISSET(c->fd, &rset))
+          {
+             char buf[16384];
+             int num;
+             
+             errno = 0;
+             num = read(c->fd, buf, sizeof(buf));
+             if (num <= 0)
+               {
+                  c->dead = 1;
+                  dead = eina_list_append(dead, c);
+               }
+             else if (num > 0)
+               {
+                  server_data(s, c, buf, num);
+               }
+          }
+        else if (FD_ISSET(c->fd, &wset))
+          {
+             client_flush(c);
+             if (c->dead) dead = eina_list_append(dead, c);
+          }
+     }
+   if (FD_ISSET(s->fd, &rset))
+     {
+        server_accept(s);
+     }
+   EINA_LIST_FREE(dead, c)
+     {
+        if (c->func) c->func(c->data, c);
+        s->clients = eina_list_remove(s->clients, c);
+        close(c->fd);
+        if (c->buf) free(c->buf);
+        if (c->inbuf) free(c->inbuf);
+        free(c);
+     }
+}
+
+#endif
index bd76eba..4b998bc 100644 (file)
@@ -5,6 +5,7 @@ MAINTAINERCLEANFILES = Makefile.in
 
 AM_CPPFLAGS         = -I. \
                       -I$(top_srcdir)/src/lib \
+                      -I$(top_srcdir)/src/lib/cserve \
                       -I$(top_srcdir)/src/lib/include \
                       -DPACKAGE_BIN_DIR=\"$(bindir)\" \
                       -DPACKAGE_LIB_DIR=\"$(libdir)\" \
index 9a6a86b..1fac753 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "evas_common.h"
 #include "evas_private.h"
+#include "evas_cs.h"
 
 extern Eina_List *evas_modules;
 
@@ -43,6 +44,16 @@ evas_common_load_rgba_image_module_from_file(Image_Entry *ie)
    char                 *dot;
    int                   i;
 
+
+#ifdef EVAS_CSERVE
+   if (evas_cserve_use_get())
+     {
+        if (evas_cserve_image_load(ie, ie->file, ie->key, &(ie->load_opts)))
+          {
+             return 0;
+          }
+     }
+#endif   
    dot = strrchr (ie->file, '.');
    if (dot)
      {
@@ -89,7 +100,7 @@ evas_common_load_rgba_image_module_from_file(Image_Entry *ie)
 
    return -1;
 
-  ok:
+   ok:
    ie->info.module = (void*) em;
    ie->info.loader = (void*) evas_image_load_func;
    evas_module_ref((Evas_Module*) ie->info.module);
@@ -101,9 +112,30 @@ evas_common_load_rgba_image_data_from_file(Image_Entry *ie)
 {
    Evas_Image_Load_Func *evas_image_load_func = NULL;
 
-   if (!ie->info.module) return -1;
    if (ie->flags.loaded) return -1;
 
+#ifdef EVAS_CSERVE
+   if (ie->data1)
+     {
+        if (evas_cserve_image_data_load(ie))
+          {
+             RGBA_Image *im = (RGBA_Image *)ie;
+             Mem *mem;
+             
+             mem = ie->data2;
+             if (mem)
+               {
+                  im->image.data = mem->data + mem->offset;
+                  im->image.no_free = 1;
+                  return 0;
+               }
+          }
+        return -1;
+     }
+#endif
+   
+   if (!ie->info.module) return -1;
+
    evas_image_load_func = ie->info.loader;
    evas_module_use((Evas_Module*) ie->info.module);
    if (!evas_image_load_func->file_data(ie, ie->file, ie->key))
index 495cf96..00976ac 100644 (file)
@@ -152,6 +152,9 @@ _evas_common_rgba_image_delete(Image_Entry *ie)
     * going to empty this struct out in case this happens again so i know
     * that something else is overwritign this struct - or not */
 //   memset(im, 0x99, sizeof(im));
+#ifdef EVAS_CSERVE
+   if (ie->data1) evas_cserve_image_free(ie);
+#endif   
    free(im);
 }
 
@@ -200,6 +203,9 @@ _evas_common_rgba_image_surface_alloc(Image_Entry *ie, int w, int h)
    RGBA_Image   *im = (RGBA_Image *) ie;
    size_t        siz = 0;
 
+#ifdef EVAS_CSERVE
+   if (ie->data1) return 0;
+#endif   
    if (im->image.no_free) return 0;
 
    if (im->flags & RGBA_IMAGE_ALPHA_ONLY)
@@ -244,6 +250,10 @@ _evas_common_rgba_image_surface_delete(Image_Entry *ie)
 
    if (im->image.data && !im->image.no_free)
      free(im->image.data);
+#ifdef EVAS_CSERVE
+   else if (ie->data1)
+     evas_cserve_image_free(ie);
+#endif   
    im->image.data = NULL;
    evas_common_rgba_image_scalecache_dirty(&im->cache_entry);
 }
@@ -258,6 +268,9 @@ _evas_common_rgba_image_dirty_region(Image_Entry* ie, int x __UNUSED__, int y __
 {
    RGBA_Image   *im = (RGBA_Image *) ie;
 
+#ifdef EVAS_CSERVE
+   if (ie->data1) evas_cserve_image_free(ie);
+#endif   
    im->flags |= RGBA_IMAGE_IS_DIRTY;
    evas_common_rgba_image_scalecache_dirty(&im->cache_entry);
 }
@@ -274,8 +287,16 @@ _evas_common_rgba_image_dirty(Image_Entry *ie_dst, const Image_Entry *ie_src)
    evas_cache_image_load_data(&src->cache_entry);
    if (_evas_common_rgba_image_surface_alloc(&dst->cache_entry,
                                              src->cache_entry.w, src->cache_entry.h))
-     return 1;
+     {
+#ifdef EVAS_CSERVE
+        if (ie_src->data1) evas_cserve_image_free(ie_src);
+#endif        
+        return 1;
+     }
 
+#ifdef EVAS_CSERVE
+   if (ie_src->data1) evas_cserve_image_free(ie_src);
+#endif   
    evas_common_image_colorspace_normalize(src);
    evas_common_image_colorspace_normalize(dst);
 /*    evas_common_blit_rectangle(src, dst, 0, 0, src->cache_entry.w, src->cache_entry.h, 0, 0); */
@@ -293,8 +314,15 @@ _evas_common_rgba_image_ram_usage(Image_Entry *ie)
 //   ram += sizeof(struct _RGBA_Image);
 //   if (im->info.real_file) ram += strlen(im->info.real_file);
 //   if (im->info.comment) ram += strlen(im->info.comment);
-   if ((im->image.data) && (!im->image.no_free))
-     size += im->cache_entry.w * im->cache_entry.h * sizeof(DATA32);
+   if (im->image.data)
+     {
+#ifdef EVAS_CSERVE
+        if ((!im->image.no_free) || (ie->data1))
+#else
+        if ((!im->image.no_free))
+#endif          
+          size += im->cache_entry.w * im->cache_entry.h * sizeof(DATA32);
+     }
    size += evas_common_rgba_image_scalecache_usage_get(&im->cache_entry);
    return size;
 }
@@ -438,6 +466,9 @@ evas_common_image_colorspace_normalize(RGBA_Image *im)
       case EVAS_COLORSPACE_ARGB8888:
        if (im->image.data != im->cs.data)
          {
+#ifdef EVAS_CSERVE
+             if (((Image_Entry *)im)->data1) evas_cserve_image_free(im);
+#endif             
             if (!im->image.no_free) free(im->image.data);
             im->image.data = im->cs.data;
             im->cs.no_free = im->image.no_free;
index 3167ee1..e63623b 100644 (file)
@@ -471,6 +471,7 @@ struct _Image_Entry
 #endif
 
    Image_Entry_Flags      flags;
+   void                  *data1, *data2;
 };
 
 struct _Engine_Image_Entry