Commit af11b213 authored by Burlen Loring's avatar Burlen Loring Committed by David Partyka
Browse files

Fix bug 12087 vtkSocket does not restart interupted call.

Change-Id: I7d98c21afbfa44ac139c58ef53c097492e43846a
parent ab5e2903
......@@ -16,6 +16,13 @@
#include "vtkObjectFactory.h"
#include <algorithm>
using std::max;
#if defined(__BORLANDC__)
# pragma warn -8012 /* signed/unsigned comparison */
#endif
// The VTK_SOCKET_FAKE_API definition is given to the compiler
// command line by CMakeLists.txt if there is no real sockets
// interface available. When this macro is defined we simply make
......@@ -38,25 +45,99 @@
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#endif
#endif
#if defined(_WIN32) && !defined(__CYGWIN__)
// TODO : document why we restrict to v1.1
#define WSA_VERSION MAKEWORD(1,1)
#define vtkCloseSocketMacro(sock) (closesocket(sock))
#define vtkCloseSocketMacro(sock) (closesocket(sock))
#define vtkErrnoMacro (WSAGetLastError())
#define vtkStrerrorMacro(_num) (wsaStrerror(_num))
#define vtkSocketErrorIdMacro(_id) (WSA##_id)
#define vtkSocketErrorReturnMacro (SOCKET_ERROR)
#else
#define vtkCloseSocketMacro(sock) (close(sock))
#define vtkCloseSocketMacro(sock) (close(sock))
#define vtkErrnoMacro (errno)
#define vtkStrerrorMacro(_num) (strerror(_num))
#define vtkSocketErrorIdMacro(_id) (_id)
#define vtkSocketErrorReturnMacro (-1)
#endif
#if defined(__BORLANDC__)
# pragma warn -8012 /* signed/unsigned comparison */
// This macro wraps a system function call(_call),
// restarting the call in case it was interrupted
// by a signal (EINTR).
#define vtkRestartInterruptedSystemCallMacro(_call,_ret)\
do \
{ \
(_ret)=(_call); \
} \
while (((_ret)==vtkSocketErrorReturnMacro) \
&& (vtkErrnoMacro==vtkSocketErrorIdMacro(EINTR)));
// use when _str may be a null pointer but _fallback is not.
#define vtkSafeStrMacro(_str,_fallback) ((_str)?(_str):(_fallback))
// convert error number to string and report via vtkErrorMacro.
#define vtkSocketErrorMacro(_eno, _message) \
vtkErrorMacro( \
<< (_message) \
<< " " \
<< vtkSafeStrMacro( \
vtkStrerrorMacro(_eno), \
"unknown error") \
<< ".");
// this pointer is not accessable in a static member function
#define vtkGenericErrorMacro(x) \
{ if (vtkObject::GetGlobalWarningDisplay()) { \
vtkOStreamWrapper::EndlType endl; \
vtkOStreamWrapper::UseEndl(endl); \
vtkOStrStreamWrapper vtkmsg; \
vtkmsg \
<< "Error: In " __FILE__ ", line " \
<< __LINE__ << "\n" x \
<< "\n\n"; \
vtkOutputWindowDisplayErrorText(vtkmsg.str()); \
vtkmsg.rdbuf()->freeze(0);}}
// convert error number to string and report via vtkGenericErrorMacro
#define vtkSocketGenericErrorMacro(_message) \
vtkGenericErrorMacro( \
<< (_message) \
<< " " \
<< vtkSafeStrMacro( \
vtkStrerrorMacro(vtkErrnoMacro), \
"unknown error") \
<< ".");
// on windows sterror doesn't handle socket error codes
#if defined(_WIN32) && !defined(__CYGWIN__)
static
const char *wsaStrerror(int wsaeid)
{
static char buf[256]={'\0'};
int ok;
ok=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,0,wsaeid,0,buf,256,0);
if (!ok)
{
return 0;
}
return buf;
}
#endif
//-----------------------------------------------------------------------------
vtkSocket::vtkSocket()
{
this->SocketDescriptor = -1;
}
//-----------------------------------------------------------------------------
......@@ -73,19 +154,39 @@ vtkSocket::~vtkSocket()
int vtkSocket::CreateSocket()
{
#ifndef VTK_SOCKET_FAKE_API
int sock = socket(AF_INET, SOCK_STREAM, 0);
int sock;
vtkRestartInterruptedSystemCallMacro(socket(AF_INET,SOCK_STREAM, 0), sock);
if (sock == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to socket.");
return -1;
}
// Elimate windows 0.2 second delay sending (buffering) data.
int on = 1;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on)))
int iErr;
vtkRestartInterruptedSystemCallMacro(
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on)),
iErr);
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to setsockopt.");
return -1;
}
return sock;
#else
return -1;
#endif
}
//-----------------------------------------------------------------------------
void vtkSocket::CloseSocket()
{
this->CloseSocket(this->SocketDescriptor);
this->SocketDescriptor = -1;
}
//-----------------------------------------------------------------------------
int vtkSocket::BindSocket(int socketdescriptor, int port)
{
......@@ -97,16 +198,31 @@ int vtkSocket::BindSocket(int socketdescriptor, int port)
server.sin_port = htons(port);
// Allow the socket to be bound to an address that is already in use
int opt=1;
int iErr=~vtkSocketErrorReturnMacro;
#ifdef _WIN32
setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, (char*) &opt, sizeof(int));
vtkRestartInterruptedSystemCallMacro(
setsockopt(socketdescriptor,SOL_SOCKET,SO_REUSEADDR,(char*)&opt,sizeof(int)),
iErr);
#elif defined(VTK_HAVE_SO_REUSEADDR)
setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, (void *) &opt, sizeof(int));
vtkRestartInterruptedSystemCallMacro(
setsockopt(socketdescriptor,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(int)),
iErr);
#endif
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to setsockopt.");
return -1;
}
if ( bind(socketdescriptor, reinterpret_cast<sockaddr*>(&server), sizeof(server)) )
vtkRestartInterruptedSystemCallMacro(
bind(socketdescriptor,reinterpret_cast<sockaddr*>(&server),sizeof(server)),
iErr);
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to bind.");
return -1;
}
return 0;
#else
static_cast<void>(socketdescriptor);
......@@ -121,9 +237,20 @@ int vtkSocket::Accept(int socketdescriptor)
#ifndef VTK_SOCKET_FAKE_API
if (socketdescriptor < 0)
{
vtkErrorMacro("Invalid descriptor.");
return -1;
}
return accept(socketdescriptor, 0, 0);
int newDescriptor;
vtkRestartInterruptedSystemCallMacro(
accept(socketdescriptor, 0, 0), newDescriptor);
if (newDescriptor == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to accept.");
return -1;
}
return newDescriptor;
#else
static_cast<void>(socketdescriptor);
return -1;
......@@ -136,9 +263,19 @@ int vtkSocket::Listen(int socketdescriptor)
#ifndef VTK_SOCKET_FAKE_API
if (socketdescriptor < 0)
{
vtkErrorMacro("Invalid descriptor.");
return -1;
}
int iErr;
vtkRestartInterruptedSystemCallMacro(listen(socketdescriptor, 1), iErr);
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to listen.");
return -1;
}
return listen(socketdescriptor, 1);
return 0;
#else
static_cast<void>(socketdescriptor);
return -1;
......@@ -151,32 +288,54 @@ int vtkSocket::SelectSocket(int socketdescriptor, unsigned long msec)
#ifndef VTK_SOCKET_FAKE_API
if (socketdescriptor < 0 )
{
// invalid socket descriptor.
vtkErrorMacro("Invalid descriptor.");
return -1;
}
fd_set rset;
struct timeval tval;
struct timeval* tvalptr = 0;
if ( msec > 0 )
int res;
do
{
tval.tv_sec = msec / 1000;
tval.tv_usec = (msec % 1000)*1000;
tvalptr = &tval;
struct timeval tval;
struct timeval* tvalptr = 0;
if (msec>0)
{
tval.tv_sec = msec / 1000;
tval.tv_usec = (msec % 1000)*1000;
tvalptr = &tval;
}
FD_ZERO(&rset);
FD_SET(socketdescriptor, &rset);
// block until socket is readable.
res = select(socketdescriptor+1, &rset, 0, 0, tvalptr);
}
FD_ZERO(&rset);
FD_SET(socketdescriptor, &rset);
int res = select(socketdescriptor + 1, &rset, 0, 0, tvalptr);
if(res == 0)
while ((res == vtkSocketErrorReturnMacro)
&& (vtkErrnoMacro == vtkSocketErrorIdMacro(EINTR)));
if (res == 0)
{
return 0;//for time limit expire
// time out
return 0;
}
if ( res < 0 || !(FD_ISSET(socketdescriptor, &rset)) )
else
if (res == vtkSocketErrorReturnMacro)
{
// Some error.
// error in the call
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to select.");
return -1;
}
else
if (!FD_ISSET(socketdescriptor, &rset))
{
vtkErrorMacro("Socket error in select. Descriptor not selected.");
return -1;
}
// NOTE: not checking for pending errors,these will be handled
// in the next call to read/recv
// The indicated socket has some activity on it.
return 1;
#else
......@@ -191,50 +350,70 @@ int vtkSocket::SelectSockets(const int* sockets_to_select, int size,
unsigned long msec, int* selected_index)
{
#ifndef VTK_SOCKET_FAKE_API
int i;
int max_fd = -1;
*selected_index = -1;
if (size < 0)
if (size < 0)
{
vtkGenericErrorMacro("Can't select fewer than 0.");
return -1;
}
fd_set rset;
struct timeval tval;
struct timeval* tvalptr = 0;
if ( msec > 0 )
{
tval.tv_sec = msec / 1000;
tval.tv_usec = msec % 1000;
tvalptr = &tval;
}
FD_ZERO(&rset);
for (i=0; i<size; i++)
int res = -1;
do
{
FD_SET(sockets_to_select[i],&rset);
max_fd = (sockets_to_select[i] > max_fd)? sockets_to_select[i] : max_fd;
struct timeval tval;
struct timeval* tvalptr = 0;
if (msec>0)
{
tval.tv_sec = msec / 1000;
tval.tv_usec = msec % 1000;
tvalptr = &tval;
}
FD_ZERO(&rset);
int max_fd = -1;
for (int i=0; i<size; i++)
{
FD_SET(sockets_to_select[i],&rset);
max_fd = max(sockets_to_select[i],max_fd);
}
// block until one socket is ready to read.
res = select(max_fd + 1, &rset, 0, 0, tvalptr);
}
int res = select(max_fd + 1, &rset, 0, 0, tvalptr);
if (res == 0)
while ((res == vtkSocketErrorReturnMacro)
&& (vtkErrnoMacro == vtkSocketErrorIdMacro(EINTR)));
if (res==0)
{
return 0; //Timeout
// time out
return 0;
}
if (res < 0)
else
if (res == vtkSocketErrorReturnMacro)
{
// SelectSocket error.
// error in the call
vtkSocketGenericErrorMacro("Socket error in call to select.");
return -1;
}
//check which socket has some activity.
for (i=0; i<size; i++)
// find the first socket which has some activity.
for (int i=0; i<size; i++)
{
if ( FD_ISSET(sockets_to_select[i],&rset) )
{
// NOTE: not checking for pending errors, these
// will be handled in the next call to read/recv
*selected_index = i;
return 1;
}
}
// no activity on any of the sockets
vtkGenericErrorMacro("Socket error in select. No descriptor selected.");
return -1;
#else
static_cast<void>(sockets_to_select);
......@@ -251,6 +430,7 @@ int vtkSocket::Connect(int socketdescriptor, const char* hostName, int port)
#ifndef VTK_SOCKET_FAKE_API
if (socketdescriptor < 0)
{
vtkErrorMacro("Invalid descriptor.");
return -1;
}
......@@ -261,10 +441,9 @@ int vtkSocket::Connect(int socketdescriptor, const char* hostName, int port)
unsigned long addr = inet_addr(hostName);
hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
}
if (!hp)
{
// vtkErrorMacro("Unknown host: " << hostName);
vtkErrorMacro("Unknown host: " << hostName);
return -1;
}
......@@ -273,8 +452,48 @@ int vtkSocket::Connect(int socketdescriptor, const char* hostName, int port)
memcpy(&name.sin_addr, hp->h_addr, hp->h_length);
name.sin_port = htons(port);
return connect(socketdescriptor, reinterpret_cast<sockaddr*>(&name),
sizeof(name));
int iErr
= connect(socketdescriptor, reinterpret_cast<sockaddr*>(&name),sizeof(name));
if ( (iErr == vtkSocketErrorReturnMacro )
&& (vtkErrnoMacro == vtkSocketErrorIdMacro(EINTR)) )
{
// Restarting an interrupted connect call only works on linux,
// other unix require a call to select which blocks until the
// connection is complete.
// See Stevens 2d ed, 15.4 p413, "interrupted connect"
iErr = this->SelectSocket(socketdescriptor,0);
if (iErr == -1)
{
// SelectSocket doesn't test for pending errors.
int pendingErr;
socklen_t pendingErrLen=sizeof(pendingErr);
vtkRestartInterruptedSystemCallMacro(
getsockopt(socketdescriptor, SOL_SOCKET, SO_ERROR, &pendingErr, &pendingErrLen),
iErr);
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(
vtkErrnoMacro, "Socket error in call to getsockopt.");
return -1;
}
else
if (pendingErr)
{
vtkSocketErrorMacro(
pendingErr, "Socket error pending from call to connect.");
return -1;
}
}
}
else
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(
vtkErrnoMacro, "Socket error in call to connect.");
return -1;
}
return 0;
#else
static_cast<void>(socketdescriptor);
static_cast<void>(hostName);
......@@ -294,8 +513,14 @@ int vtkSocket::GetPort(int sock)
#else
int sizebuf = sizeof(sockinfo);
#endif
if(getsockname(sock, reinterpret_cast<sockaddr*>(&sockinfo), &sizebuf) != 0)
int iErr;
vtkRestartInterruptedSystemCallMacro(
getsockname(sock, reinterpret_cast<sockaddr*>(&sockinfo), &sizebuf),
iErr);
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(
vtkErrnoMacro, "Socket error in call to getsockname.");
return 0;
}
return ntohs(sockinfo.sin_port);
......@@ -311,9 +536,18 @@ void vtkSocket::CloseSocket(int socketdescriptor)
#ifndef VTK_SOCKET_FAKE_API
if (socketdescriptor < 0)
{
vtkErrorMacro("Invalid descriptor.");
return;
}
vtkCloseSocketMacro(socketdescriptor);
int iErr;
vtkRestartInterruptedSystemCallMacro(
vtkCloseSocketMacro(socketdescriptor),
iErr);
if (iErr == vtkSocketErrorReturnMacro)
{
vtkSocketErrorMacro(
vtkErrnoMacro, "Socket error in call to close/closesocket.");
}
#else
static_cast<void>(socketdescriptor);
return;
......@@ -326,6 +560,7 @@ int vtkSocket::Send(const void* data, int length)
#ifndef VTK_SOCKET_FAKE_API
if (!this->GetConnected())
{
vtkErrorMacro("Not connected.");
return 0;
}
if (length == 0)
......@@ -337,22 +572,19 @@ int vtkSocket::Send(const void* data, int length)
int total = 0;
do
{
int flags;
#if defined(_WIN32) && !defined(__CYGWIN__)
flags = 0;
#else
// disabling, since not present on SUN.
// flags = MSG_NOSIGNAL; //disable signal on Unix boxes.
flags = 0;
#endif
int n = send(this->SocketDescriptor, buffer+total, length-total, flags);
if(n < 0)
int flags=0;
int nSent;
vtkRestartInterruptedSystemCallMacro(
send(this->SocketDescriptor, buffer+total, length-total, flags),
nSent);
if (nSent == vtkSocketErrorReturnMacro)
{
vtkErrorMacro("Socket Error: Send failed.");
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to send.");
return 0;
}
total += n;
total += nSent;
} while(total < length);
return 1;
#else
static_cast<void>(data);
......@@ -367,39 +599,49 @@ int vtkSocket::Receive(void* data, int length, int readFully/*=1*/)
#ifndef VTK_SOCKET_FAKE_API
if (!this->GetConnected())
{
vtkErrorMacro("Not connected.");
return 0;
}
#if defined(_WIN32) && !defined(__CYGWIN__)
int trys = 0;
#endif
char* buffer = reinterpret_cast<char*>(data);
int total = 0;
do
{
#if defined(_WIN32) && !defined(__CYGWIN__)
int trys = 0;
#endif
int n = recv(this->SocketDescriptor, buffer+total, length-total, 0);
if(n < 1)
int nRecvd;
vtkRestartInterruptedSystemCallMacro(
recv(this->SocketDescriptor, buffer+total, length-total, 0),
nRecvd);
if (nRecvd == 0)
{
// peer shut down
return 0;
}
#if defined(_WIN32) && !defined(__CYGWIN__)
if ((nRecvd == vtkSocketErrorReturnMacro)
&& (WSAGetLastError() == WSAENOBUFS))
{
// On long messages, Windows recv sometimes fails with WSAENOBUFS, but
// will work if you try again.
int error = WSAGetLastError();
if ((error == WSAENOBUFS) && (trys++ < 1000))
if ((trys++ < 1000))
{
Sleep(1);
continue;
}
#else
// On unix, a recv may be interrupted by a signal. In this case we should
// retry.
int errorNumber = errno;
if (errorNumber == EINTR) continue;
#endif
vtkErrorMacro("Socket Error: Receive failed.");
vtkSocketErrorMacro(vtkErrnoMacro, "Socket error in call to recv.");
return 0;
}
total += n;
} while(readFully && total < length);
#endif
total += nRecvd;
}
while( readFully && (total < length));
return total;
#else
static_cast<void>(data);
......
......@@ -36,7 +36,7 @@ public:
// Description:
// Close the socket.
void CloseSocket() {this->CloseSocket(this->SocketDescriptor);}
void CloseSocket();
// ------ Communication API ---
// Description:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment