[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/render_queue.c
src/evdev.c src/evdev.c
src/transport.c src/transport.c
src/transport_fallback.c src/fallback.c
src/sw_surface.c src/sw_surface.c
src/input.c src/input.c

View File

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

View File

@@ -2610,6 +2610,13 @@ static void fallbackLost(void * opaque)
app_setState(APP_STATE_SHUTDOWN); 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], static void fallbackEndpointMismatch(void * opaque, const uint8_t primary[16],
const uint8_t fallback[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 = static const LG_TransportFallbackEventOps fallbackEvents =
{ {
.connected = fallbackConnected, .connected = fallbackConnected,
.connectFailed = fallbackConnectFailed,
.lost = fallbackLost, .lost = fallbackLost,
.disconnected = fallbackDisconnected, .disconnected = fallbackDisconnected,
.videoStateChanged = fallbackVideoStateChanged, .videoStateChanged = fallbackVideoStateChanged,

View File

@@ -32,6 +32,8 @@ typedef struct LG_TransportFallbackEventOps
{ {
/* The session is valid only for the duration of this callback. */ /* The session is valid only for the duration of this callback. */
void (*connected)(void * opaque, const LG_TransportSession * session); 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. */ /* Called only when a usable session is unexpectedly lost. */
void (*lost)(void * opaque); void (*lost)(void * opaque);
void (*disconnected)(void * opaque); void (*disconnected)(void * opaque);

View File

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

View File

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

View File

@@ -62,6 +62,13 @@ static void spiceSetup(void)
.type = OPTION_TYPE_INT, .type = OPTION_TYPE_INT,
.value.x_int = 5900 .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", .module = "spice",
.name = "input", .name = "input",
@@ -127,6 +134,7 @@ static bool spiceCreate(LG_Transport ** result)
transport->usbAudioEnabled = transport->usbAudioEnabled =
option_get_bool("spice", "usbAudio"); option_get_bool("spice", "usbAudio");
transport->audioDebug = option_get_bool("audio", "debug"); transport->audioDebug = option_get_bool("audio", "debug");
transport->waitForServer = option_get_bool("spice", "waitForServer");
#if !ENABLE_USB_AUDIO #if !ENABLE_USB_AUDIO
if (transport->audioEnabled && transport->usbAudioEnabled) if (transport->audioEnabled && transport->usbAudioEnabled)
@@ -265,9 +273,12 @@ static LG_TransportStatus spiceConnectCancellable(LG_Transport * transport,
if (transport->connectStatus != LG_TRANSPORT_OK) if (transport->connectStatus != LG_TRANSPORT_OK)
{ {
const LG_TransportStatus status = transport->connectStatus;
lgJoinThread(transport->thread, NULL); lgJoinThread(transport->thread, NULL);
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; *session = transport->session;

View File

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