#include "protoEvent.h"
#include "protoDebug.h"
#ifdef WIN32
#else
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef LINUX
#include <sys/eventfd.h>
#endif #endif
ProtoEvent::ProtoEvent(bool autoReset)
: auto_reset(autoReset), notifier(NULL), listener(NULL)
{
#ifdef WIN32
event_handle = INVALID_HANDLE_VALUE;
#else
#if defined(USE_EVENTFD) || defined(USE_KEVENT)
event_fd = -1;
#else
event_pipe_fd[0] = event_pipe_fd[1] = -1;
#endif #endif }
ProtoEvent::~ProtoEvent()
{
Close();
}
bool ProtoEvent::UpdateNotification()
{
if (NULL != notifier)
{
if (IsOpen())
{
if (!notifier->UpdateEventNotification(*this, NOTIFY_INPUT))
{
PLOG(PL_FATAL, "ProtoEvent::UpdateNotification() error: unable to update notifier\n");
return false;
}
}
else
{
notifier->UpdateEventNotification(*this, NOTIFY_NONE);
}
}
return true;
}
bool ProtoEvent::SetNotifier(ProtoEvent::Notifier* theNotifier)
{
if (notifier != theNotifier)
{
if (IsOpen())
{
if (NULL != notifier)
notifier->UpdateEventNotification(*this, NOTIFY_NONE);
notifier = theNotifier;
if (!UpdateNotification())
{
notifier = NULL;
return false;
}
}
else
{
notifier = theNotifier;
}
}
return true;
}
void ProtoEvent::OnNotify()
{
if (NULL != listener)
listener->on_event(*this);
}
bool ProtoEvent::Open()
{
if (IsOpen()) return true;
#ifdef WIN32
event_handle = CreateEvent(NULL, TRUE, FALSE, NULL);
if (NULL == event_handle)
{
PLOG(PL_FATAL, "ProtoEvent::Init() CreateEvent() error: %s\n", GetErrorString());
event_handle = INVALID_HANDLE_VALUE;
return false;
}
#else
#if defined(USE_EVENTFD)
if (-1 == (event_fd = eventfd(0, 0))) {
PLOG(PL_ERROR, "ProtoEvent::Init() eventfd() error: %s\n", GetErrorString());
return false;
}
#elif defined(USE_KEVENT)
if (-1 = (event_fd = kqueue()))
{
PLOG(PL_ERROR, "ProtoEvent::Init() kqueue() error: %s\n", GetErrorString());
return false;
}
struct kevent kev;
struct kevent out_kev;
memset(&kev, 0, sizeof(kev));
kev.ident = event_fd;
kev.filter = EVFILT_USER;
kev.flags = EV_ADD;
if (-1 == kevent(event_fd, &kev, 1, NULL, 0, NULL))
{
PLOG(PL_ERROR, "ProtoEvent::Init() kqueue() error: %s\n", GetErrorString());
close(event_fd);
event_fd = -1;
return false;
}
#else
if (0 != pipe(event_pipe_fd))
{
PLOG(PL_FATAL, "ProtoEvent::Init() pipe() error: %s\n", GetErrorString());
return false;
}
if(-1 == fcntl(event_pipe_fd[0], F_SETFL, fcntl(event_pipe_fd[0], F_GETFL, 0) | O_NONBLOCK))
{
PLOG(PL_FATAL, "ProtoEvent::Init() fcntl(F_SETFL(O_NONBLOCK)) error: %s\n", GetErrorString());
close(event_pipe_fd[0]);
close(event_pipe_fd[1]);
event_pipe_fd[0] = event_pipe_fd[1] = -1;
return false;
}
#endif #endif if (NULL != notifier)
notifier->UpdateEventNotification(*this, NOTIFY_INPUT);
return true;
}
bool ProtoEvent::IsOpen() const
{
#ifdef WIN32
return (INVALID_HANDLE_VALUE != event_handle && NULL != event_handle);
#else
#if defined(USE_EVENTFD) || defined(USE_KEVENT)
return (-1 != event_fd);
#else
return (-1 != event_pipe_fd[0]);
#endif #endif }
void ProtoEvent::Close()
{
if (!IsOpen()) return;
if (NULL != notifier)
notifier->UpdateEventNotification(*this, NOTIFY_NONE);
#ifdef WIN32
CloseHandle(event_handle);
event_handle = INVALID_HANDLE_VALUE;
#else
#if defined(USE_EVENTFD) || defined(USE_KEVENT)
close(event_fd);
event_fd = -1;
#else
close(event_pipe_fd[0]);
close(event_pipe_fd[1]);
event_pipe_fd[0] = event_pipe_fd[1] = -1;
#endif #endif }
bool ProtoEvent::Set()
{
#ifdef WIN32
if (0 == SetEvent(event_handle))
{
PLOG(PL_ERROR, "ProtoEvent::Set() SetEvent() error: %s\n", GetErrorString());
return false;
}
#else
#if defined(USE_EVENTFD)
uint64_t value = 1;
if (write(event_fd, &value, sizeof(uint64_t)) < 0)
{
PLOG(PL_ERROR, "ProtoEvent::Set() write(eventfd) error: %s\n", GetErrorString());
return false;
}
#elif defined (USE_KEVENT)
struct kevent kev;
EV_SET(&kev, event_fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
if (-1 == kevent(event_fd, &kev, 1, NULL, 0, NULL))
{
PLOG(PL_ERROR, "ProtoEvent::Set() kevent() error: %s\n", GetErrorString());
return false;
}
#else
while (true)
{
char byte = 0;
ssize_t result = write(event_pipe_fd[1], &byte, 1);
if (1 == result)
{
break;
}
else if (0 == result)
{
PLOG(PL_WARN, "ProtoEvent::Set() warning: write() returned zero\n");
continue;
}
else
{
if ((EINTR == errno) || (EAGAIN == errno)) continue; PLOG(PL_ERROR, "ProtoEvent::Set() write(break_pipe) error: %s\n", GetErrorString());
return false;
}
}
#endif #endif return true;
}
bool ProtoEvent::Reset()
{
#ifdef WIN32
if (0 == ResetEvent(event_handle))
{
PLOG(PL_ERROR, "ProtoEvent::Reset() ResetEvent() error: %s\n", GetErrorString());
return false;
}
#else
#if defined(USE_EVENTFD)
uint64_t counter = 0;
if (read(event_fd, &counter, sizeof(counter)) < 0)
{
PLOG(PL_ERROR, "ProtoEvent::Reset() read(event_fd) error: %s\n", GetErrorString());
return false;
}
#elif defined(USE_KEVENT)
struct kevent kev;
if (-1 == kevent(kq, NULL, 0, &kev, 1, NULL))
{
PLOG(PL_ERROR, "ProtoEvent::Reset() kevent() error: %s\n", GetErrorString());
return false;
}
ASSERT(event_fd == kev.ident);
#else
char byte[32];
while (read(event_pipe_fd[0], byte, 32) > 0);
#endif #endif return true;
}
bool ProtoEvent::Wait()
{
#ifdef WIN32
if (WAIT_OBJECT_0 != WaitForSingleObject(event_handle, INFINITE))
{
PLOG(PL_ERROR, "ProtoEvent::Wait() WaitForSingleObject() error: %s\n", GetErrorString());
return false;
}
#else
#if defined(USE_EVENTFD) || defined (USE_KEVENT)
int eventFd = event_fd;
#else
int eventFd = event_pipe_fd[0];
#endif
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(eventFd ,&fdset);
if (-1 == select(eventFd+1,& fdset, 0, 0, 0))
{
PLOG(PL_ERROR, "ProtoEvent::Wait() select() error: %s\n", GetErrorString());
return false;
}
#endif if (auto_reset && !Reset())
{
PLOG(PL_ERROR, "ProtoEvent::Wait() error: Reset() failed!\n");
return false;
}
return true;
}