A Discrete-Event Network Simulator
API
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 <sys/select.h>
23
24#include <cerrno>
25#include <cstring>
26#include <unistd.h> // close()
27#include <fcntl.h>
28
29#include "log.h"
30#include "fatal-error.h"
31#include "simple-ref-count.h"
32#include "system-thread.h"
33#include "simulator.h"
34
35#include "unix-fd-reader.h"
36
43namespace ns3 {
44
45NS_LOG_COMPONENT_DEFINE ("FdReader");
46
48 : m_fd (-1), m_readCallback (0), m_readThread (0), m_stop (false),
49 m_destroyEvent ()
50{
51 NS_LOG_FUNCTION (this);
52 m_evpipe[0] = -1;
53 m_evpipe[1] = -1;
54}
55
57{
58 NS_LOG_FUNCTION (this);
59 Stop ();
60}
61
63{
64 NS_LOG_FUNCTION (this << fd << &readCallback);
65 int tmp;
66
67 NS_ASSERT_MSG (m_readThread == 0, "read thread already exists");
68
69 // create a pipe for inter-thread event notification
70 tmp = pipe (m_evpipe);
71 if (tmp == -1)
72 {
73 NS_FATAL_ERROR ("pipe() failed: " << std::strerror (errno));
74 }
75
76 // make the read end non-blocking
77 tmp = fcntl (m_evpipe[0], F_GETFL);
78 if (tmp == -1)
79 {
80 NS_FATAL_ERROR ("fcntl() failed: " << std::strerror (errno));
81 }
82 if (fcntl (m_evpipe[0], F_SETFL, tmp | O_NONBLOCK) == -1)
83 {
84 NS_FATAL_ERROR ("fcntl() failed: " << std::strerror (errno));
85 }
86
87 m_fd = fd;
88 m_readCallback = readCallback;
89
90 //
91 // We're going to spin up a thread soon, so we need to make sure we have
92 // a way to tear down that thread when the simulation stops. Do this by
93 // scheduling a "destroy time" method to make sure the thread exits before
94 // proceeding.
95 //
97 {
98 // hold a reference to ensure that this object is not
99 // deallocated before the destroy-time event fires
100 this->Ref ();
103 }
104
105 //
106 // Now spin up a thread to read from the fd
107 //
108 NS_LOG_LOGIC ("Spinning up read thread");
109
110 m_readThread = Create<SystemThread> (MakeCallback (&FdReader::Run, this));
111 m_readThread->Start ();
112}
113
115{
116 NS_LOG_FUNCTION (this);
117 Stop ();
118 this->Unref ();
119}
120
121void FdReader::Stop (void)
122{
123 NS_LOG_FUNCTION (this);
124 m_stop = true;
125
126 // signal the read thread
127 if (m_evpipe[1] != -1)
128 {
129 char zero = 0;
130 ssize_t len = write (m_evpipe[1], &zero, sizeof (zero));
131 if (len != sizeof (zero))
132 {
133 NS_LOG_WARN ("incomplete write(): " << std::strerror (errno));
134 }
135 }
136
137 // join the read thread
138 if (m_readThread != 0)
139 {
140 m_readThread->Join ();
141 m_readThread = 0;
142 }
143
144 // close the write end of the event pipe
145 if (m_evpipe[1] != -1)
146 {
147 close (m_evpipe[1]);
148 m_evpipe[1] = -1;
149 }
150
151 // close the read end of the event pipe
152 if (m_evpipe[0] != -1)
153 {
154 close (m_evpipe[0]);
155 m_evpipe[0] = -1;
156 }
157
158 // reset everything else
159 m_fd = -1;
161 m_stop = false;
162}
163
164// This runs in a separate thread
165void FdReader::Run (void)
166{
167 NS_LOG_FUNCTION (this);
168 int nfds;
169 fd_set rfds;
170
171 nfds = (m_fd > m_evpipe[0] ? m_fd : m_evpipe[0]) + 1;
172
173 FD_ZERO (&rfds);
174 FD_SET (m_fd, &rfds);
175 FD_SET (m_evpipe[0], &rfds);
176
177 for (;;)
178 {
179 int r;
180 fd_set readfds = rfds;
181
182 r = select (nfds, &readfds, NULL, NULL, NULL);
183 if (r == -1 && errno != EINTR)
184 {
185 NS_FATAL_ERROR ("select() failed: " << std::strerror (errno));
186 }
187
188 if (FD_ISSET (m_evpipe[0], &readfds))
189 {
190 // drain the event pipe
191 for (;;)
192 {
193 char buf[1024];
194 ssize_t len = read (m_evpipe[0], buf, sizeof (buf));
195 if (len == 0)
196 {
197 NS_FATAL_ERROR ("event pipe closed");
198 }
199 if (len < 0)
200 {
201 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
202 {
203 break;
204 }
205 else
206 {
207 NS_FATAL_ERROR ("read() failed: " << std::strerror (errno));
208 }
209 }
210 }
211 }
212
213 if (m_stop)
214 {
215 // this thread is done
216 break;
217 }
218
219 if (FD_ISSET (m_fd, &readfds))
220 {
221 struct FdReader::Data data = DoRead ();
222 // reading stops when m_len is zero
223 if (data.m_len == 0)
224 {
225 break;
226 }
227 // the callback is only called when m_len is positive (data
228 // is ignored if m_len is negative)
229 else if (data.m_len > 0)
230 {
231 m_readCallback (data.m_buf, data.m_len);
232 }
233 }
234 }
235}
236
237} // namespace ns3
void Nullify(void)
Discard the implementation, set it to null.
Definition: callback.h:1391
bool IsRunning(void) const
This method is syntactic sugar for !IsExpired().
Definition: event-id.cc:71
int m_evpipe[2]
Pipe used to signal events between threads.
void Stop(void)
Stop the read thread and reset internal state.
FdReader()
Constructor.
bool m_stop
Signal the read thread to stop.
virtual FdReader::Data DoRead(void)=0
The read implementation.
void DestroyEvent(void)
Event handler scheduled for destroy time to halt the thread.
EventId m_destroyEvent
The event scheduled for destroy time which will invoke DestroyEvent and halt the thread.
void Start(int fd, Callback< void, uint8_t *, ssize_t > readCallback)
Start a new read thread.
void Run(void)
The asynchronous function which performs the read.
int m_fd
The file descriptor to read from.
Ptr< SystemThread > m_readThread
The thread doing the read, created and launched by Start().
virtual ~FdReader()
Destructor.
Callback< void, uint8_t *, ssize_t > m_readCallback
The main thread callback function to invoke when we have data.
void Unref(void) const
Decrement the reference count.
void Ref(void) const
Increment the reference count.
static EventId ScheduleDestroy(FUNC f, Ts &&... args)
Schedule an event to run at the end of the simulation, when Simulator::Destroy() is called.
Definition: simulator.h:605
static double zero
NS_FATAL_x macro definitions.
#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:88
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
#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:265
Debug message logging.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Callback< R, Ts... > MakeCallback(R(T::*memPtr)(Ts...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:1648
ns3::SimpleRefCount declaration and template implementation.
ns3::Simulator declaration.
uint8_t data[writeSize]
A structure representing data read.
System-independent thread class ns3::SystemThread declaration.
ns3::FdReader declaration.