More fun with toStringz and the GC

Don Allen donaldcallen at gmail.com
Fri Aug 5 22:51:07 UTC 2022


Remember all the fun we had last year when I failed to heed the 
warning in the toStringz documentation about retaining a 
reference to a char * passed into C? It took a long time to find 
that one, with a lot of help from Steve Schveighoffer and others.

Well, I've got another one. Consider this:

````
         // Get number of children of the parent account
         auto gc_protect = bind_text(n_children_stmt, 1, 
parent.guid);
         parent.children.length = one_row!(int)(n_children_stmt, 
&get_int);

         auto gc_protect2 = bind_text(account_child_stmt, 1, 
parent.guid);
         for (int i = 0; next_row_available_p(account_child_stmt, 
&sqlite3_reset); i++) {
             parent.children[i] = new Account;
             parent.children[i].name = 
fromStringz(sqlite3_column_text(account_child_stmt, 0)).idup;
             parent.children[i].guid = 
fromStringz(sqlite3_column_text(account_child_stmt, 1)).idup;
             parent.children[i].flags = 
sqlite3_column_int(account_child_stmt, 2);
             parent.children[i].value = 
get_account_value(parent.children[i]);
         }
````
bind_text takes a D string, turns it into a C string with 
toStringz, uses that to call sqlite3_bind_text and returns the C 
string, which I store as you can see with the intention of 
protecting it from the gc. The code as written above does not 
work. At some point, I get an index-out-of-bounds error, because 
the loop is seeing too many children. If I turn off the GC, the 
code works correctly and the application completes normally.

With the GC on, if I put a debugging writeln inside the loop, 
right after the 'for', that prints, among other things, the value 
of gc_protect2 (I wanted to convince myself that the GC wasn't 
moving what it points to; yes, I know the documentation says the 
current GC won't do that), the problem goes away. A Heisenbug!

My theory: because gc_protect2 is never referenced, I'm guessing 
that the compiler is optimizing away the storage of the returned 
pointer, the supporting evidence being what I said in the 
previous paragraph. Anyone have a better idea?

By the way, I get the same error compiling this with dmd or ldc.

/Don Allen


More information about the Digitalmars-d-announce mailing list