Having the invalid value of descriptor here might be considered weird, but it happens nevertheless, when you're DoSing the websocket endpoint using the 3G connection. It makes sense to return the generic error here instead of trying to read() from it.
165 lines
4.4 KiB
C++
165 lines
4.4 KiB
C++
/*
|
|
* IXSelectInterruptPipe.cpp
|
|
* Author: Benjamin Sergeant
|
|
* Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
|
|
*/
|
|
|
|
//
|
|
// On UNIX we use pipes to wake up select. There is no way to do that
|
|
// on Windows so this file is compiled out on Windows.
|
|
//
|
|
#ifndef _WIN32
|
|
|
|
#include "IXSelectInterruptPipe.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sstream>
|
|
#include <string.h> // for strerror
|
|
#include <unistd.h> // for write
|
|
|
|
namespace ix
|
|
{
|
|
// File descriptor at index 0 in _fildes is the read end of the pipe
|
|
// File descriptor at index 1 in _fildes is the write end of the pipe
|
|
const int SelectInterruptPipe::kPipeReadIndex = 0;
|
|
const int SelectInterruptPipe::kPipeWriteIndex = 1;
|
|
|
|
SelectInterruptPipe::SelectInterruptPipe()
|
|
{
|
|
_fildes[kPipeReadIndex] = -1;
|
|
_fildes[kPipeWriteIndex] = -1;
|
|
}
|
|
|
|
SelectInterruptPipe::~SelectInterruptPipe()
|
|
{
|
|
::close(_fildes[kPipeReadIndex]);
|
|
::close(_fildes[kPipeWriteIndex]);
|
|
_fildes[kPipeReadIndex] = -1;
|
|
_fildes[kPipeWriteIndex] = -1;
|
|
}
|
|
|
|
bool SelectInterruptPipe::init(std::string& errorMsg)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
|
|
|
// calling init twice is a programming error
|
|
assert(_fildes[kPipeReadIndex] == -1);
|
|
assert(_fildes[kPipeWriteIndex] == -1);
|
|
|
|
if (pipe(_fildes) < 0)
|
|
{
|
|
std::stringstream ss;
|
|
ss << "SelectInterruptPipe::init() failed in pipe() call"
|
|
<< " : " << strerror(errno);
|
|
errorMsg = ss.str();
|
|
return false;
|
|
}
|
|
|
|
if (fcntl(_fildes[kPipeReadIndex], F_SETFL, O_NONBLOCK) == -1)
|
|
{
|
|
std::stringstream ss;
|
|
ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
|
|
<< " : " << strerror(errno);
|
|
errorMsg = ss.str();
|
|
|
|
_fildes[kPipeReadIndex] = -1;
|
|
_fildes[kPipeWriteIndex] = -1;
|
|
return false;
|
|
}
|
|
|
|
if (fcntl(_fildes[kPipeWriteIndex], F_SETFL, O_NONBLOCK) == -1)
|
|
{
|
|
std::stringstream ss;
|
|
ss << "SelectInterruptPipe::init() failed in fcntl(..., O_NONBLOCK) call"
|
|
<< " : " << strerror(errno);
|
|
errorMsg = ss.str();
|
|
|
|
_fildes[kPipeReadIndex] = -1;
|
|
_fildes[kPipeWriteIndex] = -1;
|
|
return false;
|
|
}
|
|
|
|
#ifdef F_SETNOSIGPIPE
|
|
if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
|
|
{
|
|
std::stringstream ss;
|
|
ss << "SelectInterruptPipe::init() failed in fcntl(.... F_SETNOSIGPIPE) call"
|
|
<< " : " << strerror(errno);
|
|
errorMsg = ss.str();
|
|
|
|
_fildes[kPipeReadIndex] = -1;
|
|
_fildes[kPipeWriteIndex] = -1;
|
|
return false;
|
|
}
|
|
|
|
if (fcntl(_fildes[kPipeWriteIndex], F_SETNOSIGPIPE, 1) == -1)
|
|
{
|
|
std::stringstream ss;
|
|
ss << "SelectInterruptPipe::init() failed in fcntl(..., F_SETNOSIGPIPE) call"
|
|
<< " : " << strerror(errno);
|
|
errorMsg = ss.str();
|
|
|
|
_fildes[kPipeReadIndex] = -1;
|
|
_fildes[kPipeWriteIndex] = -1;
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SelectInterruptPipe::notify(uint64_t value)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
|
|
|
int fd = _fildes[kPipeWriteIndex];
|
|
if (fd == -1) return false;
|
|
|
|
ssize_t ret = -1;
|
|
do
|
|
{
|
|
ret = ::write(fd, &value, sizeof(value));
|
|
} while (ret == -1 && errno == EINTR);
|
|
|
|
// we should write 8 bytes for an uint64_t
|
|
return ret == 8;
|
|
}
|
|
|
|
// TODO: return max uint64_t for errors ?
|
|
uint64_t SelectInterruptPipe::read()
|
|
{
|
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
|
|
|
int fd = _fildes[kPipeReadIndex];
|
|
|
|
// A weird state has been reached (consider reporting to the error log?)
|
|
if (-1 == fd) return std::numeric_limits<uint64_t>::max();
|
|
|
|
uint64_t value = 0;
|
|
|
|
ssize_t ret = -1;
|
|
do
|
|
{
|
|
ret = ::read(fd, &value, sizeof(value));
|
|
} while (ret == -1 && errno == EINTR);
|
|
|
|
return value;
|
|
}
|
|
|
|
bool SelectInterruptPipe::clear()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int SelectInterruptPipe::getFd() const
|
|
{
|
|
std::lock_guard<std::mutex> lock(_fildesMutex);
|
|
|
|
return _fildes[kPipeReadIndex];
|
|
}
|
|
} // namespace ix
|
|
|
|
#endif // !_WIN32
|