Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LOGCXX-431 #68

Merged
merged 17 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/cmake/pthread/log4cxx-pthread.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include(FindThreads)

try_compile(PTHREAD_SETNAME_NP_FOUND "${CMAKE_BINARY_DIR}/pthread-compile-tests"
"${CMAKE_CURRENT_LIST_DIR}/test-pthread.cpp"
LINK_LIBRARIES Threads::Threads )

6 changes: 6 additions & 0 deletions src/cmake/pthread/test-pthread.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include <pthread.h>

int main(){
pthread_t tid;
pthread_setname_np(tid, "name");
}
1 change: 1 addition & 0 deletions src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ target_sources(log4cxx
threadlocal.cpp
threadpatternconverter.cpp
threadspecificdata.cpp
threadutility.cpp
throwableinformationpatternconverter.cpp
timebasedrollingpolicy.cpp
timezone.cpp
Expand Down
3 changes: 2 additions & 1 deletion src/main/cpp/asyncappender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <log4cxx/helpers/stringhelper.h>
#include <apr_atomic.h>
#include <log4cxx/helpers/optionconverter.h>
#include <log4cxx/helpers/threadutility.h>


using namespace log4cxx;
Expand All @@ -50,7 +51,7 @@ AsyncAppender::AsyncAppender()
locationInfo(false),
blocking(true)
{
dispatcher = std::thread( &AsyncAppender::dispatch, this );
dispatcher = ThreadUtility::instance()->createThread( LOG4CXX_STR("AsyncAppender"), &AsyncAppender::dispatch, this );
}

AsyncAppender::~AsyncAppender()
Expand Down
3 changes: 2 additions & 1 deletion src/main/cpp/filewatchdog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <apr_atomic.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/threadutility.h>
#include <functional>

using namespace log4cxx;
Expand Down Expand Up @@ -92,7 +93,7 @@ void FileWatchdog::start()
{
checkAndConfigure();

thread = std::thread( &FileWatchdog::run, this );
thread = ThreadUtility::instance()->createThread( LOG4CXX_STR("FileWatchdog"), &FileWatchdog::run, this );
}

bool FileWatchdog::is_interrupted()
Expand Down
3 changes: 2 additions & 1 deletion src/main/cpp/socketappenderskeleton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <log4cxx/spi/loggingevent.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/bytearrayoutputstream.h>
#include <log4cxx/helpers/threadutility.h>
#include <functional>

using namespace log4cxx;
Expand Down Expand Up @@ -162,7 +163,7 @@ void SocketAppenderSkeleton::fireConnector()
{
LogLog::debug(LOG4CXX_STR("Connector thread not alive: starting monitor."));

thread = std::thread( &SocketAppenderSkeleton::monitor, this );
thread = ThreadUtility::instance()->createThread( LOG4CXX_STR("SocketAppend"), &SocketAppenderSkeleton::monitor, this );
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/cpp/sockethubappender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <log4cxx/helpers/objectoutputstream.h>
#include <log4cxx/helpers/socketoutputstream.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/threadutility.h>
#include <mutex>

using namespace log4cxx;
Expand Down Expand Up @@ -177,7 +178,7 @@ void SocketHubAppender::append(const spi::LoggingEventPtr& event, Pool& p)

void SocketHubAppender::startServer()
{
thread = std::thread( &SocketHubAppender::monitor, this );
thread = ThreadUtility::instance()->createThread( LOG4CXX_STR("SocketHub"), &SocketHubAppender::monitor, this );
}

void SocketHubAppender::monitor()
Expand Down
3 changes: 2 additions & 1 deletion src/main/cpp/telnetappender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <apr_strings.h>
#include <log4cxx/helpers/charsetencoder.h>
#include <log4cxx/helpers/bytebuffer.h>
#include <log4cxx/helpers/threadutility.h>
#include <mutex>

using namespace log4cxx;
Expand Down Expand Up @@ -61,7 +62,7 @@ void TelnetAppender::activateOptions(Pool& /* p */)
serverSocket->setSoTimeout(1000);
}

sh = std::thread( &TelnetAppender::acceptConnections, this );
sh = ThreadUtility::instance()->createThread( LOG4CXX_STR("TelnetAppender"), &TelnetAppender::acceptConnections, this );
}

void TelnetAppender::setOption(const LogString& option,
Expand Down
127 changes: 127 additions & 0 deletions src/main/cpp/threadutility.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include "log4cxx/helpers/threadutility.h"
#include "log4cxx/private/log4cxx_private.h"
#include "log4cxx/helpers/loglog.h"

#include <signal.h>
#include <mutex>

#if LOG4CXX_HAS_SETTHREADDESCRIPTION
#include <windows.h>
#include <processthreadsapi.h>
#endif

using log4cxx::helpers::ThreadUtility;

struct ThreadUtility::priv_data{
ams-tschoening marked this conversation as resolved.
Show resolved Hide resolved
priv_data(){
start_pre = nullptr;
started = nullptr;
start_post = nullptr;
}

log4cxx::helpers::ThreadStartPre start_pre;
log4cxx::helpers::ThreadStarted started;
log4cxx::helpers::ThreadStartPost start_post;
};
ams-tschoening marked this conversation as resolved.
Show resolved Hide resolved

#if LOG4CXX_HAS_PTHREAD_SIGMASK
static thread_local sigset_t old_mask;
static thread_local bool sigmask_valid;
#endif

ThreadUtility::ThreadUtility() :
m_priv( new priv_data() )
{}

ThreadUtility::~ThreadUtility(){}

std::shared_ptr<ThreadUtility> ThreadUtility::instance(){
static std::shared_ptr<ThreadUtility> instance( new ThreadUtility() );
return instance;
}

void ThreadUtility::configure( ThreadConfigurationType type ){
std::shared_ptr<ThreadUtility> utility = instance();

if( type == ThreadConfigurationType::NoConfiguration ){
utility->configureFuncs( nullptr, nullptr, nullptr );
}else if( type == ThreadConfigurationType::NameThreadOnly ){
utility->configureFuncs( nullptr,
std::bind( &ThreadUtility::threadStartedNameThread, utility,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3 ),
nullptr );
}else if( type == ThreadConfigurationType::BlockSignalsOnly ){
utility->configureFuncs( std::bind( &ThreadUtility::preThreadBlockSignals, utility ),
nullptr,
std::bind( &ThreadUtility::postThreadUnblockSignals, utility ) );
}else if( type == ThreadConfigurationType::BlockSignalsAndNameThread ){
utility->configureFuncs( std::bind( &ThreadUtility::preThreadBlockSignals, utility ),
std::bind( &ThreadUtility::threadStartedNameThread, utility,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3 ),
std::bind( &ThreadUtility::postThreadUnblockSignals, utility ) );
}
}

void ThreadUtility::configureFuncs( ThreadStartPre pre_start,
ThreadStarted started,
ThreadStartPost post_start ){
m_priv->start_pre = pre_start;
m_priv->started = started;
m_priv->start_post = post_start;
}

void ThreadUtility::preThreadBlockSignals(){
#if LOG4CXX_HAS_PTHREAD_SIGMASK
sigset_t set;
sigfillset(&set);
if( pthread_sigmask(SIG_SETMASK, &set, &old_mask) < 0 ){
LOGLOG_ERROR( LOG4CXX_STR("Unable to set thread sigmask") );
sigmask_valid = false;
}else{
sigmask_valid = true;
}
#endif /* LOG4CXX_HAS_PTHREAD_SIGMASK */
}

void ThreadUtility::threadStartedNameThread(LogString threadName,
std::thread::id /*threadId*/,
std::thread::native_handle_type nativeHandle){
#if LOG4CXX_HAS_PTHREAD_SETNAME
if( pthread_setname_np( static_cast<pthread_t>( nativeHandle ), threadName.c_str() ) < 0 ){
LOGLOG_ERROR( LOG4CXX_STR("unable to set thread name") );
}
#elif LOG4CXX_HAS_SETTHREADDESCRIPTION
HRESULT hr = SetThreadDescription(static_cast<HANDLE>(nativeHandle), threadName.c_str());
if(FAILED(hr)){
LOGLOG_ERROR( LOG4CXX_STR("unable to set thread name") );
}
#endif
}

void ThreadUtility::postThreadUnblockSignals(){
#if LOG4CXX_HAS_PTHREAD_SIGMASK
// Only restore the signal mask if we were able to set it in the first place.
if( sigmask_valid ){
if( pthread_sigmask(SIG_SETMASK, &old_mask, nullptr) < 0 ){
LOGLOG_ERROR( LOG4CXX_STR("Unable to set thread sigmask") );
}
}
#endif /* LOG4CXX_HAS_PTHREAD_SIGMASK */
}


log4cxx::helpers::ThreadStartPre ThreadUtility::preStartFunction(){
return m_priv->start_pre;
}

log4cxx::helpers::ThreadStarted ThreadUtility::threadStartedFunction(){
return m_priv->started;
}

log4cxx::helpers::ThreadStartPost ThreadUtility::postStartFunction(){
return m_priv->start_post;
}
20 changes: 19 additions & 1 deletion src/main/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,26 @@ CHECK_FUNCTION_EXISTS(wcstombs HAS_WCSTOMBS)
CHECK_FUNCTION_EXISTS(fwide HAS_FWIDE)
CHECK_LIBRARY_EXISTS(esmtp smtp_create_session "" HAS_LIBESMTP)
CHECK_FUNCTION_EXISTS(syslog HAS_SYSLOG)
if(UNIX)
set(CMAKE_REQUIRED_LIBRARIES "pthread")
CHECK_SYMBOL_EXISTS(pthread_sigmask "signal.h" HAS_PTHREAD_SIGMASK)

# Check for the (linux) pthread_setname_np.
# OSX and BSD are special apparently. OSX only lets you name
# the current thread, while BSD calls it pthread_set_name_np.
# Since this is really not a core feature and the end-user can configure
# it anyway, we're only going to worry about linux.
include(${LOG4CXX_SOURCE_DIR}/src/cmake/pthread/log4cxx-pthread.cmake)
if(${PTHREAD_SETNAME_NP_FOUND})
set(HAS_PTHREAD_SETNAME 1)
endif()
endif(UNIX)

foreach(varName HAS_STD_LOCALE HAS_ODBC HAS_MBSRTOWCS HAS_WCSTOMBS HAS_FWIDE HAS_LIBESMTP HAS_SYSLOG)
if(WIN32)
CHECK_SYMBOL_EXISTS(SetThreadDescription "windows.h;processthreadsapi.h" HAS_SETTHREADDESCRIPTION)
endif(WIN32)

foreach(varName HAS_STD_LOCALE HAS_ODBC HAS_MBSRTOWCS HAS_WCSTOMBS HAS_FWIDE HAS_LIBESMTP HAS_SYSLOG HAS_PTHREAD_SIGMASK HAS_PTHREAD_SETNAME HAS_SETTHREADDESCRIPTION)
if(${varName} EQUAL 0)
continue()
elseif(${varName} EQUAL 1)
Expand Down
132 changes: 132 additions & 0 deletions src/main/include/log4cxx/helpers/threadutility.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#ifndef _LOG4CXX_THREADUTILITY_H
#define _LOG4CXX_THREADUTILITY_H

#include <thread>
#include <functional>
#include <memory>

#include "log4cxx/logstring.h"

namespace log4cxx {
namespace helpers {

/**
* A function that will be called before a thread is started. This can
* be used to (for example) block all of the signals in the thread, so
* that when the thread is created it will have a correct signal mask.
*/
typedef std::function<void()> ThreadStartPre;

/**
* Called when a new thread has started. This can be used to set
* parameters for the thread in a platform-specific manner.
*
* @param threadName The name of the thread
* @param threadId The ID of the thread as reported by std::thread::get_id
* @param nativeHandle The native handle of the thread, as reported by
* std::thread::native_handle
*/
typedef std::function<void( LogString threadName,
std::thread::id threadId,
std::thread::native_handle_type nativeHandle )> ThreadStarted;

/**
* Called after a thread has started. This can be used to (for example)
* unblock the signals in the thread.
*/
typedef std::function<void()> ThreadStartPost;

enum class ThreadConfigurationType {
NoConfiguration,
BlockSignalsOnly,
NameThreadOnly,
BlockSignalsAndNameThread,
};

class ThreadUtility;
LOG4CXX_PTR_DEF(ThreadUtility);

class LOG4CXX_EXPORT ThreadUtility {
private:
ThreadUtility();

log4cxx::helpers::ThreadStartPre preStartFunction();
log4cxx::helpers::ThreadStarted threadStartedFunction();
log4cxx::helpers::ThreadStartPost postStartFunction();

struct priv_data;
std::unique_ptr<priv_data> m_priv;

public:
~ThreadUtility();

static std::shared_ptr<ThreadUtility> instance();

/**
* Utility method for configuring the ThreadUtility in a standard
* configuration.
*/
static void configure( ThreadConfigurationType type );

/**
* Configure the thread functions that log4cxx will use.
* Note that setting any of these parameters to nullptr is valid,
* and simply results in the callback not being called.
*/
void configureFuncs( ThreadStartPre pre_start,
ThreadStarted started,
ThreadStartPost post_start );

/**
* A pre-start thread function that blocks signals to the new thread
* (if the system has pthreads). If the system does not have pthreads,
* does nothing.
*/
void preThreadBlockSignals();

/**
* A thread_started function that names the thread using the appropriate
* system call.
*/
void threadStartedNameThread(LogString threadName,
std::thread::id thread_id,
std::thread::native_handle_type native_handle);

/**
* A post-start thread function that unblocks signals that preThreadBlockSignals
* blocked before starting the thread. If the system does not have pthreads,
* does nothing.
*/
void postThreadUnblockSignals();

/**
* Start a thread
*/
template<class Function, class... Args>
std::thread createThread(LogString name,
Function&& f,
Args&&... args){
log4cxx::helpers::ThreadStartPre pre_start = preStartFunction();
log4cxx::helpers::ThreadStarted thread_start = threadStartedFunction();
log4cxx::helpers::ThreadStartPost post_start = postStartFunction();

if( pre_start ){
pre_start();
}
std::thread t( f, args... );
if( thread_start ){
thread_start( name,
t.get_id(),
t.native_handle() );
}
if( post_start ){
ams-tschoening marked this conversation as resolved.
Show resolved Hide resolved
post_start();
}
return t;
}
};

} /* namespace helpers */
} /* namespace log4cxx */

#endif
Loading