A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
unix-fd-reader.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 
3 /*
4  * Copyright (c) 2010 The Boeing Company
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation;
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Author: Tom Goff <thomas.goff@boeing.com>
20  */
21 
22 #include <cerrno>
23 #include <cstring>
24 #include <unistd.h>
25 #include <fcntl.h>
26 
27 #include "log.h"
28 #include "fatal-error.h"
29 #include "simple-ref-count.h"
30 #include "system-thread.h"
31 #include "simulator.h"
32 
33 #include "unix-fd-reader.h"
34 
35 NS_LOG_COMPONENT_DEFINE ("FdReader");
36 
37 namespace ns3 {
38 
40  : m_fd (-1), m_readCallback (0), m_readThread (0), m_stop (false),
41  m_destroyEvent ()
42 {
43  NS_LOG_FUNCTION (this);
44  m_evpipe[0] = -1;
45  m_evpipe[1] = -1;
46 }
47 
49 {
50  NS_LOG_FUNCTION (this);
51  Stop ();
52 }
53 
55 {
56  NS_LOG_FUNCTION (this << fd << &readCallback);
57  int tmp;
58 
59  NS_ASSERT_MSG (m_readThread == 0, "read thread already exists");
60 
61  // create a pipe for inter-thread event notification
62  tmp = pipe (m_evpipe);
63  if (tmp == -1)
64  {
65  NS_FATAL_ERROR ("pipe() failed: " << std::strerror (errno));
66  }
67 
68  // make the read end non-blocking
69  tmp = fcntl (m_evpipe[0], F_GETFL);
70  if (tmp == -1)
71  {
72  NS_FATAL_ERROR ("fcntl() failed: " << std::strerror (errno));
73  }
74  if (fcntl (m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1)
75  {
76  NS_FATAL_ERROR ("fcntl() failed: " << std::strerror (errno));
77  }
78 
79  m_fd = fd;
80  m_readCallback = readCallback;
81 
82  //
83  // We're going to spin up a thread soon, so we need to make sure we have
84  // a way to tear down that thread when the simulation stops. Do this by
85  // scheduling a "destroy time" method to make sure the thread exits before
86  // proceeding.
87  //
88  if (!m_destroyEvent.IsRunning ())
89  {
90  // hold a reference to ensure that this object is not
91  // deallocated before the destroy-time event fires
92  this->Ref ();
95  }
96 
97  //
98  // Now spin up a thread to read from the fd
99  //
100  NS_LOG_LOGIC ("Spinning up read thread");
101 
102  m_readThread = Create<SystemThread> (MakeCallback (&FdReader::Run, this));
103  m_readThread->Start ();
104 }
105 
107 {
108  NS_LOG_FUNCTION (this);
109  Stop ();
110  this->Unref ();
111 }
112 
113 void FdReader::Stop (void)
114 {
115  NS_LOG_FUNCTION (this);
116  m_stop = true;
117 
118  // signal the read thread
119  if (m_evpipe[1] != -1)
120  {
121  char zero = 0;
122  ssize_t len = write (m_evpipe[1], &zero, sizeof (zero));
123  if (len != sizeof (zero))
124  NS_LOG_WARN ("incomplete write(): " << std::strerror (errno));
125  }
126 
127  // join the read thread
128  if (m_readThread != 0)
129  {
130  m_readThread->Join ();
131  m_readThread = 0;
132  }
133 
134  // close the write end of the event pipe
135  if (m_evpipe[1] != -1)
136  {
137  close (m_evpipe[1]);
138  m_evpipe[1] = -1;
139  }
140 
141  // close the read end of the event pipe
142  if (m_evpipe[0] != -1)
143  {
144  close (m_evpipe[0]);
145  m_evpipe[0] = -1;
146  }
147 
148  // reset everything else
149  m_fd = -1;
151  m_stop = false;
152 }
153 
154 // This runs in a separate thread
155 void FdReader::Run (void)
156 {
157  NS_LOG_FUNCTION (this);
158  int nfds;
159  fd_set rfds;
160 
161  nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
162 
163  FD_ZERO (&rfds);
164  FD_SET (m_fd, &rfds);
165  FD_SET (m_evpipe[0], &rfds);
166 
167  for (;;)
168  {
169  int r;
170  fd_set readfds = rfds;
171 
172  r = select (nfds, &readfds, NULL, NULL, NULL);
173  if (r == -1 && errno != EINTR)
174  {
175  NS_FATAL_ERROR ("select() failed: " << std::strerror (errno));
176  }
177 
178  if (FD_ISSET (m_evpipe[0], &readfds))
179  {
180  // drain the event pipe
181  for (;;)
182  {
183  char buf[1024];
184  ssize_t len = read (m_evpipe[0], buf, sizeof (buf));
185  if (len == 0)
186  {
187  NS_FATAL_ERROR ("event pipe closed");
188  }
189  if (len < 0)
190  {
191  if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
192  {
193  break;
194  }
195  else
196  {
197  NS_FATAL_ERROR ("read() failed: " << std::strerror (errno));
198  }
199  }
200  }
201  }
202 
203  if (m_stop)
204  {
205  // this thread is done
206  break;
207  }
208 
209  if (FD_ISSET (m_fd, &readfds))
210  {
211  struct FdReader::Data data = DoRead ();
212  // reading stops when m_len is zero
213  if (data.m_len == 0)
214  {
215  break;
216  }
217  // the callback is only called when m_len is positive (data
218  // is ignored if m_len is negative)
219  else if (data.m_len > 0)
220  {
221  m_readCallback (data.m_buf, data.m_len);
222  }
223  }
224  }
225 }
226 
227 } // namespace ns3
void Start(int fd, Callback< void, uint8_t *, ssize_t > readCallback)
Start a new read thread.
void DestroyEvent(void)
A structure representing data read.
#define NS_LOG_FUNCTION(parameters)
Definition: log.h:345
virtual ~FdReader()
NS_LOG_COMPONENT_DEFINE("FdReader")
bool IsRunning(void) const
This method is syntactic sugar for the ns3::Simulator::isExpired method.
Definition: event-id.cc:59
Callback< void, uint8_t *, ssize_t > m_readCallback
static double zero
void Run(void)
#define NS_FATAL_ERROR(msg)
fatal error handling
Definition: fatal-error.h:72
void Stop(void)
Stop the read thread and reset internal state.
Ptr< SystemThread > m_readThread
int m_fd
The file descriptor to read from.
void Unref(void) const
Decrement the reference count.
Callback< R > MakeCallback(R(T::*memPtr)(void), OBJ objPtr)
Definition: callback.h:1238
#define NS_LOG_LOGIC(msg)
Definition: log.h:368
EventId m_destroyEvent
#define NS_ASSERT_MSG(condition, message)
Definition: assert.h:86
#define NS_LOG_WARN(msg)
Definition: log.h:280
void Ref(void) const
Increment the reference count.
void Nullify(void)
Discard the implementation, set it to null.
Definition: callback.h:1018
virtual FdReader::Data DoRead(void)=0
The read implementation.
static EventId ScheduleDestroy(MEM mem_ptr, OBJ obj)
Schedule an event to expire at Destroy time.
Definition: simulator.h:1076