Advice on embedding HashLink without access to `stack_top`?
I would like to embed HashLink in a dynamic library that's loaded at runtime; a plugin for a software.
I've been encountering issues that appear to stem from my use of hl_register_thread. I first gave it the address to the highest stack variable in the library's initialization function, but this would result in crashes (my debugger would always point to some random pointer manipulation in GC). Sometimes the crashes would take a bit to occur, but once I started using hl.Gc.flags = hl.Gc.flags | ForceMajor; to force the GC to run, the crashes would always happen instantly.
After countless attempts, it was only after I modified the source code of the software itself, added my own custom variable to main, and gave my extension the address to THAT (just like how HashLink normally works)... things started working without crashes. 👍
Now, of course, I would prefer for users to not have to use a modified version of the software to get things working, but I can't seem to find any other way. Is it impossible to embed HashLink this way? Or is there a smart solution I'm not aware of?
I'm guessing I need a stack pointer that remains valid until HashLink is shut down? The issue is the dynamic library is a collection of functions the software calls upon. The initialize function just runs and finishes, so the stack variables go out of scope while HashLink still needs to be active until the deinitialize function is called.
You could register/unregister the thread around each function?
Oh that is true... given the information I provided I think that would work pretty well. I do have some performance concerns, as I forgot to mention the HashLink functions are expected to be called multiple times per frame.
I also make heavy use of direct static function passing from Haxe to C. Most static Haxe functions are called directly from the software. I guess I could rework it so they're wrapped before being passed over, but it would be a pretty big downer.
I see this has been discussed somewhat before in #492 which just assigns directly to stack_top at the start of the scope that calls into Haxe.
This reminds me of how it's done in hxcpp, which allows for the "stack top" to be cheaply reassigned using SetTopOfStack/hxcpp_set_top_of_stack. I'm extremely unknowledgeable in regards to GC implementations, but since both share the same design this should be the correct way to implement? Of course, hxcpp does appear to have additional checks and managements to prevent pitfalls. It would be delightful if something like this could be added to HashLink.
@byteit101 Did you ever encounter any issues with your simple implementation? I guess as long as the Haxe code is only executed immediately in the scope, it should be okay. But haxe.Timer could probably let Haxe GC executions occur outside of a wrapped scope. Hmmm... maybe the stack_top could be assigned NULL to disable the GC at the end of scope, this appears to be how it works for hxcpp.
I never had any issues as I never kept anything around long. I was using haxe as sort of a WASM-like function call
For the record manually assigning stack top should work, as long as you don't use multiple threads which then becomes more tricky. you would then also have to mark you thread as "blocked" when outside of the HL scope with hl_blocking(true) then hl_blocking(false) when reentering the HL part.