[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