blob: 533faa3ff4cee19f6ac21b79ae713448aed89ac8 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman3c837132011-11-15 12:07:43 +00002 * Copyright 2011 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
Adam Tkac8aee1a82009-09-04 12:08:56 +000020#ifdef HAVE_CONFIG_H
21#include <config.h>
Adam Tkac49e5ce62008-11-14 12:25:34 +000022#endif
23
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <stdio.h>
25#include <string.h>
26#ifdef _WIN32
27#include <winsock2.h>
28#define write(s,b,l) send(s,(const char*)b,l,0)
29#define EWOULDBLOCK WSAEWOULDBLOCK
30#undef errno
31#define errno WSAGetLastError()
32#undef EINTR
33#define EINTR WSAEINTR
34#else
35#include <sys/types.h>
36#include <errno.h>
37#include <unistd.h>
38#include <sys/time.h>
39#endif
40
Adam Tkac49e5ce62008-11-14 12:25:34 +000041/* Old systems have select() in sys/time.h */
42#ifdef HAVE_SYS_SELECT_H
43#include <sys/select.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000044#endif
45
46#include <rdr/FdOutStream.h>
47#include <rdr/Exception.h>
Pierre Ossman3c837132011-11-15 12:07:43 +000048#include <rfb/util.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049
50
51using namespace rdr;
52
53enum { DEFAULT_BUF_SIZE = 16384 };
54
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000055FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, int bufSize_)
56 : fd(fd_), blocking(blocking_), timeoutms(timeoutms_),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057 bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0)
58{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000059 ptr = start = sentUpTo = new U8[bufSize];
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060 end = start + bufSize;
Pierre Ossman3c837132011-11-15 12:07:43 +000061
62 gettimeofday(&lastWrite, NULL);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063}
64
65FdOutStream::~FdOutStream()
66{
67 try {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000068 blocking = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069 flush();
70 } catch (Exception&) {
71 }
72 delete [] start;
73}
74
75void FdOutStream::setTimeout(int timeoutms_) {
76 timeoutms = timeoutms_;
77}
78
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000079void FdOutStream::setBlocking(bool blocking_) {
80 blocking = blocking_;
81}
82
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000083int FdOutStream::length()
84{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +000085 return offset + ptr - sentUpTo;
86}
87
88int FdOutStream::bufferUsage()
89{
90 return ptr - sentUpTo;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000091}
92
Pierre Ossman3c837132011-11-15 12:07:43 +000093unsigned FdOutStream::getIdleTime()
94{
95 return rfb::msSince(&lastWrite);
96}
97
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098void FdOutStream::flush()
99{
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000100 int timeoutms_;
101
102 if (blocking)
103 timeoutms_ = timeoutms;
104 else
105 timeoutms_ = 0;
106
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000107 while (sentUpTo < ptr) {
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000108 int n = writeWithTimeout((const void*) sentUpTo,
109 ptr - sentUpTo, timeoutms_);
110
111 // Timeout?
112 if (n == 0) {
113 // If non-blocking then we're done here
114 if (!blocking)
115 break;
116
117 // Otherwise try blocking (with possible timeout)
118 if ((timeoutms_ == 0) && (timeoutms != 0)) {
119 timeoutms_ = timeoutms;
120 break;
121 }
122
123 // Proper timeout
124 throw TimedOut();
125 }
126
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000127 sentUpTo += n;
128 offset += n;
129 }
130
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000131 // Managed to flush everything?
132 if (sentUpTo == ptr)
133 ptr = sentUpTo = start;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000134}
135
136
137int FdOutStream::overrun(int itemSize, int nItems)
138{
139 if (itemSize > bufSize)
140 throw Exception("FdOutStream overrun: max itemSize exceeded");
141
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000142 // First try to get rid of the data we have
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000143 flush();
144
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000145 // Still not enough space?
146 if (itemSize > end - ptr) {
147 // Can we shuffle things around?
148 // (don't do this if it gains us less than 25%)
149 if ((sentUpTo - start > bufSize / 4) &&
150 (itemSize < bufSize - (ptr - sentUpTo))) {
151 memmove(start, sentUpTo, ptr - sentUpTo);
152 ptr = start + (ptr - sentUpTo);
153 sentUpTo = start;
154 } else {
155 // Have to get rid of more data, so turn off non-blocking
156 // for a bit...
157 bool realBlocking;
158
159 realBlocking = blocking;
160 blocking = true;
161 flush();
162 blocking = realBlocking;
163 }
164 }
165
166 // Can we fit all the items asked for?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167 if (itemSize * nItems > end - ptr)
168 nItems = (end - ptr) / itemSize;
169
170 return nItems;
171}
172
173//
174// writeWithTimeout() writes up to the given length in bytes from the given
175// buffer to the file descriptor. If there is a timeout set and that timeout
176// expires, it throws a TimedOut exception. Otherwise it returns the number of
177// bytes written. It never attempts to write() unless select() indicates that
178// the fd is writable - this means it can be used on an fd which has been set
179// non-blocking. It also has to cope with the annoying possibility of both
180// select() and write() returning EINTR.
181//
182
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000183int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000184{
185 int n;
186
187 do {
188
189 do {
190 fd_set fds;
191 struct timeval tv;
192 struct timeval* tvp = &tv;
193
194 if (timeoutms != -1) {
195 tv.tv_sec = timeoutms / 1000;
196 tv.tv_usec = (timeoutms % 1000) * 1000;
197 } else {
198 tvp = 0;
199 }
200
201 FD_ZERO(&fds);
202 FD_SET(fd, &fds);
203#ifdef _WIN32_WCE
204 // NB: This fixes a broken Winsock2 select() behaviour. select()
205 // never returns for non-blocking sockets, unless they're already
206 // ready to be written to...
207 u_long zero = 0; ioctlsocket(fd, FIONBIO, &zero);
208#endif
209 n = select(fd+1, 0, &fds, 0, tvp);
210#ifdef _WIN32_WCE
211 u_long one = 0; ioctlsocket(fd, FIONBIO, &one);
212#endif
213 } while (n < 0 && errno == EINTR);
214
215 if (n < 0) throw SystemException("select",errno);
216
Pierre Ossman4ce51ff2011-10-25 15:13:13 +0000217 if (n == 0) return 0;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218
219 do {
220 n = ::write(fd, data, length);
221 } while (n < 0 && (errno == EINTR));
222
223 // NB: This outer loop simply fixes a broken Winsock2 EWOULDBLOCK
224 // condition, found only under Win98 (first edition), with slow
225 // network connections. Should in fact never ever happen...
226 } while (n < 0 && (errno == EWOULDBLOCK));
227
228 if (n < 0) throw SystemException("write",errno);
229
Pierre Ossman3c837132011-11-15 12:07:43 +0000230 gettimeofday(&lastWrite, NULL);
231
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000232 return n;
233}