On Sep 22, 11:57 am, Alexandre Ferrieux <
alexandre.ferri...@gmail.com>
wrote:
> OK. But what about using each part where it excels ?
I am using Tcl as a user extension for user defined calculations - so
Tcl excels there because it is interpreted and I don't have to ship a
build environment with my application so that customers can link in
their C/C++ code to do the calculations.
Usually the customer supplies a proc that loops over a bunch of values
that it can retrieve from an application object passed in and return a
double that the application stashes away for use later on.
This works and performs well. The problem is that if the user colors
outside the lines in an interesting way, like opening a file, the
application crashes.
I have reproduced this in a small testcase contained entirely in C++.
I also found that by deleting the interpreter inside the same
execution thread that it was created in, I can also run successfully
(see commented code). This is difficult for me to do as I don't know
when the last task is done on each thread inside the thread context -
only the main thread knows when everything is done.
#include <pthread.h>
#include <iostream>
#include <vector>
#include <tcl.h>
using namespace std;
vector<Tcl_Interp*> interpreters;
const char* tcl_proc = "proc foo { } {\n"
" set log [ open logfile a ]\n"
" puts $log \"thread started\"\n"
// " close $log\n"
" return 3.4\n"
"}\n"
"foo\n"
"\n";
void* create_a_task_handler( void* slot_p ) {
unsigned long slot_index = *((unsigned long*)slot_p);
Tcl_Interp *interp = interpreters[slot_index] =
Tcl_CreateInterp();
cout << "Executing " << slot_index << endl;
int rc = Tcl_Eval( interp, tcl_proc );
if ( rc != TCL_OK ) {
cout << "An error was encountered during evaluation of TCL
code.";
const char * errorInfoStr = Tcl_GetVar(interp, "errorInfo",
TCL_GLOBAL_ONLY);
if ( errorInfoStr ) cout << errorInfoStr << endl;
}
Tcl_Obj *res = Tcl_GetObjResult( interp );
double ret_val;
rc = Tcl_GetDoubleFromObj(interp, res, &ret_val );
cout << "Proc returned " << ret_val << endl;
// Everything is fine if we delete the interpreters in the same
thread they were created in.
//Tcl_DeleteInterp( interpreters[slot_index] );
//interpreters[slot_index] = NULL;
return NULL;
}
main( int argc, const char** argv ) {
const char* usage = "Usage: tcl_mt [<thread_count> ]\n
thread_count is {1 2 4 8 or 16}\n\n";
const int MAX_THREADS=16;
int thread_count=1;
if ( argc>1 ) {
thread_count=atoi(argv[1]);
}
if ( thread_count != 1 && thread_count != 2 && thread_count != 4
&& thread_count != 8 && thread_count != 16 )
{
cout << "thread count must be 2,4,8 or 16." << endl;
cout << usage << endl;
exit(1);
}
pthread_t threads[MAX_THREADS];
unsigned long slots[MAX_THREADS];
interpreters.assign(thread_count, NULL);
if ( argc == 1 ) {
cout << "Running tasks in main thread." << endl;
// straight ahead - no threads.
slots[0] = 0;
create_a_task_handler( &slots[0] );
Tcl_DeleteInterp( interpreters[0] );
} else {
cout << "Running " << thread_count << " threads." << endl;
for( unsigned long i=0; i != thread_count; ++i ) {
slots[i] = i;
pthread_create( &(threads[i]), NULL,
create_a_task_handler, &slots[i] );
}
for( unsigned long i=0; i != thread_count; ++i ) {
pthread_join( threads[i], NULL );
}
for( unsigned long i=0; i != thread_count; ++i ) {
if ( interpreters[i] ) {
Tcl_DeleteInterp( interpreters[i] );
}
}
}
}
For single threaded execution:
tcl_mt
Running tasks in main thread.
Executing 0
Proc returned 3.4
this works fine,
now for multi-threaded execution:
tcl_mt 2
Running 2 threads.
Executing 0
Executing 1
Proc returned 3.4
Proc returned 3.4
FlushChannel: damaged channel list
Aborted