diff -r 277546e1dc88 -r c54245c11020 src/core/unix-fd-reader.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/unix-fd-reader.cc Wed Dec 22 16:33:36 2010 -0800 @@ -0,0 +1,216 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (c) 2010 The Boeing Company + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Tom Goff + */ + +#include +#include +#include +#include + +#include "ns3/log.h" +#include "ns3/fatal-error.h" +#include "ns3/simple-ref-count.h" +#include "ns3/system-thread.h" +#include "ns3/simulator.h" + +#include "unix-fd-reader.h" + +NS_LOG_COMPONENT_DEFINE ("FdReader"); + +namespace ns3 { + +FdReader::FdReader () + : m_fd (-1), m_readCallback (0), m_readThread (0), m_stop (false), + m_destroyEvent () +{ + m_evpipe[0] = -1; + m_evpipe[1] = -1; +} + +FdReader::~FdReader () +{ + Stop (); +} + +void FdReader::Start (int fd, Callback readCallback) +{ + int tmp; + + NS_ASSERT_MSG (m_readThread == 0, "read thread already exists"); + + // create a pipe for inter-thread event notification + tmp = pipe (m_evpipe); + if (tmp == -1) + { + NS_FATAL_ERROR ("pipe() failed: " << strerror (errno)); + } + + // make the read end non-blocking + tmp = fcntl(m_evpipe[0], F_GETFL); + if (tmp == -1) + { + NS_FATAL_ERROR ("fcntl() failed: " << strerror (errno)); + } + if (fcntl(m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1) + { + NS_FATAL_ERROR ("fcntl() failed: " << strerror (errno)); + } + + m_fd = fd; + m_readCallback = readCallback; + + // + // We're going to spin up a thread soon, so we need to make sure we have + // a way to tear down that thread when the simulation stops. Do this by + // scheduling a "destroy time" method to make sure the thread exits before + // proceeding. + // + if (! m_destroyEvent.IsRunning ()) + { + // hold a reference to ensure that this object is not + // deallocated before the destroy-time event fires + this->Ref (); + m_destroyEvent = + Simulator::ScheduleDestroy (&FdReader::DestroyEvent, this); + } + + // + // Now spin up a thread to read from the fd + // + NS_LOG_LOGIC ("Spinning up read thread"); + + m_readThread = Create (MakeCallback (&FdReader::Run, this)); + m_readThread->Start (); +} + +void FdReader::DestroyEvent (void) +{ + Stop (); + this->Unref (); +} + +void FdReader::Stop (void) +{ + m_stop = true; + + // signal the read thread and close the write end of the event pipe + if (m_evpipe[1] != -1) + { + char zero = 0; + ssize_t len = write (m_evpipe[1], &zero, sizeof (zero)); + if (len != sizeof (zero)) + NS_LOG_WARN ("incomplete write(): " << strerror (errno)); + close (m_evpipe[1]); + m_evpipe[1] = -1; + } + + // join the read thread + if (m_readThread != 0) + { + m_readThread->Join (); + m_readThread = 0; + } + + // close the read end of the event pipe + if (m_evpipe[0] != -1) + { + close (m_evpipe[0]); + m_evpipe[0] = -1; + } + + // reset everything else + m_fd = -1; + m_readCallback.Nullify (); + m_stop = false; +} + +// This runs in a separate thread +void FdReader::Run (void) +{ + int nfds; + fd_set rfds; + + nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1; + + FD_ZERO (&rfds); + FD_SET (m_fd, &rfds); + FD_SET (m_evpipe[0], &rfds); + + for (;;) + { + int r; + fd_set readfds = rfds; + + r = select (nfds, &readfds, NULL, NULL, NULL); + if (r == -1 && errno != EINTR) + { + NS_FATAL_ERROR ("select() failed: " << strerror (errno)); + } + + if (FD_ISSET (m_evpipe[0], &readfds)) + { + // drain the event pipe + ssize_t len; + for (;;) + { + char buf[1024]; + len = read (m_evpipe[0], buf, sizeof (buf)); + if (len == 0) + { + NS_FATAL_ERROR ("event pipe closed"); + } + if (len < 0) + { + break; + } + } + + if (len < 0 && errno != EAGAIN && errno != EWOULDBLOCK) + { + NS_LOG_WARN ("read() failed: " << strerror (errno)); + break; + } + } + + if (m_stop) + { + // this thread is done + break; + } + + if (FD_ISSET (m_fd, &readfds)) + { + struct FdReader::Data data = DoRead (); + // reading stops when m_len is zero + if (data.m_len == 0) + { + break; + } + // the callback is only called when m_len is positive (data + // is ignored if m_len is negative) + else if (data.m_len > 0) + { + m_readCallback (data.m_buf, data.m_len); + } + } + } +} + +} // namespace ns3 diff -r 277546e1dc88 -r c54245c11020 src/core/unix-fd-reader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/unix-fd-reader.h Wed Dec 22 16:33:36 2010 -0800 @@ -0,0 +1,113 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (c) 2010 The Boeing Company + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Tom Goff + */ + +#ifndef UNIX_FD_READER_H +#define UNIX_FD_READER_H + +#include + +#include "ns3/callback.h" +#include "ns3/system-thread.h" +#include "ns3/event-id.h" + +namespace ns3 { + +/** + * \brief A class that asynchronously reads from a file descriptor. + * + * This class can be used to start a system thread that reads from a + * given file descriptor and invokes a given callback when data is + * received. This class handles thread management automatically but + * the \p DoRead() method must be implemented by a subclass. + */ +class FdReader : public SimpleRefCount +{ +public: + FdReader(); + ~FdReader(); + + /** + * Start a new read thread. + * + * \param fd A valid file descriptor open for reading. + * + * \param readCallback A callback to invoke when new data is + * available. + */ + void Start (int fd, Callback readCallback); + + /** + * Stop the read thread and reset internal state. This does not + * close the file descriptor used for reading. + */ + void Stop (void); + +protected: + + /** + * \internal + * \brief A structure representing data read. + */ + struct Data + { + Data () : m_buf (0), m_len (0) {} + Data (uint8_t *buf, ssize_t len) : m_buf (buf), m_len (len) {} + uint8_t *m_buf; + ssize_t m_len; + }; + + /** + * \internal + * \brief The read implementation. + * + * The value of \p m_len returned controls further processing. The + * callback function is only invoked when \p m_len is positive; any + * data read is not processed when \p m_len is negative; reading + * stops when \p m_len is zero. + * + * The management of memory associated with \p m_buf must be + * compatible with the read callback. + * + * \return A structure representing what was read. + */ + virtual FdReader::Data DoRead (void) = 0; + + /** + * \internal + * \brief The file descriptor to read from. + */ + int m_fd; + +private: + + void Run (void); + void DestroyEvent (void); + + Callback m_readCallback; + Ptr m_readThread; + int m_evpipe[2]; // pipe used to signal events between threads + bool m_stop; // true means the read thread should stop + EventId m_destroyEvent; +}; + +} // namespace ns3 + +#endif // UNIX_FD_READER_H diff -r 277546e1dc88 -r c54245c11020 src/core/wscript --- a/src/core/wscript Wed Dec 22 12:23:56 2010 +0000 +++ b/src/core/wscript Wed Dec 22 16:33:36 2010 -0800 @@ -146,12 +146,14 @@ 'unix-system-thread.cc', 'unix-system-mutex.cc', 'unix-system-condition.cc', + 'unix-fd-reader.cc', ]) core.uselib = 'PTHREAD' headers.source.extend([ 'system-mutex.h', 'system-thread.h', 'system-condition.h', + 'unix-fd-reader.h', ]) if bld.env['ENABLE_GSL']: diff -r 277546e1dc88 -r c54245c11020 src/devices/tap-bridge/tap-bridge.cc --- a/src/devices/tap-bridge/tap-bridge.cc Wed Dec 22 12:23:56 2010 +0000 +++ b/src/devices/tap-bridge/tap-bridge.cc Wed Dec 22 16:33:36 2010 -0800 @@ -32,7 +32,7 @@ #include "ns3/ipv4.h" #include "ns3/simulator.h" #include "ns3/realtime-simulator-impl.h" -#include "ns3/system-thread.h" +#include "ns3/unix-fd-reader.h" #include "ns3/uinteger.h" #include @@ -65,6 +65,27 @@ namespace ns3 { +FdReader::Data TapBridgeFdReader::DoRead (void) +{ + NS_LOG_FUNCTION_NOARGS (); + + uint32_t bufferSize = 65536; + uint8_t *buf = (uint8_t *)malloc (bufferSize); + NS_ABORT_MSG_IF (buf == 0, "malloc() failed"); + + NS_LOG_LOGIC ("Calling read on tap device fd " << m_fd); + ssize_t len = read (m_fd, buf, bufferSize); + if (len <= 0) + { + NS_LOG_INFO ("TapBridgeFdReader::DoRead(): done"); + free (buf); + buf = 0; + len = 0; + } + + return FdReader::Data (buf, len); +} + #define TAP_MAGIC 95549 NS_OBJECT_ENSURE_REGISTERED (TapBridge); @@ -135,7 +156,7 @@ m_sock (-1), m_startEvent (), m_stopEvent (), - m_readThread (0), + m_fdReader (0), m_ns3AddressRewritten (false) { NS_LOG_FUNCTION_NOARGS (); @@ -147,6 +168,8 @@ { NS_LOG_FUNCTION_NOARGS (); + StopTapDevice (); + delete [] m_packetBuffer; m_packetBuffer = 0; @@ -235,11 +258,11 @@ // // Now spin up a read thread to read packets from the tap device. // - NS_ABORT_MSG_IF (m_readThread != 0,"TapBridge::StartTapDevice(): Receive thread is already running"); + NS_ABORT_MSG_IF (m_fdReader != 0,"TapBridge::StartTapDevice(): Receive thread is already running"); NS_LOG_LOGIC ("Spinning up read thread"); - m_readThread = Create (MakeCallback (&TapBridge::ReadThread, this)); - m_readThread->Start (); + m_fdReader = Create (); + m_fdReader->Start (m_sock, MakeCallback (&TapBridge::ReadCallback, this)); } void @@ -247,14 +270,17 @@ { NS_LOG_FUNCTION_NOARGS (); - close (m_sock); - m_sock = -1; + if (m_fdReader != 0) + { + m_fdReader->Stop (); + m_fdReader = 0; + } - NS_ASSERT_MSG (m_readThread != 0, "TapBridge::StopTapDevice(): Receive thread is not running"); - - NS_LOG_LOGIC ("Joining read thread"); - m_readThread->Join (); - m_readThread = 0; + if (m_sock != -1) + { + close (m_sock); + m_sock = -1; + } } void @@ -636,49 +662,33 @@ } void -TapBridge::ReadThread (void) +TapBridge::ReadCallback (uint8_t *buf, ssize_t len) { NS_LOG_FUNCTION_NOARGS (); + NS_ASSERT_MSG (buf != 0, "invalid buf argument"); + NS_ASSERT_MSG (len > 0, "invalid len argument"); + // - // It's important to remember that we're in a completely different thread - // than the simulator is running in. We need to synchronize with that - // other thread to get the packet up into ns-3. What we will need to do + // It's important to remember that we're in a completely different thread + // than the simulator is running in. We need to synchronize with that + // other thread to get the packet up into ns-3. What we will need to do // is to schedule a method to deal with the packet using the multithreaded // simulator we are most certainly running. However, I just said it -- we // are talking about two threads here, so it is very, very dangerous to do // any kind of reference counting on a shared object. Just don't do it. - // So what we're going to do is to allocate a buffer on the heap and pass - // that buffer into the ns-3 context thread where it will create the packet. + // So what we're going to do is pass the buffer allocated on the heap + // into the ns-3 context thread where it will create the packet. // - int32_t len = -1; - for (;;) - { - uint32_t bufferSize = 65536; - uint8_t *buf = (uint8_t *)malloc (bufferSize); - NS_ABORT_MSG_IF (buf == 0, "TapBridge::ReadThread(): malloc packet buffer failed"); - NS_LOG_LOGIC ("Calling read on tap device socket fd " << m_sock); - len = read (m_sock, buf, bufferSize); - - if (len == -1) - { - NS_LOG_INFO ("TapBridge::ReadThread(): Returning"); - free (buf); - buf = 0; - return; - } - - NS_LOG_INFO ("TapBridge::ReadThread(): Received packet on node " << m_nodeId); - NS_LOG_INFO ("TapBridge::ReadThread(): Scheduling handler"); - NS_ASSERT_MSG (m_rtImpl, "EmuNetDevice::ReadThread(): Realtime simulator implementation pointer not set"); - m_rtImpl->ScheduleRealtimeNowWithContext (m_nodeId, MakeEvent (&TapBridge::ForwardToBridgedDevice, this, buf, len)); - buf = 0; - } + NS_LOG_INFO ("TapBridge::ReadCallback(): Received packet on node " << m_nodeId); + NS_LOG_INFO ("TapBridge::ReadCallback(): Scheduling handler"); + NS_ASSERT_MSG (m_rtImpl, "TapBridge::ReadCallback(): Realtime simulator implementation pointer not set"); + m_rtImpl->ScheduleRealtimeNowWithContext (m_nodeId, MakeEvent (&TapBridge::ForwardToBridgedDevice, this, buf, len)); } void -TapBridge::ForwardToBridgedDevice (uint8_t *buf, uint32_t len) +TapBridge::ForwardToBridgedDevice (uint8_t *buf, ssize_t len) { NS_LOG_FUNCTION (buf << len); diff -r 277546e1dc88 -r c54245c11020 src/devices/tap-bridge/tap-bridge.h --- a/src/devices/tap-bridge/tap-bridge.h Wed Dec 22 12:23:56 2010 +0000 +++ b/src/devices/tap-bridge/tap-bridge.h Wed Dec 22 16:33:36 2010 -0800 @@ -31,11 +31,17 @@ #include "ns3/data-rate.h" #include "ns3/ptr.h" #include "ns3/mac48-address.h" -#include "ns3/system-thread.h" +#include "ns3/unix-fd-reader.h" #include "ns3/realtime-simulator-impl.h" namespace ns3 { +class TapBridgeFdReader : public FdReader +{ +private: + FdReader::Data DoRead (void); +}; + class Node; /** @@ -248,9 +254,9 @@ /** * \internal * - * Loop to read and process packets + * Callback to process packets that are read */ - void ReadThread (void); + void ReadCallback (uint8_t *buf, ssize_t len); /* * \internal @@ -262,7 +268,7 @@ * received from the host. * \param buf The length of the buffer. */ - void ForwardToBridgedDevice (uint8_t *buf, uint32_t len); + void ForwardToBridgedDevice (uint8_t *buf, ssize_t len); /** * \internal @@ -336,7 +342,7 @@ * The socket (actually interpreted as fd) to use to talk to the Tap device on * the real internet host. */ - int32_t m_sock; + int m_sock; /** * \internal @@ -357,10 +363,10 @@ /** * \internal * - * Used to identify the ns-3 read thread used to do blocking reads on the - * socket (fd) corresponding to the host device. + * Includes the ns-3 read thread used to do blocking reads on the fd + * corresponding to the host device. */ - Ptr m_readThread; + Ptr m_fdReader; /** * \internal