Either I'm confused or the gc is
donallen
donaldcallen at gmail.com
Sun Nov 15 13:43:14 UTC 2020
With the help of an excellent suggestion by Steve Schveighoffer,
I finally identified this problem yesterday. It was my error, not
D's.
I will spare you all the details. The problem is in this function:
void bind_text(sqlite3_stmt* stmt, int index, const char[]
the_text)
{
if (sqlite3_bind_text(stmt, index, toStringz(the_text), -1,
null)
!= sqlite_ok)
{
stderr.writeln("bind_text failed");
auto db = sqlite3_db_handle(stmt);
stderr.writeln("Error: %s",
fromStringz(sqlite3_errmsg(db)));
exit(EXIT_FAILURE);
}
}
which takes a sqlite prepared statement, a query parameter index
and a D string and attempts to convert the D string to a
null-terminated C string and call sqlite3_bind_text to bind the C
string to the indicated query parameter.
The culprit is the call to toStringz. The documentation for that
function give some good advice that I failed to follow:
"Important Note: When passing a char* to a C function, and the C
function keeps it around for any reason, make sure that you keep a
reference to it in your D code. Otherwise, it may become invalid
during a garbage collection cycle and cause a nasty bug when the C
code tries to use it."
That's precisely what is happening here. There is no reference to
the
string returned by the toStringz call retained and if a GC happens
during a loop retrieving all the rows of a query, the C version
of the
string that has been passed to sqlite by the sqlite3_bind_text
call gets
freed by the GC before sqlite is done with it. So now the sqlite
query
no longer has a proper value for the query parameter, and all
bets are
off as far as the query behaving correctly. This also explains
the mystery of why the loop
terminated prematurely when I first discovered this problem.
I fixed it by having bind_text return the C string to its caller,
to be stored in a variable to protect it from the garbage
collector. The original error, which was reproducible, is now
gone. Steve's suggestion, which was to insert frequent calls to
the garbage collector, provoked a different error reliably that
is also gone with the fix.
Thanks to everyone who tried to help figure this one out.
More information about the Digitalmars-d
mailing list