I have been experimenting with GNU Radio and came across the tunnel.py program. This program allows you to tunnel IP traffic over a wireless radio link using Linux TUN/TAP devices. For the most part it is working however one part of the code is confusing me.
There is a class which implements a 'basic MAC layer'. This class has a callback function which writes a new packet to the TUN device. This function (phy_rx_callback
) is called from a separate thread.
The function main_loop
does a carrier sense before transmitting a new packet. The thing I don't understand is why it is sensing a receive channel before transmitting on a separate non-overlapping transmit channel.
Both the RX and TX channels are separate frequencies, and our hardware allows full-duplex communication.
SO, my question is with main_loop
executing, what are the implications of another thread asynchronously calling the phy_rx_callback
function? The problem is I am trying to understand the purpose of the carrier sense loop, I found that commenting that code severely decreases performance. It doesn't make sense to me that you would monitor a receive channel before using a transmit channel, essentially turning it into half-duplex. Then I don't see the purpose of using two frequencies, one for transmit and one for receive. I began to wonder if there was a strange threading issue at work here.
A single instance of the cs_mac
class is created initially. A 'pointer' to the rx_callback function is passed down a few levels to the thread class which actually calls it. Here is the cs_mac class:
class cs_mac(object):
def __init__(self, tun_fd, verbose=False):
self.tun_fd = tun_fd # file descriptor for TUN/TAP interface
self.verbose = verbose
self.tb = None # top block (access to PHY)
def set_top_block(self, tb):
self.tb = tb
def phy_rx_callback(self, ok, payload):
if self.verbose:
print "Rx: ok = %r len(payload) = %4d" % (ok, len(payload))
if ok:
os.write(self.tun_fd, payload)
def main_loop(self):
min_delay = 0.001 # seconds
while 1:
payload = os.read(self.tun_fd, 10*1024)
if not payload:
self.tb.send_pkt(eof=True)
break
if self.verbose:
print "Tx: len(payload) = %4d" % (len(payload),)
delay = min_delay
while self.tb.carrier_sensed():
sys.stderr.write('B')
time.sleep(delay)
if delay < 0.050:
delay = delay * 2 # exponential back-off
self.tb.send_pkt(payload)
Ok, so using ctypes.CDLL('libc.so.6').syscall(186))
, which calls gettid
I discovered that the thread calling the rx_callback
function has the same PID, but a different TID.
The question becomes, what are the implications of having a separate thread call a function from an object in the main thread (while that thread is constantly looping)?