Files
wxWidgets/include/wx/unix/private/sockunix.h
Vadim Zeitlin 73f0c9dff8 Fix crash with blocking accepting sockets in threads under Unix
Sockets returned by wxSocket::Accept() are non-blocking by default and
the only way to use them safely in worker threads is by switching them
to the blocking mode by calling SetFlags(wxSOCKET_BLOCK).

However this didn't work correctly since at least 2.8 days, as turning
wxSOCKET_BLOCK on didn't unregister the socket from the event loop, with
which it had been registered on creation. Fix this by doing this now,
which ensures that the main thread doesn't get any notifications about
the socket if it's used, in a blocking way, in a worker thread.

Note that making the new socket blocking after accpeting is still pretty
inefficient and pre-creating the socket as blocking and using
AcceptWith() is still preferable, but at least it does work now.

Closes #12886.
2019-11-20 20:21:25 +01:00

152 lines
5.0 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: wx/unix/private/sockunix.h
// Purpose: wxSocketImpl implementation for Unix systems
// Authors: Guilhem Lavaux, Vadim Zeitlin
// Created: April 1997
// Copyright: (c) 1997 Guilhem Lavaux
// (c) 2008 Vadim Zeitlin
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
#ifndef _WX_UNIX_GSOCKUNX_H_
#define _WX_UNIX_GSOCKUNX_H_
#include <unistd.h>
#include <sys/ioctl.h>
// Under older (Open)Solaris versions FIONBIO is declared in this header only.
// In the newer versions it's included by sys/ioctl.h but it's simpler to just
// include it always instead of testing for whether it is or not.
#ifdef __SOLARIS__
#include <sys/filio.h>
#endif
#include "wx/private/fdiomanager.h"
#define wxCloseSocket close
class wxSocketImplUnix : public wxSocketImpl,
public wxFDIOHandler
{
public:
wxSocketImplUnix(wxSocketBase& wxsocket)
: wxSocketImpl(wxsocket)
{
m_fds[0] =
m_fds[1] = -1;
}
virtual wxSocketError GetLastError() const wxOVERRIDE;
virtual void ReenableEvents(wxSocketEventFlags flags) wxOVERRIDE
{
// Events are only ever used for non-blocking sockets.
if ( GetSocketFlags() & wxSOCKET_BLOCK )
return;
// enable the notifications about input/output being available again in
// case they were disabled by OnRead/WriteWaiting()
//
// notice that we'd like to enable the events here only if there is
// nothing more left on the socket right now as otherwise we're going
// to get a "ready for whatever" notification immediately (well, during
// the next event loop iteration) and disable the event back again
// which is rather inefficient but unfortunately doing it like this
// doesn't work because the existing code (e.g. src/common/sckipc.cpp)
// expects to keep getting notifications about the data available from
// the socket even if it didn't read all the data the last time, so we
// absolutely have to continue generating them
EnableEvents(flags);
}
virtual void UpdateBlockingState() wxOVERRIDE
{
// Make this int and not bool to allow passing it to ioctl().
const int isBlocking = (GetSocketFlags() & wxSOCKET_BLOCK) != 0;
ioctl(m_fd, FIONBIO, &isBlocking);
DoEnableEvents(wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG, !isBlocking);
}
// wxFDIOHandler methods
virtual void OnReadWaiting() wxOVERRIDE;
virtual void OnWriteWaiting() wxOVERRIDE;
virtual void OnExceptionWaiting() wxOVERRIDE;
virtual bool IsOk() const wxOVERRIDE { return m_fd != INVALID_SOCKET; }
private:
virtual void DoClose() wxOVERRIDE
{
DisableEvents();
wxCloseSocket(m_fd);
}
// enable or disable notifications for socket input/output events
void EnableEvents(int flags = wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG)
{ DoEnableEvents(flags, true); }
void DisableEvents(int flags = wxSOCKET_INPUT_FLAG | wxSOCKET_OUTPUT_FLAG)
{ DoEnableEvents(flags, false); }
// really enable or disable socket input/output events
void DoEnableEvents(int flags, bool enable);
protected:
// descriptors for input and output event notification channels associated
// with the socket
int m_fds[2];
private:
// notify the associated wxSocket about a change in socket state and shut
// down the socket if the event is wxSOCKET_LOST
void OnStateChange(wxSocketNotify event);
// check if there is any input available, return 1 if yes, 0 if no or -1 on
// error
int CheckForInput();
// give it access to our m_fds
friend class wxSocketFDBasedManager;
};
// A version of wxSocketManager which uses FDs for socket IO: it is used by
// Unix console applications and some X11-like ports (wxGTK and wxMotif but not
// wxX11 currently) which implement their own port-specific wxFDIOManagers
class wxSocketFDBasedManager : public wxSocketManager
{
public:
wxSocketFDBasedManager()
{
m_fdioManager = NULL;
}
virtual bool OnInit() wxOVERRIDE;
virtual void OnExit() wxOVERRIDE { }
virtual wxSocketImpl *CreateSocket(wxSocketBase& wxsocket) wxOVERRIDE
{
return new wxSocketImplUnix(wxsocket);
}
virtual void Install_Callback(wxSocketImpl *socket_, wxSocketNotify event) wxOVERRIDE;
virtual void Uninstall_Callback(wxSocketImpl *socket_, wxSocketNotify event) wxOVERRIDE;
protected:
// get the FD index corresponding to the given wxSocketNotify
wxFDIOManager::Direction
GetDirForEvent(wxSocketImpl *socket, wxSocketNotify event);
// access the FDs we store
int& FD(wxSocketImplUnix *socket, wxFDIOManager::Direction d)
{
return socket->m_fds[d];
}
wxFDIOManager *m_fdioManager;
wxDECLARE_NO_COPY_CLASS(wxSocketFDBasedManager);
};
#endif /* _WX_UNIX_GSOCKUNX_H_ */