GCC Code Coverage Report


Directory: Drv/Ip/
File: SocketComponentHelper.cpp
Date: 2026-09-03 21:14:31
Exec Total Coverage
Lines: 0 223 0.0%
Functions: 0 24 0.0%
Branches: 0 177 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title SocketComponentHelper.cpp
3 // \author mstarch, crsmith
4 // \brief cpp file for SocketComponentHelper implementation class
5 //
6 // \copyright
7 // Copyright 2009-2020, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12
13 #include <Drv/Ip/SocketComponentHelper.hpp>
14 #include <Fw/Logger/Logger.hpp>
15 #include <Fw/Types/Assert.hpp>
16 #include <cerrno>
17
18 namespace Drv {
19
20 SocketComponentHelper::SocketComponentHelper() {}
21
22 SocketComponentHelper::~SocketComponentHelper() {}
23
24 void SocketComponentHelper::start(const Fw::ConstStringBase& name,
25 const FwTaskPriorityType priority,
26 const Os::Task::ParamType stack,
27 const Os::Task::ParamType cpuAffinity,
28 const FwTaskPriorityType priorityReconnect,
29 const Os::Task::ParamType stackReconnect,
30 const Os::Task::ParamType cpuAffinityReconnect) {
31 // Reconnect Thread
32 FW_ASSERT(m_reconnectTask.getState() ==
33 Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
34 this->m_reconnectStop = false;
35 Fw::String reconnectName;
36 (void)reconnectName.format("%s_reconnect", name.toChar()); // task name may safely truncate
37 Os::Task::Arguments reconnectArguments(reconnectName, SocketComponentHelper::reconnectTask, this, priorityReconnect,
38 stackReconnect, cpuAffinityReconnect);
39 Os::Task::Status reconnectStat = m_reconnectTask.start(reconnectArguments);
40 FW_ASSERT(Os::Task::OP_OK == reconnectStat, static_cast<FwAssertArgType>(reconnectStat));
41
42 // Read Thread
43 FW_ASSERT(m_task.getState() ==
44 Os::Task::State::NOT_STARTED); // It is a coding error to start this task multiple times
45 this->m_stop = false;
46 // Note: the first step is for the IP socket to open the port
47 Os::Task::Arguments arguments(name, SocketComponentHelper::readTask, this, priority, stack, cpuAffinity);
48 Os::Task::Status stat = m_task.start(arguments);
49 FW_ASSERT(Os::Task::OP_OK == stat, static_cast<FwAssertArgType>(stat));
50 }
51
52 SocketIpStatus SocketComponentHelper::open() {
53 SocketIpStatus status = SOCK_ANOTHER_THREAD_OPENING;
54 OpenState local_open = OpenState::OPEN;
55 // Scope to guard lock
56 {
57 Os::ScopeLock scopeLock(m_lock);
58 if (this->m_open == OpenState::NOT_OPEN) {
59 this->m_open = OpenState::OPENING;
60 local_open = this->m_open;
61 } else {
62 local_open = OpenState::SKIP;
63 }
64 }
65 if (local_open == OpenState::OPENING) {
66 // Open into a local descriptor and publish it under the lock
67 SocketDescriptor descriptor;
68 {
69 Os::ScopeLock scopeLock(m_lock);
70 descriptor = this->m_descriptor;
71 }
72 FW_ASSERT(descriptor.fd == -1); // Ensure we are not opening an opened socket
73 status = this->getSocketHandler().open(descriptor);
74 // Lock scope
75 {
76 Os::ScopeLock scopeLock(m_lock);
77 if (Drv::SOCK_SUCCESS == status) {
78 this->m_descriptor = descriptor;
79 this->m_open = OpenState::OPEN;
80 } else {
81 this->m_open = OpenState::NOT_OPEN;
82 this->m_descriptor.fd = -1;
83 }
84 }
85 // Notify connection on success outside locked scope
86 if (Drv::SOCK_SUCCESS == status) {
87 this->connected();
88 }
89 }
90
91 return status;
92 }
93
94 bool SocketComponentHelper::isOpened() {
95 Os::ScopeLock scopedLock(this->m_lock);
96 bool is_open = this->m_open == OpenState::OPEN;
97 return is_open;
98 }
99
100 void SocketComponentHelper::setAutomaticOpen(bool auto_open) {
101 Os::ScopeLock scopedLock(this->m_lock);
102 this->m_reopen = auto_open;
103 }
104
105 bool SocketComponentHelper::getAutomaticOpen() {
106 Os::ScopeLock scopedLock(this->m_lock);
107 return this->m_reopen;
108 }
109
110 SocketIpStatus SocketComponentHelper::reopen() {
111 SocketIpStatus status = SOCK_SUCCESS;
112 if (not this->isOpened()) {
113 // Check for auto-open before attempting to reopen
114 bool reopen = this->getAutomaticOpen();
115 if (not reopen) {
116 status = SOCK_AUTO_CONNECT_DISABLED;
117 // Open a network connection if it has not already been open
118 } else {
119 status = this->open();
120 if (status == SocketIpStatus::SOCK_ANOTHER_THREAD_OPENING) {
121 status = SocketIpStatus::SOCK_SUCCESS;
122 }
123 }
124 }
125 return status;
126 }
127
128 SocketIpStatus SocketComponentHelper::send(const U8* const data, const FwSizeType size) {
129 SocketIpStatus status = SOCK_SUCCESS;
130 this->m_lock.lock();
131 SocketDescriptor descriptor = this->m_descriptor;
132 this->m_lock.unlock();
133 // Prevent transmission before connection, or after a disconnect
134 if (descriptor.fd == -1) {
135 this->requestReconnect();
136 SocketIpStatus reconnectStat = this->waitForReconnect();
137 if (reconnectStat == SOCK_SUCCESS) {
138 // Refresh local copy after reopen
139 this->m_lock.lock();
140 descriptor = this->m_descriptor;
141 this->m_lock.unlock();
142 } else {
143 return reconnectStat;
144 }
145 }
146 status = this->getSocketHandler().send(descriptor, data, size);
147 if (status == SOCK_DISCONNECTED) {
148 this->close();
149 }
150 return status;
151 }
152
153 void SocketComponentHelper::shutdown() {
154 Os::ScopeLock scopedLock(this->m_lock);
155 this->getSocketHandler().shutdown(this->m_descriptor);
156 }
157
158 void SocketComponentHelper::close() {
159 Os::ScopeLock scopedLock(this->m_lock);
160 this->getSocketHandler().close(this->m_descriptor);
161 this->m_descriptor.fd = -1;
162 this->m_open = OpenState::NOT_OPEN;
163 }
164
165 /* Read Thread */
166
167 Os::Task::Status SocketComponentHelper::join() {
168 Os::Task::Status stat = m_task.join();
169 Os::Task::Status reconnectStat = this->joinReconnect();
170 if (stat == Os::Task::Status::OP_OK) {
171 return reconnectStat;
172 }
173 return stat;
174 }
175
176 void SocketComponentHelper::stop() {
177 // Scope to protect lock
178 {
179 Os::ScopeLock scopeLock(m_lock);
180 this->m_stop = true;
181 }
182 this->stopReconnect();
183 this->shutdown(); // Break out of any receives and fully shutdown
184 }
185
186 bool SocketComponentHelper::running() {
187 Os::ScopeLock scopedLock(this->m_lock);
188 bool running = not this->m_stop;
189 return running;
190 }
191
192 SocketIpStatus SocketComponentHelper::recv(U8* data, FwSizeType& size) {
193 SocketIpStatus status = SOCK_SUCCESS;
194 // Check for previously disconnected socket
195 this->m_lock.lock();
196 SocketDescriptor descriptor = this->m_descriptor;
197 this->m_lock.unlock();
198 if (descriptor.fd == -1) {
199 return SOCK_DISCONNECTED;
200 }
201 status = this->getSocketHandler().recv(descriptor, data, size);
202 if (status == SOCK_DISCONNECTED) {
203 this->close();
204 }
205 return status;
206 }
207
208 void SocketComponentHelper::readLoop() {
209 SocketIpStatus status = SOCK_SUCCESS;
210 do {
211 // Prevent transmission before connection, or after a disconnect
212 if ((not this->isOpened()) and this->running()) {
213 this->requestReconnect();
214 status = this->waitForReconnect();
215 // When reopen is disabled, just break as this is a exit condition for the loop
216 if (status == SOCK_AUTO_CONNECT_DISABLED) {
217 break;
218 }
219 }
220 // If the network connection is open, read from it
221 if (this->isOpened() and this->running()) {
222 Fw::Buffer buffer = this->getBuffer();
223 if (buffer.isValid()) {
224 U8* data = buffer.getData();
225 FW_ASSERT(data != nullptr);
226 FwSizeType size = buffer.getSize();
227 // recv blocks, so it may have been a while since its done an isOpened check
228 status = this->recv(data, size);
229 if ((status != SOCK_SUCCESS) && (status != SOCK_INTERRUPTED_TRY_AGAIN) &&
230 (status != SOCK_NO_DATA_AVAILABLE)) {
231 Fw::Logger::log("[WARNING] %s failed to recv from port with status %d and errno %d\n",
232 this->m_task.getName().toChar(), status, errno);
233 this->close();
234 buffer.setSize(0);
235 } else {
236 // Send out received data
237 buffer.setSize(size);
238 }
239 this->sendBuffer(buffer, status);
240 } else {
241 Fw::Logger::log("[WARNING] %s failed to get buffer for recv\n", this->m_task.getName().toChar());
242 (void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
243 }
244 }
245 }
246 // This will loop until stopped. If auto-open is disabled, this will break when reopen returns disabled status
247 while (this->running());
248 // Close the socket
249 this->close(); // Close the port entirely
250 }
251
252 void SocketComponentHelper::readTask(void* pointer) {
253 FW_ASSERT(pointer != nullptr);
254 SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
255 self->readLoop();
256 }
257
258 /* Reconnect Thread */
259
260 Os::Task::Status SocketComponentHelper::joinReconnect() {
261 return m_reconnectTask.join();
262 }
263
264 void SocketComponentHelper::stopReconnect() {
265 Os::ScopeLock scopeLock(this->m_reconnectLock);
266 this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
267 this->m_reconnectStop = true;
268 }
269
270 bool SocketComponentHelper::runningReconnect() {
271 Os::ScopeLock scopedLock(this->m_reconnectLock);
272 bool running = not this->m_reconnectStop;
273 return running;
274 }
275
276 void SocketComponentHelper::reconnectLoop() {
277 SocketIpStatus status = SOCK_SUCCESS;
278 // @non-terminating@: runs until the reconnect thread is stopped
279 while (this->runningReconnect()) {
280 // Check if we need to reconnect
281 bool reconnect = false;
282 {
283 Os::ScopeLock scopedLock(this->m_reconnectLock);
284 FW_ASSERT(this->m_reconnectState == ReconnectState::NOT_RECONNECTING ||
285 this->m_reconnectState == ReconnectState::REQUEST_RECONNECT ||
286 this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS,
287 static_cast<FwAssertArgType>(this->m_reconnectState));
288 if (this->m_reconnectState == ReconnectState::REQUEST_RECONNECT) {
289 this->m_reconnectState = ReconnectState::RECONNECT_IN_PROGRESS;
290 reconnect = true;
291
292 }
293 // If we were already in or are now in RECONNECT_IN_PROGRESS we
294 // need to try to reconnect, again
295 else if (this->m_reconnectState == ReconnectState::RECONNECT_IN_PROGRESS) {
296 reconnect = true;
297 }
298 }
299
300 if (reconnect) {
301 status = this->reopen();
302
303 // Reopen Case 1: Auto Connect is disabled, so no longer
304 // try to reconnect
305 if (status == SOCK_AUTO_CONNECT_DISABLED) {
306 Os::ScopeLock scopedLock(this->m_reconnectLock);
307 this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
308 }
309 // Reopen Case 2: Success, so no longer
310 // try to reconnect
311 else if (status == SOCK_SUCCESS) {
312 Os::ScopeLock scopedLock(this->m_reconnectLock);
313 this->m_reconnectState = ReconnectState::NOT_RECONNECTING;
314 }
315 // Reopen Case 3: Keep trying to reconnect - NO reconnect
316 // state change
317 else {
318 Fw::Logger::log("[WARNING] %s failed to open port with status %d and errno %d\n",
319 this->m_task.getName().toChar(), status, errno);
320 (void)Os::Task::delay(SOCKET_RETRY_INTERVAL);
321 }
322 } else {
323 // After a brief delay, we will loop again
324 (void)Os::Task::delay(this->m_reconnectCheckInterval);
325 }
326 }
327 }
328
329 void SocketComponentHelper::reconnectTask(void* pointer) {
330 FW_ASSERT(pointer != nullptr);
331 SocketComponentHelper* self = reinterpret_cast<SocketComponentHelper*>(pointer);
332 self->reconnectLoop();
333 }
334
335 void SocketComponentHelper::requestReconnect() {
336 Os::ScopeLock scopedLock(this->m_reconnectLock);
337 if (m_reconnectState == ReconnectState::NOT_RECONNECTING) {
338 m_reconnectState = ReconnectState::REQUEST_RECONNECT;
339 }
340 return;
341 }
342
343 SocketIpStatus SocketComponentHelper::waitForReconnect(Fw::TimeInterval timeout) {
344 // Do not attempt to reconnect if auto reconnect config flag is disabled
345 if (!this->getAutomaticOpen()) {
346 return SOCK_AUTO_CONNECT_DISABLED;
347 }
348
349 Fw::TimeInterval elapsed = Fw::TimeInterval(0, 0);
350
351 while (elapsed < timeout) {
352 // If the reconnect thread is NOT reconnecting, we are done waiting
353 // If we are no longer running the reconnect thread, we are done waiting
354 {
355 Os::ScopeLock scopedLock(this->m_reconnectLock);
356 if (this->m_reconnectState == ReconnectState::NOT_RECONNECTING) {
357 break;
358 }
359 if (this->m_reconnectStop) {
360 break;
361 }
362 }
363 // Wait a bit before checking again
364 (void)Os::Task::delay(this->m_reconnectWaitInterval);
365 elapsed.add(this->m_reconnectWaitInterval.getSeconds(), this->m_reconnectWaitInterval.getUSeconds());
366 }
367
368 // If we have completed our loop, check if we are connected or if
369 // auto connect was disabled during our wait
370 if (this->isOpened()) {
371 return SOCK_SUCCESS;
372 }
373
374 // Check one more time if auto reconnect config flag got disabled
375 if (!this->getAutomaticOpen()) {
376 return SOCK_AUTO_CONNECT_DISABLED;
377 }
378
379 return SOCK_DISCONNECTED; // Indicates failure of this attempt, another reopen needed
380 }
381
382 } // namespace Drv
383