[next] Prevent slot int variable from being GCed. #219
Loading…
Reference in New Issue
No description provided.
Delete Branch "next-fix-HandleList-Track-gc-issue"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Before this change, there were no users of
slot
int variable in the Go world (just a pointer to it that ended up in C world only), so Go's garbage collector would free it and its value could not retrieved later (once a pointer to it comes back to Go world from C world).Keep a pointer to it in the Go world so that does not happen.
Fixes #218. See that issue for a detailed analysis of the problem, and a reproducible test case (in a package that imports this one).
Please review for correctness and style, feedback is welcome.
There are many, many ways of doing the task of "keeping some reference to
slot
in Go so it's not GCed, including keeping a pointer to it as*int
, orunsafe.Pointer
, orinterface{}
. I've reused theset
map since it seemed appropriate, but a new/different map can be created instead. Let me know if you prefer a different style and I can change it.Style wise, it might be nicer to do
handle := unsafe.Pointer(&slot)
and laterreturn handle
- to avoid doing&slot
in two places in theHandleList.Track
code.There are many, many style variations that all perform the same goal, and I don't know the most idiomatic way to solve this task, so I just went with the smallest diff PR for now. Suggestions for better style are welcome.
Thanks for looking into this. I wonder if replacing return return value to
would work as well. IIRC in go an
int
corresponds to the machine pointer size so we shouldn't have problems with the size (or we could limit it to 32 bits, having over 3 billion objects seems unlikely to be an actual limitation).That's an interesting idea. I just gave it a try. It wouldn't work exactly as you wrote it, because you can't convert from a non-pointer type to
unsafe.Pointer
directly, it results in a compilation error:However, it's possible to force that conversion by going through
uintptr
, so this will compile:However, using that results in a fatal error:
It would be possible to work around that by changing type of the
handler
fromunsafe.Pointer
to an integer type, but from what I can see, that may not be constrained due to libgit2 internals. Specifically,git_remote_callbacks
is defined inlibgit2/include/git2/remote.h
:And
git2go
'sTrack
andUntrack
uses that field:Unless... Is it possible to assign a Go integer type to C's
void*
? I would imagine C would not complain aboutvoid*
having invalid pointers. (That kind of change may need an extra change so that generatedslot
values are never 0.)What do you think?
I don't think it's possible (likely for good reasons, as
void*
will be different size depending on architecture, and Cgo maps C'svoid*
to Go'sunsafe.Pointer
). From Cgo article:With that, I conclude your suggestion is not possible without changes to libgit2's internals, unless you find another place to store the
slot
data that is not defined asvoid*
in C.Ok, it might be possible to allocate a new
int
in C code and have thevoid* payload
point to it... (If you're willing to go that route.)Let me know your thoughts before I continue forward looking for a different solution. There are many possibilities/workarounds. An alternative is to stick with the suggested solution in this PR.
So is Go's int, but we can work around that by using a
int32
variable, which will fit in any arch Go runs on. That has no problem fitting in a 32-bit or 64-bit pointer type.The issue is not the size of data, but the type.
The
payload
field in the C struct is defined with typevoid*
. Cgo maps that tounsafe.Pointer
. You can't assign different types in Go without a convertion, but when you try convert the slot, anint32
, intounsafe.Pointer
, Go will panic with:Can you please elaborate what you're suggesting?
void *
is the only type which makes sense for libgit2 to take, as it can't know what each caller is going to need. If Go can't handle the conversion, it's not a problem with libgit2.But anyway, let's merge this as it solves the issue and we can figure out if we want to do something else later.
I was going to suggest doing exactly that, sounds good. Thanks for merging.