-
-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support a GDExtension that intentionally doesn't work with Windows FreeLibrary
#8772
Comments
Thanks! I don't understand why step 3i fails:
At this point, I think the original Godot instance should be fully shutdown, and so even if you can't And if that's not the case, I think trying to figure out why that is, and if we can fix it, would be better than the solution proposed here of making more temporary copies of the DLL. (My concern with making more copies is that we'd need to make sure they get cleaned up at some point.) |
I haven't investigated the Godot editor's reload mechanism, but I assume that it's impossible for the old Godot to completely shut down first, because then who could launch the new editor? I suspect it shuts down (close/dispose/deconstruct/etc.) as much as possible, then launches the new editor, then closes itself. Because the DLL is held open by the old process itself, this precise order matters. If I'm right about that restart mechanism, then reload could be changed to a three part mechanism as a more general workaround for this class of problem (and maybe there are some other bugs in this class of issue that it would help with too?):
FWIW, I addressed this in the issue and it doesn't concern me. Currently Godot fails to clean up the copy when it fails in this way, so at worst, it's only as bad as it used to be. (Well, maybe more than just one file, if something unrelated also happens that triggers this behavior. But I haven't looked deeply into reloads and I'm concerned that limiting it to two files would hamper some other situations, so I'd rather not do something like that unless there's good reason.) |
I found this PR--it seems GDExtension reloading without restarting the editor is meant to work: This means that changes to the editor restart process wouldn't be enough to address this proposal, because the editor shouldn't have to restart. There needs to be a way to handle having multiple locked copies of the DLLs files active at the same time (like the suggestion). A more direct change to the proposal to address the cleanup concern could be having an extra |
Unfortunately, making multiple copies won't help in this case. If we can't |
I did a little research into this. It looks like you're correct with regard to the order, and needing to have an existing process around to start the new process. I'm not that familiar with Windows, so I was imagining we'd have something like Linux's On the Godot side, it's |
Unfortunate about loading it multiple times not working. That works fine for my use, I was only shooting for full support so the value of the proposal is a bit stronger. 😅 Another approach came to mind that's maybe simpler than a generalized trampoline: a new editor arg |
That's an interesting idea! Unless we want to implement that for every platform, we'd need to come up with a nice way to isolate it in the Windows platform code somehow. And, unfortunately, I'm not personally adept enough with Windows to come up with an implementation for that. |
I thought about this some more, and I'm concerned about a race that could happen if the OS schedules things a certain way:
I figure signaling of some kind is needed to avoid the race. A very simple signal would be for the new process to actually be responsible for killing the old process. The arg would be I implemented this new approach at 31/godot@12c46cf. (Not ready for PR, but want to know if the approach is ok in general.) On my machine with my simple GDExtension, it seems to work. I implemented it in a way that it should do nothing on non-Windows platforms, because it's not necessary there. |
I polished it up a bit and submitted it as a PR to |
@31 I don't like the idea of killing the process. It really doesn't solve the problem you describe, that is: a new unrelated process can still start with the same PID, but now we're killing it, which seems like a worse outcome. What if it waited for the process, but had a reasonable timeout, where it would give up waiting if it took longer than, say, 5 seconds? |
(Replied at godotengine/godot#87165 (comment).) |
Describe the project you are working on
I'm working on a project where I want to share some Go (golang) code between client and server, including some existing complicated libraries written in Go. Go lets me build a
.dll
/.so
/...The Go and cgo toolchains are also more intuitive/familiar to me than C/C++ ones (particularly SCons), so I may choose to write a GDExtension using Go even if it would be possible to implement in C++.
Describe the problem or limitation you are having in your project
When I write my GDExtension and hit "Reload Current Project" in the Godot editor, the GDExtension fails to start. This is because when you build a DLL with Go, it doesn't support FreeLibrary: golang/go#11100. Supporting it isn't trivial and there don't appear to be plans to do so.
The sequence of events looks like this:
GDExtension::open_library
copies mylibgdext.windows.x86_64.dll
to~libgdext.windows.x86_64.dll
.(Proposal: Copy GDExtension DLLs when starting the editor on Windows so the original DLL isn't locked and can be rebuilt godot-cpp#955)
~libgdext.windows.x86_64.dll
.GDExtension::close_library
callsFreeLibrary
on~libgdext.windows.x86_64.dll
, which succeeds but does nothing with my Go DLL.~libgdext.windows.x86_64.dll
, which fails because the file is still locked. It ignores the error, and we continue:GDExtension::open_library
tries to delete~libgdext.windows.x86_64.dll
again. It fails, but ignores the error.libgdext.windows.x86_64.dll
to~libgdext.windows.x86_64.dll
. This fails because~libgdext.windows.x86_64.dll
is still locked, and the GDExtension fails to load in the new Godot instance.Describe the feature / enhancement and how it helps to overcome the problem or limitation
Make the new instance kill the old instance and wait until it's terminated before attempting GDExtension initialization.
Old approach, ~1foo.dll, ~2foo.dll, ... (click to open)
If
~foo.dll
can't be deleted, try~1foo.dll
,~2foo.dll
, and so on, until the file can either be deleted or doesn't already exist.This won't change anything for normal GDExtensions: their filenames and cleanup will continue to behave the same. It only kicks in with GDExtension binaries that "stick" when opened, and lets them work with Godot editor reloads.
I don't know if anything above
~1foo.dll
would actually occur. I'm not too familiar with other ways that a GDExtension might be reloaded. (It seems to me that editor reload will go between~1foo.dll
and~foo.dll
in alternative launches, because after the old process is totally exited, the old file can be deleted.)From what I know of GDExtension, even if there is a risk of some additional garbage, I believe it would be tied to some intentional user input, so the number of garbage files would be relatively bounded over time. It would also only happen for devs (not exported games), further narrowing impact. At least, this risk is definitely acceptable on my own project to use Go DLLs. 😅
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
FreeLibrary
support godot#87165Old approach, ~1foo.dll, ~2foo.dll, ... (click to open)
This change to
GDExtension::open_library
(https://github.com/godotengine/godot/blob/13a0d6e9b253654f5cc2a44f3d0b3cae10440443/core/extension/gdextension.cpp#L692-L705) implements the suggestion, although my C++ Godot API skills are probably not the best:(As an aside: it would also be nice if the GDExtension methods handled their errors more consistently! It took some time adding more error handling to dig down to the first error that actually occurred and caused this series of events.)
If this enhancement will not be used often, can it be worked around with a few lines of script?
No external workaround.
Is there a reason why this should be core and not an add-on in the asset library?
No external workaround.
The text was updated successfully, but these errors were encountered: