-
Notifications
You must be signed in to change notification settings - Fork 32
Create interface state monitor #1579
base: master
Are you sure you want to change the base?
Conversation
882bfee
to
fd7b0a1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great commit messages!
I added a bunch of comments, but addressing them is optional. I would rather like this code to merged and changed later if needed.
Approving.
/** | ||
* @see EventLoop::register_handlers | ||
*/ | ||
bool register_handlers(int fd, const EventHandlers &handlers) override; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have to register all handlers at once?
Do we need ability to register/remove a single handler? Does underlying interface prevent us from doing that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EventHandlers
is a struct containing several EventHandler
functions (on_read, on_write, on_error, ...). Method register_handlers
registers all of them at once. This way the method can check if handlers have already been installed or not.
I cannot think of a situation where we would want to register/remove a single handler but that's possible by passing a EventHandlers
struct with its members set accordingly. You can call register_handlers
as many times as you want, provided you call remove_handlers
in between.
////////////////////////////////////////////////////////////////////////////// | ||
|
||
// Maximal number of events to process in a single epoll_wait call | ||
static constexpr int MAX_POLL_EVENTS = 17; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 17?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a curious number, yes.
This question is for @vitalybu
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No particular reason.. Just a good looking number :)
auto add_fd_to_epoll = [&](int fd) -> bool { | ||
epoll_event event = {}; | ||
event.data.fd = fd; | ||
event.events = EPOLLRDHUP | EPOLLERR | EPOLLHUP; | ||
|
||
// If read handler was set, also listen for POLL-IN events | ||
if (handlers.on_read) { | ||
event.events |= EPOLLIN; | ||
} | ||
|
||
// If write handler was set, also listen for POLL-OUT events | ||
if (handlers.on_write) { | ||
event.events |= EPOLLOUT; | ||
} | ||
|
||
if (epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, fd, &event) == -1) { | ||
LOG(ERROR) << "Failed adding FD (" << fd << ") to the poll: " << strerror(errno); | ||
return false; | ||
} | ||
|
||
// Map the file descriptor to the event handlers structure | ||
m_fd_to_event_handlers[fd] = handlers; | ||
|
||
return true; | ||
}; | ||
|
||
// Add the file descriptor to the poll | ||
if (!add_fd_to_epoll(fd)) { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this part of code :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should congratulate @vitalybu for it.
IMHO using a lambda here is not necessary and adds an extra level of indentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But it groups related code together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before removing the the timeout related source code, this lambda was called from two different places (that's why it was created in the first place)...
* | ||
* @return True on success and false otherwise (for example, if it was already closed) | ||
*/ | ||
bool close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect function named close()
to return void
, but maybe other people disagree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The close()
system call returns an int to report the result. Returning bool here is meant to forward such a result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A user aware of close
system can be confused by if (close())
statement. In the system call it checks for failure, but in FileDescriptorImpl
it checks for success.
Can we just return int
?
* @param iface_name Interface name. | ||
* @param iface_state Interface state (true if it is up and running). | ||
*/ | ||
using StateChangeHandler = std::function<void(const std::string &iface_name, bool iface_state)>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the same function for both states? Why not on_iface_up()
and on_iface_down()
? Would they share too much code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both solutions are equivalent, but I think mine is more concise: just one handler type definition, just one setter method to register it and just one member variable to store it. From the caller's perspective it's also simpler (despite it needs an if
in the handler function to check what's the new interface state)
|
||
class UdsSocket : public SocketAbstractImpl { | ||
public: | ||
UdsSocket() : SocketAbstractImpl(socket(AF_UNIX, SOCK_STREAM, 0)) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SOCK_SEQPACKET or add an intermediate class StreamSocket
to share code of TcpSocket
and UdsSocket
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we end up using SOCK_SEQPACKET, we'll derive a new class from SocketAbstractImpl
common/beerocks/bcl/source/network/interface_state_manager_impl.cpp
Outdated
Show resolved
Hide resolved
https://jira.prplfoundation.org/browse/PPM-18 This is a preparatory commit. As discussed in code reviews, associating a timeout to a read or write operation on a specific file descriptor is doubtedly useful and adds unnecessary complexity to the reactor design pattern. Moreover, current implementation of this feature has some flaws. This is not yet a problem because timeouts are not being used anywhere. As discussed in architecture meetings related to socket refactorings, operation timeouts and periodic actions will be handled by a new yet to be implemented Timer class (a wrapper around timerfd_create). Therefore current per-file-descriptor timeout handling will be removed. In order to use the current event loop with the new sockets API to implement the interface state monitor, some modifications are required in the event loop implementation. To simplify this task and avoid having to fix code that will be removed after all, this preparatory commit removes the timeout handling mechanism. Signed-off-by: Mario Maz <[email protected]>
https://jira.prplfoundation.org/browse/PPM-18 This is a preparatory commit. EventLoop interface and implementation are not intended to deal with sockets only (or any other specific class). Instead, EventLoop will deal with any OS resource implementing the file descriptor interface. So there is no need to couple EventLoop whith any specific class, even if using a template for that. In order to use the current event loop with the new sockets API to implement the interface state monitor, event loop interface and implementation have to be modified to use `int` type to refer to the source of events, being that integer the file descriptor of the OS resource where events occur. In the future, the file descriptor will be represented by a `FileDescriptor` class which, among other features, will close the file descriptor in its destructor. Current `Socket` class will be eventually deprecated in the future. Method to register handlers for events on a given file descriptor has been renamed from `add_event` to the more appropriate name `register_handlers`. For the same reason, the method to remove those handlers when they are not needed any more has been renamed from `del_event` to `remove_handlers`. `SocketEventLoop` class has been renamed to `EventLoopImpl` and the unit tests modified accordingly. While we are at it, a bug in the constructor of BrokerServer has been fixed (it was creating a shared_ptr from a reference). New `start()` and `stop()` methods have been added to BrokerServer to register and remove event handlers respectively. Registering handlers in `start()` instead of in the constructor prevents cppcheck from complaining about virtual methods being called from constructor. Signed-off-by: Mario Maz <[email protected]>
https://jira.prplfoundation.org/browse/PPM-18 This is a preparatory commit. Application event loop is currently defined in Ieee1905Transport::run(). Since event loop is going to be a dependency for the interface state monitor as well as for the transport class, move it to the main function so it can later be reused. The event loop must be then injected as a parameter in constructor to all classes that depend on it like, for example, the BrokerServer class. Since event loop is now a member variable of the Ieee1905Transport class, remove the methods add_event, del_event and run which are not necessary any more and update unit tests accordingly. Signed-off-by: Mario Maz <[email protected]>
https://jira.prplfoundation.org/browse/PPM-18 Message broker is currently defined in Ieee1905Transport::run(). As well as the event loop, the message broker is a dependency of the class. In order to have testable code, move it to the main function so it can later be replaced with a mock while unit testing (in a future PR). Strictly speaking, these changes are not required for this PR but produce clearer code as all dependencies are create and injected the same way. While we are at it, optimize parameter passing in methods to register a message handler (by const-ref instead of by value). Also call the stop() method of the message broker on application exit, which was not being called. Signed-off-by: Mario Maz <[email protected]>
https://jira.prplfoundation.org/browse/PPM-18 Create InterfaceStateManager class and all its dependencies. Follow-up commits will use this class in the ieee1905_transport process as a new mechanism to detect changes in the state of network interfaces, based on the new sockets API. Signed-off-by: Mario Maz <[email protected]>
6a7d518
to
91289f4
Compare
https://jira.prplfoundation.org/browse/PPM-18 Use the InterfaceStateManager class in the ieee1905_transport process as a new mechanism to read and detect changes in the state of the network interfaces. Existing code in `ieee1905_transport_netlink.cpp` is deprecated and will be removed in next commit. InterfaceStateManager uses interface names. Since we should use interface names everywhere rather than interface indexes (because interfaces are not renamed while prplMesh is running and conversely, indexes can change, and because names are easier to debug than indexes), modify existing code to favor names over indexes. BTW, this change also fixes a bug in calls to `handle_interface_status_change` that use a file descriptor when they shall be using an interface index. Signed-off-by: Mario Maz <[email protected]>
https://jira.prplfoundation.org/browse/PPM-18 Remove dead code after adding the InterfaceStateManager class. Signed-off-by: Mario Maz <[email protected]>
91289f4
to
5016b5b
Compare
See PPM-18
A new class
InterfaceStateMonitor
has been created and used in ieee1905_transport to monitor changes in the up-and-running state of network interfaces.