it's hard to believe he's gone / were it so easy

This commit is contained in:
Mike Griese 2020-12-18 12:45:29 -06:00
parent e1402d834f
commit f978a9c52c
4 changed files with 278 additions and 10 deletions

View file

@ -66,6 +66,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// TODO:projects/5 Wait on the peasant's PID, and remove them from the
// map if they die. This won't work great in tests though, with fake
// PIDs.
//
// We should trigger a callback. The manager will use this callback as
// an opportunity to start waiting on the new peasant.
return newPeasantsId;
}
@ -99,8 +102,20 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
// - the peasant if it exists in our map, otherwise null
Remoting::IPeasant Monarch::_getPeasant(uint64_t peasantID)
{
auto peasantSearch = _peasants.find(peasantID);
return peasantSearch == _peasants.end() ? nullptr : peasantSearch->second;
try
{
auto peasantSearch = _peasants.find(peasantID);
auto maybeThePeasant = peasantSearch == _peasants.end() ? nullptr : peasantSearch->second;
maybeThePeasant.GetPID();
return maybeThePeasant;
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
// TODO: Remove the peasant from the list of peasants
return nullptr;
}
}
void Monarch::_setMostRecentPeasant(const uint64_t peasantID)

View file

@ -16,11 +16,16 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
WindowManager::WindowManager()
{
_monarchWaitInterrupt.create();
// _peasantListenerInterrupt.create();
// wil::unique_event peasantListenerInterrupt;
// peasantListenerInterrupt.create();
// _peasantHandles.emplace_back(std::move(peasantListenerInterrupt));
// Register with COM as a server for the Monarch class
_registerAsMonarch();
// Instantiate an instance of the Monarch. This may or may not be in-proc!
_createMonarch();
_createMonarchAndCallbacks();
}
WindowManager::~WindowManager()
@ -32,10 +37,15 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
CoRevokeClassObject(_registrationHostClass);
_registrationHostClass = 0;
_monarchWaitInterrupt.SetEvent();
// _peasantListenerInterrupt.SetEvent();
if (_electionThread.joinable())
{
_electionThread.join();
}
// if (_peasantListenerThread.joinable())
// {
// _peasantListenerThread.join();
// }
}
void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args)
@ -93,6 +103,35 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
CLSCTX_LOCAL_SERVER);
}
void WindowManager::_createMonarchAndCallbacks()
{
_createMonarch();
const auto isKing = _areWeTheKing();
if (!isKing)
{
return;
}
// Here, we're the king!
// TODO:MG Add an even handler to the monarch's PeasantAdded event.
// We'll use that callback as a chance to start waiting on the peasant's
// PID. If they die, we'll tell the monarch to remove them from the
// list.
// _peasantHandles.emplace_back(_peasantListenerInterrupt.get());
// _peasantListenerThread = std::thread([this]() {
// bool exitRequested = false;
// while (!exitRequested)
// {
// }
// });
// Wait, don't. Let's just have the monarch try/catch any accesses to
// peasants. If the peasant dies, then it can't get the peasant's
// anything. In that case, _remove it_.
}
bool WindowManager::_areWeTheKing()
{
auto kingPID = _monarch.GetPID();
@ -111,7 +150,7 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
bool WindowManager::_electionNight2020()
{
_createMonarch();
_createMonarchAndCallbacks();
if (_areWeTheKing())
{
return true;
@ -134,7 +173,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
while (!exitRequested)
{
wil::unique_handle hMonarch{ OpenProcess(PROCESS_ALL_ACCESS, FALSE, static_cast<DWORD>(_monarch.GetPID())) };
// TODO:MG
// TODO:MG If we fail to open the monarch, then they don't exist
// anymore! Go straight to an election.
//
// if (hMonarch.get() == nullptr)
// {
// const auto gle = GetLastError();
@ -146,11 +187,8 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
switch (waitResult)
{
case WAIT_OBJECT_0 + 0: // waits[0] was signaled
// printf("THE KING IS \x1b[31mDEAD\x1b[m\n");
// // Return false here - this will trigger us to find the new monarch
// return false;
//
// TODO:MG Connect to the new monarch, which might be us!
// Connect to the new monarch, which might be us!
// If we become the monarch, then we'll return true and exit this thread.
exitRequested = _electionNight2020();
break;
case WAIT_OBJECT_0 + 1: // waits[1] was signaled

View file

@ -24,10 +24,17 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation
winrt::Microsoft::Terminal::Remoting::Peasant _peasant{ nullptr };
wil::unique_event _monarchWaitInterrupt;
// wil::unique_event _peasantListenerInterrupt;
std::thread _electionThread;
// std::thread _peasantListenerThread;
// // NON-OWNING HANDLES
// std::vector<HANDLE> _peasantHandles{};
void _registerAsMonarch();
void _createMonarch();
void _createMonarchAndCallbacks();
bool _areWeTheKing();
winrt::Microsoft::Terminal::Remoting::IPeasant _createOurPeasant();

View file

@ -32,6 +32,22 @@ using namespace winrt::Microsoft::Terminal;
namespace RemotingUnitTests
{
// This is a silly helper struct.
// It will always throw an hresult_error on any of it's methods.
// In the tests, it's hard to emulate a peasant process being totally dead once the Monarch has captured a reference to it. Since everything's in-proc in the tests, we can't decrement the refcount in such a way that the monarch's reference will throw a catchable exception.
// Instead, this class can be used to replace a peasant inside a Monarch, to emulate that peasant process dying. Any time the monarch tries to do something to this peasant, it'll throw an exception.
struct DeadPeasant : implements<DeadPeasant, winrt::Microsoft::Terminal::Remoting::IPeasant>
{
DeadPeasant() = default;
void AssignID(uint64_t /*id*/) { throw winrt::hresult_error{}; };
uint64_t GetID() { throw winrt::hresult_error{}; };
uint64_t GetPID() { throw winrt::hresult_error{}; };
bool ExecuteCommandline(const winrt::Microsoft::Terminal::Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error{}; }
winrt::Microsoft::Terminal::Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error{}; }
TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable);
TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, winrt::Microsoft::Terminal::Remoting::CommandlineArgs);
};
class RemotingTests
{
BEGIN_TEST_CLASS(RemotingTests)
@ -39,13 +55,34 @@ namespace RemotingUnitTests
TEST_METHOD(CreateMonarch);
TEST_METHOD(CreatePeasant);
TEST_METHOD(CreatePeasantWithNew);
TEST_METHOD(AddPeasants);
TEST_METHOD(GetPeasantsByID);
TEST_METHOD(AddPeasantsToNewMonarch);
TEST_METHOD(RemovePeasantFromMonarchWhenFreed);
TEST_CLASS_SETUP(ClassSetup)
{
return true;
}
static void _killPeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID);
};
void RemotingTests::_killPeasant(const com_ptr<Remoting::implementation::Monarch>& m,
const uint64_t peasantID)
{
if (peasantID <= 0)
{
return;
}
com_ptr<DeadPeasant> tombstone;
tombstone.attach(new DeadPeasant());
m->_peasants[peasantID] = *tombstone;
}
void RemotingTests::CreateMonarch()
{
auto m1 = winrt::make_self<Remoting::implementation::Monarch>();
@ -84,4 +121,175 @@ namespace RemotingUnitTests
L"A Peasant with an explicit PID should use the one we provided");
}
void RemotingTests::CreatePeasantWithNew()
{
Log::Comment(L"The same thing as the above test, but with `new` instead of insanity on the stack");
auto p1 = winrt::make_self<Remoting::implementation::Peasant>();
VERIFY_IS_NOT_NULL(p1);
VERIFY_ARE_EQUAL(GetCurrentProcessId(),
p1->GetPID(),
L"A Peasant without an explicit PID should use the current PID");
auto expectedFakePID = 2345u;
com_ptr<Remoting::implementation::Peasant> p2;
VERIFY_IS_NULL(p2);
p2.attach(new Remoting::implementation::Peasant(expectedFakePID));
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(expectedFakePID,
p2->GetPID(),
L"A Peasant with an explicit PID should use the one we provided");
}
void RemotingTests::AddPeasants()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
}
void RemotingTests::GetPeasantsByID()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
auto maybeP1 = m0->_getPeasant(1);
VERIFY_IS_NOT_NULL(maybeP1);
VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID());
auto maybeP2 = m0->_getPeasant(2);
VERIFY_IS_NOT_NULL(maybeP2);
VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID());
}
void RemotingTests::AddPeasantsToNewMonarch()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
const auto monarch3PID = 45678u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
com_ptr<Remoting::implementation::Monarch> m3;
m3.attach(new Remoting::implementation::Monarch(monarch3PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_IS_NOT_NULL(m3);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
m3->AddPeasant(*p1);
m3->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
}
void RemotingTests::RemovePeasantFromMonarchWhenFreed()
{
const auto monarch0PID = 12345u;
const auto peasant1PID = 23456u;
const auto peasant2PID = 34567u;
com_ptr<Remoting::implementation::Monarch> m0;
m0.attach(new Remoting::implementation::Monarch(monarch0PID));
com_ptr<Remoting::implementation::Peasant> p1;
p1.attach(new Remoting::implementation::Peasant(peasant1PID));
com_ptr<Remoting::implementation::Peasant> p2;
p2.attach(new Remoting::implementation::Peasant(peasant2PID));
VERIFY_IS_NOT_NULL(m0);
VERIFY_IS_NOT_NULL(p1);
VERIFY_IS_NOT_NULL(p2);
VERIFY_ARE_EQUAL(0, p1->GetID());
VERIFY_ARE_EQUAL(0, p2->GetID());
m0->AddPeasant(*p1);
m0->AddPeasant(*p2);
VERIFY_ARE_EQUAL(1, p1->GetID());
VERIFY_ARE_EQUAL(2, p2->GetID());
VERIFY_ARE_EQUAL(2u, m0->_peasants.size());
RemotingTests::_killPeasant(m0, p1->GetID());
auto maybeP2 = m0->_getPeasant(2);
VERIFY_IS_NOT_NULL(maybeP2);
VERIFY_ARE_EQUAL(peasant2PID, maybeP2.GetPID());
auto maybeP1 = m0->_getPeasant(1);
// VERIFY_ARE_EQUAL(peasant1PID, maybeP1.GetPID());
VERIFY_IS_NULL(maybeP1);
VERIFY_ARE_EQUAL(1u, m0->_peasants.size());
}
}