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 <errno.h>
23 #include <string.h>
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  m_evpipe[0] = -1;
44  m_evpipe[1] = -1;
45 }
46 
48 {
49  Stop ();
50 }
51 
53 {
54  int tmp;
55 
56  NS_ASSERT_MSG (m_readThread == 0, "read thread already exists");
57 
58  // create a pipe for inter-thread event notification
59  tmp = pipe (m_evpipe);
60  if (tmp == -1)
61  {
62  NS_FATAL_ERROR ("pipe() failed: " << strerror (errno));
63  }
64 
65  // make the read end non-blocking
66  tmp = fcntl (m_evpipe[0], F_GETFL);
67  if (tmp == -1)
68  {
69  NS_FATAL_ERROR ("fcntl() failed: " << strerror (errno));
70  }
71  if (fcntl (m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1)
72  {
73  NS_FATAL_ERROR ("fcntl() failed: " << strerror (errno));
74  }
75 
76  m_fd = fd;
77  m_readCallback = readCallback;
78 
79  //
80  // We're going to spin up a thread soon, so we need to make sure we have
81  // a way to tear down that thread when the simulation stops. Do this by
82  // scheduling a "destroy time" method to make sure the thread exits before
83  // proceeding.
84  //
85  if (!m_destroyEvent.IsRunning ())
86  {
87  // hold a reference to ensure that this object is not
88  // deallocated before the destroy-time event fires
89  this->Ref ();
92  }
93 
94  //
95  // Now spin up a thread to read from the fd
96  //
97  NS_LOG_LOGIC ("Spinning up read thread");
98 
99  m_readThread = Create<SystemThread> (MakeCallback (&FdReader::Run, this));
100  m_readThread->Start ();
101 }
102 
104 {
105  Stop ();
106  this->Unref ();
107 }
108 
109 void FdReader::Stop (void)
110 {
111  m_stop = true;
112 
113  // signal the read thread
114  if (m_evpipe[1] != -1)
115  {
116  char zero = 0;
117  ssize_t len = write (m_evpipe[1], &zero, sizeof (zero));
118  if (len != sizeof (zero))
119  NS_LOG_WARN ("incomplete write(): " << strerror (errno));
120  }
121 
122  // join the read thread
123  if (m_readThread != 0)
124  {
125  m_readThread->Join ();
126  m_readThread = 0;
127  }
128 
129  // close the write end of the event pipe
130  if (m_evpipe[1] != -1)
131  {
132  close (m_evpipe[1]);
133  m_evpipe[1] = -1;
134  }
135 
136  // close the read end of the event pipe
137  if (m_evpipe[0] != -1)
138  {
139  close (m_evpipe[0]);
140  m_evpipe[0] = -1;
141  }
142 
143  // reset everything else
144  m_fd = -1;
146  m_stop = false;
147 }
148 
149 // This runs in a separate thread
150 void FdReader::Run (void)
151 {
152  int nfds;
153  fd_set rfds;
154 
155  nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
156 
157  FD_ZERO (&rfds);
158  FD_SET (m_fd, &rfds);
159  FD_SET (m_evpipe[0], &rfds);
160 
161  for (;;)
162  {
163  int r;
164  fd_set readfds = rfds;
165 
166  r = select (nfds, &readfds, NULL, NULL, NULL);
167  if (r == -1 && errno != EINTR)
168  {
169  NS_FATAL_ERROR ("select() failed: " << strerror (errno));
170  }
171 
172  if (FD_ISSET (m_evpipe[0], &readfds))
173  {
174  // drain the event pipe
175  for (;;)
176  {
177  char buf[1024];
178  ssize_t len = read (m_evpipe[0], buf, sizeof (buf));
179  if (len == 0)
180  {
181  NS_FATAL_ERROR ("event pipe closed");
182  }
183  if (len < 0)
184  {
185  if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
186  {
187  break;
188  }
189  else
190  {
191  NS_FATAL_ERROR ("read() failed: " << strerror (errno));
192  }
193  }
194  }
195  }
196 
197  if (m_stop)
198  {
199  // this thread is done
200  break;
201  }
202 
203  if (FD_ISSET (m_fd, &readfds))
204  {
205  struct FdReader::Data data = DoRead ();
206  // reading stops when m_len is zero
207  if (data.m_len == 0)
208  {
209  break;
210  }
211  // the callback is only called when m_len is positive (data
212  // is ignored if m_len is negative)
213  else if (data.m_len > 0)
214  {
215  m_readCallback (data.m_buf, data.m_len);
216  }
217  }
218  }
219 }
220 
221 } // namespace ns3