Assemblyscript: Memory leak in `Map#clear`

Created on 3 Jan 2019  路  10Comments  路  Source: AssemblyScript/assemblyscript

https://github.com/AssemblyScript/assemblyscript/blob/master/std/assembly/map.ts#L61

Memory leak here at Map#clear

Proposed fix:

  clear(): void {
    const bucketsSize = INITIAL_CAPACITY * <i32>BUCKET_SIZE;

    // check to see if buckets exists and free it before setting it to a new array buffer
    if (this.buckets != null) memory.free(changetype<usize>(this.buckets));
    this.buckets = new ArrayBuffer(bucketsSize);
    this.bucketsMask = INITIAL_CAPACITY - 1;
    const entriesSize = INITIAL_CAPACITY * <i32>ENTRY_SIZE<K,V>();

    // check to see if entries exists and free it before setting it to a new array buffer
    if (this.entries!= null) memory.free(changetype<usize>(this.entries));
    this.entries = new ArrayBuffer(entriesSize, true);
    this.entriesCapacity = INITIAL_CAPACITY;
    this.entriesOffset = 0;
    this.entriesCount = 0;
  }

Most helpful comment

I don't want to submit a new pull request without making sure I know what I'm talking about. Any ideas?

All 10 comments

I don't want to submit a new pull request without making sure I know what I'm talking about. Any ideas?

Current stdlib anticipates the presence of GC, and ArrayBuffer is a managed object so is cleared by GC, not manually. Have you tried using ITCM with manual gc.collect() calls?

@dcodeIO I'm at a complete loss for how to do this sort of thing. I don't know what ITCM is, or how the gc works. Is there documentation on this?

I'm guessing this is something I need to learn and I don't know where to find this information.

To use the GC, do:

import "allocator/tlsf";
import "collector/itcm";

and whenever you need to free memory (this can only be done manually currently), do

gc.collect();

I've imported the gc as you have suggested, and now I run into a runtime error. It looks like there's a problem within the step() function inside the gc. Is there something I'm missing?

index.js:210 Uncaught (in promise) RuntimeError: function signature mismatch
    at ~lib/collector/itcm/step (wasm-function[37]:172)
    at ~lib/collector/itcm/__gc_allocate (wasm-function[38]:15)
    at ~lib/array/Array<i32>#constructor (wasm-function[77]:53)
    at assembly/primitives/CanvasMap/CanvasRenderingContext2DInitializer#getOptimized (wasm-function[99]:93)
    at assembly/example/init (wasm-function[102]:8)
    at Object.wrap [as init] (http://localhost:1234/demo-src.e31bb0bc.js:4437:12)
    at _callee$ (http://localhost:1234/demo-src.e31bb0bc.js:4727:50)
    at tryCatch (http://localhost:1234/demo-src.e31bb0bc.js:152:40)
    at Generator.invoke [as _invoke] (http://localhost:1234/demo-src.e31bb0bc.js:378:22)
    at Generator.prototype.(anonymous function) [as next] (http://localhost:1234/demo-src.e31bb0bc.js:204:21)

I notice that it's calling ~lib/array/Array<i32>#constructor, and I'm definitely not constructing an Array<i32>. I assume that's because it's trying to initialize an array of references. Are there pitfalls I need to avoid, or is there a bug here?

The source starts here on this line:
https://github.com/jtenner/canvas-as/blob/master/assembly/primitives/CanvasMap.ts#L19

It's not well tested, yet, so there might be issues. What about the array at public used: string[] = new Array<string>(0);?

I don't think so, because the error occurs when I call getOptimized() like it suggests in the stack trace. It's saying that an array is being constructed in a function that isn't even constructing an array:

    at ~lib/array/Array<i32>#constructor (:1234/wasm-function[70]:36)
    at assembly/primitives/CanvasMap/CanvasRenderingContext2DInitializer#getOptimized (:1234/wasm-function[98]:196)
    at assembly/example/init (:1234/wasm-function[101]:8)

Edit: Just to make sure, I commented out the property and the if statements that use the string array. It looks like it's not related.

The untouched wat file shows construction of an Array here:
https://github.com/jtenner/canvas-as/blob/master/build/untouched.wat#L5532

Looks like those might be the culprits.

GC itself doesn't construct managed objects, so it must be something else. For example OptimizedCanvasRenderingContext2D, which is instantiated there, contains a lot of arrays on its private properties that, since OptimizedCanvasRenderingContext2D doesn't have a constructor, are constructed and assigned on new OptimizedCanvasRenderingContext2D.

When it generates an Array<enum> it would be generating an Array<i32> as a consequence. It must be one of those. You are right. I just can't seem to replicate the problem on wasm studio as an example.

Edit: Yes it's inlining the "default" constructor because no constructor exists.

Closing this issue for now as it hasn't received any replies recently. Feel free to reopen it if necessary!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

blooddy picture blooddy  路  3Comments

jarble picture jarble  路  3Comments

torch2424 picture torch2424  路  5Comments

vladimir-tikhonov picture vladimir-tikhonov  路  4Comments

torch2424 picture torch2424  路  3Comments