531 lines
8.4 KiB
C++
531 lines
8.4 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "../hpsocket/GlobalDef.h"
|
|
#include "Singleton.h"
|
|
#include "FuncHelper.h"
|
|
#include "PollHelper.h"
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/eventfd.h>
|
|
#include <sys/timerfd.h>
|
|
#include <sys/signalfd.h>
|
|
|
|
class CPipeEvent
|
|
{
|
|
public:
|
|
|
|
enum
|
|
{
|
|
EVT_1 = 0x01,
|
|
EVT_WAKEUP = EVT_1,
|
|
EVT_EXIT = 0x7F,
|
|
EVT_SIG_0 = 0x80,
|
|
EVT_SIG_MAX = EVT_SIG_0 + _NSIG,
|
|
};
|
|
|
|
public:
|
|
|
|
int Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
|
{
|
|
pollfd pfd = {m_fd[0], POLLIN};
|
|
|
|
while(TRUE)
|
|
{
|
|
int rs = (int)::PollForSingleObject(pfd, lTimeout, pSigSet);
|
|
|
|
if(rs <= TIMEOUT) return rs;
|
|
|
|
if(pfd.revents & POLLIN)
|
|
{
|
|
BYTE v;
|
|
|
|
if(!Get(v))
|
|
return HAS_ERROR;
|
|
|
|
if(v == 0)
|
|
continue;
|
|
|
|
return (int)v;
|
|
}
|
|
|
|
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
|
{
|
|
::SetLastError(ERROR_BROKEN_PIPE);
|
|
return HAS_ERROR;
|
|
}
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL Set(BYTE bVal = EVT_WAKEUP)
|
|
{
|
|
ASSERT_CHECK_EINVAL(bVal != 0);
|
|
|
|
return VERIFY(write(m_fd[1], &bVal, 1) > 0);
|
|
}
|
|
|
|
BOOL Get(BYTE& v)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
int rs = (int)read(m_fd[0], &v, 1);
|
|
|
|
if(IS_HAS_ERROR(rs))
|
|
{
|
|
if(IS_WOULDBLOCK_ERROR())
|
|
v = 0;
|
|
else
|
|
return FALSE;
|
|
}
|
|
else if(rs == 0)
|
|
{
|
|
::SetLastError(ERROR_BROKEN_PIPE);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL Reset()
|
|
{
|
|
BYTE v;
|
|
|
|
while(TRUE)
|
|
{
|
|
if(!Get(v))
|
|
return FALSE;
|
|
|
|
if(v == 0)
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SetSignal(BYTE bSigVal)
|
|
{
|
|
ASSERT_CHECK_EINVAL(bSigVal > 0 && bSigVal < _NSIG);
|
|
|
|
return Set((BYTE)(EVT_SIG_0 + bSigVal));
|
|
}
|
|
|
|
static inline BYTE ToSignalValue(int iWaitResult)
|
|
{
|
|
if(iWaitResult <= EVT_SIG_0 || iWaitResult >= EVT_SIG_MAX)
|
|
return 0;
|
|
|
|
return (BYTE)(iWaitResult - EVT_SIG_0);
|
|
}
|
|
|
|
BOOL IsValid() {return IS_VALID_FD(m_fd[0]) && IS_VALID_FD(m_fd[1]);}
|
|
|
|
operator FD () {return m_fd[0];}
|
|
FD GetFD () {return m_fd[0];}
|
|
|
|
public:
|
|
CPipeEvent()
|
|
{
|
|
VERIFY_IS_NO_ERROR(pipe2(m_fd, O_NONBLOCK | O_CLOEXEC));
|
|
VERIFY(::fcntl_SETFL(m_fd[0], O_NOATIME));
|
|
VERIFY(::fcntl_SETFL(m_fd[1], O_NOATIME));
|
|
}
|
|
|
|
~CPipeEvent()
|
|
{
|
|
close(m_fd[1]);
|
|
close(m_fd[0]);
|
|
}
|
|
|
|
DECLARE_NO_COPY_CLASS(CPipeEvent)
|
|
|
|
private:
|
|
FD m_fd[2] = {INVALID_FD, INVALID_FD};
|
|
};
|
|
|
|
template<bool is_sem_mode = false> class CCounterEvent
|
|
{
|
|
public:
|
|
|
|
eventfd_t Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
|
{
|
|
pollfd pfd = {m_evt, POLLIN};
|
|
|
|
while(TRUE)
|
|
{
|
|
long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
|
|
|
|
if(rs <= TIMEOUT) return (eventfd_t)rs;
|
|
|
|
if(pfd.revents & POLLIN)
|
|
{
|
|
eventfd_t v;
|
|
|
|
if(!Get(v))
|
|
return HAS_ERROR;
|
|
|
|
if(v == 0)
|
|
continue;
|
|
|
|
return v;
|
|
}
|
|
|
|
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
|
{
|
|
::SetLastError(ERROR_HANDLES_CLOSED);
|
|
return HAS_ERROR;
|
|
}
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL Set(eventfd_t val = 1)
|
|
{
|
|
ASSERT_CHECK_EINVAL(val > 0);
|
|
|
|
int rs = eventfd_write(m_evt, val);
|
|
return VERIFY_IS_NO_ERROR(rs);
|
|
}
|
|
|
|
BOOL Get(eventfd_t& v)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
if(IS_HAS_ERROR(eventfd_read(m_evt, &v)))
|
|
{
|
|
if(IS_WOULDBLOCK_ERROR())
|
|
v = 0;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL Reset()
|
|
{
|
|
eventfd_t v;
|
|
|
|
while(TRUE)
|
|
{
|
|
if(!Get(v))
|
|
return FALSE;
|
|
|
|
if(v == 0)
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL IsValid() {return IS_VALID_FD(m_evt);}
|
|
|
|
operator FD () {return m_evt;}
|
|
FD GetFD () {return m_evt;}
|
|
|
|
public:
|
|
CCounterEvent(int iInitCount = 0)
|
|
{
|
|
int iFlag = EFD_NONBLOCK | EFD_CLOEXEC | (is_sem_mode ? EFD_SEMAPHORE : 0);
|
|
m_evt = eventfd(iInitCount, iFlag);
|
|
|
|
VERIFY(IsValid());
|
|
}
|
|
|
|
~CCounterEvent()
|
|
{
|
|
if(IsValid()) close(m_evt);
|
|
}
|
|
|
|
DECLARE_NO_COPY_CLASS(CCounterEvent)
|
|
|
|
private:
|
|
FD m_evt = INVALID_FD;
|
|
};
|
|
|
|
using CSimpleEvent = CCounterEvent<false>;
|
|
using CSemaphoreEvent = CCounterEvent<true>;
|
|
using CEvt = CSimpleEvent;
|
|
|
|
class CTimerEvent
|
|
{
|
|
public:
|
|
|
|
ULLONG Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
|
{
|
|
pollfd pfd = {m_tmr, POLLIN};
|
|
|
|
while(TRUE)
|
|
{
|
|
SSIZE_T rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
|
|
|
|
if(rs <= TIMEOUT) return (ULLONG)rs;
|
|
|
|
if(pfd.revents & POLLIN)
|
|
{
|
|
BOOL ok;
|
|
ULLONG v;
|
|
|
|
if(!Get(v, ok))
|
|
return HAS_ERROR;
|
|
|
|
if(!ok)
|
|
continue;
|
|
|
|
return v;
|
|
}
|
|
|
|
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
|
{
|
|
::SetLastError(ERROR_HANDLES_CLOSED);
|
|
return HAS_ERROR;
|
|
}
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL Set(LLONG llInterval, LLONG llStart = -1)
|
|
{
|
|
ASSERT_CHECK_EINVAL(llInterval >= 0L);
|
|
|
|
if(llStart < 0)
|
|
llStart = llInterval;
|
|
|
|
itimerspec its;
|
|
|
|
::MillisecondToTimespec(llStart, its.it_value);
|
|
::MillisecondToTimespec(llInterval, its.it_interval);
|
|
|
|
int rs = timerfd_settime(m_tmr, 0, &its, nullptr);
|
|
return VERIFY_IS_NO_ERROR(rs);
|
|
}
|
|
|
|
BOOL Get(ULLONG &v, BOOL& ok)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
return ::ReadTimer(m_tmr, &v, &ok);
|
|
}
|
|
|
|
BOOL Reset()
|
|
{
|
|
BOOL ok;
|
|
ULLONG v;
|
|
|
|
while(TRUE)
|
|
{
|
|
if(!Get(v, ok))
|
|
return FALSE;
|
|
|
|
if(!ok)
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetTime(LLONG& lStart, LLONG& lInterval)
|
|
{
|
|
itimerspec its;
|
|
|
|
if(IS_HAS_ERROR(timerfd_gettime(m_tmr, &its)))
|
|
return FALSE;
|
|
|
|
lStart = ::TimespecToMillisecond(its.it_value);
|
|
lInterval = ::TimespecToMillisecond(its.it_interval);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL IsValid() {return IS_VALID_FD(m_tmr);}
|
|
|
|
operator FD () {return m_tmr;}
|
|
FD GetFD () {return m_tmr;}
|
|
|
|
public:
|
|
CTimerEvent(bool bRealTimeClock = FALSE)
|
|
{
|
|
int iCID = (bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC);
|
|
m_tmr = timerfd_create(iCID, TFD_NONBLOCK | TFD_CLOEXEC);
|
|
|
|
VERIFY(IsValid());
|
|
}
|
|
|
|
~CTimerEvent()
|
|
{
|
|
if(IsValid()) close(m_tmr);
|
|
}
|
|
|
|
DECLARE_NO_COPY_CLASS(CTimerEvent)
|
|
|
|
private:
|
|
FD m_tmr = INVALID_FD;
|
|
};
|
|
|
|
class CSignalEvent
|
|
{
|
|
public:
|
|
|
|
int Wait(signalfd_siginfo& sgInfo, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
|
{
|
|
m_dwTID = SELF_THREAD_ID;
|
|
pollfd pfd = {m_sig, POLLIN};
|
|
|
|
while(TRUE)
|
|
{
|
|
long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
|
|
|
|
if(rs <= TIMEOUT) return (int)rs;
|
|
|
|
if(pfd.revents & POLLIN)
|
|
{
|
|
BOOL ok;
|
|
|
|
if(!Get(sgInfo, ok))
|
|
return HAS_ERROR;
|
|
|
|
if(!ok)
|
|
continue;
|
|
|
|
return sgInfo.ssi_signo;
|
|
}
|
|
|
|
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
|
{
|
|
::SetLastError(ERROR_HANDLES_CLOSED);
|
|
return HAS_ERROR;
|
|
}
|
|
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
m_dwTID = 0;
|
|
}
|
|
|
|
BOOL Set(int iSig, const sigval sgVal, THR_ID dwTID = 0)
|
|
{
|
|
if(dwTID == 0)
|
|
{
|
|
dwTID = m_dwTID;
|
|
|
|
if(dwTID == 0)
|
|
{
|
|
::SetLastError(ERROR_INVALID_STATE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#if !defined(__ANDROID__)
|
|
int rs = pthread_sigqueue(dwTID, iSig, sgVal);
|
|
#else
|
|
int rs = pthread_kill(dwTID, iSig);
|
|
#endif
|
|
|
|
return IS_NO_ERROR(rs);
|
|
}
|
|
|
|
BOOL Get(signalfd_siginfo& v, BOOL& ok)
|
|
{
|
|
ASSERT(IsValid());
|
|
|
|
static const SSIZE_T SIZE = sizeof(signalfd_siginfo);
|
|
|
|
if(read(m_sig, &v, SIZE) == SIZE)
|
|
ok = TRUE;
|
|
{
|
|
if(IS_WOULDBLOCK_ERROR())
|
|
ok = FALSE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
BOOL Reset()
|
|
{
|
|
BOOL ok;
|
|
signalfd_siginfo v;
|
|
|
|
while(TRUE)
|
|
{
|
|
if(!Get(v, ok))
|
|
return FALSE;
|
|
|
|
if(!ok)
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL Mask(const sigset_t* pSigMask)
|
|
{
|
|
if(!pSigMask)
|
|
{
|
|
if(!IsValid()) return TRUE;
|
|
return IS_NO_ERROR(close(m_sig));
|
|
}
|
|
|
|
FD sig = signalfd(m_sig, pSigMask, SFD_NONBLOCK | SFD_CLOEXEC);
|
|
|
|
if(IS_VALID_FD(sig))
|
|
{
|
|
m_sig = sig;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL IsValid() {return IS_VALID_FD(m_sig);}
|
|
|
|
operator FD () {return m_sig;}
|
|
FD GetFD () {return m_sig;}
|
|
|
|
public:
|
|
CSignalEvent(const sigset_t* pSigMask = nullptr)
|
|
{
|
|
if(pSigMask) VERIFY(Mask(pSigMask));
|
|
}
|
|
|
|
~CSignalEvent()
|
|
{
|
|
if(IsValid()) close(m_sig);
|
|
}
|
|
|
|
DECLARE_NO_COPY_CLASS(CSignalEvent)
|
|
|
|
private:
|
|
FD m_sig = INVALID_FD;
|
|
THR_ID m_dwTID = 0;
|
|
};
|