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
13 changes: 13 additions & 0 deletions indra/llmessage/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,19 @@ void LLMessageSystem::enableCircuit(const LLHost &host, bool trusted)
cdp->setTrusted(trusted);
}

void LLMessageSystem::closeCircuit(const LLHost &host)
{
// Sent once, unreliably: the circuit is disabled at once, so nothing
// waits on an ack, and where the packet is lost the other end times the
// circuit out as it would have.
if (mCircuitInfo.findCircuit(host))
{
newMessageFast(_PREHASH_CloseCircuit);
sendMessage(host);
}
disableCircuit(host);
}

void LLMessageSystem::disableCircuit(const LLHost &host)
{
LL_INFOS("Messaging") << "LLMessageSystem::disableCircuit for " << host << LL_ENDL;
Expand Down
4 changes: 4 additions & 0 deletions indra/llmessage/message.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,10 @@ class LLMessageSystem : public LLMessageSenderInterface

void enableCircuit(const LLHost &host, bool trusted);
void disableCircuit(const LLHost &host);
// Tells the other end of the circuit to host that it is closing, with
// CloseCircuit, then disables it here. The other end stops sending on
// it, rather than resending what goes unacked until it times it out.
void closeCircuit(const LLHost &host);

// Use this to establish trust on startup and in response to
// DenyTrustedCircuit.
Expand Down
143 changes: 143 additions & 0 deletions indra/llmessage/tests/message_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,149 @@ namespace
CHECK(response->mStatus == HTTP_NOT_FOUND);
}

// CloseCircuit alone, as the template the viewer reads has it.
const char* const CLOSE_CIRCUIT_TEMPLATE =
"version 2.0\n"
"{\n"
" CloseCircuit Fixed 0xFFFFFFFD NotTrusted Unencoded\n"
"}\n";

// A message template file of the test's own, there while this lives.
class CloseCircuitTemplateFile
{
public:
CloseCircuitTemplateFile(const std::string& path)
: mPath(path)
{
llofstream file(mPath.c_str());
file << CLOSE_CIRCUIT_TEMPLATE;
}

~CloseCircuitTemplateFile()
{
if (LLFile::remove(mPath) != 0)
{
LL_WARNS() << "Could not remove " << mPath << LL_ENDL;
}
}

CloseCircuitTemplateFile(const CloseCircuitTemplateFile&) = delete;
CloseCircuitTemplateFile& operator=(const CloseCircuitTemplateFile&) = delete;

private:
std::string mPath;
};

// gMessageSystem is the system under test while this lives, and what it
// was before once it ends: disableCircuit asks gMessageSystem for the
// circuit's code.
class CloseCircuitSystemScope
{
public:
explicit CloseCircuitSystemScope(LLMessageSystem& system)
: mPrior(gMessageSystem)
{
gMessageSystem = &system;
}

~CloseCircuitSystemScope()
{
gMessageSystem = mPrior;
}

CloseCircuitSystemScope(const CloseCircuitSystemScope&) = delete;
CloseCircuitSystemScope& operator=(const CloseCircuitSystemScope&) = delete;

private:
LLMessageSystem* mPrior;
};

// A socket of the test's own, standing for the other end of a circuit.
class PeerSocket
{
public:
PeerSocket()
{
start_net(mSocket, mPort);
}

~PeerSocket()
{
end_net(mSocket);
}

PeerSocket(const PeerSocket&) = delete;
PeerSocket& operator=(const PeerSocket&) = delete;

bool isOpen() const { return mSocket >= 0; }
LLHost host() const { return LLHost("127.0.0.1", mPort); }

// The next packet to arrive within seconds, or nothing.
std::vector<U8> receive(F32 seconds)
{
char buffer[NET_BUFFER_SIZE];
LLTimer waited;
while (waited.getElapsedTimeF32() < seconds)
{
const S32 size = receive_packet(mSocket, buffer);
if (size > 0)
{
return std::vector<U8>(buffer, buffer + size);
}
ms_sleep(5);
}
return {};
}

private:
S32 mSocket = -1;
int mPort = NET_USE_OS_ASSIGNED_PORT;
};

// The other end of a circuit closed here stops sending on it only where
// it is told so; otherwise it resends what goes unacked until it times
// the circuit out.
TEST_CASE_METHOD(LLMessageSystemTestData, "LLMessageSystem: closeCircuit tells the other end with CloseCircuit", "[LLMessageSystem]")
{
const std::string template_path = mTestConfigDir + mSep + "message_template.msg";
const CloseCircuitTemplateFile message_template(template_path);
LLMessageSystem system(template_path, NET_USE_OS_ASSIGNED_PORT, 1, 0, 0, false, 5.f, 100.f);
REQUIRE(system.isOK());
const CloseCircuitSystemScope scope(system);

PeerSocket peer;
REQUIRE(peer.isOpen());
system.enableCircuit(peer.host(), false);
REQUIRE(system.mCircuitInfo.findCircuit(peer.host()) != nullptr);

system.closeCircuit(peer.host());

// The six-byte header, then CloseCircuit's number as a Fixed
// message's: all four bytes, high first.
const std::vector<U8> packet = peer.receive(1.f);
REQUIRE(packet.size() >= 10);
CHECK(packet[5] == 0);
CHECK(packet[6] == 0xFF);
CHECK(packet[7] == 0xFF);
CHECK(packet[8] == 0xFF);
CHECK(packet[9] == 0xFD);
}

TEST_CASE_METHOD(LLMessageSystemTestData, "LLMessageSystem: closeCircuit sends nothing to a host it has no circuit with", "[LLMessageSystem]")
{
const std::string template_path = mTestConfigDir + mSep + "message_template.msg";
const CloseCircuitTemplateFile message_template(template_path);
LLMessageSystem system(template_path, NET_USE_OS_ASSIGNED_PORT, 1, 0, 0, false, 5.f, 100.f);
REQUIRE(system.isOK());
const CloseCircuitSystemScope scope(system);

PeerSocket peer;
REQUIRE(peer.isOpen());
system.closeCircuit(peer.host());
CHECK(peer.receive(0.25f).empty());
CHECK(system.mCircuitInfo.findCircuit(peer.host()) == nullptr);
}

TEST_CASE_METHOD(LLMessageSystemTestData, "LLMessageSystem: a system whose template failed to load still closes its socket", "[LLMessageSystem]")
// a system whose template failed to load still closes its socket
{
Expand Down
11 changes: 8 additions & 3 deletions indra/newview/alregionretirement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,14 @@ void ALRegionRetirement::finish()
regionp->saveObjectCache();
const F64 saved = LLTimer::getTotalSeconds();
LLWorld::getInstance()->removeRegion(host);
// As DisableSimulator does: what the simulator still sends is dropped by the message system, not handled for a
// region that is gone.
gMessageSystem->disableCircuit(host);
// The circuit is left for the simulator to close with DisableSimulator, as it would without retirement: until then
// what it sends is acked, its pings answered, and its messages for the region that is gone dropped. Where asked,
// it is closed here instead, the simulator told so with CloseCircuit.
static LLCachedControl<bool> close_circuit(gSavedSettings, "AlchemyRetireDepartedRegionsCloseCircuit", false);
if (close_circuit)
{
gMessageSystem->closeCircuit(host);
}
const F64 removed = LLTimer::getTotalSeconds();

LL_INFOS("RegionRetirement") << "Retired " << name << ": " << mObjectCount << " objects retired over " << mFrames
Expand Down
5 changes: 3 additions & 2 deletions indra/newview/alregionretirement.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ class LLViewerRegion;
// After a teleport the region left behind stays until its simulator sends DisableSimulator, about a minute later,
// with its objects, avatars, terrain and probes. Once the arrival has settled, every region the agent is neither in
// nor near is retired: marked so that its simulator creates no new objects, its objects killed a frame's budget at a
// time, then removed, and its circuit closed, as DisableSimulator removes one. One that comes near the view again
// meanwhile is kept, and what was killed of it is asked for again.
// time, then removed. Its circuit is left for its simulator to close with DisableSimulator, unless
// AlchemyRetireDepartedRegionsCloseCircuit asks for it to be closed at once, the simulator told so with CloseCircuit.
// One that comes near the view again meanwhile is kept, and what was killed of it is asked for again.
class ALRegionRetirement
{
public:
Expand Down
11 changes: 11 additions & 0 deletions indra/newview/app_settings/settings_alchemy.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2894,6 +2894,17 @@
<key>Value</key>
<boolean>1</boolean>
</map>
<key>AlchemyRetireDepartedRegionsCloseCircuit</key>
<map>
<key>Comment</key>
<string>Close a retired region's circuit at once, telling its simulator with CloseCircuit, instead of leaving it for the simulator to close when it disconnects the region.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<boolean>0</boolean>
</map>
<key>AlchemyRevokeObjectPerms</key>
<map>
<key>Comment</key>
Expand Down
3 changes: 2 additions & 1 deletion indra/newview/llviewermessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,8 @@ void process_layer_data(LLMessageSystem *mesgsys, void **user_data)

if(!regionp)
{
LL_WARNS() << "Invalid region for layer data." << LL_ENDL;
// Once for each simulator: a retired region's simulator sends on until it disables the circuit.
LL_WARNS_ONCE() << "Invalid region for layer data from " << mesgsys->getSender() << LL_ENDL;
return;
}
S32 size;
Expand Down
6 changes: 4 additions & 2 deletions indra/newview/llviewerobjectlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,8 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,

if (!regionp)
{
LL_WARNS() << "Object update from unknown region! " << region_handle << LL_ENDL;
// Once for each region: a retired region's simulator sends on until it disables the circuit.
LL_WARNS_ONCE() << "Object update from unknown region! " << region_handle << LL_ENDL;
return;
}

Expand Down Expand Up @@ -743,7 +744,8 @@ void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,
LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromHandle(region_handle);
if (!regionp)
{
LL_WARNS() << "Object update from unknown region! " << region_handle << LL_ENDL;
// Once for each region: a retired region's simulator sends on until it disables the circuit.
LL_WARNS_ONCE() << "Object update from unknown region! " << region_handle << LL_ENDL;
return;
}

Expand Down
6 changes: 5 additions & 1 deletion indra/newview/llworld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1094,7 +1094,11 @@ void process_disable_simulator(LLMessageSystem *mesgsys, void **user_data)
LLHost host = mesgsys->getSender();

//LL_INFOS() << "Disabling simulator with message from " << host << LL_ENDL;
LLWorld::getInstance()->removeRegion(host);
// A region retired before its simulator disabled it is gone already, and only its circuit is left.
if (LLWorld::getInstance()->getRegion(host))
{
LLWorld::getInstance()->removeRegion(host);
}

mesgsys->disableCircuit(host);
}
Expand Down
Loading