nasa / SBN

38 stars 23 forks source link

UDP select() busy-polling #38

Closed jbohren-hbr closed 3 years ago

jbohren-hbr commented 3 years ago

It appears that when using the UDP protocol module, the SBN_RecvPeerTask task will busy-poll the socket from each net, thus saturating a CPU core to 100% utilization. Has this already been fixed internally, or is a pull request specifying a configurable timeout in the UDP module select call welcome?

Details below:

The SBN_RecvPeerTask includes an infinite loop which repeatedly calls the given protocol module's receive function: https://github.com/nasa/SBN/blob/3eb9fe5ae480fff3e64c48124772a233c838ccd8/fsw/src/sbn_app.c#L202-L209

In the UDP protocol module, this receive function calls OS_SelectSingle with a 0 msec timeout. So if there are no new messages on the socket, it will return immediately. This leads to SBN_RecvPeerTask looping continuously as fast as possible. https://github.com/nasa/SBN/blob/3eb9fe5ae480fff3e64c48124772a233c838ccd8/modules/protocol/udp/fsw/src/sbn_udp_if.c#L229-L234

Note that the TCP protocol module uses a 1000 msec timeout in its analogous operation: https://github.com/nasa/SBN/blob/3eb9fe5ae480fff3e64c48124772a233c838ccd8/modules/protocol/tcp/fsw/src/sbn_tcp_if.c#L381-L384

CDKnightNASA commented 3 years ago

Quite right, when the peer is configured as a "task" peer it shouldn't even hit the select. I'll address.

CDKnightNASA commented 3 years ago

I believe the following "patch" should fix it, I've fallen down the rabbit hole of chasing all the "main" cfe changes.

@@ -225,13 +225,12 @@ static SBN_Status_t Recv(SBN_NetInterface_t *Net, SBN_MsgType_t *MsgTypePtr,

     SBN_UDP_Net_t *NetData = (SBN_UDP_Net_t *)Net->ModulePvt;

-    /* task-based peer connections block on reads, otherwise use select */
-  
     uint32 StateFlags = OS_STREAM_STATE_READABLE;

-    /* timeout returns an OS error */
-    if(OS_SelectSingle(NetData->Socket, &StateFlags, 0) != OS_SUCCESS
-        || !(StateFlags & OS_STREAM_STATE_READABLE))
+    /* polling uses select, otherwise drop through to block on read for task */
+    if(!(Net->TaskFlags & SBN_TASK_RECV)
+        && (OS_SelectSingle(NetData->Socket, &StateFlags, 0) != OS_SUCCESS
+        || !(StateFlags & OS_STREAM_STATE_READABLE)))
     {
         /* nothing to receive */
         return SBN_IF_EMPTY;