363 lines
7 KiB
C++
363 lines
7 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// File: io_service.h //
|
|
// Description: servicing of network IO requests for the event system //
|
|
// Project: WANProxy XTech //
|
|
// Author: Andreu Vidal Bramfeld-Software //
|
|
// Last modified: 2015-04-01 //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <unistd.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>
|
|
#include <limits.h>
|
|
#include <event/event_system.h>
|
|
#include <event/io_service.h>
|
|
|
|
IoService::IoService () : Thread ("IoService"), log_ ("/io/thread")
|
|
{
|
|
timeout_ = handle_ = rfd_ = wfd_ = -1;
|
|
|
|
int fd[2];
|
|
if (::pipe (fd) == 0)
|
|
rfd_ = fd[0], wfd_ = fd[1];
|
|
}
|
|
|
|
IoService::~IoService ()
|
|
{
|
|
if (rfd_ >= 0)
|
|
::close (rfd_);
|
|
if (wfd_ >= 0)
|
|
::close (wfd_);
|
|
}
|
|
|
|
void IoService::main ()
|
|
{
|
|
EventMessage msg;
|
|
|
|
INFO(log_) << "Starting IO thread.";
|
|
|
|
open_resources ();
|
|
IoNode node = {rfd_, true, false, 0, 0};
|
|
set_fd (rfd_, 1, 0, &node);
|
|
|
|
while (! stop_)
|
|
{
|
|
while (gateway_.read (msg))
|
|
{
|
|
if (msg.op >= 0)
|
|
handle_request (msg.action);
|
|
else
|
|
cancel (msg.action);
|
|
}
|
|
|
|
poll (timeout_);
|
|
|
|
if (timeout_ > 0)
|
|
wakeup_readers ();
|
|
}
|
|
|
|
set_fd (rfd_, -1, 0);
|
|
close_resources ();
|
|
}
|
|
|
|
void IoService::stop ()
|
|
{
|
|
stop_ = true;
|
|
wakeup ();
|
|
Thread::stop ();
|
|
}
|
|
|
|
void IoService::handle_request (EventAction* act)
|
|
{
|
|
Event ev;
|
|
|
|
if (act)
|
|
{
|
|
switch (act->mode_)
|
|
{
|
|
case StreamModeConnect:
|
|
if (connect_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev)))
|
|
schedule (act);
|
|
else
|
|
track (act);
|
|
break;
|
|
|
|
case StreamModeAccept:
|
|
track (act);
|
|
break;
|
|
|
|
case StreamModeRead:
|
|
if (read_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev), 1))
|
|
schedule (act);
|
|
else
|
|
track (act);
|
|
break;
|
|
|
|
case StreamModeWrite:
|
|
if (write_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev)))
|
|
schedule (act);
|
|
else
|
|
track (act);
|
|
break;
|
|
|
|
case StreamModeWait:
|
|
{
|
|
WaitNode node = {current_time () + act->fd_, act};
|
|
wait_list_.insert (wait_list_.end (), node);
|
|
timeout_ = IO_POLL_TIMEOUT;
|
|
}
|
|
break;
|
|
|
|
case StreamModeEnd:
|
|
if (close_channel (act->fd_, (act->callback_ ? act->callback_->param () : ev)))
|
|
schedule (act);
|
|
else
|
|
track (act);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IoService::connect_channel (int fd, Event& ev)
|
|
{
|
|
struct sockaddr adr;
|
|
int n = 0;
|
|
|
|
if (ev.buffer_.length () <= sizeof adr)
|
|
ev.buffer_.copyout ((uint8_t*) &adr, (n = ev.buffer_.length ()));
|
|
|
|
int rv = ::connect (fd, &adr, n);
|
|
switch (rv)
|
|
{
|
|
case 0:
|
|
ev.type_ = Event::Done;
|
|
break;
|
|
|
|
case -1:
|
|
switch (errno)
|
|
{
|
|
case EINPROGRESS:
|
|
return false;
|
|
default:
|
|
ev.type_ = Event::Error;
|
|
ev.error_ = errno;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IoService::read_channel (int fd, Event& ev, int flg)
|
|
{
|
|
ssize_t len;
|
|
|
|
len = ::read (fd, read_pool_, sizeof read_pool_);
|
|
if (len < 0)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case EAGAIN:
|
|
return false;
|
|
default:
|
|
ev.type_ = Event::Error;
|
|
ev.error_ = errno;
|
|
break;
|
|
}
|
|
}
|
|
else if (len == 0)
|
|
{
|
|
ev.type_ = Event::EOS;
|
|
}
|
|
else if (flg & 1)
|
|
{
|
|
ev.type_ = Event::Done;
|
|
ev.buffer_.append (read_pool_, len);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IoService::write_channel (int fd, Event& ev)
|
|
{
|
|
struct iovec iov[IOV_MAX];
|
|
size_t iovcnt;
|
|
ssize_t len;
|
|
|
|
if (ev.buffer_.empty ())
|
|
{
|
|
ev.type_ = Event::Done;
|
|
}
|
|
else
|
|
{
|
|
iovcnt = ev.buffer_.fill_iovec (iov, IOV_MAX);
|
|
len = ::writev (fd, iov, iovcnt);
|
|
if (len < 0)
|
|
{
|
|
switch (errno)
|
|
{
|
|
case EAGAIN:
|
|
return false;
|
|
default:
|
|
ev.type_ = Event::Error;
|
|
ev.error_ = errno;
|
|
break;
|
|
}
|
|
}
|
|
else if ((size_t) len < ev.buffer_.length ())
|
|
{
|
|
ev.buffer_.skip (len);
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
ev.type_ = Event::Done;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IoService::close_channel (int fd, Event& ev)
|
|
{
|
|
int rv = ::close (fd);
|
|
if (rv == -1 && errno == EAGAIN)
|
|
return false;
|
|
|
|
ev.type_ = Event::Done;
|
|
|
|
return true;
|
|
}
|
|
|
|
void IoService::track (EventAction* act)
|
|
{
|
|
std::map<int, IoNode>::iterator it;
|
|
int fd;
|
|
|
|
if (act)
|
|
{
|
|
fd = act->fd_;
|
|
it = fd_map_.find (fd);
|
|
|
|
switch (act->mode_)
|
|
{
|
|
case StreamModeAccept:
|
|
case StreamModeRead:
|
|
if (it == fd_map_.end ())
|
|
{
|
|
IoNode node = {fd, true, false, act, 0};
|
|
set_fd (fd, 1, 0, &(fd_map_[fd] = node));
|
|
}
|
|
else if (act->mode_ == StreamModeRead)
|
|
{
|
|
it->second.reading = true, it->second.read_action = act;
|
|
set_fd (fd, 1, (it->second.writing ? 2 : 0), &it->second);
|
|
}
|
|
else
|
|
{
|
|
if (act->callback_) act->callback_->param ().type_ = Event::Error;
|
|
schedule (act);
|
|
}
|
|
break;
|
|
|
|
case StreamModeConnect:
|
|
case StreamModeWrite:
|
|
case StreamModeEnd:
|
|
if (it == fd_map_.end ())
|
|
{
|
|
IoNode node = {fd, false, true, 0, act};
|
|
set_fd (fd, 0, 1, &(fd_map_[fd] = node));
|
|
}
|
|
else if (act->mode_ == StreamModeWrite)
|
|
{
|
|
it->second.writing = true, it->second.write_action = act;
|
|
set_fd (fd, (it->second.reading ? 2 : 0), 1, &it->second);
|
|
}
|
|
else
|
|
{
|
|
if (act->callback_) act->callback_->param ().type_ = Event::Error;
|
|
schedule (act);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void IoService::cancel (EventAction* act)
|
|
{
|
|
std::map<int, IoNode>::iterator it;
|
|
std::deque<WaitNode>::iterator w;
|
|
|
|
if (act)
|
|
{
|
|
switch (act->mode_)
|
|
{
|
|
case StreamModeAccept:
|
|
case StreamModeRead:
|
|
it = fd_map_.find (act->fd_);
|
|
if (it != fd_map_.end () && it->second.read_action == act)
|
|
{
|
|
it->second.reading = false, it->second.read_action = 0;
|
|
if (it->second.write_action == 0)
|
|
fd_map_.erase (it);
|
|
set_fd (act->fd_, -1, (it->second.writing ? 2 : 0), &it->second);
|
|
}
|
|
break;
|
|
|
|
case StreamModeConnect:
|
|
case StreamModeWrite:
|
|
case StreamModeEnd:
|
|
it = fd_map_.find (act->fd_);
|
|
if (it != fd_map_.end () && it->second.write_action == act)
|
|
{
|
|
it->second.writing = false, it->second.write_action = 0;
|
|
if (it->second.read_action == 0)
|
|
fd_map_.erase (it);
|
|
set_fd (act->fd_, (it->second.reading ? 2 : 0), -1, &it->second);
|
|
}
|
|
break;
|
|
|
|
case StreamModeWait:
|
|
for (w = wait_list_.begin (); w != wait_list_.end (); ++w)
|
|
{
|
|
if (w->action == act)
|
|
{
|
|
wait_list_.erase (w);
|
|
break;
|
|
}
|
|
}
|
|
if (wait_list_.empty ())
|
|
timeout_ = -1;
|
|
break;
|
|
}
|
|
|
|
terminate (act);
|
|
}
|
|
}
|
|
|
|
void IoService::wakeup_readers ()
|
|
{
|
|
std::deque<WaitNode>::iterator w;
|
|
long t = current_time ();
|
|
|
|
for (w = wait_list_.begin (); w != wait_list_.end (); ++w)
|
|
if (w->limit > 0 && w->limit <= t)
|
|
schedule (w->action), w->limit = 0;
|
|
}
|
|
|
|
void IoService::schedule (EventAction* act)
|
|
{
|
|
EventMessage msg = {1, act};
|
|
event_system.take_message (msg);
|
|
}
|
|
|
|
void IoService::terminate (EventAction* act)
|
|
{
|
|
EventMessage msg = {-1, act};
|
|
event_system.take_message (msg);
|
|
}
|