sriniiyer has asked for the wisdom of the Perl Monks concerning the following question:

All,

I am developing a xsubpp code for my project. In this I am using the call_sv() function in the .xs file to call a subroutine written in my Perl main program. Below are the program codes that I have written.

test.pl

use ExtUtils::testlib; use TestXS; sub test_callback { print "Inside test_callback(): ", scalar(@_), " arguments.\n"; my $hash = shift; print "[IN_SUB]\n"; for (keys %$hash) { print "\t", $_, " ==>> ", $hash->{$_}, "\n"; } } # # # # # MAIN # # # # # my $file_list = { "/tmp/" => undef, "/tmp/TestXS" => undef, "/usr/include/" => undef, }; TestXS::callback_sub($file_list, "test_callback");
Following is the XS code that I have written to handle these inputs.

TestXS.xs

int call_perl_sub(SV *routine, SV *data_hash) { dSP; /* Get the stack pointer. */ /* Allocate the temporary memory location. */ ENTER; SAVETMPS; PUSHMARK(SP); /* Get the temporary stack pointer. */ XPUSHs(sv_2mortal(data_hash)); /* Push the hash onto the stack. */ PUTBACK; /* Call the PERL subroutine, ignoring its return value. */ call_sv(routine, G_DISCARD); /* Free the allocated memory. */ FREETMPS; LEAVE; } /* End of function: call_perl_sub() */ MODULE = TestXS PACKAGE = TestXS void callback_sub(file_list, callback) SV *file_list; SV *callback; CODE: HV *inp_file_list = (HV *) SvRV(file_list); HE *next_entry = NULL; /* Initialize the iterator. */ hv_iterinit(inp_file_list); for (next_entry = hv_iternext(inp_file_list); next_entry != NULL; next_entry = hv_iternext(inp_file_list)) { I32 keylen = 0; SV *entry_val = hv_iterval(inp_file_list, next_entry); char *filepath = hv_iterkey(next_entry, &keylen); sv_setpv(entry_val, "Marked."); fprintf(stderr, "[UPDATED] %s ==>> %s\n", filepath, SvPV_nolen(entry_val)); call_perl_sub(callback, SvREFCNT_inc(file_list)); }
The code compiles well without any errors or warnings. When the program is run and the callback function is called, the program goes into an infinite loop and throws and output like below:
[UPDATED] /usr/include/ ==>> Marked. [IN_SUB] /usr/include/ ==>> Marked. /tmp ==>> /tmp/TestXS ==>> [UPDATED] /usr/include/ ==>> Marked. [IN_SUB] /usr/include/ ==>> Marked. /tmp ==>> /tmp/TestXS ==>> [UPDATED] /usr/include/ ==>> Marked. [IN_SUB] /usr/include/ ==>> Marked. /tmp ==>> /tmp/TestXS ==>> [UPDATED] /usr/include/ ==>> Marked. [IN_SUB] /usr/include/ ==>> Marked. /tmp ==>> /tmp/TestXS ==>> ...

Now, I am not able to understand the reason on why is it going into infinite loop. As an experiment, I edited the user-defined subroutine test_callback as below:

sub test_callback { print "Inside test_callback(): ", scalar(@_), " arguments.\n"; my $hash = shift; print "[IN_SUB] ", $hash, " reference to ", ref($hash), \n"; }

Now, this went pretty well without any infinite looping. To my understanding, there seems to something amiss when I am passing the hash reference to the callback function; no clue at all. Now, I'm not able to understand on where the problem exactly lies. Any help on this would be most welcome.

Thanking you in anticipation.

Replies are listed 'Best First'.
Re: Callback function in xsubpp (each)
by tye (Sage) on Jul 30, 2013 at 18:54 UTC

    Calling keys in your Perl sub resets the iterator (see each) in your XS code.

    - tye        

      Thank you for the reply. So, is there any way to retain the iterator position? Or, should I always copy the hash into another hash and then call the subroutine?

        What I would do is honor my best practice of not manipulating Perl data structures from XS code and just iterate over the keys in regular Perl code using for my $key ( keys %hash ) [which makes a copy of just the keys].

        - tye