Add new API to copy managed memory.
authorRodrigo Kumpera <kumpera@gmail.com>
Wed, 18 May 2011 19:04:24 +0000 (16:04 -0300)
committerRodrigo Kumpera <kumpera@gmail.com>
Thu, 19 May 2011 19:43:35 +0000 (16:43 -0300)
* gc.c: Add mono_gc_bzero and mono_gc_memmove. Those
functions do word sized load/stores so they are GC safe.

Those oddly seeing functions are required as libc equivalents
don't ensure that copies and done at least in word granularity
so we can end up pausing a thread while is has, for example,
zeroed just the first byte of a managed reference, causing the
GC to see an invalid pointer.

This happens on OSX but can potentially happen on other systems.

mono/metadata/gc-internal.h
mono/metadata/gc.c

index ac2320d9b6a6170972c61e689b030aaf09c6b4bf..608a9b4622a29ad0086b58a01540fd5cb7ecf646 100644 (file)
@@ -366,5 +366,9 @@ gboolean mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj
 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved) MONO_INTERNAL;
 #endif
 
+void mono_gc_bzero (void *dest, size_t size) MONO_INTERNAL;
+void mono_gc_memmove (void *dest, const void *src, size_t size) MONO_INTERNAL;
+
+
 #endif /* __MONO_METADATA_GC_INTERNAL_H__ */
 
index 3a0a1ecda0f14a0f56c6fbf869d4759eeb4c51eb..90e5979b508bbbd4198e8ba2b60ffce28bf163eb 100644 (file)
@@ -1496,3 +1496,98 @@ mono_gc_reference_queue_free (MonoReferenceQueue *queue)
        queue->should_be_deleted = TRUE;
 }
 
+#define unaligned_bytes(ptr) (((size_t)ptr) & (sizeof (void*) - 1))
+#define aligned_end(ptr) ((void*)(((size_t)ptr) & ~(sizeof (void*) - 1)))
+
+/**
+ * Zero @size bytes starting at @dest.
+ *
+ * Use this to zero memory that can hold managed pointers.
+ *
+ * FIXME borrow faster code from some BSD libc or bionic
+ */
+void
+mono_gc_bzero (void *dest, size_t size)
+{
+       char *p = (char*)dest;
+       char *end = p + size;
+       char *align_end = p + unaligned_bytes (p);
+       char *word_end;
+
+       while (p < align_end)
+               *p++ = 0;
+
+       word_end = aligned_end (end);
+       while (p < word_end) {
+               *((void**)p) = NULL;
+               p += sizeof (void*);
+       }
+
+       while (p < end)
+               *p++ = 0;
+}
+
+
+/**
+ * Move @size bytes from @src to @dest.
+ * size MUST be a multiple of sizeof (gpointer)
+ *
+ * FIXME borrow faster code from some BSD libc or bionic
+ */
+void
+mono_gc_memmove (void *dest, const void *src, size_t size)
+{
+       void **p, * const *s;
+
+       p = dest;
+       s = src;
+
+       /*
+        * A bit of explanation on why we align only dest before doing word copies.
+        * Pointers to managed objects must always be stored in word aligned addresses, so
+        * even if dest is misaligned, src will be by the same amount - this ensure proper atomicity of reads.
+        */
+
+       /*potentially overlap, do a backward copy*/
+       if (dest > src) {
+               char *p = (char*)dest + size;
+               char *s = (char*)src + size;
+               char *start = (char*)dest;
+               char *align_end = aligned_end (p);
+               char *word_start;
+
+               while (p >= align_end)
+                       *--p = *--s;
+
+               word_start = start + unaligned_bytes (start);
+               while (p >= word_start) {
+                       p -= sizeof (void*);
+                       s -= sizeof (void*);                    
+                       *((void**)p) = *((void**)s);
+               }
+
+               while (p > start)
+                       *--p = *--s;
+       } else {
+               char *p = (char*)dest;
+               char *s = (char*)src;
+               char *end = p + size;
+               char *align_end = p + unaligned_bytes (p);
+               char *word_end;
+
+               while (p < align_end)
+                       *p++ = *s++;
+
+               word_end = aligned_end (end);
+               while (p < word_end) {
+                       *((void**)p) = *((void**)s);
+                       p += sizeof (void*);
+                       s += sizeof (void*);
+               }
+
+               while (p < end)
+                       *p++ = *s++;
+       }
+}
+
+