Currently I am developing an ASP.NET application that for legacy reasons needs to execute some Perl scripts. For this I wrote a small C++ library that uses the embedded Perl API. This library has one single (C) entry point that allows the C# code to execute a script while passing command line arguments and an environment. This environment allows the C# code to mimic a CGI call for the Perl script. I build against Strawberry Perl 5.40.1. I need to use embedded Perl, as Perl is not installed on the shared hosting service that this application will run on and will not be installed either. In the end all pages will be translated to ASP.NET, but until then I need a solution to run the Perl code too.
Unfortunately, I see random crashes of my application due to an access violation in Perl540.dll. These crashes occur when I only execute one Perl script at a time, and more often when I run more Perl scripts concurrently. I have added quite some logging to my C++ code to see where the execution is. This indicates that the locations where the access violation occurs are quite random. I have seen them occur during calls to perl_run, perl_parse and even perl_destruct. In my opinion, this all points to some way of memory corruption somewhere, but I am lost in how I could try to debug that.
The relevant parts of my execute function look like:
extern "C" __declspec(dllexport) BOOL ExecutePerlScript(PCSTR path)
{
BOOL result(FALSE);
// Create the Perl interpreter
PerlInterpreter* my_perl(perl_alloc());
if (NULL != my_perl)
{
PERL_SET_CONTEXT(my_perl);
PL_perl_destruct_level = 1;
perl_construct(my_perl);
PL_origalen = 1;
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
// Initialize the Perl interpreter
result = (perl_parse(my_perl,
XsInit,
NR_DEFAULT_ARGUMENTS,
DEFAULT_ARGUMENTS,
NULL) == 0) ? TRUE : FALSE;
// Run the interpreter
if (result)
{
result = (perl_run(my_perl) == 0) ? TRUE : FALSE;
}
if (result)
{
result = LoadFile(path,
my_perl);
}
if (result)
{
// Execute the Perl script
eval_pv("eval \"$" SCRIPT_TO_EVALUATE_VARIABLE_NAME "; 1\" or do { $" SCRIPT_EXECUTION_ERROR_VARIABLE_NAME " = $@; }",
TRUE);
}
// Destruct the interpreter
PL_perl_destruct_level = 1;
perl_destruct(my_perl);
perl_free(my_perl);
}
return result;
}
EDIT: I compile the code using the following Windows batch commands:
:: Get the required configuration options for the compilation
for /f "delims=" %%i IN ('perl -MConfig -e "print $Config{cc}"') do set CC=%%i
for /f "delims=" %%i IN ('perl -MExtUtils::Embed -e ccopts') do set CCOPTS=%%i
for /f "delims=" %%i IN ('perl -MExtUtils::Embed -e ldopts') do set LDOPTS=%%i
:: Compile the DLL
"%CC%" -shared -o PerlInterop.dll PerlInterop.cpp -g -lstdc++ %CCOPTS% %LDOPTS%
When the application crashes during the destruct, the error output is as follows:
Free to wrong pool 2c193e1b9c0 not 2c1941b2d10 during global destruction.
Fatal error. 0xC0000005
Otherwise, the first line is missing, and just the second line is displayed. I never get any stacktrace.
Win32::OLE? It is known to not be thread-safe. You can't run multiple instances of it in multiple threads/multiple Perl interpreters.Parallel::ForkManagerto do some things in parallel. Running muliple scripts in parallel, but making sure none of those scripts is usingParallel::ForkManager(or directlyfork) either directly or indirectly via aused module seems to elimate the access violation crashes (I will try to see what happens if I set the number of parallel processes to 0). But because of this I also found out that I still have occasional crashes/exits of my entire application that result from acroakin the Perl code. Can I prevent that?