From: D. L. <lo...@no...> - 2003-04-16 12:45:01
Attachments:
log4cpp.diff
|
Hi, I did some work on the shutdown/memory leak issue. My problem was, that after using log4cpp and the PropertyConfigurator I have lots of small memory leaks during application shutdown. Calling Category::shutdown will not help in this issue. I did some investigation and came up with the following solution: 1.) The PropertyConfigurator allocates all declared appenders in the configuration file, but will not transfer ownership to one of the categories. This results in the appenders not beeing deleted during shutdown, since the Categories have not set the _ownsAppender flag to true. There are two solutions to the problem: a.) transfer the ownership of the appender to at least one Category or introduce a dummy Category with the ownership of all instanciated appenders. b.) call Appender::_deleteAllAppenders somewhere during shutdown. This yields the question to whom the AppenderMap belongs, and who is responsible for deleting the appenders. one solution would be to do the deletion via a static variable within the Appender class, but this results in a circular dependancy with the HierarchyMaintainer. The HierarchyMaintainer exist earlier than the AppenderMap, and therefor will be deleted later. Within the destructor of the HierarchyMaintainer the deleteAllCategories function is called where all Categories and the contained appenders will be deleted. Deleting the AppenderMap before this call will result in a crash during destruction. One could force the creation of the AppenderMap before the creation of the HierarchyMaintainer, but this is also not good programming practice. I solved the problem right now via a explicit call to deleteAllAppenders during the shutdown of the HierarchyMaintainer. 2.) deletion of the AppenderMap itself Don't know how to that correct, yet. I added a delete call for Appender::_allAppenders into the Appender::_deleteAllAppenders () function, since the map isn't used after a call to this function. This should be better done via a specifc AppenderMap class, that can handle its contents and the map itself. And again, we must force this AppenderMap to exist before the HierarchyMaintainer, since we otherwise get shutdown oder dependancy. 3.) deletion of the NDC The ThreadLocalDataHolder should reset its content during destruction to delete the stored data object 4.) delete the HierarchyMaintainer. This is a curcial part, since the HierarchyMaintainer should delete all Categories during destruction. But I use a lot of static references to Categories within my application, that will be invalidated in a wrong shutdown sequence. The problem is, that there is no guarantee in wich order static variables are constructed. So simply adding a shutdown class to the HierarchyMainainter will not solve the problem, since an other static Category& variable could have been initialized before the shutdown member was initialized. First I thought to introduce the a LogManager (like in Log4j) but this doesn't solve the problem, only shifts it away from the HierarchyMaintainer. The I realized, that I must add an implicit order into the atexit sequence to install an atexit handler in the moment the first static Category& is iniitialized. This can be done with an auto_ptr in the HierarchyMaintainer like so: HierarchyMaintainer* HierarchyMaintainer::_defaultMaintainer = NULL; HierarchyMaintainer& HierarchyMaintainer::getDefaultMaintainer() { if (!_defaultMaintainer) { static std::auto_ptr<HierarchyMaintainer> maintainer (new HierarchyMaintainer()); _defaultMaintainer = maintainer.get (); } return *_defaultMaintainer; } By stuffing the allocated HierarchyMaintainer into a static auto_ptr within the getDefaultMaintainer function we will hook into the atexit queue in the moment, the first time this function is called, and that is exactly one element before the first static Category& that depends on the HierarchMaintainer. With this the HierarchyMaintainer will be deleted during application shutdown, and all resources are freed. On little problem still exists: What if after the destruction of the HierarchyMaintainer someone tries to get a reference to a Category. This again will fire up the HierarchyMaintainer resulting in the allocation of objects. The solution to this is not to use local references to Categories, only static references. A second solution would be to use the LogManager/LoggerRepository class from log4j, that introduces a variable repository for Logger objects. The default repository would be the HierarchyMaintainer. All calls to getCategory/getLogger are redirected through the LogManager. After shutdown one could return a static "do nothing" category from the LogManager. Then, if you call the LogManager::getLogger("whatever") after shutdown of the log4cpp library you get a valid logger, but this logger will not do anything and all references to static Categories are still valid until the HierarchyMaintainer is deleted. ----- As a second solution I would like to say, that the Log4cppCleanup class wasn't that bad at all. I think now, that it was was simply wrong allocated. If you would do it with the auto_ptr method as described above and make a call to the Log4cppCleanup::getInstance function from all allocations of static variables it would have worked. static Log4cppCleanup::instance* = NULL; Log4cppCleanup::getInstance () { if (!instance) { // the auto_ptr will force the deletion of the Log4cppCleanup // after all depending objects have been deleted static std::auto_ptr <Log4cppCleanup> autoInstace (new Log4cppCleanup); instance = autoInstace.get (); } return instance; } HierarchyMaintainer& HierarchyMaintainer::getDefaultMaintainer() { if (!_defaultMaintainer) { // force the Cleanup class into the atExit queue Log4cppCleanup::getInstance (); // don't need the auto_ptr here, since all cleanup is done // via Log4ccpCleanup _defaultMaintainer = new HierarchyMaintainer(); } return *_defaultMaintainer; } ---------- I attach a patch to the CVS checkout from 14.04.2003 to implement all that stuff, except the proper handling of local categories during shutdown returned from LogManager::getLogger (). The patch includes also a change for the Appender::getAllAppenders to return a vector<Appender*>* and an additional method for the FileAppender to return its name. An last but not least I included a file named dontdiff to use for building diffs, to exclude unwanted files in the diff: > diff -Naur -X log4cpp/dontdiff ... Perhaps you can give me some feedback about the two proposed solutions. Thanks Dirk |
From: Bastiaan B. <Bas...@li...> - 2003-04-22 16:43:43
|
Hi Dirk, Sorry for slow reply, I do appreciate your contribution, but my time has been consumed by a pending software release. I'll read and reply to your message more thoroughly in the coming days. Some short remarks: * I'm totally unhappy with ownsAppender stuff, and think of replacing it with boosts shared_ptr stuff. This may be applicable in other areas as well and simplify your work. * static variables in C++ shared libraries appear to be rather broken on some platforms. (One of the reasons for the odd locations of Log4cppCleanup). Maybe this will improve when we switch to libtool 1.5 * in general cleaning up at program exit looks nice but is a waste of resources (all remaining memory is going to be freed by the OS anyway). OTOH it's nice if the application programmer can choose for herself, for valgrinding,etc. . * to complicate things in some environments (COM?) cleanup needs to be done earlier, at DLL unload time rather than program exit. Not to discourage you ;-) I'll follow up on this later, Regards, Bastiaan Bakker On Wed, 2003-04-16 at 14:44, D. Luetjens wrote: > Hi, > > I did some work on the shutdown/memory leak issue. My problem was, that > after using log4cpp and the PropertyConfigurator I have lots of small > memory leaks during application shutdown. Calling Category::shutdown > will not help in this issue. I did some investigation and came up with > the following solution: > > 1.) The PropertyConfigurator allocates all declared appenders in the > configuration file, but will not transfer ownership to one of the > categories. This results in the appenders not beeing deleted during > shutdown, since the Categories have not set the _ownsAppender flag to true. > > There are two solutions to the problem: > a.) transfer the ownership of the appender to at least one Category or > introduce a dummy Category with the ownership of all instanciated appenders. > > b.) call Appender::_deleteAllAppenders somewhere during shutdown. > This yields the question to whom the AppenderMap belongs, and who is > responsible for deleting the appenders. one solution would be to do > the deletion via a static variable within the Appender class, but this > results in a circular dependancy with the HierarchyMaintainer. The > HierarchyMaintainer exist earlier than the AppenderMap, and therefor > will be deleted later. Within the destructor of the HierarchyMaintainer > the deleteAllCategories function is called where all Categories and the > contained appenders will be deleted. Deleting the AppenderMap before > this call will result in a crash during destruction. One could force the > creation of the AppenderMap before the creation of the > HierarchyMaintainer, but this is also not good programming practice. > > I solved the problem right now via a explicit call to deleteAllAppenders > during the shutdown of the HierarchyMaintainer. > > > > 2.) deletion of the AppenderMap itself > Don't know how to that correct, yet. I added a delete call for > Appender::_allAppenders into the Appender::_deleteAllAppenders () > function, since the map isn't used after a call to this function. > > This should be better done via a specifc AppenderMap class, that can > handle its contents and the map itself. And again, we must force this > AppenderMap to exist before the HierarchyMaintainer, since we otherwise > get shutdown oder dependancy. > > > 3.) deletion of the NDC > The ThreadLocalDataHolder should reset its content during destruction to > delete the stored data object > > 4.) delete the HierarchyMaintainer. > This is a curcial part, since the HierarchyMaintainer should delete all > Categories during destruction. But I use a lot of static references to > Categories within my application, that will be invalidated in a wrong > shutdown sequence. The problem is, that there is no guarantee in wich > order static variables are constructed. So simply adding a shutdown > class to the HierarchyMainainter will not solve the problem, since an > other static Category& variable could have been initialized before the > shutdown member was initialized. > > First I thought to introduce the a LogManager (like in Log4j) but this > doesn't solve the problem, only shifts it away from the > HierarchyMaintainer. The I realized, that I must add an implicit order > into the atexit sequence to install an atexit handler in the moment the > first static Category& is iniitialized. This can be done with an > auto_ptr in the HierarchyMaintainer like so: > > HierarchyMaintainer* HierarchyMaintainer::_defaultMaintainer = NULL; > > HierarchyMaintainer& HierarchyMaintainer::getDefaultMaintainer() { > > if (!_defaultMaintainer) { > static std::auto_ptr<HierarchyMaintainer> maintainer (new > HierarchyMaintainer()); > _defaultMaintainer = maintainer.get (); > } > > return *_defaultMaintainer; > } > > By stuffing the allocated HierarchyMaintainer into a static auto_ptr > within the getDefaultMaintainer function we will hook into the atexit > queue in the moment, the first time this function is called, and that is > exactly one element before the first static Category& that depends on > the HierarchMaintainer. > > With this the HierarchyMaintainer will be deleted during application > shutdown, and all resources are freed. > > On little problem still exists: What if after the destruction of the > HierarchyMaintainer someone tries to get a reference to a Category. This > again will fire up the HierarchyMaintainer resulting in the allocation > of objects. > > The solution to this is not to use local references to Categories, only > static references. > > A second solution would be to use the LogManager/LoggerRepository class > from log4j, that introduces a variable repository for Logger objects. > The default repository would be the HierarchyMaintainer. All calls to > getCategory/getLogger are redirected through the LogManager. After > shutdown one could return a static "do nothing" category from the > LogManager. Then, if you call the LogManager::getLogger("whatever") > after shutdown of the log4cpp library you get a valid logger, but this > logger will not do anything and all references to static Categories are > still valid until the HierarchyMaintainer is deleted. > > > ----- > > As a second solution I would like to say, that the Log4cppCleanup class > wasn't that bad at all. I think now, that it was was simply wrong > allocated. If you would do it with the auto_ptr method as described > above and make a call to the Log4cppCleanup::getInstance function from > all allocations of static variables it would have worked. > > static Log4cppCleanup::instance* = NULL; > > Log4cppCleanup::getInstance () > { > if (!instance) > { > // the auto_ptr will force the deletion of the Log4cppCleanup > // after all depending objects have been deleted > static std::auto_ptr <Log4cppCleanup> autoInstace (new Log4cppCleanup); > > instance = autoInstace.get (); > } > return instance; > } > > HierarchyMaintainer& HierarchyMaintainer::getDefaultMaintainer() > { > if (!_defaultMaintainer) > { > // force the Cleanup class into the atExit queue > Log4cppCleanup::getInstance (); > > // don't need the auto_ptr here, since all cleanup is done > // via Log4ccpCleanup > _defaultMaintainer = new HierarchyMaintainer(); > } > return *_defaultMaintainer; > } > > ---------- > > > I attach a patch to the CVS checkout from 14.04.2003 to implement all > that stuff, except the proper handling of local categories during > shutdown returned from LogManager::getLogger (). > > The patch includes also a change for the Appender::getAllAppenders to > return a vector<Appender*>* and an additional method for the > FileAppender to return its name. An last but not least I included a file > named dontdiff to use for building diffs, to exclude unwanted files in > the diff: > > > diff -Naur -X log4cpp/dontdiff ... > > Perhaps you can give me some feedback about the two proposed solutions. > > Thanks > Dirk > > > > ______________________________________________________________________ > > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/dontdiff log4cpp/dontdiff > --- SourceForge/log4cpp/dontdiff 1970-01-01 01:00:00.000000000 +0100 > +++ log4cpp/dontdiff 2003-04-16 12:22:36.000000000 +0200 > @@ -0,0 +1,8 @@ > +*.ncb > +*.dsp > +*.dsw > +*.plg > +Debug > +Release > +CVS > +vssver.scc > \ No newline at end of file > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/Appender.hh log4cpp/include/log4cpp/Appender.hh > --- SourceForge/log4cpp/include/log4cpp/Appender.hh 2002-10-27 02:48:52.000000000 +0100 > +++ log4cpp/include/log4cpp/Appender.hh 2003-04-16 11:26:04.000000000 +0200 > @@ -14,6 +14,7 @@ > #include <string> > #include <map> > #include <set> > +#include <vector> > #include <stdarg.h> > #include <sys/types.h> > #include <sys/stat.h> > @@ -53,6 +54,15 @@ > **/ > static void closeAll(); > > + /** > + * Returns a vector of currently defined Appenders. > + * Note: this function does not pass ownership of the appenders in > + * the vector to the caller, only the ownership of the vector. > + * @since 0.3.1 > + * @returns The vector of active Appenders. > + **/ > + static std::vector<Appender*>* getAllAppenders(); > + > protected: > /** > * Constructor for Appender. Will only be used in getAppender() (and > @@ -130,6 +140,14 @@ > **/ > virtual Filter* getFilter() = 0; > > + > + /** > + * deletes all Appenders > + * @returns > + **/ > + static void deleteAllAppenders (); > + > + > private: > typedef std::map<std::string, Appender*> AppenderMap; > > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/Category.hh log4cpp/include/log4cpp/Category.hh > --- SourceForge/log4cpp/include/log4cpp/Category.hh 2002-10-27 02:38:15.000000000 +0100 > +++ log4cpp/include/log4cpp/Category.hh 2003-04-15 12:16:11.000000000 +0200 > @@ -199,11 +199,13 @@ > virtual Appender* getAppender(const std::string& name) const; > > /** > - * Returns the set of Appenders currently attached to this Catogory. > + * Returns a vector of Appenders currently attached to this Catogory. > + * Note: this function does not pass ownership of the appenders in > + * the vector to the caller, only the ownership of the vector. > * @since 0.3.1 > - * @returns The set of attached Appenders. > + * @returns The vector of attached Appenders. > **/ > - virtual AppenderSet getAllAppenders() const; > + virtual std::vector<Appender*>* getAllAppenders() const; > > /** > * Removes all appenders for this Category. > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/FileAppender.hh log4cpp/include/log4cpp/FileAppender.hh > --- SourceForge/log4cpp/include/log4cpp/FileAppender.hh 2002-03-22 22:30:07.000000000 +0100 > +++ log4cpp/include/log4cpp/FileAppender.hh 2003-02-27 16:42:25.000000000 +0100 > @@ -41,6 +41,12 @@ > virtual ~FileAppender(); > > /** > + * Get the filename of this appender. > + * @returns the filename of the appender. > + **/ > + inline const std::string& getFileName() const { return _fileName; }; > + > + /** > Reopens the logfile. > This can be useful for logfiles that are rotated externally, > e.g. by logrotate. This method is a NOOP for FileAppenders that > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/FixedContextCategory.hh log4cpp/include/log4cpp/FixedContextCategory.hh > --- SourceForge/log4cpp/include/log4cpp/FixedContextCategory.hh 2002-07-03 23:55:52.000000000 +0200 > +++ log4cpp/include/log4cpp/FixedContextCategory.hh 2003-02-27 14:42:38.000000000 +0100 > @@ -104,7 +104,7 @@ > * @since 0.3.1 > * @returns The set of attached Appenders. > **/ > - virtual AppenderSet getAllAppenders() const; > + virtual std::vector<Appender*>* getAllAppenders() const; > > /** > * Removes all appenders set for this Category. Currently a Category > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/HierarchyMaintainer.hh log4cpp/include/log4cpp/HierarchyMaintainer.hh > --- SourceForge/log4cpp/include/log4cpp/HierarchyMaintainer.hh 2002-10-27 02:48:52.000000000 +0100 > +++ log4cpp/include/log4cpp/HierarchyMaintainer.hh 2003-04-15 12:08:21.000000000 +0200 > @@ -15,6 +15,7 @@ > #include <map> > #include <vector> > #include <log4cpp/Category.hh> > +#include <log4cpp/LoggerRepository.hh> > #include <log4cpp/threading/Threading.hh> > > namespace log4cpp { > @@ -24,8 +25,7 @@ > * for maintaining the hierarchy of Categories. Applications should > * not have to use this class directly. > **/ > - class HierarchyMaintainer { > - friend class Log4cppCleanup; > + class HierarchyMaintainer : public LoggerRepository { > > public: > typedef std::map<std::string, Category*> CategoryMap; > @@ -36,8 +36,28 @@ > virtual ~HierarchyMaintainer(); > virtual Category* getExistingInstance(const std::string& name); > virtual Category& getInstance(const std::string& name); > + > virtual std::vector<Category*>* getCurrentCategories() const; > + > + > + // interface for the LoggerRepository > + virtual Category& getRootLogger() { > + return getInstance (""); > + } > + virtual Category& getLogger(const std::string& name) { > + return getInstance (name); > + } > + virtual Category* exists(const std::string& name) { > + return getExistingInstance (name); > + } > + virtual std::vector<Category*>* getCurrentLoggers() const { > + return getCurrentCategories (); > + } > + > virtual void shutdown(); > + // end interface for the LoggerRepository > + > + > virtual void deleteAllCategories(); > > protected: > @@ -47,7 +67,7 @@ > mutable threading::Mutex _categoryMutex; > > private: > - static HierarchyMaintainer* _defaultMaintainer; > + static HierarchyMaintainer* _defaultMaintainer; > }; > } > > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/LogManager.hh log4cpp/include/log4cpp/LogManager.hh > --- SourceForge/log4cpp/include/log4cpp/LogManager.hh 1970-01-01 01:00:00.000000000 +0100 > +++ log4cpp/include/log4cpp/LogManager.hh 2003-04-15 11:53:38.000000000 +0200 > @@ -0,0 +1,104 @@ > +/* > + * LogManager.hh > + * > + * See the COPYING file for the terms of usage and distribution. > + */ > + > +#ifndef _LOG4CPP_LOGMANAGER_HH > +#define _LOG4CPP_LOGMANAGER_HH > + > +#include <log4cpp/LoggerRepository.hh> > +#include <log4cpp/Category.hh> > + > + > +namespace log4cpp { > + > + /** > + * abstract base class to retrieve a LoggerRepository > + */ > + class RepositorySelector > + { > + public: > + virtual LoggerRepository& getLoggerRepository() = 0; > + }; > + > + /** > + * encapsulates and returns the default repository selector > + */ > + class DefaultRepositorySelector : public RepositorySelector > + { > + public: > + DefaultRepositorySelector (LoggerRepository& repository) > + : _loggerRepository (repository) > + { } > + > + virtual LoggerRepository& getLoggerRepository() > + { > + return _loggerRepository; > + } > + > + private: > + LoggerRepository& _loggerRepository; > + }; > + > + > + /** > + * Use the <code>LogManager</code> class to retreive {@link Logger} > + * instances or to operate on the current {@link LoggerRepository}. > + * > + * @author log4j: Ceki Gülcü > + **/ > + class LOG4CPP_EXPORT LogManager { > + public: > + /** > + Returns the underlying repository > + > + @returns the underlying repository > + */ > + static LoggerRepository& getLoggerRepository(); > + > + /** > + Retrieve the appropriate root logger. > + */ > + static Category& getRootLogger() { > + // Delegate the actual manufacturing of the logger to the logger repository. > + return getLoggerRepository().getRootLogger(); > + } > + > + /** > + Retrieve the appropriate {@link Logger} instance. > + */ > + static Category& getLogger(const std::string& name) { > + // Delegate the actual manufacturing of the logger to the logger repository. > + return getLoggerRepository().getLogger(name); > + } > + > + /** > + Check if the named logger exists in the hierarchy. If so return > + its reference, otherwise returns <code>null</code>. > + > + @param name The name of the logger to search for. > + */ > + static Category* exists(const std::string& name) { > + return getLoggerRepository().exists(name); > + } > + > + > + static std::vector<Category*>* getCurrentLoggers() { > + return getLoggerRepository().getCurrentLoggers(); > + } > + > + > + /** > + shutdown the logging API. After calling shutdown, one should not call any other > + function of this class. > + */ > + static void shutdown(); > + > + > + private: > + static RepositorySelector* _repositorySelector; > + }; > +} > + > +#endif // _LOG4CPP_LOGMANAGER_HH > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/LoggerRepository.hh log4cpp/include/log4cpp/LoggerRepository.hh > --- SourceForge/log4cpp/include/log4cpp/LoggerRepository.hh 1970-01-01 01:00:00.000000000 +0100 > +++ log4cpp/include/log4cpp/LoggerRepository.hh 2003-04-15 11:53:23.000000000 +0200 > @@ -0,0 +1,51 @@ > +/* > + * LogManager.hh > + * > + * See the COPYING file for the terms of usage and distribution. > + */ > + > +#ifndef _LOG4CPP_LOGGERREPOSITORY_HH > +#define _LOG4CPP_LOGGERREPOSITORY_HH > + > +#include <log4cpp/Category.hh> > + > + > +namespace log4cpp { > + > + /** > + * abstract base class for the LoggerRepository > + */ > + class LOG4CPP_EXPORT LoggerRepository > + { > + public: > + /** > + Retrieve the appropriate root logger. > + */ > + virtual Category& getRootLogger() = 0; > + > + /** > + Retrieve the appropriate {@link Logger} instance. > + > + @param name The name of the logger to search for. > + */ > + virtual Category& getLogger(const std::string& name) = 0; > + > + /** > + Check if the named logger exists in the hierarchy. If so return > + its reference, otherwise returns <code>null</code>. > + > + @param name The name of the logger to search for. > + */ > + virtual Category* exists(const std::string& name) = 0; > + > + > + virtual std::vector<Category*>* getCurrentLoggers() const = 0; > + > + /** > + * shutdown the Repository. > + */ > + virtual void shutdown() = 0; > + }; > +} > + > +#endif // _LOG4CPP_LOGGERREPOSITORY_HH > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/include/log4cpp/threading/MSThreads.hh log4cpp/include/log4cpp/threading/MSThreads.hh > --- SourceForge/log4cpp/include/log4cpp/threading/MSThreads.hh 2002-10-10 17:46:36.000000000 +0200 > +++ log4cpp/include/log4cpp/threading/MSThreads.hh 2003-04-15 16:23:47.000000000 +0200 > @@ -103,7 +103,7 @@ > inline ThreadLocalDataHolder() : > _key(TlsAlloc()) {}; > > - inline ~ThreadLocalDataHolder() { TlsFree(_key); }; > + inline ~ThreadLocalDataHolder() { reset (); TlsFree(_key); }; > > /** > * Obtains the Object held for the current thread. > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/src/Appender.cpp log4cpp/src/Appender.cpp > --- SourceForge/log4cpp/src/Appender.cpp 2002-10-27 02:48:52.000000000 +0100 > +++ log4cpp/src/Appender.cpp 2003-04-16 11:26:04.000000000 +0200 > @@ -11,13 +11,13 @@ > #include <log4cpp/Appender.hh> > > namespace log4cpp { > - Appender::AppenderMap* Appender::_allAppenders; > + Appender::AppenderMap* Appender::_allAppenders = NULL; > threading::Mutex Appender::_appenderMapMutex; > > /* assume _appenderMapMutex locked */ > Appender::AppenderMap& Appender::_getAllAppenders() { > if (!_allAppenders) > - _allAppenders = new Appender::AppenderMap(); > + _allAppenders = new Appender::AppenderMap(); > > return *_allAppenders; > } > @@ -59,14 +59,22 @@ > } > } > > - void Appender::_deleteAllAppenders() { > + void Appender::deleteAllAppenders() { > threading::ScopedLock lock(_appenderMapMutex); > + _deleteAllAppenders (); > + } > + > + void Appender::_deleteAllAppenders() { > AppenderMap& allAppenders = _getAllAppenders(); > for(AppenderMap::iterator i = allAppenders.begin(); i != allAppenders.end(); ) { > Appender *app = (*i).second; > i++; // increment iterator before delete or iterator will be invalid. > delete (app); > } > + > + // we need to delete the map also > + delete _allAppenders; > + _allAppenders = NULL; > } > > Appender::Appender(const std::string& name) : > @@ -77,4 +85,20 @@ > Appender::~Appender() { > _removeAppender(this); > } > + > + std::vector<Appender*>* Appender::getAllAppenders() { > + threading::ScopedLock lock(_appenderMapMutex); > + AppenderMap& allAppenders = _getAllAppenders(); > + > + std::vector<Appender*>* appenderVector = new std::vector<Appender*>; > + appenderVector->reserve(allAppenders.size()); > + > + for(AppenderMap::const_iterator i = allAppenders.begin(); i != allAppenders.end(); i++) { > + appenderVector->push_back((*i).second); > + } > + > + return appenderVector; > + } > + > + > } > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/src/Category.cpp log4cpp/src/Category.cpp > --- SourceForge/log4cpp/src/Category.cpp 2002-10-27 02:38:15.000000000 +0100 > +++ log4cpp/src/Category.cpp 2003-04-14 17:30:44.000000000 +0200 > @@ -14,7 +14,7 @@ > #endif > > #include <log4cpp/Category.hh> > -#include <log4cpp/HierarchyMaintainer.hh> > +#include <log4cpp/LogManager.hh> > #include <log4cpp/NDC.hh> > #include "StringUtil.hh" > > @@ -33,20 +33,19 @@ > } > > Category& Category::getInstance(const std::string& name) { > - return HierarchyMaintainer::getDefaultMaintainer().getInstance(name); > + return LogManager::getLogger(name); > } > > Category* Category::exists(const std::string& name) { > - return HierarchyMaintainer::getDefaultMaintainer().getExistingInstance(name); > + return LogManager::exists(name); > } > > std::vector<Category*>* Category::getCurrentCategories() { > - return HierarchyMaintainer::getDefaultMaintainer(). > - getCurrentCategories(); > + return LogManager::getCurrentLoggers(); > } > > void Category::shutdown() { > - HierarchyMaintainer::getDefaultMaintainer().shutdown(); > + LogManager::shutdown(); > } > > Category::Category(const std::string& name, Category* parent, Priority::Value priority) : > @@ -141,10 +140,19 @@ > } > } > > - AppenderSet Category::getAllAppenders() const { > - threading::ScopedLock lock(_appenderSetMutex); > + std::vector<Appender*>* Category::getAllAppenders() const { > { > - return _appender; > + std::vector<Appender*>* appender = new std::vector<Appender*>; > + appender->reserve(_appender.size()); > + > + threading::ScopedLock lock(_appenderSetMutex); > + { > + for(AppenderSet::const_iterator i = _appender.begin(); i != _appender.end(); i++) { > + appender->push_back(*i); > + } > + } > + > + return appender; > } > } > > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/src/FixedContextCategory.cpp log4cpp/src/FixedContextCategory.cpp > --- SourceForge/log4cpp/src/FixedContextCategory.cpp 2002-10-26 20:30:55.000000000 +0200 > +++ log4cpp/src/FixedContextCategory.cpp 2003-02-27 14:42:48.000000000 +0100 > @@ -61,7 +61,7 @@ > return _delegate.getAppender(name); > } > > - AppenderSet FixedContextCategory::getAllAppenders() const { > + std::vector<Appender*>* FixedContextCategory::getAllAppenders() const { > return _delegate.getAllAppenders(); > } > > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/src/HierarchyMaintainer.cpp log4cpp/src/HierarchyMaintainer.cpp > --- SourceForge/log4cpp/src/HierarchyMaintainer.cpp 2003-04-16 13:03:11.000000000 +0200 > +++ log4cpp/src/HierarchyMaintainer.cpp 2003-04-16 11:26:04.000000000 +0200 > @@ -22,10 +22,17 @@ > > namespace log4cpp { > > + HierarchyMaintainer* HierarchyMaintainer::_defaultMaintainer = NULL; > + > HierarchyMaintainer& HierarchyMaintainer::getDefaultMaintainer() { > - static HierarchyMaintainer defaultMaintainer; > > - return defaultMaintainer; > + if (!_defaultMaintainer) > + { > + static std::auto_ptr<HierarchyMaintainer> maintainer (new HierarchyMaintainer()); > + _defaultMaintainer = maintainer.get (); > + } > + > + return *_defaultMaintainer; > } > > HierarchyMaintainer::HierarchyMaintainer() { > @@ -34,6 +41,11 @@ > HierarchyMaintainer::~HierarchyMaintainer() { > shutdown(); > deleteAllCategories(); > + > + // also delete the appender map: This should be moved to a better place > + Appender::deleteAllAppenders(); > + > + _defaultMaintainer = NULL; > } > > Category* HierarchyMaintainer::getExistingInstance(const std::string& name) { > @@ -65,6 +77,7 @@ > if (NULL == result) { > if (name == "") { > result = new Category(name, NULL, Priority::INFO); > + result->addAppender(new FileAppender("_", ::dup(fileno(stderr)))); > } else { > std::string parentName; > size_t dotIndex = name.find_last_of('.'); > @@ -109,6 +122,7 @@ > for(CategoryMap::const_iterator i = _categoryMap.begin(); i != _categoryMap.end(); i++) { > delete ((*i).second); > } > + _categoryMap.clear(); > } > } > > diff -Naur -Xlog4cpp/dontdiff SourceForge/log4cpp/src/LogManager.cpp log4cpp/src/LogManager.cpp > --- SourceForge/log4cpp/src/LogManager.cpp 1970-01-01 01:00:00.000000000 +0100 > +++ log4cpp/src/LogManager.cpp 2003-04-16 11:53:49.000000000 +0200 > @@ -0,0 +1,38 @@ > +/* > + * LogManager.cpp > + * > + * See the COPYING file for the terms of usage and distribution. > + */ > + > +#include <log4cpp/LogManager.hh> > +#include <log4cpp/HierarchyMaintainer.hh> > + > +namespace log4cpp { > + > + RepositorySelector* LogManager::_repositorySelector = NULL; > + > + LoggerRepository& LogManager::getLoggerRepository() > + { > + if (!_repositorySelector) > + { > + // auto deletion of the _repositorySelector > + static std::auto_ptr<RepositorySelector> autoRepositorySelector (new DefaultRepositorySelector (HierarchyMaintainer::getDefaultMaintainer ())); > + > + _repositorySelector = autoRepositorySelector.get(); > + } > + > + return _repositorySelector->getLoggerRepository(); > + } > + > + void LogManager::shutdown() > + { > + if (_repositorySelector) > + { > + getLoggerRepository().shutdown(); > + > +// delete _repositorySelector; > +// _repositorySelector = NULL; > + } > + } > + > +} > \ No newline at end of file -- Bastiaan Bakker <Bas...@li...> LifeLine Networks bv |
From: Jon S. <jst...@ho...> - 2003-04-22 19:58:36
|
Quoth Bastiaan: > * in general cleaning up at program exit looks nice but is a waste of > resources (all remaining memory is going to be freed by the OS anyway). > OTOH it's nice if the application programmer can choose for herself, for > valgrinding,etc. . With all due respect -- I find log4cpp an invaluable tool for hunting down race conditions -- I must disagree with this statement. I've been fairly troubled troubled by log4cpp's memory leaks. Aesthetically/morally, C++ dictates that thou shalt delete thine memory. Not to do so is a bug. That's my brand of religion and I'm sticking to it. But it's an ecumenical world, and I don't want to get into a religious war. So, my *pragmatic* reason for being troubled by this sentiment (one I also had to deal with from a former coworker) is that these static memory leaks mask a user's own memory leaks. Visual Studio now tells you, somewhat unhelpfully, about memory leaks at process shutdown. All kind of tools, like Purify and Boundschecker, also do this job (in a much better way). When one is trying to use these tools to enforce a strict policy of no memory leaks in the repository, it's very difficult to separate one's own memory leaks from third party memory leaks. That's a good policy to have, as C++ is a systems language and systems typcially can't afford to have memory leaks; if you can kill memory leaks from the start, you're going to have a better chance of shipping. It would be extremely nice if log4cpp accomodated that policy. As far as implementation goes, when a number of logical singletons exist with dependencies between them, I generally prefer wrapping them in a "singleton context". That is, create one singleton class who's sole purpose is to create, destroy, and provide access to a system's logical singletons. One can then construct this singleton context and throw it into a static boost::scoped_ptr and be assured that everything will be taken care of at the appropriate time. I just got laid off. I've got a lot of work to do finding a new job, but I'd be happy to work on this problem with someone else (Dirk?). My $.02. Jon |
From: Bastiaan B. <bas...@li...> - 2003-04-22 23:15:19
|
On Tue, 2003-04-22 at 21:58, Jon Stewart wrote: > Quoth Bastiaan: > > * in general cleaning up at program exit looks nice but is a waste of > > resources (all remaining memory is going to be freed by the OS anyway). > > OTOH it's nice if the application programmer can choose for herself, for > > valgrinding,etc. . > > With all due respect -- I find log4cpp an invaluable tool for hunting down > race conditions -- I must disagree with this statement. I've been fairly > troubled troubled by log4cpp's memory leaks. > double plus troubled, that's bad ;-) > Aesthetically/morally, C++ dictates that thou shalt delete thine memory. Not Not to be nitpicking, but it does not per se . You're perfectly free to leave that to a garbage collector for example. Using one would put an end to so much waisting time with memory administrivia. Unfortunately it is not an option of a library like log4cpp to force that upon the application programmer. > to do so is a bug. That's my brand of religion and I'm sticking to it. I would want to interfere with your religious beliefs :-) > But it's an ecumenical world, and I don't want to get into a religious war. > So, my *pragmatic* reason for being troubled by this sentiment (one I also > had to deal with from a former coworker) is that these static memory leaks > mask a user's own memory leaks. Visual Studio now tells you, somewhat One way in which the static leaks differ from the dynamic ones, is that the latter grow over time, automically making themselves the center of attention with a decent leak checker. > unhelpfully, about memory leaks at process shutdown. All kind of tools, like > Purify and Boundschecker, also do this job (in a much better way). When one > is trying to use these tools to enforce a strict policy of no memory leaks > in the repository, it's very difficult to separate one's own memory leaks > from third party memory leaks. That's a good policy to have, as C++ is a > systems language and systems typcially can't afford to have memory leaks; if > you can kill memory leaks from the start, you're going to have a better > chance of shipping. It would be extremely nice if log4cpp accomodated that > policy. > Yes, it would. It just hasn't been a priority (for me), since the services in which I use log4cpp are based on omniORB, which until recently couldn't cleanup properly at exit time. One reason for my remark (other than to camouflage that I've been too lazy to work on the problem) is that I feel that it's a minor problem compared to other things in log4cpp that may need attention, e.g.: * finish multithreading support. Currently it's half baked in half of the classes. * Define a clean separation between API, SPI and internal stuff. * Improve documentation * Improve build settings (e.g. proper multithreading flags for g++ 3.x) * make PropertyConfigurator extensible for third party Appenders, etc. * interoperability with log4j's SocketAppender (in both directions) > As far as implementation goes, when a number of logical singletons exist > with dependencies between them, I generally prefer wrapping them in a > "singleton context". That is, create one singleton class who's sole purpose > is to create, destroy, and provide access to a system's logical singletons. > One can then construct this singleton context and throw it into a static > boost::scoped_ptr and be assured that everything will be taken care of at > the appropriate time. > Yes, but also all references to log Categories need to be taken into account, since the destructors of static objects may want to log as well. So I guess all Category& occurences need to be replaced with boost::shared_ptr's or similar. That's not a trivial change..... but for 0.4.x it should be OK. > I just got laid off. I've got a lot of work to do finding a new job, but I'd Sorry to hear that. > be happy to work on this problem with someone else (Dirk?). > Great! Since you mentioned boost::scoped_ptr I guess you have experience with boost? I would like to use it in log4cpp but have no experience in integrating it in a project (autoconf detection, compiler settings, etc.) Regards, Bastiaan > My $.02. > > > Jon > > > > > ------------------------------------------------------- > This sf.net email is sponsored by:ThinkGeek > Welcome to geek heaven. > http://thinkgeek.com/sf > _______________________________________________ > Log4cpp-devel mailing list > Log...@li... > https://lists.sourceforge.net/lists/listinfo/log4cpp-devel |
From: D. L. <lo...@no...> - 2003-04-29 09:54:37
|
Hi Bastiaan, hi Jon >>>* in general cleaning up at program exit looks nice but is a waste of >>>resources (all remaining memory is going to be freed by the OS anyway). >>>OTOH it's nice if the application programmer can choose for herself, for >>>valgrinding,etc. . >> >>With all due respect -- I find log4cpp an invaluable tool for hunting down >>race conditions -- I must disagree with this statement. I've been fairly >>troubled troubled by log4cpp's memory leaks. Same holds true for me. I was teached by my parents to leave the toilet room after usage in a way I would like to see it when I want using it, even if a cleaner will regularyly show up and remove people's 'aehm' mess. And second, I do get a lot of troubles of my colleques if I introduce a libarary, that does not clean up correctly upon exit. You know, those little friendly annoyances day by day. Since my collegues started to tease me, I did the modifications. > Not to be nitpicking, but it does not per se . You're perfectly free to > leave that to a garbage collector for example. Using one would put an > end to so much waisting time with memory administrivia. Unfortunately it > is not an option of a library like log4cpp to force that upon the > application programmer. Yes, but there is no built in garbage collection in C++. One have to choose a different language if he/she wants to use a garbage collector. > One way in which the static leaks differ from the dynamic ones, is that > the latter grow over time, automically making themselves the center of > attention with a decent leak checker. It must not be a dynamic leak, that increases over time. There are static leaks that can be of interest, since something is not destructed correctly upon program exit. I really like the times of winNT where I can allocate memory, do whatever I want to do, open files, alloc semaphores, and whatever. shift-F5 and we have a clean state again. Without any reboot, day by day. (I'm not a win32 fanatic, I simply like the VisualStudio). I'm not sure, wether a stable execution of an environment is possible, if everybody is not cleaning up after himself. > One reason for my remark (other than to camouflage that I've been too > lazy to work on the problem) is that I feel that it's a minor problem Ok, I did it ;-), since I don't have the other problems you describe. > * make PropertyConfigurator extensible for third party Appenders, etc. Yes, have some interest in this, too. * a MDC (mapped diagnostic context) to store tracing level indentation * a Win32 control to configure the logging API * Appenders that can log events into a CListCtrl with column selection and all that stuff. I use the CEditLog from (http://www.codeproject.com/editctrl/editlog.asp) to log formatted messages to an application provided CEditCtrl right now. You mentioned COM need to shutdown earlier than program exit (DllDetach). I have no experience on this. I just found that someone else also had a simliar problem in a Win32 environment and suggested a solution with a more or less private AtExit queue. The article can be found here: http://www.codeproject.com/cpp/atinitexitinstance.asp > Yes, but also all references to log Categories need to be taken into > account, since the destructors of static objects may want to log as > well. So I guess all Category& occurences need to be replaced with > boost::shared_ptr's or similar. > That's not a trivial change..... but for 0.4.x it should be OK. I thought of that, too. But as you said, it is not a trivial change, and further it has some external visible changes. Therefor I decided to cleanup within the library, and stick to the policy to only use static Categories in static objects destructors. In this case the shutdown procedure will be delayed until the last destructor using the logging API is executed. >>I just got laid off. I've got a lot of work to do finding a new job, but I'd I which you lots of luck. >>be happy to work on this problem with someone else (Dirk?). I haven't used boost much up to now, I have looked into the sources to understand the concepts behind it. But I would like to help. Dirk |