1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-06 17:41:04 +00:00

add some comments, and improve error handling

This commit is contained in:
Desour 2023-06-08 01:45:26 +02:00
parent 8cdf8ab95a
commit 5f13c0aa48
2 changed files with 55 additions and 32 deletions

View file

@ -78,6 +78,7 @@ IPCChannelBuffer::~IPCChannelBuffer()
#if defined(_WIN32) #if defined(_WIN32)
// returns false on timeout
static bool wait(HANDLE sem, DWORD timeout) static bool wait(HANDLE sem, DWORD timeout)
{ {
return WaitForSingleObject(sem, timeout) == WAIT_OBJECT_0; return WaitForSingleObject(sem, timeout) == WAIT_OBJECT_0;
@ -109,6 +110,8 @@ static int futex(std::atomic<u32> *uaddr, int futex_op, u32 val,
#endif // defined(__linux__) #endif // defined(__linux__)
// timeout: relative on linux, and absolute on other posix
// returns false on timeout
static bool wait(IPCChannelBuffer *buf, const struct timespec *timeout) noexcept static bool wait(IPCChannelBuffer *buf, const struct timespec *timeout) noexcept
{ {
#if defined(__linux__) #if defined(__linux__)
@ -132,8 +135,15 @@ static bool wait(IPCChannelBuffer *buf, const struct timespec *timeout) noexcept
return true; return true;
} }
int s = futex(&buf->futex, FUTEX_WAIT, 2, timeout, nullptr, 0); int s = futex(&buf->futex, FUTEX_WAIT, 2, timeout, nullptr, 0);
if (s == -1 && errno != EAGAIN) if (s == -1) {
return false; if (errno == ETIMEDOUT) {
return false;
} else if (errno != EAGAIN && errno != EINTR) {
std::string errmsg = std::string("FUTEX_WAIT failed unexpectedly: ")
+ std::strerror(errno);
FATAL_ERROR(errmsg.c_str());
}
}
} }
#else #else
bool timed_out = false; bool timed_out = false;
@ -154,9 +164,13 @@ static void post(IPCChannelBuffer *buf) noexcept
{ {
#if defined(__linux__) #if defined(__linux__)
if (buf->futex.exchange(1) == 2) { if (buf->futex.exchange(1) == 2) {
// 2 means reader needs to be notified
int s = futex(&buf->futex, FUTEX_WAKE, 1, nullptr, nullptr, 0); int s = futex(&buf->futex, FUTEX_WAKE, 1, nullptr, nullptr, 0);
if (s == -1) if (s == -1) {
FATAL_ERROR("FUTEX_WAKE failed unexpectedly"); std::string errmsg = std::string("FUTEX_WAKE failed unexpectedly: ")
+ std::strerror(errno);
FATAL_ERROR(errmsg.c_str());
}
} }
#else #else
pthread_mutex_lock(&buf->mutex); pthread_mutex_lock(&buf->mutex);
@ -188,38 +202,31 @@ static struct timespec *set_timespec(struct timespec *ts, int ms)
} }
#endif // !defined(_WIN32) #endif // !defined(_WIN32)
#if defined(_WIN32)
IPCChannelEnd IPCChannelEnd::makeA(std::unique_ptr<IPCChannelStuff> stuff) IPCChannelEnd IPCChannelEnd::makeA(std::unique_ptr<IPCChannelStuff> stuff)
{ {
IPCChannelShared *shared = stuff->getShared(); IPCChannelShared *shared = stuff->getShared();
#if defined(_WIN32)
HANDLE sem_a = stuff->getSemA(); HANDLE sem_a = stuff->getSemA();
HANDLE sem_b = stuff->getSemB(); HANDLE sem_b = stuff->getSemB();
return IPCChannelEnd(std::move(stuff), &shared->a, &shared->b, sem_a, sem_b); return IPCChannelEnd(std::move(stuff), &shared->a, &shared->b, sem_a, sem_b);
#else
return IPCChannelEnd(std::move(stuff), &shared->a, &shared->b);
#endif // !defined(_WIN32)
} }
IPCChannelEnd IPCChannelEnd::makeB(std::unique_ptr<IPCChannelStuff> stuff) IPCChannelEnd IPCChannelEnd::makeB(std::unique_ptr<IPCChannelStuff> stuff)
{ {
IPCChannelShared *shared = stuff->getShared(); IPCChannelShared *shared = stuff->getShared();
#if defined(_WIN32)
HANDLE sem_a = stuff->getSemA(); HANDLE sem_a = stuff->getSemA();
HANDLE sem_b = stuff->getSemB(); HANDLE sem_b = stuff->getSemB();
return IPCChannelEnd(std::move(stuff), &shared->b, &shared->a, sem_b, sem_a); return IPCChannelEnd(std::move(stuff), &shared->b, &shared->a, sem_b, sem_a);
} #else
#else // defined(_WIN32)
IPCChannelEnd IPCChannelEnd::makeA(std::unique_ptr<IPCChannelStuff> stuff)
{
IPCChannelShared *shared = stuff->getShared();
return IPCChannelEnd(std::move(stuff), &shared->a, &shared->b);
}
IPCChannelEnd IPCChannelEnd::makeB(std::unique_ptr<IPCChannelStuff> stuff)
{
IPCChannelShared *shared = stuff->getShared();
return IPCChannelEnd(std::move(stuff), &shared->b, &shared->a); return IPCChannelEnd(std::move(stuff), &shared->b, &shared->a);
}
#endif // !defined(_WIN32) #endif // !defined(_WIN32)
}
bool IPCChannelEnd::sendSmall(const void *data, size_t size) noexcept void IPCChannelEnd::sendSmall(const void *data, size_t size) noexcept
{ {
m_out->size = size; m_out->size = size;
memcpy(m_out->data, data, size); memcpy(m_out->data, data, size);
@ -228,7 +235,6 @@ bool IPCChannelEnd::sendSmall(const void *data, size_t size) noexcept
#else #else
post(m_out); post(m_out);
#endif #endif
return true;
} }
bool IPCChannelEnd::sendLarge(const void *data, size_t size, int timeout_ms) noexcept bool IPCChannelEnd::sendLarge(const void *data, size_t size, int timeout_ms) noexcept
@ -236,7 +242,8 @@ bool IPCChannelEnd::sendLarge(const void *data, size_t size, int timeout_ms) noe
#if defined(_WIN32) #if defined(_WIN32)
DWORD timeout = get_timeout(timeout_ms); DWORD timeout = get_timeout(timeout_ms);
#else #else
struct timespec timeout, *timeoutp = set_timespec(&timeout, timeout_ms); struct timespec timeout;
struct timespec *timeoutp = set_timespec(&timeout, timeout_ms);
#endif #endif
m_out->size = size; m_out->size = size;
do { do {
@ -269,7 +276,8 @@ bool IPCChannelEnd::recv(int timeout_ms) noexcept
#if defined(_WIN32) #if defined(_WIN32)
DWORD timeout = get_timeout(timeout_ms); DWORD timeout = get_timeout(timeout_ms);
#else #else
struct timespec timeout, *timeoutp = set_timespec(&timeout, timeout_ms); struct timespec timeout;
struct timespec *timeoutp = set_timespec(&timeout, timeout_ms);
#endif #endif
#if defined(_WIN32) #if defined(_WIN32)
if (!wait(m_sem_in, timeout)) if (!wait(m_sem_in, timeout))
@ -285,7 +293,10 @@ bool IPCChannelEnd::recv(int timeout_ms) noexcept
try { try {
m_large_recv.resize(size); m_large_recv.resize(size);
} catch (...) { } catch (...) {
return false; // it's ok for us if an attacker wants to make us abort
std::string errmsg = std::string("std::vector::resize failed, size was: ")
+ std::to_string(size);
FATAL_ERROR(errmsg.c_str());
} }
u8 *recv_data = m_large_recv.data(); u8 *recv_data = m_large_recv.data();
m_recv_size = size; m_recv_size = size;

View file

@ -41,6 +41,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
IPCChannelShared is situated in shared memory and is used by both ends of IPCChannelShared is situated in shared memory and is used by both ends of
the channel. the channel.
There are currently 3 implementations for synchronisation:
* win32: uses win32 semaphore
* linux: uses futex, and does busy waiting if on x86/x86_64
* other posix: uses posix mutex and condition variable
*/ */
#define IPC_CHANNEL_MSG_SIZE 8192U #define IPC_CHANNEL_MSG_SIZE 8192U
@ -49,16 +54,21 @@ struct IPCChannelBuffer
{ {
#if !defined(_WIN32) #if !defined(_WIN32)
#if defined(__linux__) #if defined(__linux__)
// possible values:
// 0: futex is not posted. reader will check value before blocking => no
// notify needed when posting
// 1: futex is posted
// 2: futex is not posted. reader is waiting with futex syscall, and needs
// to be notified
std::atomic<u32> futex{0}; std::atomic<u32> futex{0};
#else #else
pthread_cond_t cond; pthread_cond_t cond;
pthread_mutex_t mutex; pthread_mutex_t mutex;
// TODO: use atomic? bool posted = false; // protected by mutex
bool posted = false;
#endif #endif
#endif // !defined(_WIN32) #endif // !defined(_WIN32)
size_t size; size_t size;
u8 data[IPC_CHANNEL_MSG_SIZE]; //TODO: volatile? u8 data[IPC_CHANNEL_MSG_SIZE];
IPCChannelBuffer(); IPCChannelBuffer();
~IPCChannelBuffer(); ~IPCChannelBuffer();
@ -90,13 +100,14 @@ public:
static IPCChannelEnd makeA(std::unique_ptr<IPCChannelStuff> stuff); static IPCChannelEnd makeA(std::unique_ptr<IPCChannelStuff> stuff);
static IPCChannelEnd makeB(std::unique_ptr<IPCChannelStuff> stuff); static IPCChannelEnd makeB(std::unique_ptr<IPCChannelStuff> stuff);
// If send, recv, or exchange return false, stop using the channel. // If send, recv, or exchange return false (=timeout), stop using the channel.
// Note: timeouts may be for receiving any response, not a whole message. // Note: timeouts may be for receiving any response, not a whole message.
bool send(const void *data, size_t size, int timeout_ms = -1) noexcept bool send(const void *data, size_t size, int timeout_ms = -1) noexcept
{ {
if (size <= IPC_CHANNEL_MSG_SIZE) { if (size <= IPC_CHANNEL_MSG_SIZE) {
return sendSmall(data, size); sendSmall(data, size);
return true;
} else { } else {
return sendLarge(data, size, timeout_ms); return sendLarge(data, size, timeout_ms);
} }
@ -109,7 +120,7 @@ public:
return send(data, size, timeout_ms) && recv(timeout_ms); return send(data, size, timeout_ms) && recv(timeout_ms);
} }
// Get information about the last received message // Get the content of the last received message
inline const void *getRecvData() const noexcept { return m_recv_data; } inline const void *getRecvData() const noexcept { return m_recv_data; }
inline size_t getRecvSize() const noexcept { return m_recv_size; } inline size_t getRecvSize() const noexcept { return m_recv_size; }
@ -132,8 +143,9 @@ private:
{} {}
#endif #endif
bool sendSmall(const void *data, size_t size) noexcept; void sendSmall(const void *data, size_t size) noexcept;
// returns false on timeout
bool sendLarge(const void *data, size_t size, int timeout_ms) noexcept; bool sendLarge(const void *data, size_t size, int timeout_ms) noexcept;
std::unique_ptr<IPCChannelStuff> m_stuff; std::unique_ptr<IPCChannelStuff> m_stuff;
@ -143,7 +155,7 @@ private:
HANDLE m_sem_in; HANDLE m_sem_in;
HANDLE m_sem_out; HANDLE m_sem_out;
#endif #endif
const void *m_recv_data; const void *m_recv_data = nullptr;
size_t m_recv_size; size_t m_recv_size = 0;
std::vector<u8> m_large_recv; std::vector<u8> m_large_recv;
}; };