[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
A nasty, silent native-component gotcha (GB_BASE) - (follow-up on the uvicorn thread)
[Thread Prev] | [Thread Next]
- Subject: A nasty, silent native-component gotcha (GB_BASE) - (follow-up on the uvicorn thread)
- From: Alarch <alarch@xxxxxxxxx>
- Date: Wed, 9 Sep 2026 00:20:48 +0200
- To: user@xxxxxxxxxxxxxxxxxxxxxx
Dear Benoît, dear Claus, dear all,
Following up on last month's thread about a Gambas-native uvicorn-style
HTTP server: I've been building a small proof-of-concept native
component (a Gambas binding around Node.js' llhttp library, for
incremental HTTP/1.1 parsing) and ran into a gotcha nasty enough that I
think it's worth sharing on the list, since it can burn anyone writing
a native component and gives essentially no signal until real, later
usage.
The mistake: my class' native struct did not have GB_BASE as its first
field.
typedef struct {
llhttp_t parser;
llhttp_settings_t settings;
...
} CLLHTTPPARSER;
instead of
typedef struct {
GB_BASE ob; /* forgot this */
llhttp_t parser;
llhttp_settings_t settings;
...
} CLLHTTPPARSER;
gambas.h is completely explicit about the requirement right next to
GB_BASE's own definition ("This structure represents the base of every
Gambas object. It must be placed in the beginning of all object
structure defined in a component."), so this is entirely my own
carelessness - but what struck me is how little feedback the mistake
gives you:
- gbc3 compiles the component without any warning.
- gbi3 generates .info/.list without complaint.
- The component loads fine, the class registers fine.
- Reading from the object is fine.
- A constructor that writes nothing at all to the object is fine.
- The very first ordinary field write (THIS->field = value, or a raw
pointer write, doesn't matter which) succeeds without any visible
problem at that point.
The crash only happens later, whenever gbx3 next touches its own
per-object bookkeeping (the class pointer and refcount that should have
lived in the GB_BASE that isn't there) - which your own field write has
just silently overwritten. The resulting SIGSEGV's backtrace points into
gbx3/gbr3 itself, with no visible connection to the line of code that
actually caused it.
This sent me on a long, wrong trail. My PoC is meant to run on a
slightly older 3.19 target, and I'd built it against a newer dev
checkout, so version-ABI mismatch was my prime suspect for quite a
while - especially since a first real-world test (an actual gbr3, not
the dev build) did crash. I only ruled that out by rebuilding against
the exact matching 3.19.5 release commit (same crash), and then
conclusively by recompiling gb.net's own unmodified source through my
own toolchain (worked perfectly) versus a trivial from-scratch test
class with zero relation to my original component (crashed identically
on any write). That comparison is what finally pointed at the missing
GB_BASE.
Two things I wanted to ask/suggest, now that I've found it:
1. Would a cheap sanity check be worth adding somewhere in
CLASS_register_class (or right after a native _new call, in debug
builds only) - e.g. checking that the class pointer embedded at the
start of a freshly allocated object still matches what was written at
allocation time? I don't know the internals well enough to judge the
cost/complexity, but even a debug-only assertion would have saved me
a good few hours, since it would point straight at the object instead
of crashing somewhere unrelated much later.
2. Would it be worth a short, explicit warning on the component-writing
wiki page (with exactly this failure mode described), for anyone
writing their first native class the way I did? I'm happy to draft
that if it's welcome - it's exactly the kind of thing that's obvious
in hindsight and invisible beforehand.
Otherwise, my llhttp-based component parses real HTTP/1.1 requests
correctly now that this is fixed, so the small local web-server
proof-of-concept is moving forward again. I'll keep sharing progress on
this thread as it develops.
Best regards,
Marc
| Re: A nasty, silent native-component gotcha (GB_BASE) - (follow-up on the uvicorn thread) | Benoît Minisini <benoit.minisini@xxxxxxxxxxxxxxxx> |