fix(shutdown): stop the JSON API before plugins delete their providers

rsGlobalShutDown() stopped the JSON API almost last, after stopPlugins().
A plugin that registered a JsonApiResourceProvider deletes it in its stop(),
but the running restbed service still holds the restbed::Resource objects
that provider returned, and their handlers capture it. Any request served
between stopPlugins() and the fullstop at the end of the function therefore
dereferences freed memory.

The window is not theoretical: everything in between -- UPnP teardown, the
auto-proxy shutdown, all registered service threads, the RsServer tick
thread and the per-peer streamers -- can take tens of seconds, and a web
interface polls throughout.

Move the fullstop to the top of the function. It also keeps an API client
from touching the configuration after ConfigFinalSave(), and it must stay
outside the wasReady branch: retroshare-service and Android start the JSON
API before login, so a shutdown from that state has to stop it too.

Without this, a plugin has to restart the whole JSON API from its stop() to
make deleting its own provider safe, which costs a burst-protection wait and
brings the server back up in the middle of teardown.
This commit is contained in:
jolavillette 2026-08-11 22:55:33 +02:00
parent 339c765235
commit 1c6fa5916d

View File

@ -84,6 +84,21 @@ void RsServer::rsGlobalShutDown()
bool wasReady = coreReady;
coreReady = false;
#ifdef RS_JSONAPI
/* Stop the JSON API before anything else. Plugins delete their
* JsonApiResourceProvider in stopPlugins() below, while the restbed service
* still holds the resources that provider handed out -- their handlers
* capture it, so serving a request in that window dereferences freed
* memory. The window is not small: everything between stopPlugins() and the
* end of this function can take tens of seconds, and a web interface polls
* throughout. Stopping first also keeps an API client from touching the
* configuration after ConfigFinalSave().
*
* Not inside the wasReady branch: retroshare-service and Android start the
* JSON API before login, so a shutdown from that state must stop it too. */
if(rsJsonApi) rsJsonApi->fullstop();
#endif
if(wasReady)
{
/* Close the incoming-connection listener FIRST, before anything else.
@ -123,10 +138,6 @@ void RsServer::rsGlobalShutDown()
* iterating the peer list concurrently. */
if(pqih) pqih->fullstopAllThreads();
#ifdef RS_JSONAPI
if(rsJsonApi) rsJsonApi->fullstop();
#endif
AuthPGP::exit();
// close all databases