GCC Code Coverage Report


Directory: ./
File: Drv/Ip/SocketComponentHelper.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 204 223 91.5%
Functions: 24 24 100.0%
Branches: 134 177 75.7%

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