mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-02-24 17:00:27 -05:00

Avoid compiler warning due to virtual method being called in destructor at this point the empty default version would be called without damage but making the compiler concernig about the semantic being different from the one the programmer meant. Delete old superseeded function and do not depend on it on RsThread.
335 lines
9.6 KiB
C++
335 lines
9.6 KiB
C++
/*******************************************************************************
|
|
* libretroshare/src/util: rsthreads.cc *
|
|
* *
|
|
* libretroshare: retroshare core library *
|
|
* *
|
|
* Copyright (C) 2004-2007 Robert Fernie <retroshare@lunamutt.com> *
|
|
* Copyright (C) 2016-2021 Gioacchino Mazzurco <gio@eigenlab.org> *
|
|
* Copyright (C) 2019-2021 Asociación Civil Altermundi <info@altermundi.net> *
|
|
* *
|
|
* This program is free software: you can redistribute it and/or modify *
|
|
* it under the terms of the GNU Lesser General Public License as *
|
|
* published by the Free Software Foundation, either version 3 of the *
|
|
* License, or (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU Lesser General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU Lesser General Public License *
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
|
* *
|
|
*******************************************************************************/
|
|
|
|
#include "rsthreads.h"
|
|
|
|
#include "util/rsdebug.h"
|
|
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <iostream>
|
|
#include <thread>
|
|
|
|
#ifdef RS_MUTEX_DEBUG
|
|
#include <cstdio>
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
int __attribute__((weak)) pthread_setname_np(const char *__buf) ;
|
|
int RS_pthread_setname_np(pthread_t /*__target_thread*/, const char *__buf) {
|
|
return pthread_setname_np(__buf);
|
|
}
|
|
#else
|
|
#ifndef __WIN64__
|
|
int __attribute__((weak)) pthread_setname_np(pthread_t __target_thread, const char *__buf) ;
|
|
#endif //__WIN64__
|
|
int RS_pthread_setname_np(pthread_t __target_thread, const char *__buf) {
|
|
return pthread_setname_np(__target_thread, __buf);
|
|
}
|
|
#endif //__APPLE__
|
|
|
|
|
|
/*******
|
|
* #define DEBUG_THREADS 1
|
|
* #define RSMUTEX_ABORT 1 // Catch wrong pthreads mode.
|
|
*******/
|
|
#define THREAD_DEBUG RsDbg() << "[this=" << static_cast<void*>(this) \
|
|
<< ", caller thread ID: " << std::hex << pthread_self() << ", thread ID: " \
|
|
<< mTid << std::dec << "] "
|
|
|
|
#ifdef RSMUTEX_ABORT
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#ifdef DEBUG_THREADS
|
|
#include <iostream>
|
|
#endif
|
|
|
|
/*static*/ void* RsThread::rsthread_init(void* p)
|
|
{
|
|
RsThread* thread = reinterpret_cast<RsThread *>(p);
|
|
if(!thread) return nullptr;
|
|
|
|
/* Using pthread_detach(...) the thread resources will be automatically
|
|
* freed when this function return, so there is no need for pthread_join()
|
|
* later. */
|
|
pthread_detach(pthread_self());
|
|
|
|
#ifdef DEBUG_THREADS
|
|
std::cerr << "[Thread ID:" << std::hex << pthread_self() << std::dec
|
|
<< "] thread is started. Calling wrapRun()..." << std::endl;
|
|
#endif
|
|
|
|
thread->wrapRun();
|
|
return nullptr;
|
|
}
|
|
|
|
void RsThread::resetTid()
|
|
{
|
|
#ifdef WINDOWS_SYS
|
|
memset (&mTid, 0, sizeof(mTid));
|
|
#else
|
|
mTid = pthread_t(); //Thread identifiers should be considered opaque and can be null.
|
|
#endif
|
|
}
|
|
|
|
RsThread::RsThread() : mInitMtx("RsThread"), mHasStopped(true),
|
|
mShouldStop(false), mLastTid()
|
|
#ifdef RS_THREAD_FORCE_STOP
|
|
, mStopTimeout(0)
|
|
#endif
|
|
{ resetTid(); }
|
|
|
|
bool RsThread::isRunning() { return !mHasStopped; }
|
|
|
|
bool RsThread::shouldStop() { return mShouldStop; }
|
|
|
|
void RsThread::askForStop()
|
|
{
|
|
/* Call onStopRequested() only once even if askForStop() is called multiple
|
|
* times */
|
|
if(!mShouldStop.exchange(true)) onStopRequested();
|
|
}
|
|
|
|
void RsThread::wrapRun()
|
|
{
|
|
{RS_STACK_MUTEX(mInitMtx);} // Waiting Init done.
|
|
run();
|
|
resetTid();
|
|
mHasStopped = true;
|
|
}
|
|
|
|
void RsThread::fullstop()
|
|
{
|
|
askForStop();
|
|
waitWhileStopping();
|
|
}
|
|
|
|
void RsThread::waitWhileStopping()
|
|
{
|
|
#ifdef RS_THREAD_FORCE_STOP
|
|
const rstime_t stopRequTS = time(nullptr);
|
|
#endif
|
|
|
|
const pthread_t callerTid = pthread_self();
|
|
if(pthread_equal(mTid, callerTid))
|
|
{
|
|
RsErr() << __PRETTY_FUNCTION__ << " called by same thread. This should "
|
|
<< "never happen! this: " << static_cast<void*>(this)
|
|
<< std::hex << ", callerTid: " << callerTid
|
|
<< ", mTid: " << mTid << std::dec
|
|
<< ", mFullName: " << mFullName << std::endl;
|
|
print_stacktrace();
|
|
return;
|
|
}
|
|
|
|
// Wait for the thread being stopped
|
|
auto i = 1;
|
|
while(!mHasStopped)
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
++i;
|
|
if(!(i%5))
|
|
RsDbg() << __PRETTY_FUNCTION__ << " " << i*0.2 << " seconds passed"
|
|
<< " waiting for thread: " << std::hex << mLastTid
|
|
<< std::dec << " " << mFullName << " to stop" << std::endl;
|
|
|
|
#ifdef RS_THREAD_FORCE_STOP
|
|
if(mStopTimeout && time(nullptr) > stopRequTS + mStopTimeout)
|
|
{
|
|
RsErr() << __PRETTY_FUNCTION__ << " thread mLastTid: " << std::hex
|
|
<< mLastTid << " mTid: " << mTid << std::dec << " "
|
|
<< mFullName
|
|
<< " ignored our nice stop request for more then "
|
|
<< mStopTimeout
|
|
<< " seconds, will be forcefully stopped. "
|
|
<< "Please submit a report to RetroShare developers"
|
|
<< std::endl;
|
|
|
|
const auto terr = pthread_cancel(mTid);
|
|
if(terr == 0) mHasStopped = true;
|
|
else
|
|
{
|
|
RsErr() << __PRETTY_FUNCTION__ << " pthread_cancel("
|
|
<< std::hex << mTid << std::dec <<") returned "
|
|
<< terr << " " << rs_errno_to_condition(terr)
|
|
<< std::endl;
|
|
print_stacktrace();
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif // def RS_THREAD_FORCE_STOP
|
|
}
|
|
}
|
|
|
|
bool RsThread::start(const std::string& threadName)
|
|
{
|
|
// Atomically check if the thread was already started and set it as running
|
|
if(mHasStopped.exchange(false))
|
|
{
|
|
RS_STACK_MUTEX(mInitMtx); // Block thread starting to run
|
|
|
|
mShouldStop = false;
|
|
int pError = pthread_create(
|
|
&mTid, nullptr, &rsthread_init, static_cast<void*>(this) );
|
|
if(pError)
|
|
{
|
|
RS_ERR( "pthread_create could not create new thread: ", threadName,
|
|
rs_errno_to_condition(pError) );
|
|
mHasStopped = true;
|
|
print_stacktrace();
|
|
return false;
|
|
}
|
|
|
|
/* Store an extra copy of thread id for debugging */
|
|
mLastTid = mTid;
|
|
|
|
/* Store thread full name as PThread is not able to keep it entirely */
|
|
mFullName = threadName;
|
|
|
|
/* Set PThread thread name which is restricted to 16 characters
|
|
* including the terminating null byte */
|
|
if(pthread_setname_np && !threadName.empty())
|
|
RS_pthread_setname_np(mTid, threadName.substr(0, 15).c_str());
|
|
|
|
return true;
|
|
}
|
|
|
|
RsErr() << __PRETTY_FUNCTION__ << " attempt to start already running thread"
|
|
<< std::endl;
|
|
print_stacktrace();
|
|
return false;
|
|
}
|
|
|
|
RsQueueThread::RsQueueThread(uint32_t min, uint32_t max, double relaxFactor )
|
|
:mMinSleep(min), mMaxSleep(max), mRelaxFactor(relaxFactor)
|
|
{
|
|
mLastSleep = (uint32_t)mMinSleep ;
|
|
mLastWork = time(NULL) ;
|
|
}
|
|
|
|
void RsQueueThread::threadTick()
|
|
{
|
|
bool doneWork = false;
|
|
while(workQueued() && doWork())
|
|
{
|
|
doneWork = true;
|
|
}
|
|
time_t now = time(NULL);
|
|
if (doneWork)
|
|
{
|
|
mLastWork = now;
|
|
mLastSleep = (uint32_t) (mMinSleep + (mLastSleep - mMinSleep) / 2.0);
|
|
#ifdef DEBUG_TICKING
|
|
THREAD_DEBUG << "RsQueueThread::data_tick() done work: sleeping for: " << mLastSleep << " ms" << std::endl;
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
uint32_t deltaT = now - mLastWork;
|
|
double frac = deltaT / mRelaxFactor;
|
|
|
|
mLastSleep += (uint32_t)
|
|
((mMaxSleep-mMinSleep) * (frac + 0.05));
|
|
if (mLastSleep > mMaxSleep)
|
|
{
|
|
mLastSleep = mMaxSleep;
|
|
}
|
|
#ifdef DEBUG_TICKING
|
|
THREAD_DEBUG << "RsQueueThread::data_tick() no work: sleeping for: " << mLastSleep << " ms" << std::endl;
|
|
#endif
|
|
}
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(mLastSleep));
|
|
}
|
|
|
|
void RsMutex::unlock()
|
|
{
|
|
_thread_id = 0;
|
|
pthread_mutex_unlock(&realMutex);
|
|
}
|
|
|
|
void RsMutex::lock()
|
|
{
|
|
int err = pthread_mutex_lock(&realMutex);
|
|
if( err != 0)
|
|
{
|
|
RsErr() << __PRETTY_FUNCTION__ << "pthread_mutex_lock returned: "
|
|
<< rs_errno_to_condition(err)
|
|
#ifdef RS_MUTEX_DEBUG
|
|
<< " name: " << name()
|
|
#endif
|
|
<< std::endl;
|
|
|
|
print_stacktrace();
|
|
|
|
#ifdef RSMUTEX_ABORT
|
|
abort();
|
|
#endif
|
|
}
|
|
|
|
_thread_id = pthread_self();
|
|
}
|
|
|
|
#ifdef RS_MUTEX_DEBUG
|
|
double RsStackMutex::getCurrentTS()
|
|
{
|
|
|
|
#ifndef WINDOWS_SYS
|
|
struct timeval cts_tmp;
|
|
gettimeofday(&cts_tmp, NULL);
|
|
double cts = (cts_tmp.tv_sec) + ((double) cts_tmp.tv_usec) / 1000000.0;
|
|
#else
|
|
struct _timeb timebuf;
|
|
_ftime( &timebuf);
|
|
double cts = (timebuf.time) + ((double) timebuf.millitm) / 1000.0;
|
|
#endif
|
|
return cts;
|
|
}
|
|
#endif
|
|
|
|
|
|
RsThread::~RsThread()
|
|
{
|
|
if(!mHasStopped)
|
|
{
|
|
RsErr() << __PRETTY_FUNCTION__ << " deleting thread: " << mLastTid
|
|
<< " " << mFullName << " that is still "
|
|
<< "running! Something seems very wrong here and RetroShare is "
|
|
<< "likely to crash because of this." << std::endl;
|
|
print_stacktrace();
|
|
|
|
/* Last resort attempt to stop the thread in a less pathological state.
|
|
* Don't call fullstop() as it rely on virtual methods that at this
|
|
* point are not anymore the one from inerithing classes causing
|
|
* compilers to output a warning */
|
|
waitWhileStopping();
|
|
}
|
|
}
|
|
|
|
RsQueueThread::~RsQueueThread() = default;
|