In other words, when compiling with --gc:orc or --gc:orc (Nim version 2.0.0), what are the effects of calls to GC_ref, GC_unref, GC_fullCollect, etc.?
I ask this because I'm working to "modernize" some existing code, which uses such calls, to Nim 2.0.0 and orc (and maybe arc/atomicArc in the future), with no explicit use of a Garbage Collector.
If the calls have no effect (i.e. if they are essentially no-ops) then I can simply remove them. Otherwise I need to know what the effects are, so that I can adjust my code changes accordingly.
ORC maps GC_ref/unref/fullCollect to their ORC implementations. These calls are usually still required and not deprecated.
The biggest difference is that strings and seqs have no refcount and so GC_ref/unref are not available. You then need to use a different approach with moving things around or as a last resort ref string and the like.
But the good news is that modernizing the code base might be simpler than you think.
ORC maps GC_ref/unref/fullCollect to their ORC implementations.
I didn't understand exactly what this means, so I performed some experiments to see what happens for ORC/ARC when various combinations of GC_ref and GC_unref are called on a (non-cyclic) ref variable pointing to the heap.
In summary, calling GC_ref seems to have the same effect as incrementing the reference count, and calling GC_unref seems to have the same effect as decrementing the reference count. This is the case for both ORC and ARC.
So calling GC_ref on a ref variable without a balancing call to GC_unref will cause a memory/resource leak.
The consequence is more serious in the case where GC_unref is called on a variable without a previous corresponding call to GC_ref. Consider the following example.
type TestObj = object
name: string
proc `=destroy`(x: TestObj) =
echo "Entered destructor for ", x.name
proc newTestObj(name: string): ref TestObj =
result = TestObj.new()
result.name = name
echo "Creating reference #1"
let testobj1 = newTestObj("#1")
echo "Calling GC_unref on reference #1"
GC_unref(testobj1) # Destructor is called
echo "Calling GC_unref again on reference #1"
GC_unref(testobj1) # Destructor is called again
echo "... Done"
# Destructor is called yet again when the variable goes out of scope
The lesson I get from the above is that, unless care is taken to properly balance GC_ref and GC_unref calls in all execution paths for ORC/ARC, using them risks incurring a memory/resource leak at the best or heap corruption at the worst.
These calls are usually still required and not deprecated.
They are certainly necessary in certain circumstances. For example, I recently encountered some interesting code where Nim heap memory and a cdecl proc reference were used to create the equivalent of a closure, which was then sent through a foreign library's Event queue to be executed in a different Nim thread.
But dragons lurk here; be wary lest ye get scorched.
On another note:
...modernizing the code base might be simpler than you think
Yes, that has been my experience. The (fairly extensive) code base I'm working with is a fork of nimx. The challenge isn't modernizing the code as such - it already worked for Nim 2.0.0 and ORC when I started. Most of my activity in this regard has been getting rid of assorted deprecation warnings.
The real challenges for me are more design related. I want to remove the use of nake, eliminate the use of packages that aren't in the Nim package directory, and make its environment more "pure Nim" - use pixie for rendering and fonts, use windy instead of sdl2, etc.
The lesson I get from the above is that, unless care is taken to properly balance GC_ref and GC_unref calls in all execution paths for ORC/ARC, using them risks incurring a memory/resource leak at the best or heap corruption at the worst.
True but that's pretty much the same for the older GCs except that they tend to be a bit more lenient.
use windy instead of sdl2
thats not very partical, unless you're doing it as a "pure nim" challenge.
thats not very practical
Could you please explain your thoughts? I understand that the package has the disclaimer "This library is still in development and is not ready to be used." But it seems to be ready enough to be used by figuro.
it only supports opengl, and on linux only x11 is supported
The sdl2 part of nimx has the same restrictions. And in my fork I removed web-based graphics in order to keep the scope manageable. (I also understand support for Javascript targets was dropped in the origional nimx.)
A restriction that would concern me more is the lack of support for ios and android in windy. But when I balance that against having to take special measures to send events through sdl2's non-Nim queue (see a previous comment), I'm inclined to make the sacrifice.
But when I balance that against having to take special measures to send events through sdl2's non-Nim queue (see a previous comment), I'm inclined to make the sacrifice.
That might not be required though, you can use SDL2's event loop in Nim without any callbacks or threading shenanigans. I did it successfully for the abandoned NimEdit.
you can use SDL2's event loop in Nim without any callbacks or threading shenanigans
Very true. That occurred to me overnight as well, it just requires revamping event handling in my nimx fork - which I was intending to do anyway as part of an attempt to eliminate reference loops in the View class hierarchy.
My remaining concern is the end user - especially a non-technical one. If there are any hoops that one must jump through in order to run an app that uses sdl2, that would be a significant barrier to its use. And hence to any developer wishing to create such an app. I know for Linux that is a very simple thing, but I don't know about other platforms.