To: ace-users@cs.wustl.edu Subject: CORBA any data type with thread-per-request strategy: core dump TAO VERSION: 0.3.15 ACE VERSION: 4.6.31 HOST MACHINE and OPERATING SYSTEM: SunOS 5.6 (SPARC) TARGET MACHINE and OPERATING SYSTEM, if different from HOST: COMPILER NAME AND VERSION (AND PATCHLEVEL): SPARC Works CC compiler 4.2 I have a same problem on the RedHat Linux 6.0 with egcs. AREA/CLASS/EXAMPLE AFFECTED: CORBA Any data type (Used as a global variable) thread-per-request strategy DOES THE PROBLEM AFFECT: EXECUTION Dump core somewhere in the middle of the program. The location of the core dump varies. SYNOPSIS: If we use data type "Any" as a global (or static) variable in the server running in thread-per-request strategy, the server will dump core. Calling hello() several times will crash the server if it is running on thread-per-connection strategy. IDL: interface hello_world { void hello(); }; Server: Any any_value; // Global variable Any void hello(void) { CORBA::ULong v = 2; // By running on thread-per-request strategy, // this code will be done by many other threads. // And may crash the program any_value <<= v; return; } Client: test_program (void) { for (i = 0 ; i < 200 ; i++) { hello(); } } svc.conf: dynamic Resource_Factory Service_Object * TAO:_make_TAO_Resource_Factory() "-ORBresources global -ORBreactorlock null" dynamic Client_Strategy_Factory Service_Object * TAO:_make_TAO_Default_Client_Strategy_Factory() "-ORBiiopprofilelock null -ORBclientconnectionhandler MT" dynamic Server_Strategy_Factory Service_Object * TAO:_make_TAO_Default_Server_Strategy_Factory() "-ORBconcurrency thread-per-connection -ORBpoalock null -ORBconnectorlock null" DESCRIPTION: The storage area of Any will be released at the time of thread termination even though it is not explicitly freed and reference count is not zero. The buffer is allocated by TSS_ALLOCATOR and TSS_ALLOCATOR releases memory when the thread terminates. So if we use "any" data type as a global variable, the data buffer may points a dangling pointer and the code modifies memory which is no longer data buffer for Any, whic leads the program to dump core. This problem could happen in "TypeCode" or "union(enum)". The member_label() in the "TypeCode" implicitly uses global (or static) Any. The encode/decode of "enum" in the "union" uses "any", too. ---------------------------------------------------------- "TAO/tao/Any.h" Inside "Any" data structure, we have a message block to hold "Any" value. class Any { .... ACE_Message_Block *cdr_; ... }; ---------------------------------------------------------- "TAO/tao/Any.cpp" The message block is initialized as a TAO CDR stream. TAO_OutputCDR stream; stream.encode( .... ); this->cdr_ = stream.begin()->clone(); or this->cdr_ = ACE_Message_Block::duplicate (src.cdr_); ---------------------------------------------------------- "TAO/tao/CDR.h" The TAO CDR stream is derived from ACE CDR stream and ... class TAO_Export TAO_OutputCDR : public ACE_OutputCDR { .... }; ---------------------------------------------------------- "ace/CDR_stream.h" The ACE CDR stream has a message block to hold the encoded "Any" value. class ACE_OutputCDR { .... const ACE_Message_Block *begin (void) const; const ACE_Message_Block *end (void) const; .... }; ---------------------------------------------------------- "ace/Message_Block.h" The message block holds the data block and the allocators. class ACE_Message_Block { ... ACE_Data_Block *data_block; ... ACE_Allocator *message_block_allocator_; ACE_Allocator *data_block_allocator_; ... int reference_count; ... }; The life time of a data block is maintained by the reference count. class ACE_Data_Block { ... int reference_count; ... char *base; ... }; ---------------------------------------------------------- "TAO/tao/ORB_Core.h" The actual data storage for the data block (base) is allocated by the allocator defined in the ORB core. For output CDR, TAO uses TSS (Thread specific storage) allocator. class TAO_ORB_Core { ... TSS_ALLOCATOR output_cdr_dblock_allocator (void); // This allocator is always TSS and has no locks. It is intended for // allocating the ACE_Data_Blocks used in *outgoing* CDR streams. TSS_ALLOCATOR output_cdr_buffer_allocator (void); // This allocator is always TSS and has no locks. It is intended for // allocating the buffers used in *outgoing* CDR streams. ... }; ---------------------------------------------------------- "ace/Synch_T.cpp" In the event of the thread termination, this routine will be called and destroy TAO_ORB_Core. template void ACE_TSS::cleanup (void *ptr) { // Cast this to the concrete TYPE * so the destructor gets called. delete (TYPE *) ptr; } ---------------------------------------------------------- "TAO/tao/ORB_Core.cpp" The destructor will release all TSS storage by calling remove() method of the allocator. TAO_ORB_Core::~TAO_ORB_Core (void) { ... // Clean up memory pools this->output_cdr_dblock_allocator_.remove (); this->output_cdr_buffer_allocator_.remove (); } ---------------------------------------------------------- "ace/Malloc_T.cpp" The allocator maintains all memory chunks and if they are not explicitly released by free() method, it will be released automatically. Since the data storage for "Any" is allocated by TSS allocator, it is released even though the reference count is not zero!!! template int ACE_Malloc::remove (void) { ... // Give the memory pool a chance to release its resources. result = this->memory_pool_.release (); } REPEAT BY: SAMPLE FIX/WORKAROUND: Inside "Any.cpp", I created the special memory allocator for "Any" data type and passed it to initialize CDR stream. This will prevent us from releasing data block implicitly. More cleaner solution should be done. ------------------------------------------------------------- "TAO/tao/Any.cpp" #ifdef FIX static ACE_Allocator *Any_alloc; static int init_any_allocator_done; class Any_allocator { public: typedef ACE_Malloc ANY_MALLOC; typedef ACE_Allocator_Adapter ANY_ALLOCATOR; Any_allocator() {}; ~Any_allocator() {}; static ACE_Allocator* instance() { if (init_any_allocator_done == 0) { Any_alloc = new ANY_ALLOCATOR; } init_any_allocator_done = 1; return Any_alloc; } }; #endif CORBA_Any::CORBA_Any (CORBA::TypeCode_ptr tc, void *value, CORBA::Boolean any_owns_data) : type_ (CORBA::TypeCode::_duplicate (tc)), value_ (value), cdr_ (0), any_owns_data_ (any_owns_data) { ..... #ifdef FIX TAO_OutputCDR stream(0, ACE_CDR_BYTE_ORDER, Any_allocator::instance(), Any_allocator::instance()); #else TAO_OutputCDR stream; #endif .... } ... void CORBA_Any::replace (CORBA::TypeCode_ptr tc, const void *value, CORBA::Boolean any_owns_data, CORBA::Environment &env) { .... #ifdef FIX TAO_OutputCDR stream(0, ACE_CDR_BYTE_ORDER, Any_allocator::instance(), Any_allocator::instance()); #else TAO_OutputCDR stream; #endif ... }