fprime/Drv/Ip/SocketComponentHelper.cpp
M Starch 9ed68fdd06
Correcting shutdown/close usage; refactoring TcpServer (#2950)
* Correcting shutdown/close usage; refactoring TcpServer

* Fixing Tcp

* Review and CI fixes

* ...and the other half of the fixes
2024-10-16 09:36:34 -07:00

209 lines
7.2 KiB
C++

// ======================================================================
// \title SocketComponentHelper.cpp
// \author mstarch, crsmith
// \brief cpp file for SocketComponentHelper implementation class
//
// \copyright
// Copyright 2009-2020, by the California Institute of Technology.
// ALL RIGHTS RESERVED. United States Government Sponsorship
// acknowledged.
//
// ======================================================================
#include <Drv/Ip/SocketComponentHelper.hpp>
#include <Fw/Logger/Logger.hpp>
#include <Fw/Types/Assert.hpp>
#include <cerrno>
namespace Drv {
SocketComponentHelper::SocketComponentHelper() {}
SocketComponentHelper::~SocketComponentHelper() {}
void SocketComponentHelper::start(const Fw::StringBase &name,
const bool reconnect,
const Os::Task::ParamType priority,
const Os::Task::ParamType stack,
const Os::Task::ParamType cpuAffinity) {
FW_ASSERT(m_task.getState() == Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
this->m_stop = false;
m_reconnect = reconnect;
// Note: the first step is for the IP socket to open the port
Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity);
Os::Task::Status stat = m_task.start(arguments);
FW_ASSERT(Os::Task::OP_OK == stat, static_cast<FwAssertArgType>(stat));
}
SocketIpStatus SocketComponentHelper::open() {
SocketIpStatus status = SOCK_ANOTHER_THREAD_OPENING;
OpenState local_open = OpenState::OPEN;
// Scope to guard lock
{
Os::ScopeLock scopeLock(m_lock);
if (this->m_open == OpenState::NOT_OPEN) {
this->m_open = OpenState::OPENING;
local_open = this->m_open;
} else {
local_open = OpenState::SKIP;
}
}
if (local_open == OpenState::OPENING) {
FW_ASSERT(this->m_descriptor.fd == -1); // Ensure we are not opening an opened socket
status = this->getSocketHandler().open(this->m_descriptor);
// Lock scope
{
Os::ScopeLock scopeLock(m_lock);
if (Drv::SOCK_SUCCESS == status) {
this->m_open = OpenState::OPEN;
} else {
this->m_open = OpenState::NOT_OPEN;
this->m_descriptor.fd = -1;
}
}
// Notify connection on success outside locked scope
if (Drv::SOCK_SUCCESS == status) {
this->connected();
}
}
return status;
}
bool SocketComponentHelper::isOpened() {
Os::ScopeLock scopedLock(this->m_lock);
bool is_open = this->m_open == OpenState::OPEN;
return is_open;
}
SocketIpStatus SocketComponentHelper::reconnect() {
SocketIpStatus status = SOCK_SUCCESS;
// Open a network connection if it has not already been open
if (not this->isOpened()) {
status = this->open();
if (status == SocketIpStatus::SOCK_ANOTHER_THREAD_OPENING) {
status = SocketIpStatus::SOCK_SUCCESS;
}
}
return status;
}
SocketIpStatus SocketComponentHelper::send(const U8* const data, const U32 size) {
SocketIpStatus status = SOCK_SUCCESS;
this->m_lock.lock();
SocketDescriptor descriptor = this->m_descriptor;
this->m_lock.unlock();
// Prevent transmission before connection, or after a disconnect
if (descriptor.fd == -1) {
status = this->reconnect();
// if reconnect wasn't successful, pass the that up to the caller
if(status != SOCK_SUCCESS) {
return status;
}
// Refresh local copy after reconnect
this->m_lock.lock();
descriptor = this->m_descriptor;
this->m_lock.unlock();
}
status = this->getSocketHandler().send(descriptor, data, size);
if (status == SOCK_DISCONNECTED) {
this->close();
}
return status;
}
void SocketComponentHelper::shutdown() {
Os::ScopeLock scopedLock(this->m_lock);
this->getSocketHandler().shutdown(this->m_descriptor);
}
void SocketComponentHelper::close() {
Os::ScopeLock scopedLock(this->m_lock);
this->getSocketHandler().close(this->m_descriptor);
this->m_descriptor.fd = -1;
this->m_open = OpenState::NOT_OPEN;
}
Os::Task::Status SocketComponentHelper::join() {
return m_task.join();
}
void SocketComponentHelper::stop() {
// Scope to protect lock
{
Os::ScopeLock scopeLock(m_lock);
this->m_stop = true;
}
this->shutdown(); // Break out of any receives and fully shutdown
}
bool SocketComponentHelper::running() {
Os::ScopeLock scopedLock(this->m_lock);
bool running = not this->m_stop;
return running;
}
SocketIpStatus SocketComponentHelper::recv(U8* data, U32 &size) {
SocketIpStatus status = SOCK_SUCCESS;
// Check for previously disconnected socket
this->m_lock.lock();
SocketDescriptor descriptor = this->m_descriptor;
this->m_lock.unlock();
if (descriptor.fd == -1) {
return SOCK_DISCONNECTED;
}
status = this->getSocketHandler().recv(descriptor, data, size);
if (status == SOCK_DISCONNECTED) {
this->close();
}
return status;
}
void SocketComponentHelper::readLoop() {
SocketIpStatus status = SOCK_SUCCESS;
do {
// Prevent transmission before connection, or after a disconnect
if ((not this->isOpened()) and this->running()) {
status = this->reconnect();
if (status != SOCK_SUCCESS) {
Fw::Logger::log("[WARNING] Failed to open port with status %d and errno %d\n", status, errno);
(void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
continue;
}
}
// If the network connection is open, read from it
if (this->isOpened() and this->running()) {
Fw::Buffer buffer = this->getBuffer();
U8* data = buffer.getData();
FW_ASSERT(data);
U32 size = buffer.getSize();
// recv blocks, so it may have been a while since its done an isOpened check
status = this->recv(data, size);
if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) && (status != SOCK_NO_DATA_AVAILABLE)) {
Fw::Logger::log("[WARNING] Failed to recv from port with status %d and errno %d\n",
status,
errno);
this->close();
buffer.setSize(0);
} else {
// Send out received data
buffer.setSize(size);
}
this->sendBuffer(buffer, status);
}
}
// As long as not told to stop, and we are successful interrupted or ordered to retry, keep receiving
while (this->running() &&
(status == SOCK_SUCCESS || (status == SOCK_NO_DATA_AVAILABLE) || status == SOCK_INTERRUPTED_TRY_AGAIN || this->m_reconnect));
// Close the socket
this->close(); // Close the port entirely
}
void SocketComponentHelper::readTask(void* pointer) {
FW_ASSERT(pointer);
SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
self->readLoop();
}
} // namespace Drv