Skip to content

purego: document that NewCallback consumes one table entry per call - #516

Open
kumagi wants to merge 3 commits into
ebitengine:mainfrom
kumagi:fix/callback-docs
Open

purego: document that NewCallback consumes one table entry per call#516
kumagi wants to merge 3 commits into
ebitengine:mainfrom
kumagi:fix/callback-docs

Conversation

@kumagi

@kumagi kumagi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

What issue is this addressing?

Closes #521

What type of issue is this addressing?

bug

What this PR does | solves

Passing a Go func value to C in a loop (e.g. via RegisterFunc's func arguments, which call NewCallback on every invocation) exhausts the fixed 2000-entry callback table and panics. Function values have no comparable identity available to reflect, so automatic deduplication is not possible; document the pitfall and the workaround of resolving the callback once and reusing the pointer.

Passing a Go func value to C in a loop (e.g. via RegisterFunc's func
arguments, which call NewCallback on every invocation) exhausts the
fixed 2000-entry callback table and panics. Function values have no
comparable identity available to reflect, so automatic deduplication
is not possible; document the pitfall and the workaround of resolving
the callback once and reusing the pointer.
Comment thread syscall_unix.go Outdated
The callback table size is an internal detail and the guarantee we can
actually make on every platform is the one syscall.NewCallback gives on
Windows, so document at least 1024 callbacks and drop the concrete table
size from the NewCallback pitfall note.
@kumagi

kumagi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review! I dropped the internal 2000 figure from the docs. NewCallback now states the same kind of minimum guarantee as syscall.NewCallback on Windows — at least 1024 callbacks can always be created — and the added note explains that every call creates a new callback (including the func arguments handled by RegisterFunc) without exposing the table size. Happy to adjust if a different wording or number is preferred.

@hajimehoshi hajimehoshi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@TotallyGamerJet PTAL

purego.NewCallback delegates to syscall.NewCallback on Windows, which also
creates a new callback for every call, so the loop pitfall is not Unix
specific. Document it there as well and link RegisterFunc from both notes.
@kumagi

kumagi commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Thanks again for the review! The docs no longer mention the internal table size: NewCallback now states the same minimum guarantee as syscall.NewCallback on Windows (at least 1024 callbacks), which holds everywhere because purego.NewCallback delegates to the standard library on Windows and the Unix table is larger than that.

Since the behavior is not Unix specific — purego.NewCallback on Windows goes straight to syscall.NewCallback, which also creates a brand new callback on every call — I extended the same note to the Windows NewCallback documentation so both platforms describe the loop pitfall and the workaround of creating the callback once and reusing the returned pointer. The notes now link to RegisterFunc, which is where the per-call cost is easiest to hit accidentally.

Everything is documentation only; go build, go vet and go test still pass, and the docs were checked for both Unix and Windows.

@TotallyGamerJet TotallyGamerJet left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Passing the same Go callback to C in a loop exhausts the 2000-entry callback table

3 participants