WvStreams
wvfdstream.cc
1 /*
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
4  *
5  * Base class for streams built on Unix file descriptors.
6  */
7 #include "wvfdstream.h"
8 #include "wvmoniker.h"
9 #include <fcntl.h>
10 
11 #ifndef _WIN32
12 #include <sys/socket.h>
13 
14 inline bool isselectable(int fd)
15 {
16  return true;
17 }
18 
19 #else // _WIN32
20 
21 #define getsockopt(a,b,c,d,e) getsockopt(a,b,c,(char *)d, e)
22 #define SHUT_RD SD_RECEIVE
23 #define SHUT_WR SD_SEND
24 #define ENOBUFS WSAENOBUFS
25 #undef EAGAIN
26 #define EAGAIN WSAEWOULDBLOCK
27 
28 #include "streams.h"
29 
30 #undef errno
31 #define errno GetLastError()
32 
33 // in win32, only sockets can be in the FD_SET for select()
34 static inline bool isselectable(int s)
35 {
36  // if _get_osfhandle() works, it's a msvcrt fd, not a winsock handle.
37  // msvcrt fds can't be select()ed on correctly.
38  return ((HANDLE)_get_osfhandle(s) == INVALID_HANDLE_VALUE)
39  ? true : false;
40 }
41 
42 #endif // _WIN32
43 
44 
45 /***** WvFdStream *****/
46 
47 static IWvStream *creator(WvStringParm s, IObject *)
48 {
49  return new WvFdStream(s.num());
50 }
51 
52 static WvMoniker<IWvStream> reg("fd", creator);
53 
55  : rfd(_rwfd), wfd(_rwfd)
56 {
57  shutdown_read = shutdown_write = false;
58 }
59 
60 
61 WvFdStream::WvFdStream(int _rfd, int _wfd)
62  : rfd(_rfd), wfd(_wfd)
63 {
64  shutdown_read = shutdown_write = false;
65 }
66 
67 
69 {
70  close();
71 }
72 
73 
74 static int _cloexec(int fd, bool close_on_exec)
75 {
76 #ifndef _WIN32 // there is no exec() in win32, so this is meaningless there
77  return fcntl(fd, F_SETFD, close_on_exec ? FD_CLOEXEC : 0);
78 #else
79  return 0;
80 #endif
81 }
82 
83 
84 static int _nonblock(int fd, bool nonblock)
85 {
86 #ifndef _WIN32
87  int flag = fcntl(fd, F_GETFL);
88  return fcntl(fd, F_SETFL,
89  (flag & ~O_NONBLOCK) | (nonblock ? O_NONBLOCK : 0));
90 #else
91  u_long arg = nonblock ? 1 : 0;
92  return ioctlsocket(fd, FIONBIO, &arg);
93 #endif
94 }
95 
96 
97 void WvFdStream::set_nonblock(bool nonblock)
98 {
99  int rfd = getrfd(), wfd = getwfd();
100  if (rfd >= 0)
101  _nonblock(rfd, nonblock);
102  if (wfd >= 0 && rfd != wfd)
103  _nonblock(wfd, nonblock);
104 }
105 
106 
107 void WvFdStream::set_close_on_exec(bool close_on_exec)
108 {
109  int rfd = getrfd(), wfd = getwfd();
110  if (rfd >= 0)
111  _cloexec(rfd, close_on_exec);
112  if (wfd >= 0 && rfd != wfd)
113  _cloexec(wfd, close_on_exec);
114 }
115 
116 
118 {
119  // fprintf(stderr, "closing fdstream!\n");
120  if (!closed)
121  {
122  WvStream::close();
123  //fprintf(stderr, "closing%d:%d/%d\n", (int)this, rfd, wfd);
124  if (rfd >= 0)
125  ::close(rfd);
126  if (wfd >= 0 && wfd != rfd)
127  ::close(wfd);
128  rfd = wfd = -1;
129  //fprintf(stderr, "closed!\n");
130  }
131 }
132 
133 
134 bool WvFdStream::isok() const
135 {
136  return WvStream::isok() && (rfd != -1 || wfd != -1);
137 }
138 
139 
140 size_t WvFdStream::uread(void *buf, size_t count)
141 {
142  assert(!count || buf);
143  if (!count || !buf || !isok()) return 0;
144 
145  int in = ::read(rfd, buf, count);
146 
147  // a read that returns zero bytes signifies end-of-file (EOF).
148  if (in <= 0)
149  {
150  if (in < 0 && (errno==EINTR || errno==EAGAIN || errno==ENOBUFS))
151  return 0; // interrupted
152 
153  seterr(in < 0 ? errno : 0);
154  return 0;
155  }
156 
157  // fprintf(stderr, "read %d bytes\n", in);
158  return in;
159 }
160 
161 
162 size_t WvFdStream::uwrite(const void *buf, size_t count)
163 {
164  assert(!count || buf);
165  if (!buf || !count || !isok()) return 0;
166  // fprintf(stderr, "write %d bytes\n", count);
167 
168  int out = ::write(wfd, buf, count);
169 
170  if (out <= 0)
171  {
172  int err = errno;
173  // fprintf(stderr, "(fd%d-err-%d)", wfd, err);
174  if (out < 0 && (err == ENOBUFS || err==EAGAIN))
175  return 0; // kernel buffer full - data not written (yet!)
176 
177  seterr(out < 0 ? err : 0); // a more critical error
178  return 0;
179  }
180 
181  //TRACE("write obj 0x%08x, bytes %d/%d\n", (unsigned int)this, out, count);
182  return out;
183 }
184 
185 
187 {
188  if (stop_write && !shutdown_write && !outbuf.used())
189  {
190  shutdown_write = true;
191  if (wfd < 0)
192  return;
193  if (rfd != wfd)
194  ::close(wfd);
195  else
196  ::shutdown(wfd, SHUT_WR); // might be a socket
197  wfd = -1;
198  }
199 
200  if (stop_read && !shutdown_read && !inbuf.used())
201  {
202  shutdown_read = true;
203  if (rfd != wfd)
204  ::close(rfd);
205  else
206  ::shutdown(rfd, SHUT_RD); // might be a socket
207  rfd = -1;
208  }
209 
211 }
212 
213 
215 {
217 
218 #if 0
219  fprintf(stderr, "%d/%d wr:%d ww:%d wx:%d inh:%d\n", rfd, wfd,
220  si.wants.readable, si.wants.writable, si.wants.isexception,
221  si.inherit_request);
222 #endif
223  if (si.wants.readable && (rfd >= 0))
224  {
225  if (isselectable(rfd))
226  FD_SET(rfd, &si.read);
227  else
228  si.msec_timeout = 0; // not selectable -> *always* readable
229  }
230 
231  // FIXME: outbuf flushing should really be in WvStream::pre_select()
232  // instead! But it's hard to get the equivalent behaviour there.
233  if ((si.wants.writable || outbuf.used() || autoclose_time) && (wfd >= 0))
234  {
235  if (isselectable(wfd))
236  FD_SET(wfd, &si.write);
237  else
238  si.msec_timeout = 0; // not selectable -> *always* writable
239  }
240 
241  if (si.wants.isexception)
242  {
243  if (rfd >= 0 && isselectable(rfd)) FD_SET(rfd, &si.except);
244  if (wfd >= 0 && isselectable(wfd)) FD_SET(wfd, &si.except);
245  }
246 
247  if (si.max_fd < rfd)
248  si.max_fd = rfd;
249  if (si.max_fd < wfd)
250  si.max_fd = wfd;
251 }
252 
253 
255 {
256  bool result = WvStream::post_select(si);
257 
258  // flush the output buffer if possible
259  size_t outbuf_used = outbuf.used();
260  if (wfd >= 0 && (outbuf_used || autoclose_time)
261  && FD_ISSET(wfd, &si.write) && should_flush())
262  {
263  flush_outbuf(0);
264 
265  // flush_outbuf() might have closed the file!
266  if (!isok())
267  return result;
268  }
269 
270  bool rforce = si.wants.readable && !isselectable(rfd),
271  wforce = si.wants.writable && !isselectable(wfd);
272  bool val =
273  (rfd >= 0 && (rforce || FD_ISSET(rfd, &si.read)))
274  || (wfd >= 0 && (wforce || FD_ISSET(wfd, &si.write)))
275  || (rfd >= 0 && (FD_ISSET(rfd, &si.except)))
276  || (wfd >= 0 && (FD_ISSET(wfd, &si.except)));
277 
278  // fprintf(stderr, "fds_post_select: %d/%d %d/%d %d\n",
279  // rfd, wfd, rforce, wforce, val);
280 
281  if (val && si.wants.readable && read_requires_writable
283  && !read_requires_writable->select(0, false, true))
284  return result;
285  if (val && si.wants.writable && write_requires_readable
287  && !write_requires_readable->select(0, true, false))
288  return result;
289  return val || result;
290 }
WvStream::write_requires_readable
WvStream * write_requires_readable
If this is set, select() doesn't return true for write unless the given stream also returns true for ...
Definition: wvstream.h:42
WvStream::maybe_autoclose
virtual void maybe_autoclose()
Auto-close the stream if the time is right.
Definition: wvstream.cc:583
WvFdStream::~WvFdStream
virtual ~WvFdStream()
Destroys the stream and invokes close().
Definition: wvfdstream.cc:68
WvStream::write
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
Definition: wvstream.cc:532
WvFdStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvfdstream.cc:134
WvStream::read_requires_writable
WvStream * read_requires_writable
If this is set, select() doesn't return true for read unless the given stream also returns true for w...
Definition: wvstream.h:36
WvFdStream::uread
virtual size_t uread(void *buf, size_t count)
unbuffered I/O functions; these ignore the buffer, which is handled by read().
Definition: wvfdstream.cc:140
WvStream::select
bool select(time_t msec_timeout)
Return true if any of the requested features are true on the stream.
Definition: wvstream.h:376
WvFdStream::uwrite
virtual size_t uwrite(const void *buf, size_t count)
unbuffered I/O functions; these ignore the buffer, which is handled by write().
Definition: wvfdstream.cc:162
IWvStream
Definition: iwvstream.h:24
WvFdStream::getrfd
int getrfd() const
Returns the Unix file descriptor for reading from this stream.
Definition: wvfdstream.h:63
WvStream::close
virtual void close()
Close the stream if it is open; isok() becomes false from now on.
Definition: wvstream.cc:341
WvStream::seterr
virtual void seterr(int _errnum)
Override seterr() from WvError so that it auto-closes the stream.
Definition: wvstream.cc:451
WvFdStream::post_select
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Definition: wvfdstream.cc:254
IWvStream::SelectInfo
the data structure used by pre_select()/post_select() and internally by select().
Definition: iwvstream.h:50
WvFdStream::WvFdStream
WvFdStream(int rwfd=-1)
Creates a WvStream from an existing file descriptor.
Definition: wvfdstream.cc:54
WvMoniker
A type-safe version of WvMonikerBase that lets you provide create functions for object types other th...
Definition: wvmoniker.h:61
WvFdStream::maybe_autoclose
virtual void maybe_autoclose()
Auto-close the stream if the time is right.
Definition: wvfdstream.cc:186
WvStream::post_select
virtual bool post_select(SelectInfo &si)
post_select() is called after ::select(), and returns true if this object is now ready.
Definition: wvstream.cc:875
WvStream::read
virtual size_t read(void *buf, size_t count)
read a data block on the stream.
Definition: wvstream.cc:490
WvFdStream::set_close_on_exec
void set_close_on_exec(bool close_on_exec)
Make the fds on this stream close-on-exec or not.
Definition: wvfdstream.cc:107
IObject
Definition: IObject.h:65
WvStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvstream.cc:445
WvStream::should_flush
virtual bool should_flush()
Returns true if we want to flush the output buffer right now.
Definition: wvstream.cc:724
WvFdStream::close
virtual void close()
Closes the file descriptors.
Definition: wvfdstream.cc:117
WvFdStream::getwfd
int getwfd() const
Returns the Unix file descriptor for writing to this stream.
Definition: wvfdstream.h:70
WvFdStream
Base class for streams built on Unix file descriptors.
Definition: wvfdstream.h:20
WvBufBaseCommonImpl::used
size_t used() const
Returns the number of elements in the buffer currently available for reading.
Definition: wvbufbase.h:92
WvFdStream::pre_select
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
Definition: wvfdstream.cc:214
WvFdStream::set_nonblock
void set_nonblock(bool nonblock)
Make the fds on this stream blocking or non-blocking.
Definition: wvfdstream.cc:97
WvStream::pre_select
virtual void pre_select(SelectInfo &si)
pre_select() sets up for eventually calling ::select().
Definition: wvstream.cc:844
WvFdStream::shutdown_read
bool shutdown_read
Have we actually shut down the read/write sides?
Definition: wvfdstream.h:30
WvFdStream::wfd
int wfd
The file descriptor for writing.
Definition: wvfdstream.h:27
WvFdStream::rfd
int rfd
The file descriptor for reading.
Definition: wvfdstream.h:24
WvStream::stop_read
bool stop_read
True if noread()/nowrite()/close() have been called, respectively.
Definition: wvstream.h:57