Files
hpsocket-linux/common/FuncHelper.cpp
2025-04-17 20:38:35 +08:00

394 lines
8.3 KiB
C++

/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed 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 "FuncHelper.h"
#include "Thread.h"
#include <ctype.h>
#include <sched.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/timerfd.h>
#if !defined(__ANDROID__)
#include <sys/timeb.h>
#include <execinfo.h>
#ifdef __GNUC__
#include <cxxabi.h>
#endif
#endif
INT WaitFor(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
{
timeval tv {(time_t)dwSecond, (suseconds_t)(dwMillSecond * 1000)};
if(bExceptThreadInterrupted)
return NO_EINTR_EXCEPT_THR_INTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
return NO_EINTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
}
INT Sleep(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
{
timespec ts_req = {(time_t)dwSecond, (long)(dwMillSecond * 1000000)};
timespec ts_rem = ts_req;
INT rs = NO_ERROR;
while(IS_HAS_ERROR(rs = nanosleep(&ts_req, &ts_rem)))
{
if(!IS_INTR_ERROR())
break;
else
{
if(bExceptThreadInterrupted && ::IsThreadInterrupted())
break;
}
ts_req = ts_rem;
}
return rs;
}
__time64_t _time64(time_t* ptm)
{
return (__time64_t)time(ptm);
}
__time64_t _mkgmtime64(tm* ptm)
{
return (__time64_t)timegm(ptm);
}
tm* _gmtime64(tm* ptm, __time64_t* pt)
{
time_t t = (time_t)(*pt);
return gmtime_r(&t, ptm);
}
DWORD TimeGetTime()
{
return (DWORD)TimeGetTime64();
}
ULLONG TimeGetTime64()
{
#if !defined(__ANDROID__)
timeb tb;
if(ftime(&tb) == NO_ERROR)
return (((ULLONG)(tb.time)) * 1000 + tb.millitm);
#else
timespec ts;
if(clock_gettime(CLOCK_MONOTONIC, &ts) == NO_ERROR)
return (((ULLONG)(ts.tv_sec)) * 1000 + ts.tv_nsec / 1000000);
#endif
return 0ull;
}
DWORD GetTimeGap32(DWORD dwOriginal, DWORD dwCurrent)
{
if(dwCurrent == 0)
dwCurrent = ::TimeGetTime();
return dwCurrent - dwOriginal;
}
ULLONG GetTimeGap64(ULLONG ullOriginal, ULONGLONG ullCurrent)
{
if(ullCurrent == 0)
ullCurrent = ::TimeGetTime64();
return ullCurrent - ullOriginal;
}
LLONG TimevalToMillisecond(const timeval& tv)
{
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
timeval& MillisecondToTimeval(LLONG ms, timeval& tv)
{
tv.tv_sec = (time_t)(ms / 1000);
tv.tv_usec = (suseconds_t)((ms % 1000) * 1000);
return tv;
}
LLONG TimespecToMillisecond(const timespec& ts)
{
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
timespec& MillisecondToTimespec(LLONG ms, timespec& ts)
{
ts.tv_sec = (time_t)(ms / 1000);
ts.tv_nsec = (long)((ms % 1000) * 1000000);
return ts;
}
timeval& GetFutureTimeval(LLONG ms, timeval& tv, struct timezone* ptz)
{
gettimeofday(&tv, ptz);
tv.tv_sec += (time_t)(ms / 1000);
tv.tv_usec += (suseconds_t)((ms % 1000) * 1000);
return tv;
}
timespec& GetFutureTimespec(LLONG ms, timespec& ts, clockid_t clkid)
{
clock_gettime(clkid, &ts);
ts.tv_sec += (time_t)(ms / 1000);
ts.tv_nsec += (long)((ms % 1000) * 1000000);
return ts;
}
FD CreateTimer(LLONG llInterval, LLONG llStart, BOOL bRealTimeClock)
{
ASSERT_CHECK_EINVAL(llInterval >= 0L);
if(llStart < 0)
llStart = llInterval;
FD fdTimer = timerfd_create((bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC), TFD_NONBLOCK | TFD_CLOEXEC);
itimerspec its;
::MillisecondToTimespec(llStart, its.it_value);
::MillisecondToTimespec(llInterval, its.it_interval);
if(IS_HAS_ERROR(timerfd_settime(fdTimer, 0, &its, nullptr)))
{
close(fdTimer);
fdTimer = INVALID_FD;
}
return fdTimer;
}
BOOL ReadTimer(FD tmr, ULLONG* pVal, BOOL* pRs)
{
static const SSIZE_T SIZE = sizeof(ULLONG);
if(pVal == nullptr)
pVal = CreateLocalObject(ULLONG);
if(pRs == nullptr)
pRs = CreateLocalObject(BOOL);
if(read(tmr, pVal, SIZE) == SIZE)
*pRs = TRUE;
else
{
*pRs = FALSE;
if(!IS_WOULDBLOCK_ERROR())
return FALSE;
}
return TRUE;
}
BOOL fcntl_SETFL(FD fd, INT fl, BOOL bSet)
{
int val = fcntl(fd, F_GETFL);
if(IS_HAS_ERROR(val))
return FALSE;
val = bSet ? (val | fl) : (val & (~fl));
return IS_NO_ERROR(fcntl(fd, F_SETFL , val));
}
void PrintStackTrace()
{
#if !defined(__ANDROID__)
const int MAX_SIZE = 51;
void* arr[MAX_SIZE];
int size = backtrace(arr, MAX_SIZE);
char** messages = backtrace_symbols(arr, size);
for(int i = 1; i < size && messages != nullptr; i++)
{
char* mangled_name = nullptr;
char* offset_end = nullptr;
const char* offset_begin = nullptr;
for(char* p = messages[i]; *p; ++p)
{
if(*p == '(')
{
mangled_name = p;
}
else if(*p == '+')
{
offset_begin = p;
}
else if(*p == ')')
{
offset_end = p;
break;
}
}
if(mangled_name && offset_end && mangled_name < offset_end)
{
*mangled_name++ = 0;
*offset_end++ = 0;
if(offset_begin == nullptr)
offset_begin = "";
else
*(char*)offset_begin++ = 0;
while(*offset_end == ' ')
++offset_end;
#ifdef __GNUC__
int status;
char* real_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
if(status == 0)
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], real_name, offset_begin, offset_end);
else
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
if(real_name != nullptr)
free(real_name);
#else
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
#endif
}
else
{
FPRINTLN(stderr, " -> [%02d] %s", i, messages[i]);
}
}
free(messages);
#endif
}
void __EXIT_FN_(void (*fn)(int), LPCSTR lpszFnName, int* lpiExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
if(iErrno >= 0)
SetLastError(iErrno);
else
iErrno = GetLastError();
if(!lpszTitle)
{
lpszTitle = CreateLocalObjects(char, 64);
if(lpiExitCode)
sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s(%d) [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, *lpiExitCode, iErrno);
else
sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s() [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, iErrno);
}
if(lpszFile && iLine > 0)
FPRINTLN(stderr, "%s : %s\n => %s (%d) : %s", lpszTitle, strerror(iErrno), lpszFile, iLine, lpszFunc ? lpszFunc : "");
else
FPRINTLN(stderr, "%s : %s", lpszTitle, strerror(iErrno));
if(lpiExitCode)
fn(*lpiExitCode);
else
((void (*)())fn)();
}
void EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
__EXIT_FN_(exit, "exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
}
void _EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
__EXIT_FN_(_exit, "_exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
}
void ABORT(int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
{
__EXIT_FN_((void (*)(int))abort, "abort", nullptr, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
}
BOOL SetSequenceThreadName(THR_ID tid, LPCTSTR lpszPrefix, volatile UINT& vuiSeq)
{
UINT uiSequence = InterlockedIncrement(&vuiSeq);
return SetThreadName(tid, lpszPrefix, uiSequence);
}
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszPrefix, UINT uiSequence)
{
int iMaxSeqLength = (int)(MAX_THREAD_NAME_LENGTH - lstrlen(lpszPrefix));
ASSERT(iMaxSeqLength > 0);
if(iMaxSeqLength <= 0)
{
::SetLastError(ERROR_OUT_OF_RANGE);
return FALSE;
}
ULONGLONG uiDiv = 1;
for(int i = 0; i < iMaxSeqLength; i++)
uiDiv *= 10;
uiSequence = (UINT)(uiSequence % uiDiv);
CString strName;
strName.Format(_T("%s%u"), lpszPrefix, uiSequence);
return SetThreadName(tid, strName);
}
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszName)
{
ASSERT(lstrlen(lpszName) <= MAX_THREAD_NAME_LENGTH);
if(tid == 0)
tid = SELF_THREAD_ID;
int rs = pthread_setname_np(tid, CT2A(lpszName));
CHECK_ERROR_CODE(rs)
return TRUE;
}