blob: a04a90a2f3a5e66e2661b2401d8f91caf66e443a [file] [log] [blame]
/*
* 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/threadutility.h"
#if !defined(LOG4CXX)
#define LOG4CXX 1
#endif
#include "log4cxx/private/log4cxx_private.h"
#include "log4cxx/helpers/loglog.h"
#include "log4cxx/helpers/transcoder.h"
#include <signal.h>
#include <mutex>
#if WIN32
#include <windows.h>
#include <processthreadsapi.h>
#endif
using log4cxx::helpers::ThreadUtility;
struct ThreadUtility::priv_data
{
priv_data()
{
start_pre = nullptr;
started = nullptr;
start_post = nullptr;
}
log4cxx::helpers::ThreadStartPre start_pre;
log4cxx::helpers::ThreadStarted started;
log4cxx::helpers::ThreadStartPost start_post;
};
#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() )
{
// Block signals by default.
configureFuncs( std::bind( &ThreadUtility::preThreadBlockSignals, this ),
nullptr,
std::bind( &ThreadUtility::postThreadUnblockSignals, this ) );
}
ThreadUtility::~ThreadUtility() {}
ThreadUtility* ThreadUtility::instance()
{
static ThreadUtility instance;
return &instance;
}
void ThreadUtility::configure( ThreadConfigurationType type )
{
auto 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
LOG4CXX_ENCODE_CHAR(sthreadName, threadName);
if (pthread_setname_np(static_cast<pthread_t>(nativeHandle), sthreadName.c_str()) < 0) {
LOGLOG_ERROR(LOG4CXX_STR("unable to set thread name"));
}
#elif WIN32
typedef HRESULT (WINAPI *TSetThreadDescription)(HANDLE, PCWSTR);
static struct initialiser
{
HMODULE hKernelBase;
TSetThreadDescription SetThreadDescription;
initialiser()
: hKernelBase(GetModuleHandleA("KernelBase.dll"))
, SetThreadDescription(nullptr)
{
if (hKernelBase)
SetThreadDescription = reinterpret_cast<TSetThreadDescription>(GetProcAddress(hKernelBase, "SetThreadDescription"));
}
} win32Func;
if (win32Func.SetThreadDescription)
{
LOG4CXX_ENCODE_WCHAR(wthreadName, threadName);
if(FAILED(win32Func.SetThreadDescription(static_cast<HANDLE>(nativeHandle), wthreadName.c_str())))
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;
}