2009-05-18 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / utils / mono-mmap.c
index 91bc0b6d90fc94f6441c8cb41709c27af7aa57f9..7976ce15cda84b1512333c8e06dcf718e1bad1b2 100644 (file)
@@ -1,3 +1,12 @@
+/*
+ * mono-mmap.c: Support for mapping code into the process address space
+ *
+ * Author:
+ *   Mono Team (mono-list@lists.ximian.com)
+ *
+ * Copyright 2001-2008 Novell, Inc.
+ */
+
 #include "config.h"
 
 #ifdef PLATFORM_WIN32
 #include <io.h>
 #else
 #include <sys/types.h>
+#if HAVE_SYS_STAT_H
 #include <sys/stat.h>
+#endif
+#if HAVE_SYS_MMAN_H
 #include <sys/mman.h>
+#endif
 #include <fcntl.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
 #endif
 
 #include "mono-mmap.h"
+#include "mono-proclib.h"
 
 #ifndef MAP_ANONYMOUS
 #define MAP_ANONYMOUS MAP_ANON
 #define MAP_32BIT 0
 #endif
 
+typedef struct {
+       int size;
+       int pid;
+       int reserved;
+       short stats_start;
+       short stats_end;
+} SAreaHeader;
+
+static void* malloced_shared_area = NULL;
+
+static void*
+malloc_shared_area (int pid)
+{
+       int size = mono_pagesize ();
+       SAreaHeader *sarea = g_malloc0 (size);
+       sarea->size = size;
+       sarea->pid = pid;
+       sarea->stats_start = sizeof (SAreaHeader);
+       sarea->stats_end = sizeof (SAreaHeader);
+
+       return sarea;
+}
+
 #ifdef PLATFORM_WIN32
 
 int
@@ -71,7 +110,11 @@ mono_valloc (void *addr, size_t length, int flags)
 int
 mono_vfree (void *addr, size_t length)
 {
-       return VirtualFree (addr, length, MEM_RELEASE) == 0;
+       int res = VirtualFree (addr, 0, MEM_RELEASE);
+
+       g_assert (res);
+
+       return 0;
 }
 
 void*
@@ -132,7 +175,39 @@ mono_mprotect (void *addr, size_t length, int flags)
        return VirtualProtect (addr, length, prot, &oldprot) == 0;
 }
 
-#elif defined(HAVE_MMAP)
+void*
+mono_shared_area (void)
+{
+       /* get the pid here */
+       return malloc_shared_area (0);
+}
+
+void
+mono_shared_area_remove (void)
+{
+       if (malloced_shared_area)
+               g_free (malloced_shared_area);
+}
+
+void*
+mono_shared_area_for_pid (void *pid)
+{
+       return NULL;
+}
+
+void
+mono_shared_area_unload (void *area)
+{
+}
+
+int
+mono_shared_area_instances (void **array, int count)
+{
+       return 0;
+}
+
+#else
+#if defined(HAVE_MMAP)
 
 /**
  * mono_pagesize:
@@ -334,40 +409,202 @@ mono_vfree (void *addr, size_t length)
        return 0;
 }
 
+int
+mono_mprotect (void *addr, size_t length, int flags)
+{
+       if (flags & MONO_MMAP_DISCARD) {
+               memset (addr, 0, length);
+       }
+       return 0;
+}
+#endif // HAVE_MMAP
+
+#if defined(HAVE_SHM_OPEN)
+
+static int
+mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
+{
+       int i, j = 0;
+       int num;
+       void *data;
+       gpointer *processes = mono_process_list (&num);
+       for (i = 0; i < num; ++i) {
+               data = mono_shared_area_for_pid (processes [i]);
+               if (!data)
+                       continue;
+               mono_shared_area_unload (data);
+               if (!cleanup) {
+                       if (j < count)
+                               array [j++] = processes [i];
+                       else
+                               break;
+               }
+       }
+       g_free (processes);
+       return j;
+}
+
+static int
+mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
+{
+       const char *name;
+       int i = 0;
+       int curpid = getpid ();
+       GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
+       if (!dir)
+               return mono_shared_area_instances_slow (array, count, cleanup);
+       while ((name = g_dir_read_name (dir))) {
+               int pid;
+               char *nend;
+               if (strncmp (name, "mono.", 5))
+                       continue;
+               pid = strtol (name + 5, &nend, 10);
+               if (pid <= 0 || nend == name + 5 || *nend)
+                       continue;
+               if (!cleanup) {
+                       if (i < count)
+                               array [i++] = GINT_TO_POINTER (pid);
+                       else
+                               break;
+               }
+               if (curpid != pid && kill (pid, SIGCONT) == -1 && errno == ESRCH) {
+                       char buf [128];
+                       g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
+                       shm_unlink (buf);
+               }
+       }
+       g_dir_close (dir);
+       return i;
+}
+
 void*
-mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
+mono_shared_area (void)
+{
+       int fd;
+       int pid = getpid ();
+       /* we should allow the user to configure the size */
+       int size = mono_pagesize ();
+       char buf [128];
+       void *res;
+       SAreaHeader *header;
+
+       /* perform cleanup of segments left over from dead processes */
+       mono_shared_area_instances_helper (NULL, 0, TRUE);
+
+       g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
+
+       fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
+       if (fd == -1 && errno == EEXIST) {
+               /* leftover */
+               shm_unlink (buf);
+               fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
+       }
+       /* in case of failure we try to return a memory area anyway,
+        * even if it means the data can't be read by other processes
+        */
+       if (fd == -1)
+               return malloc_shared_area (pid);
+       if (ftruncate (fd, size) != 0) {
+               shm_unlink (buf);
+               close (fd);
+       }
+       res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+       if (res == MAP_FAILED) {
+               shm_unlink (buf);
+               close (fd);
+               return malloc_shared_area (pid);
+       }
+       /* we don't need the file descriptor anymore */
+       close (fd);
+       header = res;
+       header->size = size;
+       header->pid = pid;
+       header->stats_start = sizeof (SAreaHeader);
+       header->stats_end = sizeof (SAreaHeader);
+
+       atexit (mono_shared_area_remove);
+       return res;
+}
+
+void
+mono_shared_area_remove (void)
+{
+       char buf [128];
+       g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
+       shm_unlink (buf);
+       if (malloced_shared_area)
+               g_free (malloced_shared_area);
+}
+
+void*
+mono_shared_area_for_pid (void *pid)
 {
-       guint64 cur_offset;
-       size_t bytes_read;
-       void *ptr = malloc (length);
-       if (!ptr)
+       int fd;
+       /* we should allow the user to configure the size */
+       int size = mono_pagesize ();
+       char buf [128];
+       void *res;
+
+       g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
+
+       fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
+       if (fd == -1)
                return NULL;
-       cur_offset = lseek (fd, 0, SEEK_CUR);
-       if (lseek (fd, offset, SEEK_SET) != offset) {
-               free (ptr);
+       res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+       if (res == MAP_FAILED) {
+               close (fd);
                return NULL;
        }
-       bytes_read = read (fd, ptr, length);
-       lseek (fd, cur_offset, SEEK_SET);
-       *ret_handle = NULL;
-       return ptr;
+       /* FIXME: validate the area */
+       /* we don't need the file descriptor anymore */
+       close (fd);
+       return res;
+}
+
+void
+mono_shared_area_unload  (void *area)
+{
+       /* FIXME: currently we load only a page */
+       munmap (area, mono_pagesize ());
 }
 
 int
-mono_file_unmap (void *addr, void *handle)
+mono_shared_area_instances (void **array, int count)
+{
+       return mono_shared_area_instances_helper (array, count, FALSE);
+}
+#else
+void*
+mono_shared_area (void)
+{
+       return malloc_shared_area (getpid ());
+}
+
+void
+mono_shared_area_remove (void)
+{
+       if (malloced_shared_area)
+               g_free (malloced_shared_area);
+       malloced_shared_area = NULL;
+}
+
+void*
+mono_shared_area_for_pid (void *pid)
+{
+       return NULL;
+}
+
+void
+mono_shared_area_unload (void *area)
 {
-       free (addr);
-       return 0;
 }
 
 int
-mono_mprotect (void *addr, size_t length, int flags)
+mono_shared_area_instances (void **array, int count)
 {
-       if (flags & MONO_MMAP_DISCARD) {
-               memset (addr, 0, length);
-       }
        return 0;
 }
 
-#endif
+#endif // HAVE_SHM_OPEN
 
+#endif // PLATFORM_WIN32