A Discrete-Event Network Simulator
API
win32-fd-reader.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010 The Boeing Company
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Tom Goff <thomas.goff@boeing.com>
18  */
19 
20 #include "fatal-error.h"
21 #include "fd-reader.h"
22 #include "log.h"
23 #include "simple-ref-count.h"
24 #include "simulator.h"
25 
26 #include <cerrno>
27 #include <cstring>
28 #include <fcntl.h>
29 #include <winsock.h>
30 
31 // #define pipe(fds) _pipe(fds,4096, _O_BINARY)
32 
39 namespace ns3
40 {
41 
42 NS_LOG_COMPONENT_DEFINE("FdReader");
43 
44 bool FdReader::winsock_initialized = false;
45 
47  : m_fd(-1),
48  m_stop(false),
49  m_destroyEvent()
50 {
51  NS_LOG_FUNCTION(this);
52  m_evpipe[0] = -1;
53  m_evpipe[1] = -1;
54 }
55 
56 FdReader::~FdReader()
57 {
58  NS_LOG_FUNCTION(this);
59  Stop();
60 }
61 
62 void
63 FdReader::Start(int fd, Callback<void, uint8_t*, ssize_t> readCallback)
64 {
65  NS_LOG_FUNCTION(this << fd << &readCallback);
66  int tmp;
67 
68  if (!winsock_initialized)
69  {
70  WSADATA wsaData;
71  tmp = WSAStartup(MAKEWORD(2, 2), &wsaData);
72  NS_ASSERT_MSG(tmp != NO_ERROR, "Error at WSAStartup()");
73  winsock_initialized = true;
74  }
75 
76  NS_ASSERT_MSG(!m_readThread.joinable(), "read thread already exists");
77 
78  // create a pipe for inter-thread event notification
79  m_evpipe[0] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
80  m_evpipe[1] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
81  if ((static_cast<uint64_t>(m_evpipe[0]) == INVALID_SOCKET) ||
82  (static_cast<uint64_t>(m_evpipe[1]) == INVALID_SOCKET))
83  {
84  NS_FATAL_ERROR("pipe() failed: " << std::strerror(errno));
85  }
86 
87  // make the read end non-blocking
88  ULONG iMode = 1;
89  tmp = ioctlsocket(m_evpipe[0], FIONBIO, &iMode);
90  if (tmp != NO_ERROR)
91  {
92  NS_FATAL_ERROR("fcntl() failed: " << std::strerror(errno));
93  }
94 
95  m_fd = fd;
96  m_readCallback = readCallback;
97 
98  //
99  // We're going to spin up a thread soon, so we need to make sure we have
100  // a way to tear down that thread when the simulation stops. Do this by
101  // scheduling a "destroy time" method to make sure the thread exits before
102  // proceeding.
103  //
104  if (!m_destroyEvent.IsRunning())
105  {
106  // hold a reference to ensure that this object is not
107  // deallocated before the destroy-time event fires
108  this->Ref();
109  m_destroyEvent = Simulator::ScheduleDestroy(&FdReader::DestroyEvent, this);
110  }
111 
112  //
113  // Now spin up a thread to read from the fd
114  //
115  NS_LOG_LOGIC("Spinning up read thread");
116 
117  m_readThread = std::thread(&FdReader::Run, this);
118 }
119 
120 void
121 FdReader::DestroyEvent()
122 {
123  NS_LOG_FUNCTION(this);
124  Stop();
125  this->Unref();
126 }
127 
128 void
129 FdReader::Stop()
130 {
131  NS_LOG_FUNCTION(this);
132  m_stop = true;
133 
134  // signal the read thread
135  if (m_evpipe[1] != -1)
136  {
137  char zero = 0;
138  ssize_t len = send(m_evpipe[1], &zero, sizeof(zero), 0);
139  if (len != sizeof(zero))
140  {
141  NS_LOG_WARN("incomplete write(): " << std::strerror(errno));
142  }
143  }
144 
145  if (m_readThread.joinable())
146  {
147  m_readThread.join();
148  }
149 
150  // close the write end of the event pipe
151  if (m_evpipe[1] != -1)
152  {
153  closesocket(m_evpipe[1]);
154  m_evpipe[1] = -1;
155  }
156 
157  // close the read end of the event pipe
158  if (m_evpipe[0] != -1)
159  {
160  closesocket(m_evpipe[0]);
161  m_evpipe[0] = -1;
162  }
163 
164  // reset everything else
165  m_fd = -1;
166  m_readCallback.Nullify();
167  m_stop = false;
168 }
169 
170 // This runs in a separate thread
171 void
172 FdReader::Run()
173 {
174  NS_LOG_FUNCTION(this);
175  int nfds;
176  fd_set rfds;
177 
178  nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
179 
180  FD_ZERO(&rfds);
181  FD_SET(m_fd, &rfds);
182  FD_SET(m_evpipe[0], &rfds);
183 
184  for (;;)
185  {
186  int r;
187  fd_set readfds = rfds;
188 
189  r = select(nfds, &readfds, nullptr, nullptr, nullptr);
190  if (r == -1 && errno != EINTR)
191  {
192  NS_FATAL_ERROR("select() failed: " << std::strerror(errno));
193  }
194 
195  if (FD_ISSET(m_evpipe[0], &readfds))
196  {
197  // drain the event pipe
198  for (;;)
199  {
200  char buf[1024];
201  ssize_t len = recv(m_evpipe[0], buf, sizeof(buf), 0);
202  if (len == 0)
203  {
204  NS_FATAL_ERROR("event pipe closed");
205  }
206  if (len < 0)
207  {
208  if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
209  {
210  break;
211  }
212  else
213  {
214  NS_FATAL_ERROR("read() failed: " << std::strerror(errno));
215  }
216  }
217  }
218  }
219 
220  if (m_stop)
221  {
222  // this thread is done
223  break;
224  }
225 
226  if (FD_ISSET(m_fd, &readfds))
227  {
228  FdReader::Data data = DoRead();
229  // reading stops when m_len is zero
230  if (data.m_len == 0)
231  {
232  break;
233  }
234  // the callback is only called when m_len is positive (data
235  // is ignored if m_len is negative)
236  else if (data.m_len > 0)
237  {
238  m_readCallback(data.m_buf, data.m_len);
239  }
240  }
241  }
242 }
243 
244 } // namespace ns3
FdReader()
Constructor.
static double zero
NS_FATAL_x macro definitions.
ns3::FdReader declaration.
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
ns3::SimpleRefCount declaration and template implementation.
ns3::Simulator declaration.
uint8_t data[writeSize]