views:

164

answers:

4

EDIT: I have created a ticket for this which has data on an alternative to this way of doing things.

I have updated the code in an attempt to use MY_CXT's callback as gcxt was not storing across threads. However this segfaults at ENTER.

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#ifndef aTHX_
#define aTHX_
#endif

#ifdef USE_THREADS
#define HAVE_TLS_CONTEXT
#endif

/* For windows  */
#ifndef SDL_PERL_DEFINES_H
#define SDL_PERL_DEFINES_H

#ifdef HAVE_TLS_CONTEXT
PerlInterpreter *parent_perl = NULL;
extern PerlInterpreter *parent_perl;
#define GET_TLS_CONTEXT parent_perl =  PERL_GET_CONTEXT;
#define ENTER_TLS_CONTEXT \
        PerlInterpreter *current_perl = PERL_GET_CONTEXT; \
            PERL_SET_CONTEXT(parent_perl); { \
                                PerlInterpreter *my_perl = parent_perl;
#define LEAVE_TLS_CONTEXT \
                                        } PERL_SET_CONTEXT(current_perl);
#else
#define GET_TLS_CONTEXT         /* TLS context not enabled */
#define ENTER_TLS_CONTEXT       /* TLS context not enabled */
#define LEAVE_TLS_CONTEXT       /* TLS context not enabled */
#endif

#endif


#include <SDL.h>

#define MY_CXT_KEY "SDL::Time::_guts" XS_VERSION 


 typedef struct {
 void* data;
 SV* callback;
 Uint32 retval;
 } my_cxt_t;

static my_cxt_t gcxt;

START_MY_CXT 


static Uint32 add_timer_cb ( Uint32 interval, void* param )
{

        ENTER_TLS_CONTEXT
        dMY_CXT;
        dSP;
        int back;
        ENTER; //SEGFAULTS RIGHT HERE!
        SAVETMPS;
        PUSHMARK(SP);
        XPUSHs(sv_2mortal(newSViv(interval)));
        PUTBACK;

        if (0 != (back = call_sv(MY_CXT.callback,G_SCALAR))) {
     SPAGAIN;
     if (back != 1 ) Perl_croak (aTHX_ "Timer Callback failed!");
     MY_CXT.retval = POPi;     
        } else {
     Perl_croak(aTHX_ "Timer Callback failed!");
        }

        FREETMPS;
        LEAVE;

        LEAVE_TLS_CONTEXT
        dMY_CXT;
        return MY_CXT.retval;

}

MODULE = SDL::Time  PACKAGE = SDL::Time    PREFIX = time_

BOOT:
{
  MY_CXT_INIT;
}


SDL_TimerID
time_add_timer ( interval, cmd )
    Uint32 interval
    void *cmd
    PREINIT:
     dMY_CXT;
    CODE:
     MY_CXT.callback=cmd; 
     gcxt = MY_CXT;
     RETVAL = SDL_AddTimer(interval,add_timer_cb,(void *)cmd);    
    OUTPUT:
     RETVAL

void
CLONE(...)
  CODE:
    MY_CXT_CLONE;

This segfaults as soon as I go into ENTER for the callback.

use SDL;
use SDL::Time;

SDL::init(SDL_INIT_TIMER);
my $time = 0;
SDL::Timer::add_timer(100, sub { $time++; return $_[0]} );
sleep(10);
print "Never Prints";

Output is

$

it should be

$ Never Prints
A: 

$time needs to be a shared variable - otherwise perl works with separate copies of the variable.

weismat
um ... I have no clue what you are referring to. Care to put some code up?
kthakore
Add sleep 200 at the end and see if you see any output.
weismat
nope no go. It doesn't even get to sleep. :(
kthakore
+4  A: 

Quick comments:

  • Do not use Perl structs (SV, AV, HV, ...) outside of the context of a Perl interpreter object. I.e. do not use it as C-level static data. It will blow up in a threading context. Trust me, I've been there.
  • Check out the "Safely Storing Static Data in XS" section in the perlxs manpage.
  • Some of that stuff you're doing looks rather non-public from the point of view of the perlapi. I'm not quite certain, though.
tsee
please see the update
kthakore
A: 

My preferred way of handling this is storing the data in the PL_modglobal hash. It's automatically tied to the current interpreter.

Leon Timmermans
A: 

We have found a solution to this using Perl interpreter threads and threads::shared. Please see these

Time.xs

Also here is an example of a script using this code.

TestTimer.pl

kthakore