Hi Perl Monks,

I am sort of new to Perl. Using SWIG and with some XS code I am trying to accomplish event-driven callback into Perl from my library (written in C/C++ for windows platform). Callback is fired from different thread context.

The implementation is when Perl registers for callback, Perl's subroutine reference is stored in a global SV pointer and instead a wrapper function is actually registered to the library API. When library fires the callback, the wrapper function calls into perl and puts function arguments into Perl stack.

This approach is working fine for most part. However, it is observed that 1/5 times this implementation runs into race condition and Perl throws strange errors which I don't understand.

1. Somewhere in between processing of callback wrapper function, Perl control seems to be lost (it never returns to main. Perl dies gracefully and no error is reported).

2. panic: pp_iter at F:\TEST\SWIG_API\Debug\run_cb.pl line 22. (Points to Win32::Sleep(int(rand(100))); in main thread).

3. Crash somewhere in perl interpreter (Stack trace shows only perl512.dll calls. Haven't tried debug build).

4. Some other "panic: " errors.

The details part is as below. Please help me understand why this might be happening.

use strict; use warnings; use MyLibSWIG; use Win32; my $cnt_cback = \&my_connect_cb; for(1..100){ print("\n\n ########## TEST ITERATE : $_ ########## \n"); MyTest(); } sub MyTest { MyLibSWIG::MyRegister($cnt_cback); #Test: engage interpreter here while callback is being processed for (1..10){ print(sprintf("[%d] PRL: Doing something %d\n",Win32::GetCurre +ntThreadId(),$_)); Win32::Sleep(int(rand(100))); } MyLibSWIG::MyDeregister(); } sub my_connect_cb { print(sprintf("[%d] PRL: my_connect_cb called bConn = %d\n",Win32: +:GetCurrentThreadId(), $_[0])); }
During the callback Registration I'm storing the Perl subroutine reference and Perl context in global pointers.
SV* MyConnectCbPerl = NULL; void* pMyConnectCbPerlCTX = NULL; extern void wrap_connect_cback_handler(BOOL bConnected); XS(_wrap_MyRegister) { { PFN_CONNECT_CALLBACK arg1 = (PFN_CONNECT_CALLBACK) 0 ; int argvi = 0; DWORD result; dXSARGS; if ((items < 1) || (items > 1)) { SWIG_croak("Usage: MyRegister(pfnConnectCallback);"); } { int status = IsValidCBRef(ST(0)); if (status == 0) { MyConnectCbPerl = (SV *)ST(0); //Save reg +istered sub refrence pMyConnectCbPerlCTX = Perl_get_context(); //Save +Perl Context arg1 = wrap_connect_cback_handler; //Register a + wrapper function. When fired, the wrapper function invokes the perl +subroutine. } } result = (DWORD)MyRegister(arg1); ST(argvi) = SWIG_From_unsigned_SS_long SWIG_PERL_CALL_ARGS_1((uns +igned long)(result)); argvi++ ; XSRETURN(argvi); fail: SWIG_croak_null(); } }

When Wrapper callback function is invoked, call perl subroutine :

void wrap_connect_cback_handler(BOOL bConnected) { PERL_SET_CONTEXT(pMyConnectCbPerlCTX); SV * sv = NULL; sv = MyConnectCbPerl; if (sv == (SV*)NULL) croak("Internal error...MyConnectCbPerl not registered\n") +; //Sleep(50); dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(newSViv(bConnected))); PUTBACK; /* Call the Perl sub */ call_sv(sv, G_DISCARD); //PERL_SET_CONTEXT(pMyConnectCbPerlCTX); SPAGAIN; PUTBACK; FREETMPS; LEAVE; }

When issue occurs output looks like this (note below: there was no race condition problem iteration run 1 to 5 )-

########## TEST ITERATE : 6 ########## [5528] LIB: MyRegister pfnConnectCallback = 54E310A5 [5528] PRL: Doing something 1 [6116] LIB: CallbackWorker firing ConnectCallback(0) [6116] PRL: my_connect_cb called bConn = 1 [5528] PRL: Doing something 2 [6116] LIB: CallbackWorker firing ConnectCallback(1) [6116] PRL: my_connect_cb called bConn = 0 [6116] LIB: CallbackWorker firing ConnectCallback(2) [6116] PRL: my_connect_cb called bConn = 1 [5528] PRL: Doing something 3 [6116] LIB: CallbackWorker firing ConnectCallback(3) [6116] PRL: my_connect_cb called bConn = 0 [5528] PRL: Doing something 4 [6116] LIB: CallbackWorker firing ConnectCallback(4) [6116] PRL: my_connect_cb called bConn = 0 [6116] PRL: my_connect_cb called bConn = 0 panic: pp_iter at F:\TEST\SWIG_API\Debug\run_cb.pl line 22. panic: pp_iter at F:\TEST\SWIG_API\Debug\run_cb.pl line 22.

Should you want to take a look at the library code -

// MyLib.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" #include "MyLib.h" HANDLE hCallbackThreadHandle; PFN_CONNECT_CALLBACK g_pfnConnectCallback = NULL; static DWORD WINAPI CallbackWorker (LPVOID Context) { //Test: Fire callback N times. Event-Driven. for(int i = 0; i < 10; i++) { printf_s("%d LIB: CallbackWorker firing ConnectCallback(%d)\n", GetCurrentThreadId(), i); if (g_pfnConnectCallback) (*g_pfnConnectCallback)(rand()%2); Sleep(rand()%100); } return ERROR_SUCCESS; } DWORD WINAPI MyRegister (PFN_CONNECT_CALLBACK pfnConnectCallback) { printf_s("%d LIB: MyRegister pfnConnectCallback = %p\n", GetCurrentThreadId(), pfnConnectCallback); g_pfnConnectCallback = pfnConnectCallback; hCallbackThreadHandle = CreateThread (NULL, 0, CallbackWorker, 0, 0, NULL); if (hCallbackThreadHandle == NULL) printf_s("%d LIB: MyRegister Error Creating callback thread\n", GetCurrentThreadId()); return ERROR_SUCCESS; } void WINAPI MyDeregister () { printf_s("%d LIB: MyDeregister Waiting for callback thread to go down\n", GetCurrentThreadId()); DWORD rc = WaitForSingleObject(hCallbackThreadHandle, 100000); if (rc != WAIT_OBJECT_0) printf_s("%d LIB: ERROR worker thread goofed up\n", GetCurrentThreadId()); else printf_s("%d LIB: thread gone!\n", GetCurrentThreadId()); CloseHandle (hCallbackThreadHandle); hCallbackThreadHandle = NULL; g_pfnConnectCallback = NULL; }

Thanks.


In reply to Perl interpreter throws strange errors for event-driven callback processing under race conditions by Cotton4Lunch

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.