394 lines
8.3 KiB
C++
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;
|
|
}
|