| |
| if (GC_SUPPORT) { |
| EXPORTED_FUNCTIONS['_calloc'] = 1; |
| EXPORTED_FUNCTIONS['_realloc'] = 1; |
| |
| var LibraryGC = { |
| $GC__deps: ['sbrk', 'realloc', 'calloc'], |
| $GC: { |
| ALLOCATIONS_TO_GC: 1*1024*1024, |
| |
| sizes: {}, // if in this map, then a live allocated object. this is iterable |
| scannables: {}, |
| uncollectables: {}, |
| finalizers: {}, |
| finalizerArgs: {}, |
| |
| totalAllocations: 0, // bytes of all currently active objects |
| recentAllocations: 0, // bytes allocated since last gc. ignores free()s |
| |
| init: function() { |
| assert(!GC.initted); |
| GC.initted = true; |
| |
| _GC_finalize_on_demand = _malloc(4); setValue(_GC_finalize_on_demand, 0, 'i32') |
| _GC_java_finalization = _malloc(4); setValue(_GC_java_finalization, 0, 'i32'); |
| _GC_finalizer_notifier = _malloc(4); setValue(_GC_finalizer_notifier, 0, 'i32'); |
| |
| if (ENVIRONMENT_IS_WEB) { |
| setInterval(function() { |
| GC.maybeCollect(); |
| }, 1000); |
| } else { |
| #if ASSERTIONS |
| Module.print('No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually'); |
| #endif |
| } |
| }, |
| |
| malloc: function(bytes, clear, scannable, collectable) { |
| if (!bytes) return 0; |
| var ptr; |
| if (clear) { |
| ptr = _calloc(1, bytes); |
| } else { |
| ptr = _malloc(bytes); |
| } |
| if (!collectable) { |
| GC.uncollectables[ptr] = true; |
| } |
| GC.scannables[ptr] = scannable; |
| GC.sizes[ptr] = bytes; |
| GC.totalAllocations += bytes; |
| GC.recentAllocations += bytes; |
| 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) { |
| Runtime.getFuncWrapper(finalizer, 'vii')(ptr, GC.finalizerArgs[ptr]); |
| GC.finalizers[ptr] = 0; |
| } |
| _free(ptr); |
| GC.totalAllocations -= GC.sizes[ptr]; |
| delete GC.sizes[ptr]; |
| }, |
| |
| registerFinalizer: function(ptr, func, arg, oldFunc, oldArg) { |
| var finalizer = GC.finalizers[ptr]; |
| if (finalizer) { |
| if (oldFunc) { |
| {{{ makeSetValue('oldFunc', '0', 'finalizer', 'i32') }}}; |
| } |
| if (oldArg) { |
| {{{ makeSetValue('oldArg', '0', 'GC.finalizerArgs[ptr]', 'i32') }}}; |
| } |
| } |
| GC.finalizers[ptr] = func; |
| GC.finalizerArgs[ptr] = arg; |
| }, |
| |
| getHeapSize: function() { |
| return GC.totalAllocations; |
| }, |
| |
| maybeCollect: function() { |
| if (GC.needCollect()) GC.collect(); |
| }, |
| |
| needCollect: function() { |
| return GC.recentAllocations >= GC.ALLOCATIONS_TO_GC; // TODO: time, etc. |
| }, |
| |
| collect: function() { |
| GC.prep(); |
| GC.mark(); |
| GC.sweep(); |
| GC.recentAllocations = 0; |
| }, |
| |
| scan: function(start, end) { // scans a memory region and adds new reachable objects |
| for (var i = start; i < end; i += {{{ Runtime.getNativeTypeSize('void*') }}}) { |
| var ptr = {{{ makeGetValue('i', '0', 'void*') }}}; |
| if (GC.sizes[ptr] && !GC.reachable[ptr]) { |
| GC.reachable[ptr] = 1; |
| if (GC.scannables[ptr]) { |
| GC.reachableList.push(ptr); |
| } |
| } |
| } |
| }, |
| |
| prep: function() { // Clear reachables and scan for roots |
| GC.reachable = {}; // 1 if reachable. XXX |
| GC.reachableList = []; // each reachable is added once to this. XXX |
| // static data areas |
| var staticStart = STACK_MAX; |
| var staticEnd = _sbrk.DYNAMIC_START || STATICTOP; // after DYNAMIC_START, sbrk manages it (but it might not exist yet) |
| GC.scan(staticStart, staticEnd); |
| // TODO: scan stack and registers. Currently we assume we run from a timeout or such, so no stack/regs |
| // stack: STACK_ROOT to STACKTOP |
| // registers: call scanners |
| }, |
| |
| mark: function() { // mark all reachable from roots |
| for (var i = 0; i < GC.reachableList.length; i++) { // note that the list length changes as we push more |
| var ptr = GC.reachableList[i]; |
| GC.scan(ptr, ptr + GC.sizes[ptr]); |
| } |
| }, |
| |
| sweep: function() { // traverse all objects and free all unreachable |
| var freeList = []; |
| for (var ptr in GC.sizes) { |
| if (!GC.reachable[ptr] && !GC.uncollectables[ptr]) { |
| freeList.push(parseInt(ptr)); |
| } |
| } |
| for (var i = 0; i < freeList.length; i++) { |
| GC.free(freeList[i]); |
| } |
| } |
| }, |
| |
| GC_INIT__deps: ['$GC'], |
| GC_INIT: function() { |
| GC.init(); |
| }, |
| |
| GC_MALLOC__deps: ['$GC'], |
| GC_MALLOC: function(bytes) { |
| return GC.malloc(bytes, true, true, true); |
| }, |
| |
| GC_MALLOC_ATOMIC__deps: ['$GC'], |
| GC_MALLOC_ATOMIC: function(bytes) { |
| 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'], |
| GC_FREE: function(ptr) { |
| GC.free(ptr); |
| }, |
| |
| GC_REGISTER_FINALIZER_NO_ORDER__deps: ['$GC'], |
| GC_REGISTER_FINALIZER_NO_ORDER: function(ptr, func, arg, old_func, old_arg) { |
| 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(); |
| }, |
| |
| GC_FORCE_COLLECT__deps: ['$GC'], |
| GC_FORCE_COLLECT: function() { |
| GC.collect(); |
| }, |
| |
| GC_finalize_on_demand: 0, |
| GC_java_finalization: 0, |
| GC_finalizer_notifier: 0, |
| |
| GC_enable_incremental: function(){}, |
| }; |
| |
| mergeInto(LibraryManager.library, LibraryGC); |
| } |
| |