smb2: allocate nothing at all when no SMB server is configured

The pool-locking commit created the backend's bootstrap mutex whenever
the configuration was published, whether or not SMB would ever be
used: one allocation at startup on every HAVE_SMBCLIENT build. Remove
even that.

Intent is precisely expressible: SMB connections use only the
configured server address - never the host embedded in an smb:// URL -
so a publication carrying no server address means SMB cannot function
this session and needs nothing. smb_init_cfg() now creates the
bootstrap lock only when the published configuration carries a server
address. A session with no SMB server configured performs a pointer
store and a handful of scalar copies per publication and allocates
exactly nothing: no mutex, no slot locks, no contexts, no sockets.

Every publication site runs on the main thread (startup, configuration
loads, and now saves), so gated lock creation stays race-free, and the
happens-before argument for the slot locks is unchanged: operations
reach a slot lock only through a handle, handles only exist after
initialization, and initialization runs under the pool lock.

The one flow that needs care is configuring a server through the menu
mid-session. The menu writes land in settings->arrays, which the
published struct aliases, so the backend can see a server address that
no publication has carried yet - and without a publication there is no
lock to serialize on. Operations refuse in that state with a message
saying a configuration save activates SMB, and config_save_file() now
publishes on success exactly like config_load_file() does: the menu
flow reaches a save (manual or the default save-on-exit), which both
refreshes the numeric snapshot and creates the bootstrap lock. Before
the pool-locking work this same flow only half-worked anyway - the
frozen numeric snapshot meant a mid-session server ran with fallback
values until a republication - so the save requirement aligns
activation with the point where the settings become correct at all.

Verified offline before commit: the sanitizer harness gains scenarios
asserting the zero-footprint contract behaviourally - a publication
with an empty server sets up nothing and every operation refuses with
zero connection attempts; a server visible only through the aliased
strings still refuses until a publication carries it, then works after
one does. Full battery clean: ASan+UBSan+LSan and TSan, single- and
multi-threaded with heal chaos, zero contexts and handles alive after
shutdown. Full Makefile build with --enable-builtinsmbclient and
threads, settings table drift check and griffin-order unity compile
all clean.
This commit is contained in:
LibretroAdmin authored and libretroadmin committed 2026-08-10 02:27:03 +02:00
1 parent 3c7447d9d0
commit cea330ebe2
2 files changed
+40 -8

No files matched your search

+12
View File
@@ -9004,6 +9004,18 @@ bool config_save_file(const char *path)
if (path_settings)
free(path_settings);
#ifdef HAVE_SMBCLIENT
/* Publish the SMB client settings on save as well as on load. This
* is what lets SMB configured through the menu take effect in the
* same session: the menu flow reaches a save (manual or
* save-on-exit) and the publication both refreshes the numeric
* snapshot and, now that a server address is configured, creates
* the backend's bootstrap lock. A configuration with no server
* address publishes for free - no lock, no allocation. */
if (ret)
retroarch_smb_init();
#endif
return ret;
}
+28 -8
View File
@@ -219,14 +219,23 @@ static struct smb2_context *smb_slot_heal_if_dead(unsigned slot,
bool smb_init_cfg(const struct smb_settings *new_cfg)
{
#ifdef HAVE_THREADS
/* First publication happens on the main thread during startup,
* before any thread can reach an smb:// path; see the pool
* comment. Only the bootstrap pool lock is created here - one
* mutex, whether or not SMB is ever used. The per-slot locks are
* created under the pool lock on first actual SMB use, sized to
* the configured pool, so a build that never touches an smb://
* path never allocates them. */
if (!smb_pool_lock && !(smb_pool_lock = slock_new()))
/* Publications happen on the main thread (startup, configuration
* loads and saves); see the pool comment. The bootstrap pool lock
* is created only once a publication carries a configured server
* address: SMB connections use only the configured server - never
* the host embedded in an smb:// URL - so with no server
* configured SMB cannot function this session and needs nothing.
* A session with no intention of using SMB therefore allocates
* exactly nothing here. Configuring a server in the menu takes
* effect at the next publication, which the menu flow reaches by
* saving the configuration (manually or via save-on-exit); the
* per-slot locks are created later still, under the pool lock on
* first actual smb:// access, sized to the configured pool. */
if ( new_cfg
&& new_cfg->server_address
&& *new_cfg->server_address
&& !smb_pool_lock
&& !(smb_pool_lock = slock_new()))
return false;
#endif
smb_cfg = new_cfg;
@@ -263,7 +272,18 @@ static bool smb_init(void)
bool ok = true;
if (!SMB_LOCKS_READY())
{
/* A server address is visible through the aliased configuration
* strings but no publication has carried it yet: the SMB
* settings were changed after the last configuration load.
* They take effect at the next publication (configuration save
* or load). */
if (smb_cfg && smb_cfg->server_address && *smb_cfg->server_address)
fprintf(stderr,
"smb_init: SMB configured after startup; save the "
"configuration to activate it\n");
return false;
}
SMB_POOL_LOCK();