/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "event_sources.h"
#include "debug.h"
#include "utils.h"
static void set_non_blocking(int fd)
{
int flags;
if ((flags = ::fcntl(fd, F_GETFL)) == -1) {
THROW("failed to set socket non block: %s", strerror(errno));
}
if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
THROW("failed to set socket non block: %s", strerror(errno));
}
}
static void set_blocking(int fd)
{
int flags;
if ((flags = ::fcntl(fd, F_GETFL)) == -1) {
THROW("failed to clear socket non block: %s", strerror(errno));
}
if (::fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1) {
THROW("failed to clear socket non block: %s", strerror(errno));
}
}
EventSources::EventSources()
{
}
EventSources::~EventSources()
{
}
void EventSources_p::add_event(int fd, EventSource* source)
{
int size = _events.size();
_events.resize(size + 1);
_fds.resize(size + 1);
_events[size] = source;
_fds[size] = fd;
}
void EventSources_p::remove_event(EventSource* source)
{
int size = _events.size();
for (int i = 0; i < size; i++) {
if (_events[i] == source) {
for (i++; i < size; i++) {
_events[i - 1] = _events[i];
_fds[i - 1] = _fds[i];
}
_events.resize(size - 1);
_fds.resize(size - 1);
return;
}
}
THROW("event not found");
}
bool EventSources::wait_events(int timeout_msec)
{
int maxfd = 0;
fd_set rfds;
struct timeval tv;
struct timeval *tvp;
int ready;
FD_ZERO(&rfds);
int size = _events.size();
for (int i = 0; i < size; i++) {
maxfd = MAX(maxfd, _fds[i]);
FD_SET(_fds[i], &rfds);
}
if (timeout_msec == INFINITE) {
tvp = NULL;
} else {
tv.tv_sec = timeout_msec / 1000;
tv.tv_usec = (timeout_msec % 1000) * 1000;
tvp = &tv;
}
/* Right now we only use read polling in spice */
ready = ::select(maxfd+1, &rfds, NULL, NULL, tvp);
if (ready == -1) {
if (errno == EINTR) {
return false;
}
THROW("wait error select failed");
} else if (ready == 0) {
return false;
}
for (int i = 0; i < _events.size(); i++) {
if (FD_ISSET(_fds[i], &rfds)) {
_events[i]->action();
/* The action may have removed / added event sources changing
our array, so leave the loop and handle other events the next
time we are called */
return false;
}
}
return false;
}
void EventSources::add_trigger(Trigger& trigger)
{
add_event(trigger.get_fd(), &trigger);
}
void EventSources::remove_trigger(Trigger& trigger)
{
remove_event(&trigger);
}
EventSources::Trigger::Trigger()
{
int fd[2];
if (::pipe(fd) == -1) {
THROW("create pipe failed");
}
_event_fd = fd[0];
_event_write_fd = fd[1];
set_non_blocking(_event_fd);
}
EventSources::Trigger::~Trigger()
{
close(_event_fd);
close(_event_write_fd);
}
void EventSources::Trigger::trigger()
{
Lock lock(_lock);
if (_pending_int) {
return;
}
_pending_int = true;
const uint8_t val = 1;
if (::write(_event_write_fd, &val, sizeof(val)) != sizeof(val)) {
THROW("write event failed");
}
}
bool Trigger_p::reset_event()
{
Lock lock(_lock);
if (!_pending_int) {
return false;
}
uint8_t val;
if (::read(_event_fd, &val, sizeof(val)) != sizeof(val)) {
THROW("event read error");
}
_pending_int = false;
return true;
}
void EventSources::Trigger::reset()
{
reset_event();
}
void EventSources::Trigger::action()
{
if (reset_event()) {
on_event();
}
}
void EventSources::add_socket(Socket& socket)
{
add_event(socket.get_socket(), &socket);
set_non_blocking(socket.get_socket());
}
void EventSources::remove_socket(Socket& socket)
{
remove_event(&socket);
int fd = socket.get_socket();
set_blocking(fd);
}
void EventSources::add_file(File& file)
{
add_event(file.get_fd(), &file);
}
void EventSources::remove_file(File& file)
{
remove_event(&file);
}
void EventSources::add_handle(Handle& file)
{
}
void EventSources::remove_handle(Handle& file)
{
}