[client] spice: add fail-fast connection option

This commit is contained in:
Geoffrey McRae
2026-08-16 22:29:20 +10:00
parent 209ac4d749
commit 056be1242a
9 changed files with 54 additions and 16 deletions

View File

@@ -189,7 +189,7 @@ set(SOURCES
src/render_queue.c
src/evdev.c
src/transport.c
src/transport_fallback.c
src/fallback.c
src/sw_surface.c
src/input.c

View File

@@ -381,14 +381,16 @@ static bool publishConnection(LG_TransportFallback * fallback,
return published;
}
static bool connectFallback(LG_TransportFallback * fallback)
static LG_TransportStatus connectFallback(LG_TransportFallback * fallback,
bool retrying)
{
LG_TransportInstance transport = { 0 };
if (!lgTransport_create(fallback->transportName, &transport))
{
DEBUG_ERROR("Failed to create fallback transport %s",
fallback->transportName);
return false;
if (!retrying)
DEBUG_ERROR("Failed to create fallback transport %s",
fallback->transportName);
return LG_TRANSPORT_ERROR;
}
LG_LOCK_EXCLUSIVE(fallback->lock);
@@ -401,11 +403,12 @@ static bool connectFallback(LG_TransportFallback * fallback)
if (status != LG_TRANSPORT_OK)
{
if (!atomic_load_explicit(&fallback->stop, memory_order_acquire))
if (!retrying && !atomic_load_explicit(
&fallback->stop, memory_order_acquire))
DEBUG_ERROR("Fallback transport %s failed to connect: %d",
fallback->transportName, status);
cleanupConnection(fallback, false);
return false;
return status;
}
LG_LOCK_EXCLUSIVE(fallback->lock);
@@ -445,7 +448,7 @@ static bool connectFallback(LG_TransportFallback * fallback)
if (reportMismatch && fallback->eventOps.endpointMismatch)
fallback->eventOps.endpointMismatch(
fallback->eventOpaque, primaryUUID, fallbackUUID);
return false;
return LG_TRANSPORT_UNAVAILABLE;
}
notifyConnected(fallback);
@@ -483,18 +486,34 @@ static bool connectFallback(LG_TransportFallback * fallback)
fallback->eventOps.endpointMismatch(
fallback->eventOpaque, primaryUUID, fallbackUUID);
notifyDisconnected(fallback, reportDisconnect);
return true;
return lost ? LG_TRANSPORT_DISCONNECTED : LG_TRANSPORT_OK;
}
static int fallbackThread(void * opaque)
{
LG_TransportFallback * fallback = opaque;
unsigned int retry = RETRY_INITIAL_MS;
bool retrying = false;
while (!atomic_load_explicit(&fallback->stop, memory_order_acquire))
{
if (connectFallback(fallback))
const LG_TransportStatus status = connectFallback(fallback, retrying);
if (status == LG_TRANSPORT_OK)
{
retry = RETRY_INITIAL_MS;
retrying = false;
}
else if (status == LG_TRANSPORT_ERROR)
{
if (!atomic_load_explicit(&fallback->stop, memory_order_acquire) &&
fallback->eventOps.connectFailed)
fallback->eventOps.connectFailed(fallback->eventOpaque);
break;
}
else if (status == LG_TRANSPORT_DISCONNECTED)
break;
else
retrying = true;
if (atomic_load_explicit(&fallback->stop, memory_order_acquire))
break;

View File

@@ -2610,6 +2610,13 @@ static void fallbackLost(void * opaque)
app_setState(APP_STATE_SHUTDOWN);
}
static void fallbackConnectFailed(void * opaque)
{
(void)opaque;
DEBUG_INFO("SPICE fallback server is unavailable");
app_setState(APP_STATE_SHUTDOWN);
}
static void fallbackEndpointMismatch(void * opaque, const uint8_t primary[16],
const uint8_t fallback[16])
{
@@ -2624,6 +2631,7 @@ static void fallbackEndpointMismatch(void * opaque, const uint8_t primary[16],
static const LG_TransportFallbackEventOps fallbackEvents =
{
.connected = fallbackConnected,
.connectFailed = fallbackConnectFailed,
.lost = fallbackLost,
.disconnected = fallbackDisconnected,
.videoStateChanged = fallbackVideoStateChanged,

View File

@@ -32,6 +32,8 @@ typedef struct LG_TransportFallbackEventOps
{
/* The session is valid only for the duration of this callback. */
void (*connected)(void * opaque, const LG_TransportSession * session);
/* Called when the fallback transport cannot establish a session. */
void (*connectFailed)(void * opaque);
/* Called only when a usable session is unexpectedly lost. */
void (*lost)(void * opaque);
void (*disconnected)(void * opaque);

View File

@@ -577,7 +577,7 @@ endforeach()
add_executable(transport-fallback-tests
transport_fallback_test.c
../src/transport_fallback.c
../src/fallback.c
../src/sw_surface.c
)
target_include_directories(transport-fallback-tests PRIVATE

View File

@@ -325,10 +325,7 @@ int spiceSession_thread(void * opaque)
PSStatus status = PS_STATUS_SHUTDOWN;
if (!purespice_connect(&config))
{
DEBUG_ERROR("Failed to connect to SPICE server");
goto done;
}
transport->connected = true;
status = PS_STATUS_RUN;

View File

@@ -62,6 +62,13 @@ static void spiceSetup(void)
.type = OPTION_TYPE_INT,
.value.x_int = 5900
},
{
.module = "spice",
.name = "waitForServer",
.description = "Wait for the SPICE server to become available",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "spice",
.name = "input",
@@ -127,6 +134,7 @@ static bool spiceCreate(LG_Transport ** result)
transport->usbAudioEnabled =
option_get_bool("spice", "usbAudio");
transport->audioDebug = option_get_bool("audio", "debug");
transport->waitForServer = option_get_bool("spice", "waitForServer");
#if !ENABLE_USB_AUDIO
if (transport->audioEnabled && transport->usbAudioEnabled)
@@ -265,9 +273,12 @@ static LG_TransportStatus spiceConnectCancellable(LG_Transport * transport,
if (transport->connectStatus != LG_TRANSPORT_OK)
{
const LG_TransportStatus status = transport->connectStatus;
lgJoinThread(transport->thread, NULL);
transport->thread = NULL;
return transport->connectStatus;
if (status == LG_TRANSPORT_ERROR && transport->waitForServer)
return LG_TRANSPORT_UNAVAILABLE;
return status;
}
*session = transport->session;

View File

@@ -51,6 +51,7 @@ struct LG_Transport
bool playbackEnabled;
bool recordEnabled;
bool audioDebug;
bool waitForServer;
SpiceInput * input;
SpiceClipboard * clipboard;