 | To: ace-bugs@cs.wustl.edu Subject: C++NPv2 display_logfile.cpp patch
ACE VERSION: 5.3b, 5.4
HOST MACHINE and OPERATING SYSTEM: Intel/Linux RH9.0
TARGET MACHINE and OPERATING SYSTEM, if different from HOST: COMPILER NAME AND VERSION (AND PATCHLEVEL): not applicable
CONTENTS OF $ACE_ROOT/ace/config.h: not applicable
CONTENTS OF $ACE_ROOT/include/makeinclude/platform_macros.GNU (unless this isn't used in this case, e.g., with Microsoft Visual C++): not applicable AREA/CLASS/EXAMPLE AFFECTED:
$ACE_ROOT/examples/C++NPv2/display_logfile.cpp
DOES THE PROBLEM AFFECT: COMPILATION? LINKING? On Unix systems, did you run make realclean first? EXECUTION? OTHER (please specify)?
Other: Incorrect use of C++ (a) constructor initialization list and (b) inheritance.
SYNOPSIS:
Incorrect use of C++ (a) constructor initialization list and (b) inheritance
DESCRIPTION:
(a) Incorrect use of constructor initialization list
The display_logfile.cpp Logrec_Module template, and the Logrec_Reader_Module class both use the constructor initialization list to pass a member variable to the base class. Since the base class construction will occur before the inherited class construction, the member variable does not exist at the point that it is passed to the base-class constructor.
When the derived class construction occurs, the task_ member variable is initialized (again). The result of which is probably undefined. The effect of this sequence is that the task has no idea that it is part of a module, so none of the module-specific task member variables that the base class would have setup eg. the task's module(), is_reader(), is_writer(), etc are setup.
FIX: In the derived class constructor, let the base-class default construction occur and then call ACE_Module<>::open() in the body of the derived class constructor.
(b) Incorrect use of inheritance The inherited modules contained within display_logfile.cpp are inserted into a stream, and the stream becomes responsible for deleting the modules. Since the ACE_Module<> destructor is NOT virtual, when the stream calls delete through the base class pointer, the derived module destructor will NOT be called.
Since the example derived modules all contain task member variables, the destructors for these members are never called and you have a resource leak.
FIX#1: As per the examples in the "ACE Programmers Guide", use inheritance, but do not introduce any new member variables. Hence, for the display_logfile.cpp example dynamically allocate the task and set the module flags such that the baseclass will delete the task object. The inherited object destructor will still NOT get called, but I think that this will not leak resources (but I am not 100% certain).
FIX#2: Make the ACE_Module<> destructor virtual. (Is there any reason NOT to do this?) REPEAT BY: Read the original example source. SAMPLE FIX/WORKAROUND:
Here's a patch against the example code from the ACE 5.4 distribution.
--- orig/display_logfile.cpp 2005-01-22 19:10:29.000000000 -0800 +++ work/display_logfile.cpp 2005-01-22 19:10:24.000000000 -0800 @@ -22,15 +22,30 @@ class Logrec_Module : public ACE_Module { public: + typedef ACE_Module parent; Logrec_Module (const ACE_TCHAR *name) - : ACE_Module - (name, - &task_, // Initialize writer-side task. - 0, // Ignore reader-side task. - 0, - ACE_Module::M_DELETE_READER) {} -private: - TASK task_; + // Base-class default construction occurs + { + // The module deletes the task + TASK *task = new TASK; + parent::open(name, + task, // Initialize writer-side task. + 0, // Ignore reader-side task. + 0, + parent::M_DELETE_WRITER); + } + + // WARNING: + // + // ACE_Module<> does not have a virtual destructor so, + // when this class is inserted into a stream as an + // ACE_Module<> pointer, it will NOT act polymorphically, + // i.e., when the stream calls delete on the ACE_Module<> + // pointer, the destructor for this class will not be + // called. Because of this, this class can not contain + // any member variables. This class serves only as a + // convenience to convert a task into a module. + // }; #define LOGREC_MODULE(NAME) \ typedef Logrec_Module NAME##_Module @@ -195,16 +210,22 @@ class Logrec_Reader_Module : public ACE_Module { public: + typedef ACE_Module parent; Logrec_Reader_Module (const ACE_TString &filename) - : ACE_Module - (ACE_TEXT ("Logrec Reader"), - &task_, // Initialize writer-side. - 0, // Ignore reader-side. - 0, - ACE_Module::M_DELETE_READER), - task_ (filename) {} -private: - Logrec_Reader task_; + // Base-class default construction occurs + { + // The module deletes the task + Logrec_Reader *task = new Logrec_Reader(filename); + parent::open(ACE_TEXT ("Logrec Reader"), + task, // Initialize writer-side task. + 0, // Ignore reader-side task. + 0, + parent::M_DELETE_WRITER); + } + + // NOTE: + // + // read the WARNING above in the Logrec_Module template }; class Logrec_Writer : public ACE_Task
|
|