diff --git a/include/tr/comm_manager.h b/include/tr/comm_manager.h new file mode 100644 index 0000000..e1027f7 --- /dev/null +++ b/include/tr/comm_manager.h @@ -0,0 +1,48 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2014 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TR_COMM_MANAGER_H__ +#define __TR_COMM_MANAGER_H__ + +#include + +#include "trbase.h" +#include "trevent.h" + +TR_CLASS(TR_CommManager) { + TR_EXTENDS(TR_EventHandler); + + TR_CommEndPoint * endpoints; + nfds_t n_endpoints; +}; +TR_INSTANCE_INIT(TR_CommManager); +TR_CLASSVARS_DECL(TR_CommManager) { + TR_CV_EXTENDS(TR_EventHandler); +}; + +void TR_commManagerAddEndpoint(void *, TR_CommEndPoint); +int TR_commManagerShutdown(void * _this, TR_Event event); + +#endif // __TR_COMM_MANAGER_H__ + +// vim: set ts=4 sw=4: + diff --git a/include/tr/comm_manager_poll.h b/include/tr/comm_manager_poll.h new file mode 100644 index 0000000..78d4fde --- /dev/null +++ b/include/tr/comm_manager_poll.h @@ -0,0 +1,45 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2014 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TR_COMM_MANAGER_POLL_H__ +#define __TR_COMM_MANAGER_POLL_H__ + +#include +#include + +#include "trbase.h" +#include "trevent.h" + +TR_CLASS(TR_CommManagerPoll) { + TR_EXTENDS(TR_CommManager); + + struct pollfd * fds; +}; +TR_INSTANCE_INIT(TR_CommManagerPoll); +TR_CLASSVARS_DECL(TR_CommManagerPoll) { + TR_CV_EXTENDS(TR_EventHandler); +}; + +#endif // __TR_COMM_MANAGER_POLL_H__ + +// vim: set ts=4 sw=4: + diff --git a/include/tr/interface/comm_manager.h b/include/tr/interface/comm_manager.h new file mode 100644 index 0000000..c440c1e --- /dev/null +++ b/include/tr/interface/comm_manager.h @@ -0,0 +1,56 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2014 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __TR_INTERFACE_COMM_MANAGER_H__ +#define __TR_INTERFACE_COMM_MANAGER_H__ + +#include + +#include "trbase.h" +#include "trevent.h" + +#include "tr/comm_end_point.h" + +typedef void (* fptr_TR_commManagerAddEndpoint)(void *, TR_CommEndPoint); +typedef void (* fptr_TR_commManagerSelect)(void *, TR_Event, int); +typedef void (* fptr_TR_commManagerEnableWrite)(void *, TR_Event); +typedef void (* fptr_TR_commManagerDisableWrite)(void *, TR_Event); +typedef void (* fptr_TR_commManagerClose)(void *, TR_Event); +typedef void (* fptr_TR_commManagerShutdownRead)(void *, TR_Event); +typedef void (* fptr_TR_commManagerShutdownRead)(void *, TR_Event); + +TR_INTERFACE(TR_CommManager) { + TR_IFID; + fptr_TR_commManagerAddEndpoint addEndpoint; + fptr_TR_commManagerSelect select; + fptr_TR_commManagerEnableWrite enableWrite; + fptr_TR_commManagerDisableWrite disableWrite; + fptr_TR_commManagerClose close; + fptr_TR_commManagerShutdownRead shutdownWrite; + fptr_TR_commManagerShutdownRead shutdownRead; +}; + +void TR_commManagerAddEndpoint(void *, TR_CommEndPoint); + +#endif // __TR_INTERFACE_COMM_MANAGER_H__ + +// vim: set ts=4 sw=4: diff --git a/src/comm_end_point.c b/src/comm_end_point.c index 9c83dfe..2a1dac8 100644 --- a/src/comm_end_point.c +++ b/src/comm_end_point.c @@ -54,6 +54,7 @@ commEndPointDtor(void * _this) { TR_CommEndPoint this = _this; + TR_delete(this->transport); TR_delete(this->read_buffer); TR_delete(this->write_buffer); } diff --git a/src/comm_manager.c b/src/comm_manager.c new file mode 100644 index 0000000..bf245b5 --- /dev/null +++ b/src/comm_manager.c @@ -0,0 +1,141 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2014 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "trbase.h" +#include "trevent.h" + +#include "tr/comm_end_point.h" +#include "tr/connection.h" +#include "tr/comm_manager.h" +#include "tr/interface/comm_manager.h" + +static +int +commManagerCtor(void * _this, va_list * params) +{ + TR_CommManager this = _this; + + this->n_endpoints = sysconf(_SC_OPEN_MAX); + this->endpoints = TR_calloc(sizeof(TR_CommEndPoint), this->n_endpoints); + + return 0; +} + +static +void +commManagerDtor(void * _this) +{ + TR_CommManager this = _this; + nfds_t i; + + for (i = 0; i < this->n_endpoints; i++) { + TR_delete(this->endpoints[i]); + } + + TR_PARENTCALL(_this, TR_Class, dtor); +} + +static +int +TR__commManagerAddEndpoint(void * _this, Event event) +{ + TR_commManagerAddEndpoint( + (TR_CommManager)_this, + (TR_CommEndPoint)event->subject); + return 1; +} + +void TR_commManagerSelect(void *, TR_Event, int); +void TR_commManagerEnableWrite(void *, TR_Event); +void TR_commManagerDisableWrite(void *, TR_Event); +void TR_commManagerClose(void *, TR_Event); +void TR_commManagerShutdownRead(void *, TR_Event); +void TR_commManagerShutdownRead(void *, TR_Event); + +static +void +commManagerCvInit(TR_class_ptr cls) +{ + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_EventDispatcher, + TR_DISPATCHER_EVENT_DATA_WAIT, + TR_commManagerSelect); + + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_EventDispatcher, + TR_DISPATCHER_EVENT_SHUTDOWN, + TR_commManagerShutdown); + + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_Connection, + TR_CON_EVENT_NEW_CON, + TR__commManagerAddEndpoint); + + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_Connection, + TR_CON_EVENT_PENDING_DATA, + TR_commManagerEnableWrite); + + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_Connection, + TR_CON_EVENT_END_DATA, + TR_commManagerDisableWrite); + + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_CommEndPoint, + TR_CEP_EVENT_CLOSE, + TR_commManagerClose); + + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_CommEndPoint, + TR_CEP_EVENT_SHUT_READ, + TR_commManagerShutdownRead); + + TR_EVENT_HANDLER_SET_METHOD( + cls, + TR_CommEndPoint, + TR_CEP_EVENT_SHUT_READ, + TR_commManagerShutdownWrite); +} + +TR_INSTANCE(TR_Hash, _event_methods); +TR_INIT_IFACE(TR_Class, commManagerCtor, commManagerDtor, NULL); +TR_INIT_IFACE(TR_CommManager, NULL, NULL, NULL, NULL, NULL, NULL, NULL); +TR_CREATE_CLASS( + TR_CommManager, + TR_EventHandler, + commManagerCvInit, + TR_IF(TR_Class), + TR_IF(TR_CommManager)) = { + { &(__event_methods.data) } +}; + +// vim: set ts=4 sw=4: diff --git a/src/comm_manager_poll.c b/src/comm_manager_poll.c new file mode 100644 index 0000000..bbaad41 --- /dev/null +++ b/src/comm_manager_poll.c @@ -0,0 +1,185 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2014 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "trbase.h" +#include "trevent.h" + +#include "tr/comm_end_point.h" +#include "tr/connection.h" +#include "tr/comm_manager.h" +#include "tr/interface/comm_manager.h" + +static +int +commManagerPollCtor(void * _this, va_list * params) +{ + TR_CommManagerPoll this = _this; + TR_CommManager cmgr = _this; + nfds_t i; + + TR_PARENTCALL(_this, TR_Class, ctor, params); + this->fds = TR_malloc(sizeof(struct pollfd) * cmgr->n_endpoints); + for (i = 0; i < cmgr->n_endpoints; i++) { + this->fds[i] = {-1, 0, 0}; + } + + return 0; +} + +static +void +commManagerPollDtor(void * _this) +{ + TR_CommManagerPoll this = _this; + + TR_MEM_FREE(this->fds); + TR_PARENTCALL(_this, TR_Class, dtor); +} + +static +void +TR_commManagerAddEndpoint(void * _this, TR_CommEndPoint endpoint) +{ + TR_CommManagerPoll this = _this; + this->fds[endpoint->transport->handle].fd = endpoint->transport->handle; + this->fds[endpoint->transport->handle].events = POLLIN; +} + +static +void +TR_commManagerSelect(void * _this, TR_Event event, int timeout) +{ + TR_CommManagerPoll this = _this; + TR_CommManager cmgr = _this; + nfds_t i; + int nevents; + + nevents = poll(this->fds, cmgr->n_endpoints, timeout); + + for (i = 0; i < cmgr->n_endpoints, i++) { + TR_CommEndPoint endpoint = this->endpoints[i]; + + if (this->fds[i].revents & POLLIN == POLLIN) { + nevents--; + if (TR_INSTANCE_OF(TR_TcpSocket, endpoints->transport) + && ((TR_TcpSocket)endpoint->transport)->listen) { + TR_eventHandlerIssueEvent( + (TR_EventHandler)this, + (TR_Connection)endpoint, + TR_CON_EVENT_ACC_READY, + NULL); + } else { + TR_eventHandlerIssueEvent( + (TR_EventHandler)this, + endpoint, + TR_CET_EVENT_READ_READY, + NULL); + } + } + + if (this->fds[i].revents & POLLOUT == POLLOUT) { + nevents--; + TR_eventHandlerIssueEvent( + (TR_EventHandler)this, + endpoint, + TR_CET_EVENT_WRITE_READY, + NULL); + } + + if (nevents <= 0) break; + } +} + +static +void +TR_commManagerEnableWrite(void * _this, TR_Event event) +{ + TR_CommManagerPoll this = _this; + TR_CommEndPoint endpoint = (TR_CommEndPoint)event->subject; + + if (! TR_socketFinWr(endpoint->transport)) { + this->fds[endpoint->transport->handle].event |= POLLOUT; + } +} + +static +int +TR_commManagerDisableWrite(void * _this, TR_Event event) +{ + TR_CommManagerPoll this = _this; + TR_CommEndPoint endpoint = (TR_CommEndPoint)event->subject; + + this->fds[endpoint->transport->handle].event &= ~POLLOUT; +} + +static +int +TR_commManagerClose(void * _this, TR_Event event) +{ + TR_CommManagerPoll this = _this; + TR_CommEndPoint endpoint = (TR_CommEndPoint)event->subject; + + this->fds[endpoint->transport->handle].event = 0; + this->fds[endpoint->transport->handle].fs = -1; +} + +static +int +TR_commManagerDisableRead(void * _this, TR_Event event) +{ + TR_CommManagerPoll this = _this; + TR_CommEndPoint endpoint = (TR_CommEndPoint)event->subject; + + this->fds[endpoint->transport->handle].event &= ~POLLIN; +} + +static +int +TR_commManagerShutdownRead(void * _this, TR_Event event) +{ +} + +static +void +commManagerPollCvInit(TR_class_ptr cls) { + TR_INHERIT_CLASSVARS(TR_CommManagerPoll, TR_CommManager); +} + +TR_INIT_IFACE(TR_Class, commManagerPollCtor, commManagerPollDtor, NULL); +TR_INIT_IFACE( + TR_CommManager, + TR_commManagerPollAddEndpoint, + TR_commManagerPollSelect, + TR_commManagerPollEnableWrite, + TR_commManagerPollDisableWrite, + TR_commManagerPollClose, + TR_commManagerPollDisableRead, + TR_commManagerPollDisableWrite); +TR_CREATE_CLASS( + TR_CommManagerPoll, + TR_CommManager, + TR_CommManagerPollCvInit, + TR_IF(TR_Class)); + +// vim: set ts=4 sw=4: diff --git a/src/comm_manager_shutdown.c b/src/comm_manager_shutdown.c new file mode 100644 index 0000000..741352f --- /dev/null +++ b/src/comm_manager_shutdown.c @@ -0,0 +1,48 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2014 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "trbase.h" +#include "trio.h" +#include "trevent.h" + +#include "tr/comm_manager.h" + +int +TR_commManagerShutdown(void * _this, TR_Event event) +{ + TR_CommManager this = _this; + nfds_t i; + + for (i=0; in_endpoints; i++) { + if (this->endpoints[handle]) { + TR_eventHandlerIssueEvent( + (TR_EventHandler)_this, + (TR_EventSubject)this->endpoints[handle], + TR_CEP_EVENT_CLOSE, + NULL); + } + } + + return 0; +} + +// vim: set ts=4 sw=4: diff --git a/src/i_comm_manager.c b/src/i_comm_manager.c new file mode 100644 index 0000000..1f56f8e --- /dev/null +++ b/src/i_comm_manager.c @@ -0,0 +1,148 @@ +/** + * \file + * + * \author Georg Hopp + * + * \copyright + * Copyright © 2014 Georg Hopp + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "trbase.h" +#include "trevent.h" + +#include "tr/interface/comm_manager.h" +#include "tr/comm_end_point.h" + +TR_CREATE_INTERFACE(TR_CommManager, 7); + +void +TR_commManagerAddEndpoint(void * _this, TR_CommEndPoint endpoint) +{ + TR_ConnManager this = _this; + + if (this->endpoints[endpoint->transport->handle]) { + // this should never happen, but if so we assume this is a leftover + // that still has to be deleted. + TR_delete(this->endpoints[endpoint->transport->handle]); + } + + this->endpoints[endpoint->transport->handle] = endpoint; + TR_CALL(_this, TR_CommManager, addEndpoint, endpoint); +} + +int +TR_commManagerSelect(void * _this, TR_Event event) +{ + int timeout; // milliseconds + int * timeoutptr = event->data; + TR_EventDispatcher dispatcher = (TR_EventDispatcher)event->subject; + + if (NULL == timeoutptr) { + timeout = TR_eventDispatcherGetDataWaitTime(dispatcher); + } else { + timeout = *timeoutptr; + TR_MEM_FREE(timeoutptr); + } + + TR_CALL(_this, TR_CommManager, select, event, timeout); + return 1; +} + +int +TR_commManagerEnableWrite(void * _this, TR_Event event) +{ + TR_CALL(_this, TR_CommManager, enableWrite, event); + return 1; +} + +int +TR_commManagerDisableWrite(void * _this, TR_Event event) +{ + TR_EventHandler this = _this; + TR_CommEndPoint endpoint = (TR_CommEndPoint)event->subject; + + TR_CALL(_this, TR_CommManager, disableWrite, event); + + if (TR_socketFinRd(endpoint->transport)) { + TR_eventHandlerIssueEvent( + this, + event->subject, + TR_CEP_EVENT_SHUT_READ, + NULL); + } + + return 1; +} + +int +TR_commManagerClose(void * _this, TR_Event event) +{ + TR_ConnManager this = _this; + TR_CommEndPoint endpoint = (TR_CommEndPoint)event->subject; + + TR_socketShutdown(endpoint->transport); + TR_CALL(_this, TR_CommManager, close, event); + TR_delete(this->endpoints[endpoint->transport->handle]); + + return 0; +} + +int +TR_commManagerShutdownRead(void * _this, TR_Event event) +{ + TR_CALL(_this, TR_CommManager, shutdownRead, event); + + if (TR_socketFinRd(((TR_CommEndPoint)event->subject)->transport)) { + // close + TR_eventHandlerIssueEvent( + (TR_EventHandler)_this, + event->subject, + TR_CEP_EVENT_CLOSE, + NULL); + } else if (TR_cepHasPendingData(endpoint)) { + // handle pending data... close is issued from disableWrite + TR_eventHandlerIssueEvent( + (TR_EventHandler)_this, + event->subject, + TR_CEP_EVENT_CLOSE, + NULL); + } else { + TR_cepSetClose(endpoint); + } + + return 0; +} + +int +TR_commManagerShutdownWrite(void * _this, TR_Event event) +{ + TR_CALL(_this, TR_CommManager, shutdownWrite, event); + + if (TR_socketFinRd(((TR_CommEndPoint)event->subject)->transport)) { + TR_eventHandlerIssueEvent( + (TR_EventHandler)_this, + event->subject, + TR_CEP_EVENT_CLOSE, + NULL); + } + + return 0; +} + + +// vim: set ts=4 sw=4: