From a0d11b02096e225d031d1eb42d873fc2edc818e8 Mon Sep 17 00:00:00 2001 From: Craig Dowell Date: Mon, 30 Nov 2009 18:22:10 -0800 Subject: [PATCH] Get emu working again: Add Dix/Llc option, add and use contextual realtime schedule ops, don't refcount realtime simulator impl --- .../apidefs/gcc-ILP32/ns3_module_emu.py | 11 ++ .../apidefs/gcc-ILP32/ns3_module_simulator.py | 8 + .../python/apidefs/gcc-LP64/ns3_module_emu.py | 11 ++ .../apidefs/gcc-LP64/ns3_module_simulator.py | 8 + examples/emulation/emu-udp-echo.cc | 9 +- src/devices/emu/emu-net-device.cc | 155 +++++++++++++----- src/devices/emu/emu-net-device.h | 44 +++++ src/simulator/realtime-simulator-impl.cc | 28 +++- src/simulator/realtime-simulator-impl.h | 2 + 9 files changed, 224 insertions(+), 52 deletions(-) diff --git a/bindings/python/apidefs/gcc-ILP32/ns3_module_emu.py b/bindings/python/apidefs/gcc-ILP32/ns3_module_emu.py index fe6a6f040..b70ee783c 100644 --- a/bindings/python/apidefs/gcc-ILP32/ns3_module_emu.py +++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_emu.py @@ -5,6 +5,8 @@ def register_types(module): ## emu-net-device.h: ns3::EmuNetDevice [class] module.add_class('EmuNetDevice', parent=root_module['ns3::NetDevice']) + ## emu-net-device.h: ns3::EmuNetDevice::EncapsulationMode [enumeration] + module.add_enum('EncapsulationMode', ['ILLEGAL', 'DIX', 'LLC'], outer_class=root_module['ns3::EmuNetDevice']) ## Register a nested module for the namespace Config @@ -115,6 +117,11 @@ def register_Ns3EmuNetDevice_methods(root_module, cls): 'ns3::Ptr< ns3::Channel >', [], is_const=True, is_virtual=True) + ## emu-net-device.h: ns3::EmuNetDevice::EncapsulationMode ns3::EmuNetDevice::GetEncapsulationMode() const [member function] + cls.add_method('GetEncapsulationMode', + 'ns3::EmuNetDevice::EncapsulationMode', + [], + is_const=True) ## emu-net-device.h: uint32_t ns3::EmuNetDevice::GetIfIndex() const [member function] cls.add_method('GetIfIndex', 'uint32_t', @@ -194,6 +201,10 @@ def register_Ns3EmuNetDevice_methods(root_module, cls): cls.add_method('SetDataRate', 'void', [param('ns3::DataRate', 'bps')]) + ## emu-net-device.h: void ns3::EmuNetDevice::SetEncapsulationMode(ns3::EmuNetDevice::EncapsulationMode mode) [member function] + cls.add_method('SetEncapsulationMode', + 'void', + [param('ns3::EmuNetDevice::EncapsulationMode', 'mode')]) ## emu-net-device.h: void ns3::EmuNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/apidefs/gcc-ILP32/ns3_module_simulator.py b/bindings/python/apidefs/gcc-ILP32/ns3_module_simulator.py index a83268dde..9744008e5 100644 --- a/bindings/python/apidefs/gcc-ILP32/ns3_module_simulator.py +++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_simulator.py @@ -1486,6 +1486,14 @@ def register_Ns3RealtimeSimulatorImpl_methods(root_module, cls): cls.add_method('ScheduleRealtimeNow', 'void', [param('ns3::EventImpl *', 'event')]) + ## realtime-simulator-impl.h: void ns3::RealtimeSimulatorImpl::ScheduleRealtimeNowWithContext(uint32_t context, ns3::EventImpl * event) [member function] + cls.add_method('ScheduleRealtimeNowWithContext', + 'void', + [param('uint32_t', 'context'), param('ns3::EventImpl *', 'event')]) + ## realtime-simulator-impl.h: void ns3::RealtimeSimulatorImpl::ScheduleRealtimeWithContext(uint32_t context, ns3::Time const & time, ns3::EventImpl * event) [member function] + cls.add_method('ScheduleRealtimeWithContext', + 'void', + [param('uint32_t', 'context'), param('ns3::Time const &', 'time'), param('ns3::EventImpl *', 'event')]) ## realtime-simulator-impl.h: void ns3::RealtimeSimulatorImpl::ScheduleWithContext(uint32_t context, ns3::Time const & time, ns3::EventImpl * event) [member function] cls.add_method('ScheduleWithContext', 'void', diff --git a/bindings/python/apidefs/gcc-LP64/ns3_module_emu.py b/bindings/python/apidefs/gcc-LP64/ns3_module_emu.py index fe6a6f040..b70ee783c 100644 --- a/bindings/python/apidefs/gcc-LP64/ns3_module_emu.py +++ b/bindings/python/apidefs/gcc-LP64/ns3_module_emu.py @@ -5,6 +5,8 @@ def register_types(module): ## emu-net-device.h: ns3::EmuNetDevice [class] module.add_class('EmuNetDevice', parent=root_module['ns3::NetDevice']) + ## emu-net-device.h: ns3::EmuNetDevice::EncapsulationMode [enumeration] + module.add_enum('EncapsulationMode', ['ILLEGAL', 'DIX', 'LLC'], outer_class=root_module['ns3::EmuNetDevice']) ## Register a nested module for the namespace Config @@ -115,6 +117,11 @@ def register_Ns3EmuNetDevice_methods(root_module, cls): 'ns3::Ptr< ns3::Channel >', [], is_const=True, is_virtual=True) + ## emu-net-device.h: ns3::EmuNetDevice::EncapsulationMode ns3::EmuNetDevice::GetEncapsulationMode() const [member function] + cls.add_method('GetEncapsulationMode', + 'ns3::EmuNetDevice::EncapsulationMode', + [], + is_const=True) ## emu-net-device.h: uint32_t ns3::EmuNetDevice::GetIfIndex() const [member function] cls.add_method('GetIfIndex', 'uint32_t', @@ -194,6 +201,10 @@ def register_Ns3EmuNetDevice_methods(root_module, cls): cls.add_method('SetDataRate', 'void', [param('ns3::DataRate', 'bps')]) + ## emu-net-device.h: void ns3::EmuNetDevice::SetEncapsulationMode(ns3::EmuNetDevice::EncapsulationMode mode) [member function] + cls.add_method('SetEncapsulationMode', + 'void', + [param('ns3::EmuNetDevice::EncapsulationMode', 'mode')]) ## emu-net-device.h: void ns3::EmuNetDevice::SetIfIndex(uint32_t const index) [member function] cls.add_method('SetIfIndex', 'void', diff --git a/bindings/python/apidefs/gcc-LP64/ns3_module_simulator.py b/bindings/python/apidefs/gcc-LP64/ns3_module_simulator.py index a83268dde..9744008e5 100644 --- a/bindings/python/apidefs/gcc-LP64/ns3_module_simulator.py +++ b/bindings/python/apidefs/gcc-LP64/ns3_module_simulator.py @@ -1486,6 +1486,14 @@ def register_Ns3RealtimeSimulatorImpl_methods(root_module, cls): cls.add_method('ScheduleRealtimeNow', 'void', [param('ns3::EventImpl *', 'event')]) + ## realtime-simulator-impl.h: void ns3::RealtimeSimulatorImpl::ScheduleRealtimeNowWithContext(uint32_t context, ns3::EventImpl * event) [member function] + cls.add_method('ScheduleRealtimeNowWithContext', + 'void', + [param('uint32_t', 'context'), param('ns3::EventImpl *', 'event')]) + ## realtime-simulator-impl.h: void ns3::RealtimeSimulatorImpl::ScheduleRealtimeWithContext(uint32_t context, ns3::Time const & time, ns3::EventImpl * event) [member function] + cls.add_method('ScheduleRealtimeWithContext', + 'void', + [param('uint32_t', 'context'), param('ns3::Time const &', 'time'), param('ns3::EventImpl *', 'event')]) ## realtime-simulator-impl.h: void ns3::RealtimeSimulatorImpl::ScheduleWithContext(uint32_t context, ns3::Time const & time, ns3::EventImpl * event) [member function] cls.add_method('ScheduleWithContext', 'void', diff --git a/examples/emulation/emu-udp-echo.cc b/examples/emulation/emu-udp-echo.cc index 0f4a6ac9f..6fe4e5449 100644 --- a/examples/emulation/emu-udp-echo.cc +++ b/examples/emulation/emu-udp-echo.cc @@ -75,6 +75,7 @@ int main (int argc, char *argv[]) { std::string deviceName ("eth1"); + std::string encapMode ("Dix"); uint32_t nNodes = 4; // @@ -83,7 +84,9 @@ main (int argc, char *argv[]) // CommandLine cmd; cmd.AddValue("deviceName", "device name", deviceName); + cmd.AddValue("encapsulationMode", "encapsulation mode of emu device (\"Dix\" [default] or \"Llc\")", encapMode); cmd.AddValue("nNodes", "number of nodes to create (>= 2)", nNodes); + cmd.Parse (argc, argv); GlobalValue::Bind ("SimulatorImplementationType", @@ -110,6 +113,8 @@ main (int argc, char *argv[]) NS_LOG_INFO ("Create channels."); EmuHelper emu; emu.SetAttribute ("DeviceName", StringValue (deviceName)); + emu.SetAttribute ("EncapsulationMode", StringValue (encapMode)); + NetDeviceContainer d = emu.Install (n); // @@ -133,8 +138,8 @@ main (int argc, char *argv[]) // Create a UdpEchoClient application to send UDP datagrams from node zero to node one. // uint32_t packetSize = 1024; - uint32_t maxPacketCount = 1; - Time interPacketInterval = Seconds (1.); + uint32_t maxPacketCount = 20; + Time interPacketInterval = Seconds (0.1); UdpEchoClientHelper client (i.GetAddress (1), 9); client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount)); client.SetAttribute ("Interval", TimeValue (interPacketInterval)); diff --git a/src/devices/emu/emu-net-device.cc b/src/devices/emu/emu-net-device.cc index efcce72db..a96889b4b 100644 --- a/src/devices/emu/emu-net-device.cc +++ b/src/devices/emu/emu-net-device.cc @@ -32,8 +32,8 @@ #include "ns3/trace-source-accessor.h" #include "ns3/channel.h" #include "ns3/system-thread.h" -#include "ns3/realtime-simulator-impl.h" #include "ns3/mac48-address.h" +#include "ns3/enum.h" #include #include @@ -83,6 +83,12 @@ EmuNetDevice::GetTypeId (void) TimeValue (Seconds (0.)), MakeTimeAccessor (&EmuNetDevice::m_tStop), MakeTimeChecker ()) + .AddAttribute ("EncapsulationMode", + "The link-layer encapsulation type to use.", + EnumValue (LLC), + MakeEnumAccessor (&EmuNetDevice::SetEncapsulationMode), + MakeEnumChecker (DIX, "Dix", + LLC, "Llc")) // // Transmit queueing discipline for the device which includes its own set @@ -165,7 +171,6 @@ EmuNetDevice::GetTypeId (void) return tid; } - EmuNetDevice::EmuNetDevice () : m_startEvent (), @@ -178,11 +183,17 @@ EmuNetDevice::EmuNetDevice () m_isMulticast (false) { NS_LOG_FUNCTION (this); + m_packetBuffer = new uint8_t[65536]; Start (m_tStart); } EmuNetDevice::~EmuNetDevice () { + if (m_packetBuffer) + { + delete [] m_packetBuffer; + m_packetBuffer = 0; + } } void @@ -193,6 +204,21 @@ EmuNetDevice::DoDispose() NetDevice::DoDispose (); } +void +EmuNetDevice::SetEncapsulationMode (enum EncapsulationMode mode) +{ + NS_LOG_FUNCTION (mode); + m_encapMode = mode; + NS_LOG_LOGIC ("m_encapMode = " << m_encapMode); +} + +EmuNetDevice::EncapsulationMode +EmuNetDevice::GetEncapsulationMode (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_encapMode; +} + void EmuNetDevice::Start (Time tStart) { @@ -203,6 +229,20 @@ EmuNetDevice::Start (Time tStart) // Simulator::Cancel (m_startEvent); m_startEvent = Simulator::Schedule (tStart, &EmuNetDevice::StartDevice, this); + + // + // We're going to need a pointer to the realtime simulator implementation. + // It's important to remember that access to that implementation may happen + // in a completely different thread than the simulator is running in. We are + // talking about multiple threads here, so it is very, very dangerous to do + // any kind of reference couning on a shared object. So what we are going to + // do is to get a reference to the realtime simulator and then save a raw + // pointer to that implementation for use by the other threads. We must not + // free this pointer or we may delete the simulator out from under us an + // everyone else. + // + Ptr impl = DynamicCast (Simulator::GetImplementation ()); + m_rtImpl = GetPointer (impl); } void @@ -308,6 +348,7 @@ EmuNetDevice::StartDevice (void) // This one is OK to enable at runtime m_isMulticast = true; } + // // Now spin up a read thread to read packets. // @@ -563,6 +604,10 @@ EmuNetDevice::FindCreator (std::string creatorName) locations.push_back ("../../../build/optimized/src/devices/emu/"); locations.push_back ("../../../build/debug/src/devices/emu/"); + // src/devices/emu (or build/debug/examples/emulation) + locations.push_back ("../../../../build/optimized/src/devices/emu/"); + locations.push_back ("../../../../build/debug/src/devices/emu/"); + for (std::list::const_iterator i = locations.begin (); i != locations.end (); ++i) { struct stat st; @@ -632,31 +677,43 @@ EmuNetDevice::ForwardUp (uint8_t *buf, uint32_t len) uint16_t protocol; - // - // If the length/type is less than 1500, it corresponds to a length - // interpretation packet. In this case, it is an 802.3 packet and - // will also have an 802.2 LLC header. If greater than 1500, we - // find the protocol number (Ethernet type) directly. - // - if (header.GetLengthType () <= 1500) + switch (m_encapMode) { - LlcSnapHeader llc; + case LLC: // - // Check to see that the packet is long enough to possibly contain the - // header we want to remove before just naively calling. + // If the length/type is less than 1500, it corresponds to a length + // interpretation packet. In this case, it is an 802.3 packet and + // will also have an 802.2 LLC header. If greater than 1500, we + // find the protocol number (Ethernet type) directly. // - if (packet->GetSize() < llc.GetSerializedSize()) + if (header.GetLengthType () <= 1500) { - m_phyRxDropTrace (originalPacket); - return; - } + LlcSnapHeader llc; + // + // Check to see that the packet is long enough to possibly contain the + // header we want to remove before just naively calling. + // + if (packet->GetSize() < llc.GetSerializedSize()) + { + m_phyRxDropTrace (originalPacket); + return; + } - packet->RemoveHeader (llc); - protocol = llc.GetType (); - } - else - { + packet->RemoveHeader (llc); + protocol = llc.GetType (); + } + else + { + protocol = header.GetLengthType (); + } + break; + + case DIX: protocol = header.GetLengthType (); + break; + + default: + NS_FATAL_ERROR ("invalid encapsulation mode"); } PacketType packetType; @@ -709,13 +766,9 @@ EmuNetDevice::ReadThread (void) { NS_LOG_FUNCTION_NOARGS (); - // - // It's important to remember that we're in a completely different thread than the simulator is running in. We - // need to synchronize with that other thread to get the packet up into ns-3. What we will need to do is to schedule - // a method to forward up the packet using the multithreaded simulator we are most certainly running. However, I just - // said it -- we are talking about two threads here, so it is very, very dangerous to do any kind of reference couning - // on a shared object. Just don't do it. So what we're going to do is to allocate a buffer on the heap and pass that - // buffer into the ns-3 context thread where it will create the packet. + // It's important to remember that we're in a completely different thread than the simulator is running in. + // We are talking about multiple threads here, so it is very, very dangerous to do any kind of reference couning + // on a shared object. // int32_t len = -1; @@ -724,6 +777,10 @@ EmuNetDevice::ReadThread (void) for (;;) { + // + // to avoid any issues with a shared reference counted packet, we allocate a buffer on the heap and pass that + // buffer into the ns-3 context thread where it will create the packet, copy the buffer and then free it. + // uint32_t bufferSize = 65536; uint8_t *buf = (uint8_t *)malloc (bufferSize); if (buf == 0) @@ -743,8 +800,8 @@ EmuNetDevice::ReadThread (void) NS_LOG_INFO ("EmuNetDevice::ReadThread(): Received packet"); NS_LOG_INFO ("EmuNetDevice::ReadThread(): Scheduling handler"); - DynamicCast (Simulator::GetImplementation ())->ScheduleRealtimeNow ( - MakeEvent (&EmuNetDevice::ForwardUp, this, buf, len)); + NS_ASSERT_MSG (m_rtImpl, "EmuNetDevice::ReadThread(): Realtime simulator implementation pointer not set"); + m_rtImpl->ScheduleRealtimeNowWithContext (GetNode ()->GetId (), MakeEvent (&EmuNetDevice::ForwardUp, this, buf, len)); buf = 0; } } @@ -795,19 +852,30 @@ EmuNetDevice::SendFrom (Ptr packet, const Address &src, const Address &d NS_LOG_LOGIC ("Transmit packet from " << source); NS_LOG_LOGIC ("Transmit packet to " << destination); - // - // We've got to pick either DIX (Ethernet) or LLC/SNAP (IEEE 802.3) as a - // packet format. IEEE 802.3 is slightly more formally correct, so we - // go that route. - // - LlcSnapHeader llc; - llc.SetType (protocolNumber); - packet->AddHeader (llc); - EthernetHeader header (false); header.SetSource (source); header.SetDestination (destination); - header.SetLengthType (packet->GetSize ()); + + switch (m_encapMode) + { + case LLC: + { + LlcSnapHeader llc; + llc.SetType (protocolNumber); + packet->AddHeader (llc); + + header.SetLengthType (packet->GetSize ()); + } + break; + + case DIX: + header.SetLengthType (protocolNumber); + break; + + default: + NS_FATAL_ERROR ("invalid encapsulation mode"); + } + packet->AddHeader (header); // @@ -827,7 +895,6 @@ EmuNetDevice::SendFrom (Ptr packet, const Address &src, const Address &d m_promiscSnifferTrace (packet); m_snifferTrace (packet); - struct sockaddr_ll ll; bzero (&ll, sizeof (ll)); @@ -837,8 +904,10 @@ EmuNetDevice::SendFrom (Ptr packet, const Address &src, const Address &d NS_LOG_LOGIC ("calling sendto"); - int32_t rc; - rc = sendto (m_sock, packet->PeekData (), packet->GetSize (), 0, reinterpret_cast (&ll), sizeof (ll)); + NS_ASSERT_MSG (packet->GetSize () <= 65536, "EmuNetDevice::SendFrom(): Packet too big " << packet->GetSize ()); + packet->CopyData (m_packetBuffer, packet->GetSize ()); + + int32_t rc = sendto (m_sock, m_packetBuffer, packet->GetSize (), 0, reinterpret_cast (&ll), sizeof (ll)); NS_LOG_LOGIC ("sendto returns " << rc); return rc == -1 ? false : true; diff --git a/src/devices/emu/emu-net-device.h b/src/devices/emu/emu-net-device.h index 279bd3492..7ec687e76 100644 --- a/src/devices/emu/emu-net-device.h +++ b/src/devices/emu/emu-net-device.h @@ -32,6 +32,7 @@ #include "ns3/ptr.h" #include "ns3/mac48-address.h" #include "ns3/system-thread.h" +#include "ns3/realtime-simulator-impl.h" namespace ns3 { @@ -46,6 +47,15 @@ class EmuNetDevice : public NetDevice public: static TypeId GetTypeId (void); + /** + * Enumeration of the types of packets supported in the class. + */ + enum EncapsulationMode { + ILLEGAL, /**< Encapsulation mode not set */ + DIX, /**< DIX II / Ethernet II packet */ + LLC, /**< 802.2 LLC/SNAP Packet*/ + }; + /** * Construct a EmuNetDevice * @@ -176,6 +186,22 @@ public: virtual bool SupportsSendFrom (void) const; + /** + * Set the encapsulation mode of this device. + * + * \param mode The encapsulation mode of this device. + * + * \see SetFrameSize + */ + void SetEncapsulationMode (EmuNetDevice::EncapsulationMode mode); + + /** + * Get the encapsulation mode of this device. + * + * \returns The encapsulation mode of this device. + */ + EmuNetDevice::EncapsulationMode GetEncapsulationMode (void) const; + private: virtual void DoDispose (void); @@ -444,6 +470,13 @@ private: */ int32_t m_sll_ifindex; + /** + * The type of packet that should be created by the AddHeader + * function and that should be processed by the ProcessHeader + * function. + */ + EncapsulationMode m_encapMode; + /** * Flag indicating whether or not the link is up. In this case, * whether or not the device is connected to a channel. @@ -471,6 +504,17 @@ private: * The unix/linux name of the underlying device (e.g., eth0) */ std::string m_deviceName; + + /** + * A 64K buffer to hold packet data while it is being sent. + */ + uint8_t *m_packetBuffer; + + /** + * A copy of a raw pointer to the required real-time simulator implementation. + * Never free this pointer! + */ + RealtimeSimulatorImpl *m_rtImpl; }; } // namespace ns3 diff --git a/src/simulator/realtime-simulator-impl.cc b/src/simulator/realtime-simulator-impl.cc index 8b6cc68c3..6523053cd 100644 --- a/src/simulator/realtime-simulator-impl.cc +++ b/src/simulator/realtime-simulator-impl.cc @@ -620,10 +620,9 @@ RealtimeSimulatorImpl::Now (void) const // Schedule an event for a _relative_ time in the future. // void -RealtimeSimulatorImpl::ScheduleRealtime (Time const &time, EventImpl *impl) +RealtimeSimulatorImpl::ScheduleRealtimeWithContext (uint32_t context, Time const &time, EventImpl *impl) { - NS_LOG_FUNCTION (time << impl); - + NS_LOG_FUNCTION (context << time << impl); { CriticalSection cs (m_mutex); @@ -639,13 +638,19 @@ RealtimeSimulatorImpl::ScheduleRealtime (Time const &time, EventImpl *impl) m_events->Insert (ev); m_synchronizer->Signal (); } - } void -RealtimeSimulatorImpl::ScheduleRealtimeNow (EventImpl *impl) +RealtimeSimulatorImpl::ScheduleRealtime (Time const &time, EventImpl *impl) { - NS_LOG_FUNCTION_NOARGS (); + NS_LOG_FUNCTION (time << impl); + ScheduleRealtimeWithContext (GetContext (), time, impl); +} + +void +RealtimeSimulatorImpl::ScheduleRealtimeNowWithContext (uint32_t context, EventImpl *impl) +{ + NS_LOG_FUNCTION (context << impl); { CriticalSection cs (m_mutex); @@ -654,11 +659,13 @@ RealtimeSimulatorImpl::ScheduleRealtimeNow (EventImpl *impl) // realtime clock. If we're not, then m_currentTs is were we stopped. // uint64_t ts = m_running ? m_synchronizer->GetCurrentRealtime () : m_currentTs; - NS_ASSERT_MSG (ts >= m_currentTs, "RealtimeSimulatorImpl::ScheduleRealtimeNow(): schedule for time < m_currentTs"); + NS_ASSERT_MSG (ts >= m_currentTs, + "RealtimeSimulatorImpl::ScheduleRealtimeNowWithContext(): schedule for time < m_currentTs"); Scheduler::Event ev; ev.impl = impl; ev.key.m_ts = ts; ev.key.m_uid = m_uid; + ev.key.m_context = context; m_uid++; m_unscheduledEvents++; m_events->Insert (ev); @@ -666,6 +673,13 @@ RealtimeSimulatorImpl::ScheduleRealtimeNow (EventImpl *impl) } } +void +RealtimeSimulatorImpl::ScheduleRealtimeNow (EventImpl *impl) +{ + NS_LOG_FUNCTION (impl); + ScheduleRealtimeNowWithContext (GetContext (), impl); +} + Time RealtimeSimulatorImpl::RealtimeNow (void) const { diff --git a/src/simulator/realtime-simulator-impl.h b/src/simulator/realtime-simulator-impl.h index b7ff62476..a00c275d9 100644 --- a/src/simulator/realtime-simulator-impl.h +++ b/src/simulator/realtime-simulator-impl.h @@ -71,7 +71,9 @@ public: virtual void SetScheduler (ObjectFactory schedulerFactory); virtual uint32_t GetContext (void) const; + void ScheduleRealtimeWithContext (uint32_t context, Time const &time, EventImpl *event); void ScheduleRealtime (Time const &time, EventImpl *event); + void ScheduleRealtimeNowWithContext (uint32_t context, EventImpl *event); void ScheduleRealtimeNow (EventImpl *event); Time RealtimeNow (void) const;