TEST_F(CoreContextTest, TerminatedContextHarmless) { AutoCurrentContext ctxt; ctxt->Initiate(); AutoRequired<TriesToCreateChild>{}; ctxt->SignalShutdown(); ASSERT_THROW(ctxt->Create<void>(), dispatch_aborted_exception) << "An exception should have been thrown when attempting to create a child from a terminated context"; }
int main(){ // The 2 main thread classes in Autowiring are the BasicThread and CoreThread. // Classes that inherit from these types will have thread capabilities // Both start when their enclosing context is 'initiated'. Threads injected // after the context is initiated will start immediatly AutoRequired<MyBasicThread> myBasic; AutoCurrentContext ctxt; ctxt->Initiate(); // myBasic->Run() starts now in its own thread std::this_thread::sleep_for(std::chrono::milliseconds(250)); std::cout << "injecting a CoreThread" << std::endl; // Types inheriting from CoreThread implement a dispatch queue in their 'run()' // function. Lambdas can be appended with operator+= AutoRequired<MyCoreThread> myCore; myCore->AddToQueue(42); myCore->AddToQueue(1337); *myCore += []{ std::cout << "This gets run after '1337'" << std::endl; }; // This should be run before 'myCore' is finished std::cout << "This thread is faster\n"; // This will wait for all outstanding threads to finish before terminating the context ctxt->SignalShutdown(true); }
TEST_F(AutoPacketFactoryTest, AutoPacketStatistics) { // Create a context, fill it up, kick it off: AutoCurrentContext ctxt; AutoRequired<DelaysAutoPacketsOneMS> dapoms; AutoRequired<AutoPacketFactory> factory; ctxt->Initiate(); int numPackets = 20; // Send 20 packets which should all be delayed 1ms for (int i = 0; i < numPackets; ++i) { auto packet = factory->NewPacket(); packet->Decorate(i); } // Shutdown our context, and rundown our factory ctxt->SignalShutdown(); factory->Wait(); // Ensure that the statistics are not too wrong // We delayed each packet by one ms, and our statistics are given in nanoseconds double packetDelay = (double) std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(1)).count(); ASSERT_EQ(numPackets, factory->GetTotalPacketCount()) << "The factory did not get enough packets"; ASSERT_LE(packetDelay, factory->GetMeanPacketLifetime()) << "The mean packet lifetime was less than the delay on each packet"; }
TEST_F(AutoPacketFactoryTest, IsRunningWhilePacketIssued) { AutoCurrentContext ctxt; ctxt->Initiate(); AutoRequired<AutoPacketFactory> factory; auto packet = factory->NewPacket(); ctxt->SignalShutdown(); ASSERT_TRUE(factory->IsRunning()) << "Factory should be considered to be running as long as packets are outstanding"; }
TEST_F(CoreContextTest, AwaitWithShutdown) { AutoCurrentContext x; std::thread killer{ [x] { std::this_thread::sleep_for(std::chrono::milliseconds{ 1 }); x->SignalShutdown(); } }; ASSERT_THROW(x->Await<ChildListener>(), dispatch_aborted_exception) << "A context was terminated during await but did not throw as expected"; killer.join(); }
TEST_F(IPCMessagingTest, SequentialMessageTransmission) { AutoCurrentContext ctxt; std::string ns = GenerateNamespaceName(); // Create client and server AutoConstruct<IPCClient> client(IPCTestScope(), ns.c_str()); AutoConstruct<IPCListener> listener(IPCTestScope(), ns.c_str()); auto val = std::make_shared<std::promise<int>>(); listener->onClientConnected += [&val](const std::shared_ptr<IPCEndpoint>& ep) { AutoCreateContext ctxt; ctxt->Add(ep); auto channel = ep->AcquireChannel(0, IPCEndpoint::Channel::READ_ONLY); int nMessages = 0; while (!ep->IsClosed()) { auto buffers = channel->ReadMessageBuffers(); if (buffers.size() != 4) break; nMessages++; } val->set_value(nMessages); }; auto ep = client->Connect(std::chrono::minutes(1)); ASSERT_NE(nullptr, ep) << " Failed to connect in time"; { auto channel = ep->AcquireChannel(0, IPCEndpoint::Channel::WRITE_ONLY); for (uint32_t i = 0; i < 300; i++) { Message message{ i, i + 1, i + 2, i + 3 }; channel->Write(&message, sizeof(message)); channel->Write(&message, sizeof(message)); channel->Write(&message, sizeof(message)); channel->Write(&message, sizeof(message)); // Indicate that the entire message has been written channel->WriteMessageComplete(); } } // Close connection, shut down context: ep.reset(); ctxt->SignalShutdown(); // Block until gatherer stops: auto f = val->get_future(); ASSERT_EQ(std::future_status::ready, f.wait_for(std::chrono::minutes(1))) << "Server did not receive messages in a timely fashion"; ASSERT_EQ(300, f.get()) << "Not all messages were received as expected"; }
TEST_F(CoreContextTest, AppropriateShutdownInterleave) { // Need both an outer and an inner context AutoCurrentContext ctxtOuter; AutoCreateContext ctxtInner; // Need to inject types at both scopes AutoRequired<ExplicitlyHoldsOutstandingCount> outer(ctxtOuter); AutoRequired<ExplicitlyHoldsOutstandingCount> inner(ctxtInner); // Start both contexts up ctxtOuter->Initiate(); ctxtInner->Initiate(); // Now shut down the outer context. Hand off to an async, we want this to block. std::thread holder{ [ctxtOuter] { ctxtOuter->SignalShutdown(true); } }; auto holderClean = MakeAtExit([&holder] { holder.join(); }); // Need to ensure that both outstanding counters are reset at some point: { auto cleanup = MakeAtExit([&] { outer->Proceed(); inner->Proceed(); }); // Outer entry should have called "stop": auto future = outer->calledStop.get_future(); ASSERT_EQ( std::future_status::ready, future.wait_for(std::chrono::seconds(5)) ) << "Outer scope's OnStop method was incorrectly blocked by a child context member taking a long time to shut down"; } // Both contexts should be stopped now: ASSERT_TRUE(ctxtOuter->Wait(std::chrono::seconds(5))) << "Outer context did not tear down in a timely fashion"; ASSERT_TRUE(ctxtOuter->IsQuiescent()) << "Quiescence not achieved by outer context after shutdown"; ASSERT_TRUE(ctxtInner->Wait(std::chrono::seconds(5))) << "Inner context did not tear down in a timely fashion"; }