[prev in list] [next in list] [prev in thread] [next in thread] 

List:       log4cxx-dev
Subject:    svn commit: r580508 - in /logging/log4cxx/trunk/src: main/cpp/
From:       carnold () apache ! org
Date:       2007-09-28 22:57:58
Message-ID: 20070928225801.0C7011A9832 () eris ! apache ! org
[Download RAW message or body]

Author: carnold
Date: Fri Sep 28 15:57:53 2007
New Revision: 580508

URL: http://svn.apache.org/viewvc?rev=580508&view=rev
Log:
LOGCXX-129: AsyncAppender is full of race conditions

Added:
    logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp
    logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h
    logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml   (with \
props) Modified:
    logging/log4cxx/trunk/src/main/cpp/Makefile.am
    logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp
    logging/log4cxx/trunk/src/main/cpp/condition.cpp
    logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp
    logging/log4cxx/trunk/src/main/cpp/exception.cpp
    logging/log4cxx/trunk/src/main/cpp/file.cpp
    logging/log4cxx/trunk/src/main/cpp/thread.cpp
    logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h
    logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h
    logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h
    logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h
    logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h
    logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp
    logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp

Modified: logging/log4cxx/trunk/src/main/cpp/Makefile.am
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/Makefile.am?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/Makefile.am (original)
+++ logging/log4cxx/trunk/src/main/cpp/Makefile.am Fri Sep 28 15:57:53 2007
@@ -150,6 +150,7 @@
         systemoutwriter.cpp \
         telnetappender.cpp \
         thread.cpp \
+        threadlocal.cpp \
         threadspecificdata.cpp \
         threadpatternconverter.cpp \
         throwableinformationpatternconverter.cpp \

Modified: logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/asyncappender.cpp Fri Sep 28 15:57:53 2007
@@ -16,6 +16,8 @@
  */
 
 #include <log4cxx/asyncappender.h>
+
+
 #include <log4cxx/helpers/loglog.h>
 #include <log4cxx/spi/loggingevent.h>
 #include <apr_thread_proc.h>
@@ -23,7 +25,9 @@
 #include <apr_thread_cond.h>
 #include <log4cxx/helpers/condition.h>
 #include <log4cxx/helpers/synchronized.h>
+#include <log4cxx/helpers/stringhelper.h>
 #include <apr_atomic.h>
+#include <log4cxx/helpers/optionconverter.h>
 
 
 using namespace log4cxx;
@@ -38,15 +42,17 @@
 
 AsyncAppender::AsyncAppender()
 : AppenderSkeleton(),
-  pool(),
-  queue(),
-  size(DEFAULT_BUFFER_SIZE),
-  available(pool),
-  pending(pool),
-  thread(),
-  locationInfo(true),
-  aai(new AppenderAttachableImpl()) {
-  thread.run(dispatch, this);
+  buffer(),
+  bufferMutex(pool),
+  bufferNotFull(pool),
+  bufferNotEmpty(pool),
+  discardMap(),
+  bufferSize(DEFAULT_BUFFER_SIZE),
+  appenders(new AppenderAttachableImpl()),
+  dispatcher(),
+  locationInfo(false),
+  blocking(true) {
+  dispatcher.run(dispatch, this);
 }
 
 AsyncAppender::~AsyncAppender()
@@ -56,157 +62,283 @@
 
 void AsyncAppender::addAppender(const AppenderPtr& newAppender)
 {
-        synchronized sync(aai->getMutex());
-        aai->addAppender(newAppender);
+        synchronized sync(appenders->getMutex());
+        appenders->addAppender(newAppender);
 }
 
-void AsyncAppender::append(const spi::LoggingEventPtr& event, Pool& /* p */ )
-{
+
+void AsyncAppender::setOption(const LogString& option,
+        const LogString& value) {
+        if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOCATIONINFO"), \
LOG4CXX_STR("locationinfo"))) { +             \
setLocationInfo(OptionConverter::toBoolean(value, false)); +        }
+        if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BUFFERSIZE"), \
LOG4CXX_STR("buffersize"))) { +             \
setBufferSize(OptionConverter::toInt(value, DEFAULT_BUFFER_SIZE)); +        }
+        if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("BLOCKING"), \
LOG4CXX_STR("blocking"))) { +             \
setBlocking(OptionConverter::toBoolean(value, true)); +        } else {
+             AppenderSkeleton::setOption(option, value);
+        }
+}
+
+
+void AsyncAppender::append(const spi::LoggingEventPtr& event, Pool& p) {
+        //
+        //   if dispatcher has died then
+        //      append subsequent events synchronously
+        //
+        if (!dispatcher.isAlive() || bufferSize <= 0) {
+            synchronized sync(appenders->getMutex());
+            appenders->appendLoopOnAppenders(event, p);
+            return;
+        }
+
         // Set the NDC and thread name for the calling thread as these
         // LoggingEvent fields were not set at event creation time.
         event->getNDC();
+        event->getThreadName();
         // Get a copy of this thread's MDC.
         event->getMDCCopy();
 
 
-        //
-        //   will block if queue is full
-        //
-        size_t count = 0;
-        bool full = false;
         {
-                synchronized sync(mutex);
-                count = queue.size();
-                full = count >= size;
-                if (!full) {
-                        queue.push_back(event);
+             synchronized sync(bufferMutex);
+             while(true) {
+                 int previousSize = buffer.size();
+                 if (previousSize < bufferSize) {
+                     buffer.push_back(event);
+                     if (previousSize == 0) {
+                        bufferNotEmpty.signalAll();
+                     }
+                     break;
+                 }
+             
+                //
+                //   Following code is only reachable if buffer is full
+                //
+                //
+                //   if blocking and thread is not already interrupted
+                //      and not the dispatcher then
+                //      wait for a buffer notification
+                bool discard = true;
+                if (blocking
+                    && !Thread::interrupted()
+                    && !dispatcher.isCurrentThread()) {
+                    try {
+                        bufferNotFull.await(bufferMutex);
+                        discard = false;
+                    } catch (InterruptedException& e) {
+                        //
+                        //  reset interrupt status so
+                        //    calling code can see interrupt on
+                        //    their next wait or sleep.
+                        Thread::currentThreadInterrupt();
+                    }
                 }
-        }
 
-        //
-        //   if the queue had been empty then
-        //      notify that there are messages pending
-        if (count == 0) {
-                pending.broadcast();
+                //
+                //   if blocking is false or thread has been interrupted
+                //   add event to discard map.
+                //
+                if (discard) {
+                    LogString loggerName = event->getLoggerName();
+                    DiscardMap::iterator iter = discardMap.find(loggerName);
+                    if (iter == discardMap.end()) {
+                        DiscardSummary summary(event);
+                        discardMap.insert(DiscardMap::value_type(loggerName, \
summary)); +                    } else {
+                        (*iter).second.add(event);
+                    }
+                    break;
+                }
+            }
         }
+  }
+  
 
-        //
-        //   if queue was full, wait until signalled
-        //
-        if (full) {
-                available.wait();
+void AsyncAppender::close() {
+    {
+        synchronized sync(bufferMutex);
+        closed = true;
+        bufferNotEmpty.signalAll();
+        bufferNotFull.signalAll();
+    }
+    
+    try {
+        dispatcher.join();
+    } catch(InterruptedException& e) {
+        Thread::currentThreadInterrupt();
+        LogLog::error("Got an InterruptedException while waiting for the dispatcher \
to finish,", e); +    }
+    
+    {
+        synchronized sync(appenders->getMutex());
+        AppenderList appenderList = appenders->getAllAppenders();
+        for (AppenderList::iterator iter = appenderList.begin();
+             iter != appenderList.end();
+             iter++) {
+             (*iter)->close();
         }
+    }
+}
 
-        //
-        //   if was full, add it now
-        //
-        if (full) {
-                synchronized sync(mutex);
-                queue.push_back(event);
-                pending.broadcast();
-        }
+AppenderList AsyncAppender::getAllAppenders() const
+{
+        synchronized sync(appenders->getMutex());
+        return appenders->getAllAppenders();
 }
 
-void AsyncAppender::close()
+AppenderPtr AsyncAppender::getAppender(const LogString& name) const
 {
-        bool wasClosed = closed;
-        closed = true;
-        if (!wasClosed) {
-                pending.broadcast();
-                thread.join();
-                // close and remove all appenders
-                synchronized sync(mutex);
-                aai->removeAllAppenders();
+        synchronized sync(appenders->getMutex());
+        return appenders->getAppender(name);
+}
 
-        }
+bool AsyncAppender::isAttached(const AppenderPtr& appender) const
+{
+        synchronized sync(appenders->getMutex());
+        return appenders->isAttached(appender);
 }
 
-AppenderList AsyncAppender::getAllAppenders() const
+bool AsyncAppender::requiresLayout() const {
+    return false;
+}
+
+void AsyncAppender::removeAllAppenders()
 {
-        synchronized sync(aai->getMutex());
-        return aai->getAllAppenders();
+    synchronized sync(appenders->getMutex());
+    appenders->removeAllAppenders();
 }
 
-AppenderPtr AsyncAppender::getAppender(const LogString& name1) const
+void AsyncAppender::removeAppender(const AppenderPtr& appender)
 {
-        synchronized sync(aai->getMutex());
-        return aai->getAppender(name1);
+    synchronized sync(appenders->getMutex());
+    appenders->removeAppender(appender);
 }
 
-bool AsyncAppender::isAttached(const AppenderPtr& appender) const
+void AsyncAppender::removeAppender(const LogString& name)
 {
-        synchronized sync(aai->getMutex());
-        return aai->isAttached(appender);
+    synchronized sync(appenders->getMutex());
+    appenders->removeAppender(name);
 }
 
-void AsyncAppender::setBufferSize(int size1)
+bool AsyncAppender::getLocationInfo() const {
+    return locationInfo;
+}
+
+void AsyncAppender::setLocationInfo(bool flag) {
+    locationInfo = flag;
+}
+
+
+void AsyncAppender::setBufferSize(int size)
 {
-    if (size1 < 0) {
+    if (size < 0) {
           throw IllegalArgumentException("size argument must be non-negative");
     }
-    this->size = size1;
+    synchronized sync(bufferMutex);
+    bufferSize = (size < 1) ? 1 : size;
+    bufferNotFull.signalAll();
 }
 
 int AsyncAppender::getBufferSize() const
 {
-        return size;
+        return bufferSize;
 }
 
-void AsyncAppender::removeAllAppenders()
-{
-    synchronized sync(aai->getMutex());
-        aai->removeAllAppenders();
+void AsyncAppender::setBlocking(bool value) {
+    synchronized sync(bufferMutex);
+    blocking = value;
+    bufferNotFull.signalAll();
 }
 
-void AsyncAppender::removeAppender(const AppenderPtr& appender)
-{
-    synchronized sync(aai->getMutex());
-        aai->removeAppender(appender);
+bool AsyncAppender::getBlocking() const {
+    return blocking;
 }
 
-void AsyncAppender::removeAppender(const LogString& name1)
-{
-    synchronized sync(aai->getMutex());
-        aai->removeAppender(name1);
+AsyncAppender::DiscardSummary::DiscardSummary(const LoggingEventPtr& event) : 
+      maxEvent(event), count(1) {
 }
 
-void* LOG4CXX_THREAD_FUNC AsyncAppender::dispatch(log4cxx_thread_t* /* thread */ , \
                void* data) {
-        AsyncAppender* pThis = (AsyncAppender*) data;
-        LoggingEventPtr event;
-        while(true) {
-
-                size_t count = 0;
-                {
-                        synchronized sync(pThis->mutex);
-                        count = pThis->queue.size();
-                        if (count > 0) {
-                                event = pThis->queue.front();
-                                pThis->queue.pop_front();
-                        }
-                }
+AsyncAppender::DiscardSummary::DiscardSummary(const DiscardSummary& src) : 
+      maxEvent(src.maxEvent), count(src.count) {
+}
 
-                if (count == 0) {
-                        if (pThis->closed) {
-                                return 0;
-                        }
-                        pThis->pending.wait();
-                } else {
-                        if(pThis->aai != 0) {
-                            synchronized sync(pThis->aai->getMutex());
-                                Pool p;
-                            pThis->aai->appendLoopOnAppenders(event, p);
-                        }
-
-                        if (count == (size_t) pThis->getBufferSize()) {
-                                pThis->available.broadcast();
-                        }
+AsyncAppender::DiscardSummary& AsyncAppender::DiscardSummary::operator=(const \
DiscardSummary& src) { +      maxEvent = src.maxEvent;
+      count = src.count; 
+      return *this;
+}
 
-                        LoggingEventPtr nullEvent;
-                        event = nullEvent;
-                }
-        }
+void AsyncAppender::DiscardSummary::add(const LoggingEventPtr& event) {
+      if (event->getLevel()->toInt() > maxEvent->getLevel()->toInt()) {
+        maxEvent = event;
+      }
+      count++;
 }
 
-#endif
+LoggingEventPtr AsyncAppender::DiscardSummary::createEvent(Pool& p) {
+    LogString msg(LOG4CXX_STR("Discarded "));
+    StringHelper::toString(count, p, msg);
+    msg.append(LOG4CXX_STR(" messages due to a full event buffer including: "));
+    msg.append(maxEvent->getMessage()); 
+    return new LoggingEvent(   
+              Logger::getLogger(maxEvent->getLoggerName()),
+              maxEvent->getLevel(),
+              msg,
+              LocationInfo::getLocationUnavailable());
+}
 
 
+
+void* LOG4CXX_THREAD_FUNC AsyncAppender::dispatch(log4cxx_thread_t* /* thread */ , \
void* data) { +    AsyncAppender* pThis = (AsyncAppender*) data;
+    bool isActive = true;
+    try {
+        while (isActive) {
+             //
+             //   process events after lock on buffer is released.
+             //
+            Pool p;
+            LoggingEventList events;
+            {
+                   synchronized sync(pThis->bufferMutex);
+                   size_t bufferSize = pThis->buffer.size();
+                   isActive = !pThis->closed;
+               
+                   while((bufferSize == 0) && isActive) {
+                       pThis->bufferNotEmpty.await(pThis->bufferMutex);
+                       bufferSize = pThis->buffer.size();
+                       isActive = !pThis->closed;
+                   }
+                   for(LoggingEventList::iterator eventIter = pThis->buffer.begin();
+                       eventIter != pThis->buffer.end();
+                       eventIter++) {
+                       events.push_back(*eventIter);
+                   }
+                   for(DiscardMap::iterator discardIter = pThis->discardMap.begin();
+                       discardIter != pThis->discardMap.end();
+                       discardIter++) {
+                       events.push_back(discardIter->second.createEvent(p));
+                   }
+                   pThis->buffer.clear();
+                   pThis->discardMap.clear();
+                   pThis->bufferNotFull.signalAll();
+            }
+            
+            for (LoggingEventList::iterator iter = events.begin();
+                 iter != events.end();
+                 iter++) {
+                 synchronized sync(pThis->appenders->getMutex());
+                 pThis->appenders->appendLoopOnAppenders(*iter, p);
+            }
+        }
+    } catch(InterruptedException& ex) {
+            Thread::currentThreadInterrupt();
+    } catch(...) {
+    }
+    return 0;
+}
+                
+#endif

Modified: logging/log4cxx/trunk/src/main/cpp/condition.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/condition.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/condition.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/condition.cpp Fri Sep 28 15:57:53 2007
@@ -21,6 +21,7 @@
 #include <apr_thread_cond.h>
 #include <log4cxx/helpers/synchronized.h>
 #include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/thread.h>
 
 using namespace log4cxx::helpers;
 using namespace log4cxx;
@@ -31,18 +32,10 @@
 Condition::Condition(Pool& p)
 {
         apr_pool_t* aprPool = (apr_pool_t*) p.getAPRPool();
-        apr_thread_mutex_t* aprMutex = NULL;
-        apr_status_t stat = apr_thread_mutex_create(&aprMutex,
-                APR_THREAD_MUTEX_DEFAULT, aprPool);
-        if (stat != APR_SUCCESS) {
-                throw ConditionException(stat);
-        }
-        mutex = aprMutex;
         apr_thread_cond_t* aprCondition = NULL;
-        stat = apr_thread_cond_create(&aprCondition, aprPool);
+        apr_status_t stat = apr_thread_cond_create(&aprCondition, aprPool);
         if (stat != APR_SUCCESS) {
-                stat = apr_thread_mutex_destroy(aprMutex);
-                throw ConditionException(stat);
+                throw RuntimeException(stat);
         }
         condition = aprCondition;
 }
@@ -50,33 +43,23 @@
 Condition::~Condition()
 {
         apr_thread_cond_destroy((apr_thread_cond_t*) condition);
-        apr_thread_mutex_destroy((apr_thread_mutex_t*)mutex);
 }
 
-void Condition::broadcast()
+log4cxx_status_t Condition::signalAll()
 {
-        apr_status_t stat = apr_thread_cond_broadcast((apr_thread_cond_t*) \
                condition);
-        if (stat != APR_SUCCESS) {
-                throw ConditionException(stat);
-        }
+        return apr_thread_cond_broadcast((apr_thread_cond_t*) condition);
 }
 
-
-void Condition::wait()
+void Condition::await(Mutex& mutex)
 {
-        apr_status_t stat = apr_thread_mutex_lock((apr_thread_mutex_t*) mutex);
-        if (stat != APR_SUCCESS) {
-           throw MutexException(stat);
+        if (Thread::interrupted()) {
+             throw InterruptedException();
         }
-        stat = apr_thread_cond_wait(
+        apr_status_t stat = apr_thread_cond_wait(
              (apr_thread_cond_t*) condition,
-             (apr_thread_mutex_t*) mutex);
-        if (stat != APR_SUCCESS) {
-                throw ConditionException(stat);
-        }
-        stat = apr_thread_mutex_unlock((apr_thread_mutex_t*) mutex);
+             (apr_thread_mutex_t*) mutex.getAPRMutex());
         if (stat != APR_SUCCESS) {
-           throw MutexException(stat);
+                throw InterruptedException(stat);
         }
 }
 

Modified: logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/domconfigurator.cpp Fri Sep 28 15:57:53 2007
@@ -111,21 +111,22 @@
 /**
 Used internally to parse appenders by IDREF name.
 */
-AppenderPtr DOMConfigurator::findAppenderByName(apr_xml_elem* element, 
+AppenderPtr DOMConfigurator::findAppenderByName(apr_xml_elem* element,
+                                                apr_xml_doc* doc,
                                                 const LogString& appenderName,
                                                 AppenderMap& appenders) {
     AppenderPtr appender;
     std::string tagName(element->name);
     if (tagName == APPENDER_TAG) {
         if (appenderName == getAttribute(element, NAME_ATTR)) {
-              appender = parseAppender(element, 0, appenders);
+              appender = parseAppender(element, doc, appenders);
         }
     }
     if (element->first_child && !appender) {
-         appender = findAppenderByName(element->first_child, appenderName, \
appenders); +         appender = findAppenderByName(element->first_child, doc, \
appenderName, appenders);  }
     if (element->next && !appender) {
-        appender = findAppenderByName(element->next, appenderName, appenders);
+        appender = findAppenderByName(element->next, doc, appenderName, appenders);
     }
     return appender;
 }
@@ -143,7 +144,7 @@
         if (match != appenders.end()) {
             appender = match->second;
         } else if (doc) {
-            appender = findAppenderByName(doc->root, appenderName, appenders);
+            appender = findAppenderByName(doc->root, doc, appenderName, appenders);
             if (appender) {
                 appenders.insert(AppenderMap::value_type(appenderName, appender));
             }

Modified: logging/log4cxx/trunk/src/main/cpp/exception.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/exception.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/exception.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/exception.cpp Fri Sep 28 15:57:53 2007
@@ -54,6 +54,9 @@
   return msg;
 }
 
+RuntimeException::RuntimeException(log4cxx_status_t stat)
+     : Exception(formatMessage(stat)) {
+}
 
 RuntimeException::RuntimeException(const std::string& msg1)
      : Exception(msg1) {
@@ -68,6 +71,13 @@
       return *this;
 }
 
+std::string RuntimeException::formatMessage(log4cxx_status_t stat) {
+   std::string s("RuntimeException: return code = ");
+   Pool p;
+   StringHelper::toString(stat, p, s);
+   return s;
+}
+
 NullPointerException::NullPointerException(const std::string& msg1)
      : RuntimeException(msg1) {
 }
@@ -201,21 +211,24 @@
       return s;
 }
 
-ConditionException::ConditionException(log4cxx_status_t stat)
+InterruptedException::InterruptedException() : Exception("Thread was interrupted") {
+}
+
+InterruptedException::InterruptedException(log4cxx_status_t stat)
      : Exception(formatMessage(stat)) {
 }
 
-ConditionException::ConditionException(const ConditionException &src)
+InterruptedException::InterruptedException(const InterruptedException &src)
      : Exception(src) {
 }
 
-ConditionException& ConditionException::operator=(const MutexException& src) {
+InterruptedException& InterruptedException::operator=(const InterruptedException& \
src) {  Exception::operator=(src);
       return *this;
 }
 
-std::string ConditionException::formatMessage(log4cxx_status_t stat) {
-      std::string s("Condition exception: stat = ");
+std::string InterruptedException::formatMessage(log4cxx_status_t stat) {
+      std::string s("InterruptedException: stat = ");
       Pool p;
       StringHelper::toString(stat, p, s);
       return s;

Modified: logging/log4cxx/trunk/src/main/cpp/file.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/file.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/file.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/file.cpp Fri Sep 28 15:57:53 2007
@@ -174,4 +174,4 @@
      apr_status_t stat = apr_dir_make_recursive(convertBackSlashes(osName).c_str(),
           APR_OS_DEFAULT, (apr_pool_t*) p.getAPRPool());
      return stat == APR_SUCCESS;
-}
\ No newline at end of file
+}

Modified: logging/log4cxx/trunk/src/main/cpp/thread.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/thread.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/thread.cpp (original)
+++ logging/log4cxx/trunk/src/main/cpp/thread.cpp Fri Sep 28 15:57:53 2007
@@ -19,12 +19,14 @@
 #include <log4cxx/helpers/thread.h>
 #include <log4cxx/helpers/exception.h>
 #include <apr_thread_proc.h>
+#include <apr_atomic.h>
 #include <log4cxx/helpers/pool.h>
+#include <log4cxx/helpers/threadlocal.h>
 
 using namespace log4cxx::helpers;
 using namespace log4cxx;
 
-Thread::Thread() : thread(NULL), alive(false) {
+Thread::Thread() : thread(NULL), alive(0), interruptedStatus(0) {
 }
 
 Thread::~Thread() {
@@ -33,28 +35,69 @@
 #endif
 }
 
-#if APR_HAS_THREADS
+Thread::LaunchPackage::LaunchPackage(Thread* t, Runnable r, void* d) : thread(t), \
runnable(r), data(d) { +}
+
+Thread* Thread::LaunchPackage::getThread() const {
+    return thread;
+}
+
+Runnable Thread::LaunchPackage::getRunnable() const {
+    return runnable;
+}
+
+void* Thread::LaunchPackage::getData() const {
+    return data;
+}
+
+void* Thread::LaunchPackage::operator new(size_t sz, Pool& p) {
+    return p.palloc(sz);
+}
+
 void Thread::run(Runnable start, void* data) {
-        if (thread != NULL && alive) {
-                throw ThreadException(0);
+        //
+        //    if attempting a second run method on the same Thread object
+        //         throw an exception
+        //
+        if (thread != NULL) {
+            throw IllegalStateException();
         }
         apr_threadattr_t* attrs;
         apr_status_t stat = apr_threadattr_create(&attrs, (apr_pool_t*) \
p.getAPRPool());  if (stat != APR_SUCCESS) {
                 throw ThreadException(stat);
         }
-        alive = true;
+        
+        //   create LaunchPackage on the thread's memory pool
+        LaunchPackage* package = new(p) LaunchPackage(this, start, data);
         stat = apr_thread_create((apr_thread_t**) &thread, attrs,
-            (apr_thread_start_t) start, data, (apr_pool_t*) p.getAPRPool());
+            (apr_thread_start_t) launcher, package, (apr_pool_t*) p.getAPRPool());
         if (stat != APR_SUCCESS) {
                 throw ThreadException(stat);
         }
 }
 
+
+Thread::LaunchStatus::LaunchStatus(volatile unsigned int* p) : alive(p) {
+    apr_atomic_set32(alive, 0xFFFFFFFF);
+}
+
+Thread::LaunchStatus::~LaunchStatus() {
+    apr_atomic_set32(alive, 0);
+}
+    
+
+void* Thread::launcher(log4cxx_thread_t* thread, void* data) {
+    LaunchPackage* package = (LaunchPackage*) data;
+    ThreadLocal& tls = getThreadLocal();
+    tls.set(package->getThread());
+    LaunchStatus alive(&package->getThread()->alive);
+    return (package->getRunnable())(thread, package->getData());
+}
+
 void Thread::stop() {
-    if (thread != NULL && alive) {
+    if (thread != NULL) {
                 apr_status_t stat = apr_thread_exit((apr_thread_t*) thread, 0);
-                alive = false;
                 thread = NULL;
                 if (stat != APR_SUCCESS) {
                         throw ThreadException(stat);
@@ -63,23 +106,59 @@
 }
 
 void Thread::join() {
-        if (thread != NULL && alive) {
+        if (thread != NULL) {
                 apr_status_t startStat;
                 apr_status_t stat = apr_thread_join(&startStat, (apr_thread_t*) \
                thread);
-                alive = false;
                 thread = NULL;
                 if (stat != APR_SUCCESS) {
                         throw ThreadException(stat);
                 }
         }
 }
-#endif
+
+ThreadLocal& Thread::getThreadLocal() {
+     static ThreadLocal tls;
+     return tls;
+}
+
+void Thread::currentThreadInterrupt() {
+   void* tls = getThreadLocal().get();
+   if (tls != 0) {
+       ((Thread*) tls)->interrupt();
+   }
+}
+
+void Thread::interrupt() {
+    apr_atomic_set32(&interruptedStatus, 0xFFFFFFFF);
+}
+
+bool Thread::interrupted() {
+   void* tls = getThreadLocal().get();
+   if (tls != 0) {
+       return apr_atomic_xchg32(&(((Thread*) tls)->interruptedStatus), 0) != 0;
+   }
+   return false;
+}
+
+bool Thread::isCurrentThread() const {
+    void* tls = getThreadLocal().get();
+    return (tls == this);
+}
+
+bool Thread::isAlive() {
+    return apr_atomic_read32(&alive) != 0;
+}
 
 void Thread::ending() {
-        alive = false;
+    apr_atomic_set32(&alive, 0);
 }
 
 
-void Thread::sleep(log4cxx_time_t duration) {
-    apr_sleep(duration);
+void Thread::sleep(int duration) {
+    if(interrupted()) {
+         throw InterruptedException();
+    }
+    if (duration > 0) {
+        apr_sleep(duration*1000);
+    }
 }

Added: logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp?rev=580508&view=auto
 ==============================================================================
--- logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp (added)
+++ logging/log4cxx/trunk/src/main/cpp/threadlocal.cpp Fri Sep 28 15:57:53 2007
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "log4cxx/helpers/threadlocal.h"
+#include "apr_thread_proc.h"
+#include "log4cxx/helpers/exception.h"
+
+using namespace log4cxx::helpers;
+using namespace log4cxx;
+
+ThreadLocal::ThreadLocal() {
+    apr_status_t stat = apr_pool_create((apr_pool_t**) &pool, 0);
+    if (stat != APR_SUCCESS) {
+        throw RuntimeException(stat);
+    }
+    stat = apr_threadkey_private_create((apr_threadkey_t**) &key, 0, (apr_pool_t*) \
pool); +    if (stat != APR_SUCCESS) {
+         throw RuntimeException(stat);
+    }
+}
+              
+ThreadLocal::~ThreadLocal() {
+    apr_pool_destroy((apr_pool_t*) pool);
+}
+              
+void ThreadLocal::set(void* priv) {
+    apr_status_t stat = apr_threadkey_private_set(priv, (apr_threadkey_t*) key);
+    if (stat != APR_SUCCESS) {
+        throw RuntimeException(stat);
+    }
+}
+               
+void* ThreadLocal::get() {
+    void* retval = 0;
+    apr_status_t stat = apr_threadkey_private_get(&retval, (apr_threadkey_t*) key);
+    if (stat != APR_SUCCESS) {
+        throw RuntimeException(stat);
+    }
+    return retval;
+}
+

Modified: logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/asyncappender.h Fri Sep 28 \
15:57:53 2007 @@ -58,11 +58,26 @@
                         LOG4CXX_CAST_ENTRY(spi::AppenderAttachable)
                 END_LOG4CXX_CAST_MAP()
 
+                /**
+                 * Create new instance.
+                */
                 AsyncAppender();
+                
+                /**
+                 *  Destructor.
+                 */
                 virtual ~AsyncAppender();
 
+                /**
+                 * Add appender.
+                 *
+                 * @param newAppender appender to add, may not be null.
+                */
                 void addAppender(const AppenderPtr& newAppender);
 
+                /**
+                 * {@inheritDoc}
+                 */
                 void append(const spi::LoggingEventPtr& event, \
log4cxx::helpers::Pool& p);  
                 /**
@@ -72,38 +87,61 @@
                 */
                 void close();
 
+                /**
+                 * Get iterator over attached appenders.
+                 * @return list of all attached appenders.
+                */
                 AppenderList getAllAppenders() const;
+                
+                /**
+                 * Get appender by name.
+                 *
+                 * @param name name, may not be null.
+                 * @return matching appender or null.
+                */
                 AppenderPtr getAppender(const LogString& name) const;
 
                 /**
-                Returns the current value of the <b>LocationInfo</b> option.
+                 * Gets whether the location of the logging request call
+                 * should be captured.
+                 *
+                 * @return the current value of the <b>LocationInfo</b> option.
                 */
-                inline bool getLocationInfo() const
-                        { return locationInfo; }
-
+                bool getLocationInfo() const;
                 /**
-                Is the appender passed as parameter attached to this asyncappender?
+                * Determines if specified appender is attached.
+                * @param appender appender.
+                * @return true if attached.
                 */
                 bool isAttached(const AppenderPtr& appender) const;
 
+                /**
+                 * {@inheritDoc}
+                */
+                virtual bool requiresLayout() const;
+                    
+                /**
+                 * Removes and closes all attached appenders.
+                */
                 void removeAllAppenders();
-                void removeAppender(const AppenderPtr& appender);
-                void removeAppender(const LogString& name);
 
                 /**
-                The <code>AsyncAppender</code> does not require a layout. Hence,
-                this method always returns <code>false</code>.
+                 * Removes an appender.
+                 * @param appender appender to remove.
+                */
+                void removeAppender(const AppenderPtr& appender);
+                /**
+                * Remove appender by name.
+                * @param name name.
                 */
-                virtual bool requiresLayout() const
-                        { return false; }
+                void removeAppender(const LogString& name);                        
 
                 /**
                 * The <b>LocationInfo</b> attribute is provided for compatibility
-                * with log4j and has no effect.
-                * */
-                inline void setLocationInfo(bool flag) {
-                        locationInfo = flag;
-                }
+                * with log4j and has no effect on the log output.
+                * @param flag new value.
+                */
+                void setLocationInfo(bool flag);
 
                 /**
                 * The <b>BufferSize</b> option takes a non-negative integer value.
@@ -113,30 +151,129 @@
                 void setBufferSize(int size);
 
                 /**
-                Returns the current value of the <b>BufferSize</b> option.
+                 * Gets the current buffer size.
+                 * @return the current value of the <b>BufferSize</b> option.
                 */
                 int getBufferSize() const;
 
+                /**
+                 * Sets whether appender should wait if there is no
+                 * space available in the event buffer or immediately return.
+                 *
+                 * @param value true if appender should wait until available space \
in buffer. +                 */
+                 void setBlocking(bool value);
+
+                /**
+                 * Gets whether appender should block calling thread when buffer is \
full. +                 * If false, messages will be counted by logger and a summary
+                 * message appended after the contents of the buffer have been \
appended. +                 *
+                 * @return true if calling thread will be blocked when buffer is \
full. +                 */
+                 bool getBlocking() const;
+                 
+                 
+                 /**
+                  * Set appender properties by name.
+                  * @param option property name.
+                  * @param value property value.
+                  */
+                 void setOption(const LogString& option, const LogString& value);
+
+
         private:
-                log4cxx::helpers::Pool pool;
-                std::deque<log4cxx::spi::LoggingEventPtr> queue;
-                size_t size;
-                //
-                //   Condition is signaled when there is room available on the queue
-                //
-                log4cxx::helpers::Condition available;
-                //
-                //   Condition is signaled when there is at least one event in the \
                queue.
-                //
-                log4cxx::helpers::Condition pending;
+                /**
+                 * The default buffer size is set to 128 events.
+                */
+                enum { DEFAULT_BUFFER_SIZE = 128 };
+
+                /**
+                 * Event buffer.
+                */
+                typedef std::vector<log4cxx::spi::LoggingEventPtr> LoggingEventList;
+                LoggingEventList buffer;
 
-                helpers::Thread thread;
+                /**
+                 *  Mutex used to guard access to buffer and discardMap.
+                 */
+                ::log4cxx::helpers::Mutex bufferMutex;
+                ::log4cxx::helpers::Condition bufferNotFull;
+                ::log4cxx::helpers::Condition bufferNotEmpty;
+    
+                class DiscardSummary {
+                private:
+                    /**
+                     * First event of the highest severity.
+                    */
+                    ::log4cxx::spi::LoggingEventPtr maxEvent;
+                    
+                    /**
+                    * Total count of messages discarded.
+                    */
+                    int count;
+                    
+                public:
+                    /**
+                     * Create new instance.
+                     *
+                     * @param event event, may not be null.
+                    */
+                    DiscardSummary(const ::log4cxx::spi::LoggingEventPtr& event);
+                    /** Copy constructor.  */
+                    DiscardSummary(const DiscardSummary& src);
+                    /** Assignment operator. */
+                    DiscardSummary& operator=(const DiscardSummary& src);
+                    
+                    /**
+                     * Add discarded event to summary.
+                     *
+                     * @param event event, may not be null.
+                    */
+                    void add(const ::log4cxx::spi::LoggingEventPtr& event);
+                    
+                    /**
+                     * Create event with summary information.
+                     *
+                     * @return new event.
+                     */
+                     ::log4cxx::spi::LoggingEventPtr \
createEvent(::log4cxx::helpers::Pool& p); +                };
+
+                /**
+                  * Map of DiscardSummary objects keyed by logger name.
+                */
+                typedef std::map<LogString, DiscardSummary> DiscardMap;
+                DiscardMap discardMap;
+                
+                /**
+                 * Buffer size.
+                */
+                int bufferSize;
 
+                /**
+                 * Nested appenders.
+                */
+                helpers::AppenderAttachableImplPtr appenders;
+
+                /**
+                 *  Dispatcher.
+                 */
+                helpers::Thread dispatcher;
+
+                /**
+                 * Should location info be included in dispatched messages.
+                */
                 bool locationInfo;
-                helpers::AppenderAttachableImplPtr aai;
 
-                enum { DEFAULT_BUFFER_SIZE = 128 };
+                /**
+                 * Does appender block when buffer is full.
+                */
+                bool blocking;
 
+                /**
+                 *  Dispatch routine.
+                 */
                 static void* LOG4CXX_THREAD_FUNC dispatch(helpers::log4cxx_thread_t* \
thread, void* data);  
         }; // class AsyncAppender

Modified: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/condition.h Fri Sep 28 \
15:57:53 2007 @@ -19,7 +19,10 @@
 #define _LOG4CXX_HELPERS_CONDITION_H
 
 #include <log4cxx/log4cxx.h>
+#include <log4cxx/helpers/mutex.h>
 
+typedef void log4cxx_pool_t;
+typedef void log4cxx_thread_cond_t;
 
 namespace log4cxx
 {
@@ -27,17 +30,39 @@
         {
                 class Pool;
 
+                /**
+                 *   This class provides a means for one thread to suspend exception \
until +                 *   notified by another thread to result.  This class should \
have  +                 *   similar semantics to \
java.util.concurrent.locks.Condition. +                 */
                 class LOG4CXX_EXPORT Condition
                 {
                 public:
+                        /**
+                         *  Create new instance.
+                         *  @param p pool on which condition will be created.  Needs \
to be +                         *  longer-lived than created instance.
+                         */
                         Condition(log4cxx::helpers::Pool& p);
+                        /**
+                         *  Destructor.
+                         */
                         ~Condition();
-                        void broadcast();
-                        void wait();
+                        /**
+                         *   Signal all waiting threads.
+                         */
+                        log4cxx_status_t signalAll();
+                        /**
+                         *  Await signaling of condition.
+                         *  @param lock lock associated with condition, calling \
thread must +                         *  own lock.  Lock will be released while \
waiting and reacquired +                         *  before returning from wait.
+                         *  @throws InterruptedException if thread is interrupted.
+                         */
+                        void await(Mutex& lock);
 
                 private:
-                        void* condition;
-                        void* mutex;
+                        log4cxx_thread_cond_t* condition;
                         Condition(const Condition&);
                         Condition& operator=(const Condition&);
                 };

Modified: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/exception.h Fri Sep 28 \
15:57:53 2007 @@ -47,9 +47,12 @@
                 class LOG4CXX_EXPORT RuntimeException : public Exception
                 {
                 public:
+                        RuntimeException(log4cxx_status_t stat);
                         RuntimeException(const std::string& msg);
                         RuntimeException(const RuntimeException& msg);
                         RuntimeException& operator=(const RuntimeException& src);
+                private:
+                        static std::string formatMessage(log4cxx_status_t stat);
                 }; // class RuntimeException
 
                 /** Thrown when an application attempts to use null in a case where \
an @@ -120,12 +123,13 @@
                       static std::string formatMessage(log4cxx_status_t stat);
                 };
 
-                class LOG4CXX_EXPORT ConditionException : public Exception
+                class LOG4CXX_EXPORT InterruptedException : public Exception
                 {
                 public:
-                      ConditionException(log4cxx_status_t stat);
-                      ConditionException(const ConditionException &src);
-                      ConditionException& operator=(const MutexException&);
+                      InterruptedException();
+                      InterruptedException(log4cxx_status_t stat);
+                      InterruptedException(const InterruptedException &src);
+                      InterruptedException& operator=(const InterruptedException&);
                 private:
                       static std::string formatMessage(log4cxx_status_t stat);
                 };

Modified: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/thread.h Fri Sep 28 \
15:57:53 2007 @@ -35,33 +35,138 @@
         namespace helpers
         {
                 class Pool;
+                class ThreadLocal;
                 typedef void log4cxx_thread_t;
 
 				typedef void* (LOG4CXX_THREAD_FUNC *Runnable)(log4cxx_thread_t* thread, void* \
data); +                /**
+                 *  This class implements an approximation of java.util.Thread.
+                 */
                 class LOG4CXX_EXPORT Thread
                 {
                 public:
+                        /**
+                         *  Create new instance.
+                         */
                         Thread();
+                        /**
+                         *  Destructor.
+                         */
                         ~Thread();
 
+                        /**
+                         *  Runs the specified method on a newly created thread.
+                         */
                         void run(Runnable start, void* data);
                         void stop();
                         void join();
-                        //
-                        //  called on the worker thread to indicate
-                        //    immediate exit from the start method
-                        void ending();
 
                         inline bool isActive() { return thread != 0; }
 
-                        static void sleep(log4cxx_time_t duration);
+                        /**
+                         * Causes the currently executing thread to sleep for the
+                         * specified number of milliseconds.
+                         * @param millis milliseconds.
+                         * @throws Interrupted Exception if the thread is \
interrupted. +                         */
+                        static void sleep(int millis);
+                        /**
+                         *  Sets interrupted status for current thread to true.  
+                         */
+                        static void currentThreadInterrupt();
+                        /**
+                         *  Sets interrupted status to true.  
+                         */
+                        void interrupt();
+                        /**
+                         *  Tests if the current thread has been interrupted and
+                         *  sets the interrupted status to false.
+                         */
+                        static bool interrupted();
+                        
+                        bool isAlive();
+                        bool isCurrentThread() const;
+                        void ending();
+                        
 
                 private:
                         Pool p;
                         log4cxx_thread_t* thread;
-                        volatile bool alive;
+                        volatile unsigned int alive;
+                        volatile unsigned int interruptedStatus;
                         Thread(const Thread&);
                         Thread& operator=(const Thread&);
+                        
+                        /**
+                         *   This class is used to encapsulate the parameters to
+                         *   Thread::run when they are passed to Thread::launcher.
+                         *
+                         */
+                        class LaunchPackage {
+                        public:
+                            /**
+                             *  Placement new to create LaunchPackage in specified \
pool. +                             *  LaunchPackage needs to be dynamically \
allocated since +                             *  since a stack allocated instance may \
go out of scope +                             *  before thread is launched.
+                             */
+                            static void* operator new(size_t, Pool& p);
+                            /**
+                             *  Create new instance.
+                             */
+                            LaunchPackage(Thread* thread, Runnable runnable, void* \
data); +                            /**
+                             * Gets thread parameter.
+                             * @return thread.
+                             */
+                            Thread* getThread() const;
+                            /**
+                             *  Gets runnable parameter.
+                             *  @return runnable.
+                             */
+                            Runnable getRunnable() const;
+                            /**
+                             *  gets data parameter.
+                             *  @return thread.
+                             */
+                            void* getData() const;
+                        private:
+                            Thread* thread;
+                            Runnable runnable; 
+                            void* data;
+                        };
+                        
+                        /**
+                         *  This object atomically sets the specified memory \
location +                         *  to non-zero on construction and to zero on \
destruction.   +                         *  Used to maintain Thread.alive.
+                         */
+                        class LaunchStatus {
+                        public:
+                            /*
+                             *  Construct new instance.
+                             *  @param p address of memory to set to non-zero on \
construction, zero on destruction. +                             */
+                            LaunchStatus(volatile unsigned int* p);
+                            /**
+                             *  Destructor.
+                             */
+                            ~LaunchStatus();
+                        private:
+                            volatile unsigned int* alive;
+                        };
+                        
+                        /**
+                         *  This method runs on the created thread and sets up \
thread-local storage +                         *  used to keep the reference to the \
corresponding Thread object and +                         *  is responsible for \
maintaining Thread.alive. +                         */
+                        static void* LOG4CXX_THREAD_FUNC launcher(log4cxx_thread_t* \
thread, void* data); +                        /**
+                         *   Get a key to the thread local storage used to hold the \
reference to +                         *   the corresponding Thread object.
+                         */                        
+                        static ThreadLocal& getThreadLocal();
                 };
         } // namespace helpers
 } // namespace log4cxx

Added: logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h?rev=580508&view=auto
 ==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h (added)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/helpers/threadlocal.h Fri Sep 28 \
15:57:53 2007 @@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LOG4CXX_HELPERS_THREAD_LOCAL_H
+#define _LOG4CXX_HELPERS_THREAD_LOCAL_H
+
+#include <log4cxx/log4cxx.h>
+#include <log4cxx/helpers/pool.h>
+
+#if !defined(LOG4CXX_THREAD_FUNC)
+#if defined(_WIN32)
+#define LOG4CXX_THREAD_FUNC __stdcall
+#else
+#define LOG4CXX_THREAD_FUNC
+#endif
+#endif
+
+
+namespace log4cxx
+{
+        namespace helpers
+        {
+                typedef void log4cxx_threadkey_t;
+                typedef void log4cxx_pool_t;
+
+                /**
+                 *  This class provides thread-local variables.  This class is \
similar in function +                 *  to java.lang.ThreadLocal.
+                 */
+                class LOG4CXX_EXPORT ThreadLocal {
+                public:
+                    /**
+                     *   Create new instance.
+                     */
+                    ThreadLocal();
+                    /**
+                     *    Destructor.
+                     */
+                    ~ThreadLocal();
+                    /**
+                     *  Sets the value in the current thread's copy of this \
thread-local variable. +                     *  @param priv new value.
+                     */
+                    void set(void* priv);                    
+                    /**
+                     *  Returns the value in the current thread's copy of this \
thread-local variable. +                     *  @return value of thread-local \
variable for the current thread. +                     */
+                    void* get();
+               
+                private:
+                    log4cxx_pool_t* pool;
+                    log4cxx_threadkey_t* key;
+                };
+        } // namespace helpers
+} // namespace log4cxx
+
+#endif //_LOG4CXX_HELPERS_THREAD_LOCAL_H

Modified: logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h (original)
+++ logging/log4cxx/trunk/src/main/include/log4cxx/xml/domconfigurator.h Fri Sep 28 \
15:57:53 2007 @@ -95,6 +95,7 @@
                         Used internally to parse appenders by IDREF name.
                         */
                         AppenderPtr findAppenderByName(apr_xml_elem* elem,
+                                apr_xml_doc* doc,
                                 const LogString& appenderName,
                                 AppenderMap& appenders);
 

Modified: logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp (original)
+++ logging/log4cxx/trunk/src/test/cpp/asyncappendertestcase.cpp Fri Sep 28 15:57:53 \
2007 @@ -28,12 +28,78 @@
 #include <apr_strings.h>
 #include "testchar.h"
 #include <log4cxx/helpers/stringhelper.h>
+#include <log4cxx/helpers/synchronized.h>
+#include <log4cxx/spi/location/locationinfo.h>
+#include <log4cxx/xml/domconfigurator.h>
+#include <log4cxx/file.h>
 
 using namespace log4cxx;
 using namespace log4cxx::helpers;
+using namespace log4cxx::spi;
+
+class NullPointerAppender : public AppenderSkeleton {
+public:
+    NullPointerAppender() {
+    }
+
+
+    /**
+     * @{inheritDoc}
+     */
+    void append(const spi::LoggingEventPtr&, log4cxx::helpers::Pool&) {
+         throw NullPointerException("Intentional NullPointerException");
+    }
+
+    void close() {
+    }
+
+    bool requiresLayout() const {
+            return false;
+    }
+};
+
+    /**
+     * Vector appender that can be explicitly blocked.
+     */
+class BlockableVectorAppender : public VectorAppender {
+private:
+      Mutex blocker;
+public:
+      /**
+       * Create new instance.
+       */
+      BlockableVectorAppender() : blocker(pool) {
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+    void append(const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& p) {
+          synchronized sync(blocker);
+          VectorAppender::append(event, p);
+            //
+            //   if fatal, echo messages for testLoggingInDispatcher
+            //
+            if (event->getLevel() == Level::getInfo()) {
+                LoggerPtr logger = Logger::getLogger(event->getLoggerName());
+                LOG4CXX_ERROR(logger, event->getMessage());
+                LOG4CXX_WARN(logger, event->getMessage());
+                LOG4CXX_INFO(logger, event->getMessage());
+                LOG4CXX_DEBUG(logger, event->getMessage());
+            }
+      }
+      
+      Mutex& getBlocker() {
+          return blocker;
+      }
+
+    };
+
+typedef helpers::ObjectPtrT<BlockableVectorAppender> BlockableVectorAppenderPtr;
+
 
 /**
-   A superficial but general test of log4j.
+ * Tests of AsyncAppender.
  */
 class AsyncAppenderTestCase : public AppenderSkeletonTestCase
 {
@@ -46,10 +112,10 @@
 
                 CPPUNIT_TEST(closeTest);
                 CPPUNIT_TEST(test2);
-//  TODO: deadlocks on Win32, will debug on Linux
-#if !defined(_WIN32)
                 CPPUNIT_TEST(test3);
-#endif
+                CPPUNIT_TEST(testBadAppender);
+                CPPUNIT_TEST(testLocationInfoTrue);
+                CPPUNIT_TEST(testConfiguration);
         CPPUNIT_TEST_SUITE_END();
 
 
@@ -69,7 +135,7 @@
         }
 
         // this test checks whether it is possible to write to a closed \
                AsyncAppender
-        void closeTest() throw(Exception)
+        void closeTest() 
         {
                 LoggerPtr root = Logger::getRootLogger();
                 LayoutPtr layout = new SimpleLayout();
@@ -84,7 +150,7 @@
                 root->debug(LOG4CXX_TEST_STR("m2"));
 
                 const std::vector<spi::LoggingEventPtr>& v = \
                vectorAppender->getVector();
-                CPPUNIT_ASSERT(v.size() == 1);
+                CPPUNIT_ASSERT_EQUAL((size_t) 1, v.size());
         }
 
         // this test checks whether appenders embedded within an AsyncAppender are \
also @@ -104,7 +170,7 @@
                 root->debug(LOG4CXX_TEST_STR("m2"));
 
                 const std::vector<spi::LoggingEventPtr>& v = \
                vectorAppender->getVector();
-                CPPUNIT_ASSERT(v.size() == 1);
+                CPPUNIT_ASSERT_EQUAL((size_t) 1, v.size());
                 CPPUNIT_ASSERT(vectorAppender->isClosed());
         }
 
@@ -112,23 +178,16 @@
         // closed
         void test3()
         {
-                typedef std::vector<spi::LoggingEventPtr>::size_type size_type;
                 size_t LEN = 200;
                 LoggerPtr root = Logger::getRootLogger();
-                LayoutPtr layout = new SimpleLayout();
                 VectorAppenderPtr vectorAppender = new VectorAppender();
                 AsyncAppenderPtr asyncAppender = new AsyncAppender();
                 asyncAppender->setName(LOG4CXX_STR("async-test3"));
                 asyncAppender->addAppender(vectorAppender);
                 root->addAppender(asyncAppender);
 
-                Pool pool;
-                std::string msg("message");
-
                 for (size_t i = 0; i < LEN; i++) {
-                        msg.erase(msg.begin() + 7, msg.end());
-                        StringHelper::toString(i, pool, msg);
-                        LOG4CXX_DEBUG(root, msg);
+                        LOG4CXX_DEBUG(root, "message" << i);
                 }
 
                 asyncAppender->close();
@@ -138,6 +197,89 @@
                 CPPUNIT_ASSERT_EQUAL(LEN, v.size());
                 CPPUNIT_ASSERT_EQUAL(true, vectorAppender->isClosed());
         }
+        
+    /**
+     * Tests that a bad appender will switch async back to sync.
+     */
+    void testBadAppender() {
+        AppenderPtr nullPointerAppender = new NullPointerAppender();
+        AsyncAppenderPtr asyncAppender = new AsyncAppender();
+        asyncAppender->addAppender(nullPointerAppender);
+        asyncAppender->setBufferSize(5);
+        Pool p;
+        asyncAppender->activateOptions(p);
+        LoggerPtr root = Logger::getRootLogger();
+        root->addAppender(asyncAppender);
+        LOG4CXX_INFO(root, "Message");
+        Thread::sleep(10);
+        try {
+           LOG4CXX_INFO(root, "Message");
+           CPPUNIT_FAIL("Should have thrown exception");
+        } catch(NullPointerException& ex) {
+        }
+    }
+    
+    /**
+     * Tests non-blocking behavior.
+     */
+    void testLocationInfoTrue() {
+        BlockableVectorAppenderPtr blockableAppender = new \
BlockableVectorAppender(); +        AsyncAppenderPtr async = new AsyncAppender();
+        async->addAppender(blockableAppender);
+        async->setBufferSize(5);
+        async->setLocationInfo(true);
+        async->setBlocking(false);
+        Pool p;
+        async->activateOptions(p);
+        LoggerPtr rootLogger = Logger::getRootLogger();
+        rootLogger->addAppender(async);
+        {
+            synchronized sync(blockableAppender->getBlocker());
+            for (int i = 0; i < 100; i++) {
+                   LOG4CXX_INFO(rootLogger, "Hello, World");
+                   Thread::sleep(1);
+            }
+            LOG4CXX_ERROR(rootLogger, "That's all folks.");
+        }
+        async->close();
+        const std::vector<spi::LoggingEventPtr>& events = \
blockableAppender->getVector(); +        LoggingEventPtr initialEvent = events[0];
+        LoggingEventPtr discardEvent = events[events.size() - 1];
+        CPPUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Hello, World")), \
initialEvent->getMessage()); +        \
CPPUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Discarded ")), \
discardEvent->getMessage().substr(0,10)); +        \
CPPUNIT_ASSERT_EQUAL(log4cxx::spi::LocationInfo::getLocationUnavailable().getClassName(), \
 +            discardEvent->getLocationInformation().getClassName()); 
+    }
+    
+        void testConfiguration() {
+              log4cxx::xml::DOMConfigurator::configure("input/xml/asyncAppender1.xml");
 +              AsyncAppenderPtr \
asyncAppender(Logger::getRootLogger()->getAppender(LOG4CXX_STR("ASYNC"))); +          \
CPPUNIT_ASSERT(0 != asyncAppender); +              CPPUNIT_ASSERT_EQUAL(100, \
asyncAppender->getBufferSize()); +              CPPUNIT_ASSERT_EQUAL(false, \
asyncAppender->getBlocking()); +              CPPUNIT_ASSERT_EQUAL(true, \
asyncAppender->getLocationInfo()); +              AppenderList \
nestedAppenders(asyncAppender->getAllAppenders()); +              //   TODO:
+              //   test seems to work okay, but have not found a working way to 
+              //      get a reference to the nested vector appender 
+              //
+//              CPPUNIT_ASSERT_EQUAL((size_t) 1, nestedAppenders.size());
+//              VectorAppenderPtr vectorAppender(nestedAppenders[0]);
+//              CPPUNIT_ASSERT(0 != vectorAppender);
+              LoggerPtr root(Logger::getRootLogger()); 
+              
+              size_t LEN = 20;
+              for (size_t i = 0; i < LEN; i++) {
+                        LOG4CXX_DEBUG(root, "message" << i);
+              }
+              
+              asyncAppender->close();
+//              const std::vector<spi::LoggingEventPtr>& v = \
vectorAppender->getVector(); +//              CPPUNIT_ASSERT_EQUAL(LEN, v.size());
+//              CPPUNIT_ASSERT_EQUAL(true, vectorAppender->isClosed());
+        }
+
+        
 };
 
-//CPPUNIT_TEST_SUITE_REGISTRATION(AsyncAppenderTestCase);
+CPPUNIT_TEST_SUITE_REGISTRATION(AsyncAppenderTestCase);

Modified: logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp?rev=580508&r1=580507&r2=580508&view=diff
 ==============================================================================
--- logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp (original)
+++ logging/log4cxx/trunk/src/test/cpp/vectorappender.cpp Fri Sep 28 15:57:53 2007
@@ -27,7 +27,7 @@
 {
         try
         {
-            Thread::sleep(5000);
+            Thread::sleep(100);
         }
         catch (Exception&)
         {

Added: logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml
URL: http://svn.apache.org/viewvc/logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml?rev=580508&view=auto
 ==============================================================================
--- logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml (added)
+++ logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml Fri Sep 28 \
15:57:53 2007 @@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+-->
+
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/' debug="true">
+
+  <appender name="VECTOR" class="org.apache.log4j.VectorAppender"/>
+  
+  <appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
+    <param name="BufferSize" value="100"/>
+    <param name="Blocking" value="false"/>
+    <param name="LocationInfo" value="true"/>
+    <appender-ref ref="VECTOR"/>
+  </appender>
+
+  <root>
+    <level value="DEBUG"/>
+    <appender-ref ref="ASYNC" />
+  </root>
+</log4j:configuration>

Propchange: logging/log4cxx/trunk/src/test/resources/input/xml/asyncAppender1.xml
------------------------------------------------------------------------------
    svn:executable = *


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic