class SimulatorImpl : public Object
{
public:
//仿真结束的时候调用
virtual void Destroy () = 0;
//检查是否事件队列空或者仿真结束时间到
virtual bool IsFinished (void) const = 0;
virtual void Stop (void) = 0;
virtual void Stop (Time const &time) = 0;
virtual EventId Schedule (Time const &time, EventImpl *event) = 0;
virtual void ScheduleWithContext (uint32_t context, Time const &time, EventImpl *event) = 0;
virtual EventId ScheduleNow (EventImpl *event) = 0;
virtual EventId ScheduleDestroy (EventImpl *event) = 0;
virtual void Remove (const EventId &id) = 0;
//设置对应时间的calcel bit,使得对应的函数不会被执行。有了remove,为什么还要用这个呢
virtual void Cancel (const EventId &id) = 0;
//检查事件是否执行过或者已经被取消
virtual bool IsExpired (const EventId &id) const = 0;
virtual void Run (void) = 0;
virtual Time Now (void) const = 0;
virtual Time GetDelayLeft (const EventId &id) const = 0;
virtual Time GetMaximumSimulationTime (void) const = 0;
virtual void SetScheduler (ObjectFactory schedulerFactory) = 0;
//不知干嘛用的
virtual uint32_t GetSystemId () const = 0;
virtual uint32_t GetContext (void) const = 0;
};
DefaultSimulatorImpl
想不通的
- 带Context的事件怎么ScheduleNow呢
类型定义
struct EventWithContext {
uint32_t context;
uint64_t timestamp;
EventImpl *event;
};
typedef std::list<struct EventWithContext> EventsWithContext;
typedef std::list<EventId> DestroyEvents;
数据成员
关于m_unscheduledEvents
用于记录已经插入Scheduler的但是没有被Process的事件。当有事件插入到Scheduler的map时该值加1,当有事件被取出执行的时候该值减1.
- 使其值变大的成员函数
- Schedule
- ScheduleNow
- ScheduleWithContext:当系统线程为主线程,直接把事件插入Scheduler的Map中的情况下
- ProcessEventWithContext:在需要将eventsWithContext中的事件(在别的非主线程生成的有Context的事件)插入到Scheduler的map中的时候。
- 使其值变小的成员函数
- ProcessOne
- Remove
关于m_currentContext
- 构造函数中赋值为0xffffffff
- ProcessOne中被设置为Scheduler的Map的队首事件的Context
- 在GetContext()函数中被返回,函数被调用的地方:
- Schedule中,函数返回结果用于设置新事件的Context
- Schedulenow函数类似
那到底是怎么个运行流程呢,假设一直都是主线程。那么是否可以理解:
对于Schedule函数,新Schedule进来的事件的Context设置为与最近一个执行的事件的Context相同。直到新执行的事件的context发生改变或者
关于m_eventsWithContextEmpty
- 构造函数中赋值为true
- 在SchedulerWithContext中,如果系统线程不是main线程,将事件放入m_eventsWithContext,并将m_eventsWithContextEmpty设为false
- 在ProcessEventsWithContext()中,如果m_eventsWithContext非空,则取出其中事件处理,并将该值设为true
EventsWithContext m_eventsWithContext;
bool m_eventsWithContextEmpty;
SystemMutex m_eventsWithContextMutex;
DestroyEvents m_destroyEvents;//存放一些需要在Destroy时执行的事件的EventId,初始化的时候是空的
bool m_stop;
Ptr<Scheduler> m_events;
uint32_t m_uid;
uint32_t m_currentUid;
uint64_t m_currentTs;
uint32_t m_currentContext;
int m_unscheduledEvents;
SystemThread::ThreadId m_main;
};
成员函数
构造函数
DefaultSimulatorImpl::DefaultSimulatorImpl ()
{
m_stop = false;
// uids are allocated from 4.
// uid 0 is "invalid" events
// uid 1 is "now" events
// uid 2 is "destroy" events
m_uid = 4;
// before ::Run is entered, the m_currentUid will be zero
m_currentUid = 0;
m_currentTs = 0;
m_currentContext = 0xffffffff;
m_unscheduledEvents = 0;
m_eventsWithContextEmpty = true;
m_main = SystemThread::Self();
}
DoDispose
while (!m_events->IsEmpty ())
{
Scheduler::Event next = m_events->RemoveNext ();
next.impl->Unref ();
}
m_events = 0;
SimulatorImpl::DoDispose ();//SimulatorImpl中并没有复写DoDispose,因此此处调用的是其基类Object的DoDispose
Destroy
EventImpl::Invoke:如果状态为非取消状态,则调用EventImpl::Notify(实际上就是表征事件的对应函数
想不通的地方:
- 事件到底是在调用SimulatorImpl::ScheduleDestroy时加入到m_destroyEvents的。而m_destroyEvents就是用于存放一些需要在Destroy时执行的事件。
while (!m_destroyEvents.empty ()){
Ptr<EventImpl> ev = m_destroyEvents.front ().PeekEventImpl ();
m_destroyEvents.pop_front ();
if (!ev->IsCancelled ())
ev->Invoke ();
}
ProcessOneEvent
事件有序执行的重要函数,取出scheduler头部的事件并推进仿真时间。
Scheduler::Event next = m_events->RemoveNext ();
m_unscheduledEvents--;
m_currentTs = next.key.m_ts;
m_currentContext = next.key.m_context;
m_currentUid = next.key.m_uid;
next.impl->Invoke ();//调用 next.impl->Notify
next.impl->Unref ();
ProcessEventsWithContext ();
ProcessEventsWithContext
if (m_eventsWithContextEmpty)
return;
// swap queues
EventsWithContext eventsWithContext;
{
CriticalSection cs (m_eventsWithContextMutex);
m_eventsWithContext.swap(eventsWithContext);//交换两个list的内容,而交换之前eventsWithContext是空的
m_eventsWithContextEmpty = true;
}
while (!eventsWithContext.empty ()){
EventWithContext event = eventsWithContext.front ();
eventsWithContext.pop_front ();
Scheduler::Event ev;
ev.impl = event.event;
ev.key.m_ts = m_currentTs + event.timestamp;
ev.key.m_context = event.context;
ev.key.m_uid = m_uid;
m_uid++;
m_unscheduledEvents++;
m_events->Insert (ev);
}
Run
一开是就调用 ProcessEventsWithContext ()到底是什么意思嘛~~
m_main = SystemThread::Self();
ProcessEventsWithContext ();
m_stop = false;
while (!m_events->IsEmpty () && !m_stop) {
ProcessOneEvent ();
}
Schedule
m_uid初始值为4。此后在每一次schedule的时候将其赋给event的key的m_uid,然后自增1,实现了每个事件m_uid的唯一性。
**想不清楚的地方:
为什么insert进来的Event的Context的值是DefaultSimulatorImpl的m_currentContext呢?这个值又是什么时候改变的??**
EventId DefaultSimulatorImpl::Schedule (Time const &time, EventImpl *event)
{
//TimeStep()将uint64_t转换成Time结构
Time tAbsolute = time + TimeStep (m_currentTs);
Scheduler::Event ev;
ev.impl = event;
ev.key.m_ts = (uint64_t) tAbsolute.GetTimeStep ();
ev.key.m_context = GetContext ();
ev.key.m_uid = m_uid;
m_uid++;
m_unscheduledEvents++;
m_events->Insert (ev);
return EventId (event, ev.key.m_ts, ev.key.m_context, ev.key.m_uid);
}
ScheduleWithContext
- 如果是m_main线程的话,直接根据函数创建相应的Event(对应设置先关的Context)然后插入Scheduler的队列中。
- 如果SystemThread不是m_main的话,则生成一个EventWithContext (其中包含时间戳,EventImp,和context)的对象ev。将ev插入m_eventsWithContext中。
- 看不懂的地方:什么时候分别是这两种情况呢??
void DefaultSimulatorImpl::ScheduleWithContext (uint32_t context, Time const &time, EventImpl *event)
{
if (SystemThread::Equals (m_main))
{
Time tAbsolute = time + TimeStep (m_currentTs);
Scheduler::Event ev;
ev.impl = event;
ev.key.m_ts = (uint64_t) tAbsolute.GetTimeStep ();
ev.key.m_context = context;
ev.key.m_uid = m_uid;
m_uid++;
m_unscheduledEvents++;
m_events->Insert (ev);
}
else
{
EventWithContext ev;
ev.context = context;
ev.timestamp = time.GetTimeStep ();
ev.event = event;
{
CriticalSection cs (m_eventsWithContextMutex);
m_eventsWithContext.push_back(ev);
m_eventsWithContextEmpty = false;
}
}
}
ScheduleNow
为什么事件的uid不统一定成1呢?就像ScheduleDestroy那个样子。
Scheduler::Event ev;
ev.impl = event;
ev.key.m_ts = m_currentTs;
ev.key.m_context = GetContext ();
ev.key.m_uid = m_uid;
m_uid++;
m_unscheduledEvents++;
m_events->Insert (ev);
return EventId (event, ev.key.m_ts, ev.key.m_context, ev.key.m_uid);
ScheduleDestroy
Schedule一些需要在调用Simulator::Destroy时执行的事件,IidManager的单例销毁就用了这个
- 在Dstroy时执行的事件的id统一定为2.但是整体计数的m_uid仍需自增
EventId id (Ptr<EventImpl> (event, false), m_currentTs, 0xffffffff, 2);
m_destroyEvents.push_back (id);
m_uid++;
return id;
IsExpired
判断相应的事件是否失效
bool
DefaultSimulatorImpl::IsExpired (const EventId &id) const
- uid为2(Destroy事件)
- 能在m_destroyEvents中找到,则未失效,返回false。
- 其他情况:失效
- uid不为2时满足如下条件即为失效,返回true
- id.PeekEventImpl () == 0
- id.GetTs () < m_currentTs
- id.GetTs () == m_currentTs && id.GetUid () <= m_currentUid
- id.PeekEventImpl ()->IsCancelled ()
该函数被调用的地方:
- GetDelayLeft
if (IsExpired (id)) return TimeStep (0);
- Remove
如果已经失效,则不进行任何操作 - Cancel
if (!IsExpired (id)) id.PeekEventImpl ()->Cancel ();
GetContext
uint32_t DefaultSimulatorImpl::GetContext (void) const{
return m_currentContext;
}