Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/fork-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
# The jobs only build and run the tests; they upload nothing. The workflow
# uses GitHub's own actions only, so it runs under a repository setting that
# allows no other actions.
#
# Each job gets 90 minutes (timeout-minutes; the slowest, Windows, takes
# about 35), and each test case its own limit from tests/CMakeLists.txt. A
# step that hangs then fails in time instead of holding the job until GitHub
# cancels it after six hours.
name: Fork CI

on:
Expand All @@ -32,6 +37,7 @@ jobs:
name: Windows x64 Release, editor ${{ matrix.editor }}
if: github.repository != 'sven-n/MuMain' || github.event_name == 'workflow_dispatch'
runs-on: windows-latest
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -120,6 +126,7 @@ jobs:
name: Linux x64 Release, editor ON
if: github.repository != 'sven-n/MuMain' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 90

steps:
- name: Checkout repository
Expand Down Expand Up @@ -175,6 +182,7 @@ jobs:
name: macOS arm64 Release, editor ON
if: github.repository != 'sven-n/MuMain' || github.event_name == 'workflow_dispatch'
runs-on: macos-latest
timeout-minutes: 90

steps:
- name: Checkout repository
Expand Down
27 changes: 7 additions & 20 deletions src/source/Core/Platform/LocalSocket.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "Core/Platform/LocalSocket.h"

#include "Core/Platform/NonBlockingSocket.h"

#include <cstring>

#ifdef _WIN32
Expand Down Expand Up @@ -28,12 +30,6 @@ constexpr int SendFlags = 0;
constexpr int SendFlags = MSG_NOSIGNAL;
#endif

bool SetNonBlocking(SOCKET handle)
{
u_long nonBlocking = 1;
return ioctlsocket(handle, FIONBIO, &nonBlocking) != SOCKET_ERROR;
}

// Whether a non-blocking connect has not finished yet, rather than failed.
bool ConnectPending(int error)
{
Expand All @@ -44,15 +40,6 @@ bool ConnectPending(int error)
#endif
}

bool WouldBlock(int error)
{
#ifdef _WIN32
return error == WSAEWOULDBLOCK;
#else
return error == EWOULDBLOCK || error == EAGAIN || error == EINTR;
#endif
}

void ApplyOwnerOnlyMode(SOCKET handle, const std::string& path)
{
#ifdef _WIN32
Expand Down Expand Up @@ -171,7 +158,7 @@ bool LocalSocketConnection::ReadAvailable()
return true;
}

if (WouldBlock(WSAGetLastError()))
if (NonBlockingSocket::WouldBlock(WSAGetLastError()))
{
return true;
}
Expand Down Expand Up @@ -262,7 +249,7 @@ bool LocalSocketConnection::Flush()
continue;
}

if (sent < 0 && WouldBlock(WSAGetLastError()))
if (sent < 0 && NonBlockingSocket::WouldBlock(WSAGetLastError()))
{
// Peer is not reading yet; the rest goes out on a later poll.
return true;
Expand Down Expand Up @@ -395,7 +382,7 @@ bool SomethingIsListening(const std::string& path)
// very case this probe detects. If the mode cannot be changed the probe
// is abandoned rather than run blocking: not detecting a second client is
// better than refusing to start.
if (!SetNonBlocking(probe))
if (!NonBlockingSocket::Enable(probe))
{
closesocket(probe);
return true;
Expand Down Expand Up @@ -529,7 +516,7 @@ bool LocalSocketListener::Listen(const std::string& path, std::string& error)

ApplyOwnerOnlyMode(handle, path);

if (!SetNonBlocking(handle))
if (!NonBlockingSocket::Enable(handle))
{
error = "setting the control socket non-blocking failed: " + DescribeLastSocketError();
closesocket(handle);
Expand Down Expand Up @@ -572,7 +559,7 @@ std::unique_ptr<LocalSocketConnection> LocalSocketListener::Accept()
return nullptr;
}

if (!SetNonBlocking(accepted))
if (!NonBlockingSocket::Enable(accepted))
{
closesocket(accepted);
return nullptr;
Expand Down
28 changes: 28 additions & 0 deletions src/source/Core/Platform/NonBlockingSocket.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Non-blocking socket calls, for Windows and POSIX alike: the local socket
// transport and its tests share them, so both treat "try again later" the
// same way.
#pragma once

#include "Core/Platform/WinSock.h"

namespace Core::Platform::NonBlockingSocket
{
// Switches a socket to non-blocking mode. Returns false when the mode could
// not be changed.
[[nodiscard]] inline bool Enable(SOCKET handle)
{
u_long nonBlocking = 1;
return ioctlsocket(handle, FIONBIO, &nonBlocking) != SOCKET_ERROR;
}

// Whether a call on a non-blocking socket failed only because it would have
// had to wait, and can simply be tried again later.
[[nodiscard]] inline bool WouldBlock(int error)
{
#ifdef _WIN32
return error == WSAEWOULDBLOCK;
#else
return error == EWOULDBLOCK || error == EAGAIN || error == EINTR;
#endif
}
} // namespace Core::Platform::NonBlockingSocket
7 changes: 6 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ if(MSVC)
target_compile_options(mu_test_main PUBLIC /utf-8)
endif()

# Longest one TEST_CASE may run before ctest stops it and reports it as
# failed. The slowest case takes about a second; without a limit, a case that
# hangs holds its CI job until the runner gives up hours later.
set(MU_TEST_CASE_TIMEOUT_SECONDS 120)

# Helper for module CMakeLists. Usage:
# mu_add_test(NAME <test_name> SOURCES a.cpp b.cpp LINK_LIBS lib1 lib2)
# Registers each TEST_CASE inside the binary as its own CTest entry, so
Expand All @@ -47,7 +52,7 @@ function(mu_add_test)
if(MSVC)
target_compile_options(${MAT_NAME} PRIVATE /utf-8)
endif()
doctest_discover_tests(${MAT_NAME})
doctest_discover_tests(${MAT_NAME} PROPERTIES TIMEOUT ${MU_TEST_CASE_TIMEOUT_SECONDS})
endfunction()

find_package(Python3 COMPONENTS Interpreter REQUIRED)
Expand Down
Loading
Loading