diff options
-rw-r--r-- | AUTHORS | 1 | ||||
-rwxr-xr-x | emcc | 9 | ||||
-rw-r--r-- | src/library_gc.js | 71 | ||||
-rw-r--r-- | system/include/gc.h | 9 | ||||
-rw-r--r-- | system/include/libc/sys/features.h | 5 | ||||
-rw-r--r-- | system/include/libc/sys/types.h | 6 | ||||
-rwxr-xr-x | tests/runner.py | 19 |
7 files changed, 106 insertions, 14 deletions
@@ -47,6 +47,7 @@ a license to everyone to use it as detailed in LICENSE.) * Michael Riss <Michael.Riss@gmx.de> * Jasper St. Pierre <jstpierre@mecheye.net> * Manuel Schölling <manuel.schoelling@gmx.de> +* Bruce Mitchener, Jr. <bruce.mitchener@gmail.com> @@ -248,6 +248,15 @@ Options that are modified or new in %s include: are compiling to. To run your code, you will need both the .html and the .data. + emcc runs tools/file_packager.py to do the + actual packaging of embedded and preloaded + files. You can run the file packager yourself + if you want, see docs inside that file. You + should then put the output of the file packager + in an emcc --pre-js, so that it executes before + your main compiled code (or run it before in + some other way). + --compression <codec> Compress both the compiled code and embedded/ preloaded files. <codec> should be a triple, diff --git a/src/library_gc.js b/src/library_gc.js index fe4cbf63..083019ca 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -1,14 +1,16 @@ if (GC_SUPPORT) { EXPORTED_FUNCTIONS['_calloc'] = 1; + EXPORTED_FUNCTIONS['_realloc'] = 1; var LibraryGC = { - $GC__deps: ['sbrk'], + $GC__deps: ['sbrk', 'realloc'], $GC: { ALLOCATIONS_TO_GC: 1*1024*1024, sizes: {}, // if in this map, then a live allocated object. this is iterable scannables: {}, + uncollectables: {}, finalizers: {}, finalizerArgs: {}, @@ -34,7 +36,7 @@ if (GC_SUPPORT) { } }, - malloc: function(bytes, clear, scannable) { + malloc: function(bytes, clear, scannable, collectable) { if (!bytes) return 0; var ptr; if (clear) { @@ -42,6 +44,9 @@ if (GC_SUPPORT) { } else { ptr = _malloc(bytes); } + if (!collectable) { + GC.uncollectables[ptr] = true; + } GC.scannables[ptr] = scannable; GC.sizes[ptr] = bytes; GC.totalAllocations += bytes; @@ -49,6 +54,41 @@ if (GC_SUPPORT) { return ptr; }, + realloc: function(ptr, newBytes) { + if (newBytes != 0) { + var oldBytes = GC.sizes[ptr]; + var newPtr = _realloc(ptr, newBytes); + if (newBytes > oldBytes) { + _memset(newPtr + oldBytes, 0, newBytes - oldBytes); + } + delete GC.sizes[ptr]; + GC.sizes[newPtr] = newBytes; + scannable = GC.scannables[ptr]; + delete GC.scannables[ptr]; + GC.scannables[newPtr] = scannable; + var finalizer = GC.finalizers[ptr]; + if (finalizer) { + delete GC.finalizers[ptr]; + GC.finalizers[newPtr] = finalizer; + } + var finalizerArgs = GC.finalizerArgs[ptr]; + if (finalizerArgs) { + delete GC.finalizerArgs[ptr]; + GC.finalizerArgs[newPtr] = finalizerArgs; + } + var uncollectable = GC.uncollectables[ptr]; + if (uncollectable) { + delete GC.uncollectables[ptr]; + GC.uncollectables[newPtr] = true; + } + GC.totalAllocations += (newBytes - oldBytes); + return newPtr; + } else { + GC.free(ptr); + return 0; + } + }, + free: function(ptr) { // does not check if anything refers to it, this is a forced free var finalizer = GC.finalizers[ptr]; if (finalizer) { @@ -56,8 +96,8 @@ if (GC_SUPPORT) { GC.finalizers[ptr] = 0; } _free(ptr); - delete GC.sizes[ptr]; GC.totalAllocations -= GC.sizes[ptr]; + delete GC.sizes[ptr]; }, registerFinalizer: function(ptr, func, arg, oldFunc, oldArg) { @@ -74,6 +114,10 @@ if (GC_SUPPORT) { GC.finalizerArgs[ptr] = arg; }, + getHeapSize: function() { + return GC.totalAllocations; + }, + maybeCollect: function() { if (GC.needCollect()) GC.collect(); }, @@ -123,7 +167,7 @@ if (GC_SUPPORT) { sweep: function() { // traverse all objects and free all unreachable var freeList = []; for (var ptr in GC.sizes) { - if (!GC.reachable[ptr]) { + if (!GC.reachable[ptr] && !GC.uncollectables[ptr]) { freeList.push(parseInt(ptr)); } } @@ -140,12 +184,22 @@ if (GC_SUPPORT) { GC_MALLOC__deps: ['$GC'], GC_MALLOC: function(bytes) { - return GC.malloc(bytes, true, true); + return GC.malloc(bytes, true, true, true); }, GC_MALLOC_ATOMIC__deps: ['$GC'], GC_MALLOC_ATOMIC: function(bytes) { - return GC.malloc(bytes, false, false); + return GC.malloc(bytes, false, false, true); + }, + + GC_MALLOC_UNCOLLECTABLE__deps: ['$GC'], + GC_MALLOC_UNCOLLECTABLE: function(bytes) { + return GC.malloc(bytes, true, true, false); + }, + + GC_REALLOC__deps: ['$GC'], + GC_REALLOC: function(ptr, newBytes) { + return GC.realloc(ptr, newBytes); }, GC_FREE__deps: ['$GC'], @@ -158,6 +212,11 @@ if (GC_SUPPORT) { GC.registerFinalizer(ptr, func, arg, old_func, old_arg); }, + GC_get_heap_size__deps: ['$GC'], + GC_get_heap_size: function() { + return GC.getHeapSize(); + }, + GC_MAYBE_COLLECT__deps: ['$GC'], GC_MAYBE_COLLECT: function() { GC.maybeCollect(); diff --git a/system/include/gc.h b/system/include/gc.h index e0419dcb..8c5a8989 100644 --- a/system/include/gc.h +++ b/system/include/gc.h @@ -29,6 +29,12 @@ void *GC_MALLOC(int bytes); /* Allocate memory for an object that the user promises will not contain pointers. */ void *GC_MALLOC_ATOMIC(int bytes); +/* Allocate memory that might container pointers but that can't be collected. */ +void *GC_MALLOC_UNCOLLECTABLE(int bytes); + +/* Reallocate a GC managed memory block to a new size. */ +void *GC_REALLOC(void *ptr, int newBytes); + /* Explicitly deallocate an object. Dangerous as it forces a free and does not check if the object is reffed. */ void GC_FREE(void *ptr); @@ -36,6 +42,9 @@ void GC_FREE(void *ptr); void GC_REGISTER_FINALIZER_NO_ORDER(void *ptr, void (*func)(void *, void *), void *arg, void *(*old_func)(void *, void *), void *old_arg); +/* Gets the bytes allocated and managed by the GC */ +int GC_get_heap_size(); + /* Non-Boehm additions */ /* Call this once per frame or such, it will collect if necessary */ diff --git a/system/include/libc/sys/features.h b/system/include/libc/sys/features.h index 87a520a0..8c32bf04 100644 --- a/system/include/libc/sys/features.h +++ b/system/include/libc/sys/features.h @@ -26,7 +26,10 @@ extern "C" { #endif #if EMSCRIPTEN -#define _POSIX_REALTIME_SIGNALS 1 +#define _POSIX_REALTIME_SIGNALS 1 +#define _POSIX_THREADS 200112L +#define _UNIX98_THREAD_MUTEX_ATTRIBUTES 1 +#define _POSIX_READER_WRITER_LOCKS 200112L #endif /* RTEMS adheres to POSIX -- 1003.1b with some features from annexes. */ diff --git a/system/include/libc/sys/types.h b/system/include/libc/sys/types.h index e90a74ac..c36f724c 100644 --- a/system/include/libc/sys/types.h +++ b/system/include/libc/sys/types.h @@ -24,12 +24,6 @@ #include <machine/_types.h> -#if EMSCRIPTEN - #define _POSIX_THREADS - #define _UNIX98_THREAD_MUTEX_ATTRIBUTES - #define _POSIX_READER_WRITER_LOCKS -#endif - #if defined(__rtems__) || defined(__XMK__) || defined(EMSCRIPTEN) /* * The following section is RTEMS specific and is needed to more diff --git a/tests/runner.py b/tests/runner.py index ea76aec6..4c8b6379 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -7889,7 +7889,7 @@ def process(filename): int main() { GC_INIT(); - void *local, *local2, *local3, *local4; + void *local, *local2, *local3, *local4, *local5, *local6; // Hold on to global, drop locals @@ -7934,6 +7934,20 @@ def process(filename): GC_REGISTER_FINALIZER_NO_ORDER(local3, finalizer, (void*)3, 0, 0); local4 = GC_MALLOC(12); GC_REGISTER_FINALIZER_NO_ORDER(local4, finalizer, (void*)4, 0, 0); + local5 = GC_MALLOC_UNCOLLECTABLE(12); + // This should never trigger since local5 is uncollectable + GC_REGISTER_FINALIZER_NO_ORDER(local5, finalizer, (void*)5, 0, 0); + + printf("heap size = %d\n", GC_get_heap_size()); + + local4 = GC_REALLOC(local4, 24); + + printf("heap size = %d\n", GC_get_heap_size()); + + local6 = GC_MALLOC(12); + GC_REGISTER_FINALIZER_NO_ORDER(local6, finalizer, (void*)6, 0, 0); + // This should be the same as a free + GC_REALLOC(local6, 0); void **globalData = (void**)global; globalData[0] = local; @@ -7970,6 +7984,9 @@ finalizing2 2 (global == 0) finalizing2 3 (global == 0) * finalizing 0 (global == 1) +heap size = 72 +heap size = 84 +finalizing 6 (global == 0) object scan test test finalizing 4 (global == 0) * |