Create a constructor function that serves as the type for an externally ref-counted
object type. Erc instances can then be created for manually managing memory for
external objects, typically through FFI.
Since JS is garbage collected and has no way to enforce certain memory management,
the programmer must ensure that Erc instances are handled correctly!!!
// 2 functions are required to create an Erc type: // free: free the underlying value (essentially decrementing the ref count) // addRef: increment the ref count and return the new reference
// here, assume `number` is the JS type used to represent the external object // for example, this can be a pointer to a C++ object declarefunctionfreeFoo(obj: number) =>void; declarefunctionaddRefFoo(obj: number) =>number;
// assume another function to create (allocate) the object declarefunctioncreateFoo(): number;
// The recommended way is to create a unique symbol for tracking // the external type, you can also use a string literal constFoo = Symbol("Foo"); typeFoo = typeofFoo;
// now, we can create the Erc type constmakeFooErc = makeErcType({ marker:Foo, free:freeFoo, addRef:addRefFoo, });
// and create Erc instances constmyFoo: Erc<Foo> = makeFooErc(createFoo());
Using Erc (strong reference)
Each Erc instance is a strong reference, corresponding to some ref-counted
object externally. Therefore, owner of the Erc instance should
not expose the Erc instance to others (for example, returning it from a function),
since it will lead to memory leak or double free.
// create a foo instance externally, and wrap it with Erc constmyFoo = makeFooErc(createFoo());
// if ownership of myFoo should be returned to external, use `take()` // this will make myFoo empty, and doSomethingWithFooExternally should free it doSomethingWithFooExternally(myFoo.take());
// you can also free it directly foo.free();
Using ErcRef (weak reference)
Calling getWeak on an Erc will return a weak reference that has the same
inner value. The weak reference is safe to be passed around and copied.
constmyFooWeak = myFoo.getWeak();
The weak references are tracked by the Erc instance.
In the example above, when myFoo is freed, all weak references created by
getWeak will be invalidated. If some other code kept the inner value of
the weak reference, it will become a dangling pointer.
To avoid this, getStrong can be used to create a strong reference if
the weak reference is still valid, to ensure that the underlying object
is never freed while still in use
constmyFooWeak = myFoo.getWeak();
// assume we have some async code that needs to use myFoo declareasyncfunctiondoSomethingWithFoo(foo: FooErcRef): Promise<void>;
// Below is BAD! awaitdoSomethingWithFoo(myFooWeak); // Reason: doSomethingWithFoo is async, so it's possible that // myFooWeak is invalidated when it's still needed. If the implementation // does not check for that, it could be deferencing a dangling pointer // (of course, it could actually be fine depending on the implementation of doSomethingWithFoo)
// Recommendation is to use strong reference for async operations constmyFooStrong = myFooWeak.getStrong(); awaitdoSomethingWithFoo(myFooStrong.getWeak()); // will never be freed while awaiting // now we free myFooStrong.free();
Assigning to Erc
Each Erc instance should only ever have one external reference. So you should not
assign to an Erc variable directly:
// DO NOT DO THIS letmyFoo = makeFooErc(createFoo()); myFoo = makeFooErc(createFoo()); // previous Erc is overriden without proper clean up
If you want to attach a new value to an existing Erc, use the assign method:
constmyFoo = makeFooErc(createFoo()); myFoo.assign(createFoo()); // previous Erc is freed, and the new one is assigned myFoo.free(); // new one is freed
The example above does not cause leaks, since the previous Erc is freed
However, if you call assign with the value of another Erc, it will cause memory
issues:
// DO NOT DO THIS constmyFoo1 = makeFooErc(createFoo()); constmyFoo2 = makeFooErc(createFoo()); myFoo1.assign(myFoo2.value); // myFoo1 is freed, and myFoo2 is assigned // BAD: now both myFoo1 and myFoo2 references the same object, but the ref count is 1 myFoo1.free(); // no issue here, object is freed, but myFoo2 now holds a dangling pointer myFoo2.free(); // double free!
// The correct way to do this is to use `take`: constmyFoo1 = makeFooErc(createFoo()); constmyFoo2 = makeFooErc(createFoo()); myFoo1.assign(myFoo2.take()); // myFoo1 is freed, and myFoo2 is assigned, myFoo2 is empty myFoo1.free(); // no issue here, object is freed // myFoo2 is empty, so calling free() has no effect
Assign also works if both Erc are 2 references of the same object:
constmyFoo1 = makeFooErc(createFoo()); // ref count is 1 constmyFoo2 = myFoo1.getStrong(); // ref count is 2 myFoo1.assign(myFoo2.take()); // frees old value, ref count is 1
// This also works: constmyFoo1 = makeFooErc(createFoo()); // ref count is 1 myFoo1.assign(myFoo1.take()); // take() makes myFoo1 empty, so assign() doesn't free it
// DO NOT DO THIS: myFoo1.assign(myFoo1.value); // this will free the value since ref count is 0, and result in a dangling pointer
Create a constructor function that serves as the type for an externally ref-counted object type. Erc instances can then be created for manually managing memory for external objects, typically through FFI.
Since JS is garbage collected and has no way to enforce certain memory management, the programmer must ensure that Erc instances are handled correctly!!!
Defining Erc type
Using Erc (strong reference)
Each
Erc
instance is a strong reference, corresponding to some ref-counted object externally. Therefore, owner of theErc
instance should not expose theErc
instance to others (for example, returning it from a function), since it will lead to memory leak or double free.Using ErcRef (weak reference)
Calling
getWeak
on anErc
will return a weak reference that has the same inner value. The weak reference is safe to be passed around and copied.The weak references are tracked by the
Erc
instance. In the example above, whenmyFoo
is freed, all weak references created bygetWeak
will be invalidated. If some other code kept the inner value of the weak reference, it will become a dangling pointer.To avoid this,
getStrong
can be used to create a strong reference if the weak reference is still valid, to ensure that the underlying object is never freed while still in useAssigning to Erc
Each
Erc
instance should only ever have one external reference. So you should not assign to anErc
variable directly:If you want to attach a new value to an existing
Erc
, use theassign
method:The example above does not cause leaks, since the previous Erc is freed
However, if you call assign with the value of another
Erc
, it will cause memory issues:Assign also works if both
Erc
are 2 references of the same object: