Example #1
0
int main(){
  AutoCurrentContext ctxt;
  
  // A context must be initiated before events can be received. Similar to CoreThread
  ctxt->Initiate();
  
  // This creates an proxy object that can fire MyEvent::* events
  AutoFired<MyEvent> eventFirer;
  
  // Inject receiver types into current context
  AutoRequired<FooEvent> foo;
  AutoRequired<BarEvent> bar;
  std::cout << "Foo should be 0: " << foo->getSecret() << std::endl;
  std::cout << "Bar Should be 0: " << bar->getSecret() << std::endl;
  
  // Fire event, this should set m_secret on both foo and bar
  eventFirer(&MyEvent::myFunction)(42);
  std::cout << "Foo should be 42: " << foo->getSecret() << std::endl;
  std::cout << "Bar should be 42: " << bar->getSecret() << std::endl;
  
  // You can also manually fire events on a context with `Invoke`
  // Since the function pointer is to `BarEvent`, `FooEvent` won't receive the event
  ctxt->Invoke(&BarEvent::myFunction)(77);
  std::cout << "Foo should be 42: " << foo->getSecret() << std::endl;
  std::cout << "Bar should be 77: " << bar->getSecret() << std::endl;
}
Example #2
0
TEST_F(BasicThreadTest, ValidateThreadTimes) {
  AutoCurrentContext ctxt;
  ctxt->Initiate();

  static const size_t spinCount = 10000000;
  auto spinsThenQuits = ctxt->Construct<SpinsAndThenQuits>(spinCount);

  // Instantaneous benchmark on the time it takes to decrement the counter value:
  std::chrono::nanoseconds benchmark;
  {
    auto startTime = std::chrono::high_resolution_clock::now();
    for(volatile size_t i = spinCount; i--;);
    benchmark = std::chrono::high_resolution_clock::now() - startTime;
  }

  // By this point, not much should have happened:
  std::chrono::milliseconds kernelTime;
  std::chrono::milliseconds userTime;
  spinsThenQuits->GetThreadTimes(kernelTime, userTime);

  // Kick off the thread and wait for it to exit:
  spinsThenQuits->Continue();
  ASSERT_TRUE(spinsThenQuits->WaitFor(std::chrono::seconds(10))) << "Spin-then-quit test took too long to execute";

  // Thread should not have been able to complete in less time than we completed, by a factor of ten or so at least
  ASSERT_LE(benchmark, spinsThenQuits->m_userTime * 10) <<
    "Reported execution time could not possibly be correct, spin operation took less time to execute than should have been possible with the CPU";
}
Example #3
0
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);
}
Example #4
0
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";
}
Example #5
0
TEST_F(AutoConfig_SliderTest, ConcurrentModification) {
  AutoCurrentContext ctxt;
  auto mgr = ctxt->Inject<SliderManager>();
  auto mic = ctxt->Inject<MonotonicIncreaseChecker>();
  ctxt->Initiate();

  ASSERT_EQ(2U, mgr->all_sliders.size()) << "Slider manager did not find all sliders as expected";

  // Set all integer sliders:
  for (int i = 0; i < 100; i++)
  {
    for (auto& slider : mgr->all_sliders)
      if(sizeof(int) == slider->size)
        slider->assigner(i);
  }

  // Now set boolean sliders, this should cause our class to exit because
  // the only boolean slider is there to cause the thread to exit
  for (auto& slider : mgr->all_sliders)
    if (sizeof(bool) == slider->size)
      slider->assigner(1);

  ASSERT_TRUE(mic->WaitFor(std::chrono::seconds{ 5 })) << "Counter class did not exit in time";
  ASSERT_TRUE(mic->succeeded) << "Values received out-of-order in the sequence checker";
}
Example #6
0
TEST_F(AutoPacketFactoryTest, AddSubscriberTest) {
  AutoCurrentContext ctxt;
  AutoRequired<AutoPacketFactory> factory;
  ctxt->Initiate();

  bool first_called = false;
  bool second_called = false;

  factory->AddSubscriber(AutoFilterDescriptor([&first_called](int) {first_called = true; }));
  {
    std::vector<AutoFilterDescriptor> descs;
    factory->AppendAutoFiltersTo(descs);
    ASSERT_EQ(1UL, descs.size()) << "Expected exactly one AutoFilters after call to AddSubscriber";
  }

  *factory += [&second_called] (int v) {
    second_called = true;
    ASSERT_EQ(101, v) << "Decoration value mismatch";
  };
  {
    std::vector<AutoFilterDescriptor> descs;
    factory->AppendAutoFiltersTo(descs);
    ASSERT_EQ(2UL, descs.size()) << "Expected exactly two AutoFilters on this packet";
  }

  auto packet = factory->NewPacket();

  ASSERT_FALSE(first_called) << "Normal subscriber called too early";
  ASSERT_FALSE(second_called) << "Subscriber added with operator+= called too early";

  packet->DecorateImmediate(int(101));

  ASSERT_TRUE(first_called) << "Normal subscriber never called";
  ASSERT_TRUE(second_called) << "Subscriber added with operator+= never called";
}
Example #7
0
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";
}
Example #8
0
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";
}
Example #9
0
TEST_F(CoreContextTest, InitiateOrder) {
  AutoCurrentContext testCtxt;
  testCtxt->Initiate();
  // Initiate inner to outer
  {
    auto outerCtxt = testCtxt->Create<void>();
    auto middleCtxt = outerCtxt->Create<void>();
    auto innerCtxt = middleCtxt->Create<void>();

    innerCtxt->Initiate();
    middleCtxt->Initiate();
    outerCtxt->Initiate();

    ASSERT_TRUE(outerCtxt->IsRunning()) << "Context not running after begin initiated";
    ASSERT_TRUE(middleCtxt->IsRunning()) << "Context not running after begin initiated";
    ASSERT_TRUE(innerCtxt->IsRunning()) << "Context not running after begin initiated";

    outerCtxt->SignalShutdown(true);
  }

  // Initiate outer to inner
  {
    auto outerCtxt = testCtxt->Create<void>();
    auto middleCtxt = outerCtxt->Create<void>();
    auto innerCtxt = middleCtxt->Create<void>();

    outerCtxt->Initiate();
    middleCtxt->Initiate();
    innerCtxt->Initiate();

    ASSERT_TRUE(outerCtxt->IsRunning()) << "Context not running after begin initiated";
    ASSERT_TRUE(middleCtxt->IsRunning()) << "Context not running after begin initiated";
    ASSERT_TRUE(innerCtxt->IsRunning()) << "Context not running after begin initiated";

    outerCtxt->SignalShutdown(true);
  }

  // Initiate middle, inner, then outer
  {
    auto outerCtxt = testCtxt->Create<void>();
    auto middleCtxt = outerCtxt->Create<void>();
    auto innerCtxt = middleCtxt->Create<void>();

    middleCtxt->Initiate();
    innerCtxt->Initiate();
    outerCtxt->Initiate();

    ASSERT_TRUE(outerCtxt->IsRunning()) << "Context not running after begin initiated";
    ASSERT_TRUE(middleCtxt->IsRunning()) << "Context not running after begin initiated";
    ASSERT_TRUE(innerCtxt->IsRunning()) << "Context not running after begin initiated";

    outerCtxt->SignalShutdown(true);
  }
}
Example #10
0
Benchmark PriorityBoost::CanBoostPriority(void) {
  AutoCurrentContext ctxt;

  // Create two spinners and kick them off at the same time:
  AutoRequired<JustIncrementsANumber<ThreadPriority::BelowNormal>> lower;
  AutoRequired<JustIncrementsANumber<ThreadPriority::Normal>> higher;
  ctxt->Initiate();

#ifdef _MSC_VER
  // We want all of our threads to run on ONE cpu for awhile, and then we want to put it back at exit
  DWORD_PTR originalAffinity, systemAffinity;
  GetProcessAffinityMask(GetCurrentProcess(), &originalAffinity, &systemAffinity);
  SetProcessAffinityMask(GetCurrentProcess(), 1);
  auto onreturn = MakeAtExit([originalAffinity] {
    SetProcessAffinityMask(GetCurrentProcess(), originalAffinity);
  });
#else
  // TODO:  Implement on Unix so that this benchmark is trustworthy
#endif

  // Poke the conditional variable a lot:
  AutoRequired<std::mutex> contended;
  for(size_t i = 100; i--;) {
    // We sleep while holding contention lock to force waiting threads into the sleep queue.  The reason we have to do
    // this is due to the way that mutex is implemented under the hood.  The STL mutex uses a high-frequency variable
    // and attempts to perform a CAS (check-and-set) on this variable.  If it succeeds, the lock is obtained; if it
    // fails, it will put the thread into a non-ready state by calling WaitForSingleObject on Windows or one of the
    // mutex_lock methods on Unix.
    //
    // When a thread can't be run, it's moved from the OS's ready queue to the sleep queue.  The scheduler knows that
    // the thread can be moved back to the ready queue if a particular object is signalled, but in the case of a lock,
    // only one of the threads waiting on the object can actually be moved to the ready queue.  It's at THIS POINT that
    // the operating system consults the thread priority--if only thread can be moved over, then the highest priority
    // thread will wind up in the ready queue every time.
    //
    // Thread priority does _not_ necessarily influence the amount of time the scheduler allocates allocated to a ready
    // thread with respect to other threads of the same process.  This is why we hold the lock for a full millisecond,
    // in order to force the thread over to the sleep queue and ensure that the priority resolution mechanism is
    // directly tested.
    std::lock_guard<std::mutex> lk(*contended);
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  }

  // Need to terminate before we try running a comparison.
  ctxt->SignalTerminate();

  return Benchmark {
    {"Low priority CPU time", std::chrono::nanoseconds{lower->val}},
    {"High priority CPU time", std::chrono::nanoseconds{higher->val}},
  };
}
Example #11
0
TEST_F(CoreContextTest, ChildContextSignalOrder) {
  AutoCurrentContext ctxt;

  AutoCreateContext childCtxt;
  AutoRequired<ChildListener> cl(childCtxt);
  childCtxt->Initiate();

  // Now verify correct ordering:
  bool gotStartObservation = false;
  childCtxt->onRunning += [&] {
    gotStartObservation = cl->gotStart;
  };

  ctxt->Initiate();
  ASSERT_TRUE(gotStartObservation) << "Start observation obtained";
}
Example #12
0
TEST_F(AutoPacketFactoryTest, MultipleInstanceAddition) {
  AutoCurrentContext ctxt;
  AutoRequired<AutoPacketFactory> factory;
  ctxt->Initiate();

  bool ary[2] = {};
  for (size_t i = 0; i < 2; i++)
    *factory += [i, &ary] (int) {
      ary[i] = true;
    };

  auto packet = factory->NewPacket();
  packet->Decorate(101);
  ASSERT_TRUE(ary[0]) << "First of two identically typed AutoFilter lambdas was not called";
  ASSERT_TRUE(ary[1]) << "Second of two identically typed AutoFilter lambdas was not called";
}
Example #13
0
TEST_F(CoreContextTest, InitiateAssertsSignals) {
  AutoCurrentContext outer;

  auto teardown = std::make_shared<bool>(false);
  {
    AutoCreateContext ctxt;
    auto initiated = std::make_shared<bool>(false);
    auto running = std::make_shared<bool>(false);
    auto shutdown = std::make_shared<bool>(false);

    ctxt->onInitiated += [initiated] { *initiated = true; };
    ctxt->onRunning += [running] { *running = true; };
    ctxt->onShutdown += [shutdown] { *shutdown = true; };
    ctxt->onTeardown += [teardown] (const CoreContext&) { *teardown = true; };

    ctxt->Initiate();
    ASSERT_TRUE(*initiated) << "Initiation signal not asserted on context startup";
    ASSERT_FALSE(*running) << "Running signal asserted before the outer context was started";
    ASSERT_FALSE(*shutdown) << "Termination signal asserted prematurely";
    *initiated = false;

    outer->Initiate();
    ASSERT_FALSE(*initiated) << "Initiation signal was redundantly asserted";
    ASSERT_TRUE(*running) << "Running signal not asserted when the outer context was started";
    ASSERT_FALSE(*shutdown) << "Termination signal asserted prematurely";

    *running = false;

    ctxt->Initiate();
    ASSERT_FALSE(*initiated) << "Initiation signal redundantly asserted";
    ASSERT_FALSE(*running) << "Running signal redundantly asserted";
    ASSERT_FALSE(*shutdown) << "Termination signal asserted unexpectedly";

    ctxt->SignalShutdown();
    ASSERT_FALSE(*initiated) << "Initiation signal not asserted during teardown";
    ASSERT_FALSE(*running) << "Running signal asserted improperly on teardown";
    ASSERT_TRUE(*shutdown) << "Termination signal not asserted as expected";

    ASSERT_FALSE(*teardown) << "Teardown handler notified prematurely";
  }
  ASSERT_TRUE(*teardown) << "Teardown handler not correctly notified on context teardown";
}
Example #14
0
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";
}
Example #15
0
TEST_F(CoreContextTest, InitiateMultipleChildren) {
  AutoCurrentContext testCtxt;
  testCtxt->Initiate();
  // Initiate all children
  {
    auto outerCtxt = testCtxt->Create<void>();
    auto child1 = outerCtxt->Create<void>();
    auto child2 = outerCtxt->Create<void>();
    auto child3 = outerCtxt->Create<void>();

    child1->Initiate();
    child2->Initiate();
    child3->Initiate();

    outerCtxt->Initiate();

    ASSERT_TRUE(child1->IsRunning());
    ASSERT_TRUE(child2->IsRunning());
    ASSERT_TRUE(child3->IsRunning());

    outerCtxt->SignalShutdown(true);
  }

  // Don't initiate middle child
  {
    auto outerCtxt = testCtxt->Create<void>();
    auto child1 = outerCtxt->Create<void>();
    auto child2 = outerCtxt->Create<void>();
    auto child3 = outerCtxt->Create<void>();

    child1->Initiate();
    child3->Initiate();

    outerCtxt->Initiate();

    ASSERT_TRUE(child1->IsRunning());
    ASSERT_FALSE(child2->IsInitiated());
    ASSERT_TRUE(child3->IsRunning());

    outerCtxt->SignalShutdown(true);
  }

  // Don't initiate middle child and initiate parent first
  {
    auto outerCtxt = testCtxt->Create<void>();
    auto child1 = outerCtxt->Create<void>();
    auto child2 = outerCtxt->Create<void>();
    auto child3 = outerCtxt->Create<void>();

    outerCtxt->Initiate();

    child1->Initiate();
    child3->Initiate();

    ASSERT_TRUE(child1->IsRunning());
    ASSERT_FALSE(child2->IsInitiated());
    ASSERT_TRUE(child3->IsRunning());

    outerCtxt->SignalShutdown(true);
  }
}