Index: src/test/cpp/helpers/filewatchdogtest.cpp =================================================================== --- src/test/cpp/helpers/filewatchdogtest.cpp (revision 0) +++ src/test/cpp/helpers/filewatchdogtest.cpp (revision 0) @@ -0,0 +1,66 @@ +/* + * 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 +#include +#include "../logunit.h" +#include "apr_time.h" + +using namespace log4cxx; +using namespace log4cxx::helpers; + + +/** + * + * FileWatchdog tests. + */ +LOGUNIT_CLASS(FileWatchdogTest) { + LOGUNIT_TEST_SUITE(FileWatchdogTest); + LOGUNIT_TEST(testShutdownDelay); + LOGUNIT_TEST_SUITE_END(); + +private: + class MockWatchdog : public FileWatchdog { + public: + MockWatchdog(const File& file) : FileWatchdog(file) { + } + + void doOnChange() { + } + }; + +public: + + /** + * Tests that FileWatchdog will respond to a shutdown request more rapidly + * than waiting out its delay. + */ + void testShutdownDelay() { + apr_time_t start = apr_time_now(); + { + MockWatchdog dog(File(LOG4CXX_STR("input/patternlayout1.properties"))); + dog.start(); + // wait 50 ms for thread to get rolling + apr_sleep(50000); + } + apr_time_t delta = apr_time_now() - start; + LOGUNIT_ASSERT(delta < 30000000); + } + +}; + +LOGUNIT_TEST_SUITE_REGISTRATION(FileWatchdogTest); + Index: src/main/cpp/mutex.cpp =================================================================== --- src/main/cpp/mutex.cpp (revision 679864) +++ src/main/cpp/mutex.cpp (working copy) @@ -19,6 +19,7 @@ #include #include #include +#include #include #if !defined(LOG4CXX) #define LOG4CXX 1 Index: src/main/cpp/domconfigurator.cpp =================================================================== --- src/main/cpp/domconfigurator.cpp (revision 679864) +++ src/main/cpp/domconfigurator.cpp (working copy) @@ -45,6 +45,9 @@ #include #include +#define LOG4CXX 1 +#include + using namespace log4cxx; using namespace log4cxx::xml; using namespace log4cxx::helpers; @@ -809,6 +812,7 @@ File file(filename); #if APR_HAS_THREADS XMLWatchdog * xdog = new XMLWatchdog(file); + APRInitializer::registerCleanup(xdog); xdog->setDelay(delay); xdog->start(); #else Index: src/main/cpp/propertyconfigurator.cpp =================================================================== --- src/main/cpp/propertyconfigurator.cpp (revision 679864) +++ src/main/cpp/propertyconfigurator.cpp (working copy) @@ -39,7 +39,10 @@ #include #include +#define LOG4CXX 1 +#include + using namespace log4cxx; using namespace log4cxx::spi; using namespace log4cxx::helpers; @@ -135,6 +138,7 @@ const File& configFilename, long delay) { PropertyWatchdog * pdog = new PropertyWatchdog(configFilename); + APRInitializer::registerCleanup(pdog); pdog->setDelay(delay); pdog->start(); } Index: src/main/cpp/aprinitializer.cpp =================================================================== --- src/main/cpp/aprinitializer.cpp (revision 679864) +++ src/main/cpp/aprinitializer.cpp (working copy) @@ -21,9 +21,12 @@ #include #include #include -#include #include #include +#include +#include +#include +#include using namespace log4cxx::helpers; using namespace log4cxx; @@ -37,7 +40,7 @@ } } -APRInitializer::APRInitializer() { +APRInitializer::APRInitializer() : p(0), mutex(0), startTime(0), tlsKey(0) { apr_initialize(); apr_pool_create(&p, NULL); apr_atomic_init(p); @@ -45,10 +48,22 @@ #if APR_HAS_THREADS apr_status_t stat = apr_threadkey_private_create(&tlsKey, tlsDestruct, p); assert(stat == APR_SUCCESS); + stat = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, p); + assert(stat == APR_SUCCESS); #endif } APRInitializer::~APRInitializer() { + { +#if APR_HAS_THREADS + synchronized sync(mutex); +#endif + for(std::vector::iterator iter = watchdogs.begin(); + iter != watchdogs.end(); + iter++) { + delete *iter; + } + } apr_terminate(); isDestructed = true; } @@ -71,3 +86,11 @@ return getInstance().tlsKey; } +void APRInitializer::registerCleanup(FileWatchdog* watchdog) { + APRInitializer& instance(getInstance()); +#if APR_HAS_THREADS + synchronized sync(instance.mutex); +#endif + instance.watchdogs.push_back(watchdog); +} + Index: src/main/cpp/filewatchdog.cpp =================================================================== --- src/main/cpp/filewatchdog.cpp (revision 679864) +++ src/main/cpp/filewatchdog.cpp (working copy) @@ -22,8 +22,8 @@ #include #include #include +#include - using namespace log4cxx; using namespace log4cxx::helpers; @@ -39,7 +39,11 @@ FileWatchdog::~FileWatchdog() { apr_atomic_set32(&interrupted, 0xFFFF); - thread.join(); + try { + thread.interrupt(); + thread.join(); + } catch(Exception &e) { + } } void FileWatchdog::checkAndConfigure() @@ -73,10 +77,10 @@ unsigned int interrupted = apr_atomic_read32(&pThis->interrupted); while(!interrupted) { - apr_sleep(APR_INT64_C(1000) * pThis->delay); - interrupted = apr_atomic_read32(&pThis->interrupted); - if (!interrupted) { + try { + Thread::sleep(pThis->delay); pThis->checkAndConfigure(); + } catch(InterruptedException& ex) { interrupted = apr_atomic_read32(&pThis->interrupted); } } Index: src/main/cpp/threadspecificdata.cpp =================================================================== --- src/main/cpp/threadspecificdata.cpp (revision 679864) +++ src/main/cpp/threadspecificdata.cpp (working copy) @@ -18,6 +18,7 @@ #include #include #include +#include #if !defined(LOG4CXX) #define LOG4CXX 1 #endif Index: src/main/include/log4cxx/helpers/aprinitializer.h =================================================================== --- src/main/include/log4cxx/helpers/aprinitializer.h (revision 679864) +++ src/main/include/log4cxx/helpers/aprinitializer.h (working copy) @@ -22,28 +22,44 @@ #error "aprinitializer.h should only be included by log4cxx implementation" #endif -#include -#include -#include +#include +extern "C" { +typedef struct apr_thread_mutex_t apr_thread_mutex_t; +typedef struct apr_threadkey_t apr_threadkey_t; +} + +#include + namespace log4cxx { namespace helpers { + class FileWatchdog; + class APRInitializer { public: - static log4cxx_time_t initialize(); + static apr_time_t initialize(); static apr_pool_t* getRootPool(); static apr_threadkey_t* getTlsKey(); static bool isDestructed; + + /** + * Register a FileWatchdog for deletion prior to + * APR termination. FileWatchdog must be + * allocated on heap and not deleted elsewhere. + */ + static void registerCleanup(FileWatchdog* watchdog); private: APRInitializer(); APRInitializer(const APRInitializer&); APRInitializer& operator=(const APRInitializer&); apr_pool_t* p; - log4cxx_time_t startTime; + apr_thread_mutex_t* mutex; + std::vector watchdogs; + apr_time_t startTime; apr_threadkey_t* tlsKey; static APRInitializer& getInstance(); Index: src/examples/cpp/trivial.cpp =================================================================== --- src/examples/cpp/trivial.cpp (revision 679864) +++ src/examples/cpp/trivial.cpp (working copy) @@ -21,6 +21,8 @@ #include #include #include +#include +#include using namespace log4cxx; using namespace log4cxx::helpers; @@ -31,13 +33,14 @@ int result = EXIT_SUCCESS; try { - BasicConfigurator::configure(); + PropertyConfigurator::configureAndWatch("input/patternlayout1.properties"); LoggerPtr rootLogger = Logger::getRootLogger(); NDC::push("trivial context"); LOG4CXX_DEBUG(rootLogger, "debug message") LOG4CXX_INFO(rootLogger, "info message") + Thread::sleep(130000); LOG4CXX_WARN(rootLogger, "warn message") LOG4CXX_ERROR(rootLogger, "error message") LOG4CXX_FATAL(rootLogger, "fatal message") @@ -48,6 +51,5 @@ { result = EXIT_FAILURE; } - return result; }