2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2002 Net Integration Technologies, Inc.
5 * Base class for streams built on Unix file descriptors.
7 #include "wvfdstream.h"
12 #include <sys/socket.h>
14 inline bool isselectable(int fd
)
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
26 #define EAGAIN WSAEWOULDBLOCK
31 #define errno GetLastError()
33 // in win32, only sockets can be in the FD_SET for select()
34 static inline bool isselectable(int s
)
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
)
45 /***** WvFdStream *****/
47 static IWvStream
*creator(WvStringParm s
, IObject
*)
49 return new WvFdStream(s
.num());
52 static WvMoniker
<IWvStream
> reg("fd", creator
);
54 WvFdStream::WvFdStream(int _rwfd
)
55 : rfd(_rwfd
), wfd(_rwfd
)
57 shutdown_read
= shutdown_write
= false;
61 WvFdStream::WvFdStream(int _rfd
, int _wfd
)
62 : rfd(_rfd
), wfd(_wfd
)
64 shutdown_read
= shutdown_write
= false;
68 WvFdStream::~WvFdStream()
74 static int _cloexec(int fd
, bool close_on_exec
)
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);
84 static int _nonblock(int fd
, bool nonblock
)
87 int flag
= fcntl(fd
, F_GETFL
);
88 return fcntl(fd
, F_SETFL
,
89 (flag
& ~O_NONBLOCK
) | (nonblock
? O_NONBLOCK
: 0));
91 u_long arg
= nonblock
? 1 : 0;
92 return ioctlsocket(fd
, FIONBIO
, &arg
);
97 void WvFdStream::set_nonblock(bool nonblock
)
99 int rfd
= getrfd(), wfd
= getwfd();
101 _nonblock(rfd
, nonblock
);
102 if (wfd
>= 0 && rfd
!= wfd
)
103 _nonblock(wfd
, nonblock
);
107 void WvFdStream::set_close_on_exec(bool close_on_exec
)
109 int rfd
= getrfd(), wfd
= getwfd();
111 _cloexec(rfd
, close_on_exec
);
112 if (wfd
>= 0 && rfd
!= wfd
)
113 _cloexec(wfd
, close_on_exec
);
117 void WvFdStream::close()
119 // fprintf(stderr, "closing fdstream!\n");
123 //fprintf(stderr, "closing%d:%d/%d\n", (int)this, rfd, wfd);
126 if (wfd
>= 0 && wfd
!= rfd
)
129 //fprintf(stderr, "closed!\n");
134 bool WvFdStream::isok() const
136 return WvStream::isok() && (rfd
!= -1 || wfd
!= -1);
140 size_t WvFdStream::uread(void *buf
, size_t count
)
142 assert(!count
|| buf
);
143 if (!count
|| !buf
|| !isok()) return 0;
145 int in
= ::read(rfd
, buf
, count
);
147 // a read that returns zero bytes signifies end-of-file (EOF).
150 if (in
< 0 && (errno
==EINTR
|| errno
==EAGAIN
|| errno
==ENOBUFS
))
151 return 0; // interrupted
153 seterr(in
< 0 ? errno
: 0);
157 // fprintf(stderr, "read %d bytes\n", in);
162 size_t WvFdStream::uwrite(const void *buf
, size_t count
)
164 assert(!count
|| buf
);
165 if (!buf
|| !count
|| !isok()) return 0;
166 // fprintf(stderr, "write %d bytes\n", count);
168 int out
= ::write(wfd
, buf
, count
);
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!)
177 seterr(out
< 0 ? err
: 0); // a more critical error
181 //TRACE("write obj 0x%08x, bytes %d/%d\n", (unsigned int)this, out, count);
186 void WvFdStream::maybe_autoclose()
188 if (stop_write
&& !shutdown_write
&& !outbuf
.used())
190 shutdown_write
= true;
196 ::shutdown(wfd
, SHUT_WR
); // might be a socket
200 if (stop_read
&& !shutdown_read
&& !inbuf
.used())
202 shutdown_read
= true;
206 ::shutdown(rfd
, SHUT_RD
); // might be a socket
210 WvStream::maybe_autoclose();
214 void WvFdStream::pre_select(SelectInfo
&si
)
216 WvStream::pre_select(si
);
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
,
223 if (si
.wants
.readable
&& (rfd
>= 0))
225 if (isselectable(rfd
))
226 FD_SET(rfd
, &si
.read
);
228 si
.msec_timeout
= 0; // not selectable -> *always* readable
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))
235 if (isselectable(wfd
))
236 FD_SET(wfd
, &si
.write
);
238 si
.msec_timeout
= 0; // not selectable -> *always* writable
241 if (si
.wants
.isexception
)
243 if (rfd
>= 0 && isselectable(rfd
)) FD_SET(rfd
, &si
.except
);
244 if (wfd
>= 0 && isselectable(wfd
)) FD_SET(wfd
, &si
.except
);
254 bool WvFdStream::post_select(SelectInfo
&si
)
256 bool result
= WvStream::post_select(si
);
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())
265 // flush_outbuf() might have closed the file!
270 bool rforce
= si
.wants
.readable
&& !isselectable(rfd
),
271 wforce
= si
.wants
.writable
&& !isselectable(wfd
);
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
)));
278 // fprintf(stderr, "fds_post_select: %d/%d %d/%d %d\n",
279 // rfd, wfd, rforce, wforce, val);
281 if (val
&& si
.wants
.readable
&& read_requires_writable
282 && read_requires_writable
->isok()
283 && !read_requires_writable
->select(0, false, true))
285 if (val
&& si
.wants
.writable
&& write_requires_readable
286 && write_requires_readable
->isok()
287 && !write_requires_readable
->select(0, true, false))
289 return val
|| result
;