[erlang-bugs] erl_interface and threads

Benjamin Winkler Benjamin.Winkler@REDACTED
Thu Apr 7 09:53:05 CEST 2011


Hi!

We are using Erlang and specifically the erl_interface for C (Visual 
Studio C++ 2008, Windows 7) to connect to our Erlang servers. We have 
noticed weird errors when creating (and freeing) terms in different 
threads. It seems to me that the memory allocation module is not 
thread-aware.

I have managed to isolate the problem and written an exemplary program 
(see end of mail) where several threads create and free erlang terms 
(tuples "{hello,world}"). The terms are continously checked for 
correctness by assertions (assert(type != ERL_UNDEF)). Note, that the 
terms are local in regard to the thread.

When running a single thread, everything is just fine. No memory leaks, 
no exceptions, no assertions. However, when I use multiple threads 
something goes wrong (usually just several milliseconds after starting) 
and I get completely unpredictable results:
- sometimes an atom that I have just created in the preceding line is 
suddenly a tuple (assertion fails)
- sometimes a tuple references itself as a child, thus creating a 
recursion and a stack overflow when checking its children
- sometimes the type of a term becomes ERL_UNDEF
- etc.

I have tried this with the precompiled binaries for version R12B05 and 
with the most recent version R14B02. (In the latter case I had to link 
with "ei_md.lib" and "erl_interface_md.lib", since I don't have the 
corresponding VS2005-Debug-Runtime-DLLs)

Am I missing something obvious? Is this problem known or worked upon or 
is there any fix?

I have searched the documentation and the web but I only found vague 
references to a compiler flag _REENTRANT, which would fix some pthreads 
concurrency issues. But judging from the Makefile I am using 
Win32-Threads (also I would like to get around having to build 
erl_interface with cygwin).

Any help would be greatly appreciated, since this is a real show-stopper.

Thanks in advance,
Benni


Here is the source code:


#include <erl_interface.h>
#include <boost/thread/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <vector>

#include <time.h>


//
// select the number of threads that simultaneously allocate and
// deallocate erlang terms:
//
const int numberOfThreads = 3;

volatile bool abortThread = false;

//
// check whether the term is valid; recurse if we have a tuple
//
void checkTerm(const ETERM* p)
{
   if (p)
   {
     int type = ERL_TYPE(p);
     assert( (type != ERL_UNDEF) && "invalid erlang term!" );

     if (ERL_IS_TUPLE(p))
     {
       for (int i = 0; i < ERL_TUPLE_SIZE(p); ++i)
         checkTerm( ERL_TUPLE_ELEMENT(p, i) );
     }
   }
}


//
// free the term; recurse if we have a tuple
// (I could have used erl_free_compound() instead)
//
void deleteTerm(ETERM* p)
{
   if (p)
   {
     if (ERL_IS_TUPLE(p))
     {
       for (int i = 0; i < ERL_TUPLE_SIZE(p); ++i)
         deleteTerm( ERL_TUPLE_ELEMENT(p, i) );
     }

     erl_free_term(p);
   }
}


//
// the thread routine:
//
// holds 64 erlang terms (locally!), which are initially NULL.
// in every iteration:
// - one of the terms is replaced by a tuple {hello, world}
// - every created tuple is checked for validity
// - all 64 tuples are checked for validity as well
//
void threadRoutine()
{
   // hold 64 erlang elements
   std::vector<ETERM*> terms(64, (ETERM*)0);

   while (!abortThread)
   {
     // check all terms
     for (std::size_t i = 0; i < terms.size(); ++i)
       checkTerm( terms[i] );

     // pick random term
     int idx = rand() % (int)(terms.size());

     // create atom
     ETERM* a = erl_mk_atom("hello");
     checkTerm( a );

     // create atom
     ETERM* b = erl_mk_atom("world");
     checkTerm( b );

     // create tuple {hello, world}
     ETERM* hw[2];
     hw[0] = a;
     hw[1] = b;

     ETERM* tuple = erl_mk_tuple(hw, 2);
     checkTerm( tuple );

     // replace current term with this tuple
     deleteTerm( terms[idx] );
     terms[idx] = tuple;
   }

   // release all terms
   for (std::size_t i = 0; i < terms.size(); ++i)
     deleteTerm( terms[i] );
}


typedef boost::shared_ptr< boost::thread > thread_ptr;

//
// main routine:
// start the threads, wait for <Return>, stop the threads
//
int main(int argc, char* argv[])
{
   // init
   erl_init(NULL, 0);
   srand( (unsigned)time(NULL) );

   // start threads
   std::vector< thread_ptr > threads;
   for (int i = 0; i < numberOfThreads; ++i)
     threads.push_back( thread_ptr( new boost::thread(&threadRoutine) ) );

   // wait
   std::cout << "Press Return to stop" << std::endl;
   std::string str;
   std::getline(std::cin, str);

   // wait for threads
   abortThread = true;
   for (std::size_t i = 0; i < threads.size(); ++i)
     threads[i]->join();
}

-- 

Benjamin Winkler
Software Engineer

SPECS Surface Nano Analysis GmbH
Voltastrasse 5
13355 Berlin
Germany

Tel.:	+49 30 46 78 24-92 47
Fax:	+49 30 46 42 083
Email:	Benjamin.Winkler@REDACTED
Web:	www.specs.com

SPECS Surface Nano Analysis GmbH
Innovation in Surface Spectroscopy and Microscopy Systems
CEO: Reinhard Lembke
Registered Office: Berlin
Companies Commercial Register No.: 93 HRB 21390
Register Court: Charlottenburg

This e-mail contains confidential and/or privileged information. If you are not the intended recipient (or have received this e-mail in error) please notify the sender immediately and delete this e-mail. Any unauthorized copying, disclosure or distribution of the material in this e-mail is strictly forbidden.




More information about the erlang-bugs mailing list