ns-3 Direct Code Execution
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
task-manager.cc
Go to the documentation of this file.
1 #include "task-manager.h"
2 #include "fiber-manager.h"
5 #include "task-scheduler.h"
6 #include "ns3/log.h"
7 #include "ns3/uinteger.h"
8 #include "ns3/enum.h"
9 #include "ns3/simulator.h"
10 #include "ns3/node.h"
11 #include "ns3/node-list.h"
12 #include "dce-manager.h"
13 #include "dce-stdio.h"
14 #include "process.h"
15 #include "utils.h"
16 #include "process-delay-model.h"
17 #include "dce-cxa.h"
18 
19 namespace ns3 {
20 
21 NS_LOG_COMPONENT_DEFINE ("TaskManager");
22 NS_OBJECT_ENSURE_REGISTERED (TaskManager);
23 
24 
25 
26 bool
27 Task::IsActive (void) const
28 {
29  return m_state == Task::ACTIVE;
30 }
31 bool
32 Task::IsRunning (void) const
33 {
34  return m_state == Task::RUNNING;
35 }
36 bool
37 Task::IsBlocked (void) const
38 {
39  return m_state == Task::BLOCKED;
40 }
41 bool
42 Task::IsDead (void) const
43 {
44  return m_state == Task::DEAD;
45 }
46 void
48 {
49  m_extraContext = ctx;
50 }
51 void
52 Task::SetContext (void *ctx)
53 {
54  m_context = ctx;
55 }
56 void *
58 {
59  return m_extraContext;
60 }
61 void *
62 Task::GetContext (void) const
63 {
64  return m_context;
65 }
66 
67 void
68 Task::SetSwitchNotifier (void (*fn)(enum SwitchType, void *), void *context)
69 {
70  m_switchNotifier = fn;
71  m_switchNotifierContext = context;
72 }
73 
75 {
76 }
77 
78 
79 TypeId
81 {
82  static TypeId tid = TypeId ("ns3::TaskManager")
83  .SetParent<Object> ()
84  .AddConstructor<TaskManager> ()
85  .AddAttribute ("DefaultStackSize",
86  "The default size of the stack of every task created by this manager.",
87  UintegerValue (8192),
88  MakeUintegerAccessor (&TaskManager::m_defaultStackSize),
89  MakeUintegerChecker<uint32_t> (4096))
90  .AddAttribute ("FiberManagerType",
91  "The type of FiberManager implementation to use to allocate, "
92  "deallocate and switch among fibers.",
93  TypeId::ATTR_CONSTRUCT,
94  EnumValue (PTHREAD_FIBER_MANAGER),
95  MakeEnumAccessor (&TaskManager::SetFiberManagerType),
96  MakeEnumChecker (PTHREAD_FIBER_MANAGER, "PthreadFiberManager",
97  UCONTEXT_FIBER_MANAGER, "UcontextFiberManager"))
98  ;
99  return tid;
100 }
101 
103  : m_current (0),
104  m_scheduler (0),
105  m_fiberManager (0),
106  m_reSchedule (0),
107  m_disposing (0),
108  m_todoOnMain (0),
109  m_noSignal (0),
110  m_hightask (0)
111 {
112  NS_LOG_FUNCTION (this);
113 }
115 {
116  NS_LOG_FUNCTION (this);
119  delete m_fiberManager;
120 
121  m_mainFiber = 0;
122  m_fiberManager = 0;
123  m_scheduler = 0;
124 }
125 
127 {
128  if (m_disposing)
129  {
130  return;
131  }
132  m_disposing = 1;
133 
134  // Flush every FILEs in every processes.
135  Ptr<DceManager> dceManager = this->GetObject<DceManager> ();
136 
137  if (0 != dceManager)
138  {
139  std::map<uint16_t, Process *> procs = dceManager->GetProcs ();
140  std::map<uint16_t, Process *>::iterator it;
141 
142  for (it = procs.begin (); it != procs.end (); it++)
143  {
144  if (0 != it->second)
145  {
146  gDisposingThreadContext = it->second->threads.back ();
147 
148  // call atexit handler for linux kernel
149  dce___cxa_finalize (0);
150  if (0 != gDisposingThreadContext)
151  {
152  Process *p = it->second;
153  for (std::vector<FILE *>::const_iterator i = p->openStreams.begin ();
154  i != p->openStreams.end (); ++i)
155  {
156  fflush (*i);
157  }
158  }
160  }
161  }
162  }
164 }
165 
166 void
168 {
169  NS_LOG_FUNCTION (this);
170  while (!m_deadTasks.empty ())
171  {
172  Task *task = m_deadTasks.front ();
173  m_deadTasks.pop_front ();
174  NS_LOG_DEBUG ("delete " << task);
175  if (task->m_fiber)
176  {
177  m_fiberManager->Delete (task->m_fiber);
178  }
179  task->m_waitTimer.Cancel ();
180  task->m_fiber = 0;
181  delete task;
182  }
183  m_deadTasks.clear ();
184 }
185 
186 
187 void
188 TaskManager::SetScheduler (Ptr<TaskScheduler> scheduler)
189 {
190  m_scheduler = scheduler;
191 }
192 
193 void
194 TaskManager::SetDelayModel (Ptr<ProcessDelayModel> model)
195 {
196  m_delayModel = model;
197 }
198 
199 Task *
200 TaskManager::Start (void (*fn)(void*), void *context)
201 {
202  return Start (fn, context, m_defaultStackSize);
203 }
204 // At every switch of context this method is called and it runs the signal handlers of pending signals.
205 static void SwitchNotifEatSignal (void)
206 {
207  TaskManager *manager = TaskManager::Current ();
208  if (manager == 0)
209  {
210  return;
211  }
212  if (!manager->CurrentTask ())
213  {
214  return;
215  }
216  Thread *current = Current ();
217  if (0 == current)
218  {
219  return;
220  }
221  if (manager->GetNoSignal ())
222  {
223  return;
224  }
225  UtilsDoSignal () ;
226 }
227 Task *
228 TaskManager::Start (void (*fn)(void*), void *context, uint32_t stackSize)
229 {
230  NS_LOG_FUNCTION (this << fn << context << stackSize);
231  Task *task = new Task ();
232  struct StartTaskContext *ctx = new StartTaskContext ();
233  ctx->function = fn;
234  ctx->context = context;
235  task->m_fiber = m_fiberManager->Create (&TaskManager::Trampoline, ctx, stackSize);
236  NS_LOG_DEBUG ("create " << task << " fiber=" << task->m_fiber);
237  task->m_state = Task::BLOCKED; // must call Wakeup on task later.
238  task->m_context = 0;
239  task->m_extraContext = 0;
240  task->m_switchNotifier = 0;
241  task->m_switchNotifierContext = 0;
242  Wakeup (task);
243  return task;
244 }
245 
246 Task *
248 {
249  Task *clone = new Task ();
250  clone->m_state = Task::BLOCKED; // must call Wakeup on task later.
251  clone->m_context = 0;
252  clone->m_extraContext = 0;
253  clone->m_switchNotifier = 0;
254  clone->m_switchNotifierContext = 0;
255  struct Fiber *cloneFiber = m_fiberManager->Clone (task->m_fiber);
256  NS_LOG_DEBUG ("clone " << clone << " fiber=" << cloneFiber);
257  if (cloneFiber != 0)
258  {
259  // parent.
260  clone->m_fiber = cloneFiber;
261  Wakeup (clone);
262  return clone;
263  }
264  return 0;
265 }
266 
267 void
268 TaskManager::Trampoline (void *context)
269 {
270  struct StartTaskContext *ctx = (struct StartTaskContext *)context;
271  void (*fn)(void*) = ctx->function;
272  void *fn_context = ctx->context;
273  delete ctx;
274  fn (fn_context);
275  NS_FATAL_ERROR ("The user function must not return.");
276 }
277 
278 void
280 {
281  NS_LOG_FUNCTION (this << task);
282  if (m_current == task)
283  {
284  // we ignore Stop on self.
285  return;
286  }
287 
288  // we can delete the task immediately.
289  NS_LOG_DEBUG ("delete " << task << " fiber=" << task->m_fiber);
290  m_scheduler->Dequeue (task);
291  if (task->m_fiber)
292  {
293  m_fiberManager->Delete (task->m_fiber);
294  }
295  task->m_state = Task::DEAD;
296  task->m_waitTimer.Cancel ();
297  task->m_fiber = 0;
298  delete task;
299 }
300 
301 void
303 {
304  NS_LOG_FUNCTION (this << task << task->m_state);
305  if (task->m_state == Task::ACTIVE
306  || task->m_state == Task::RUNNING)
307  {
308  return;
309  }
310  task->m_state = Task::ACTIVE;
311  m_scheduler->Enqueue (task);
312  if ((0 == m_current) && (!m_nextSchedule.IsRunning ()))
313  {
314  m_nextSchedule = Simulator::ScheduleNow (&TaskManager::Schedule, this);
315  }
316 }
317 
318 void
320 {
321  NS_LOG_FUNCTION (this << m_current);
322  NS_ASSERT (m_current != 0);
323  NS_ASSERT (m_current->m_state == Task::RUNNING);
324  Task *current = m_current;
325  current->m_state = Task::BLOCKED;
326  Schedule ();
327 }
328 
329 Time
330 TaskManager::Sleep (Time timeout)
331 {
332  NS_LOG_FUNCTION (this << m_current);
333  NS_ASSERT (m_current != 0);
334  NS_ASSERT (m_current->m_state == Task::RUNNING);
335  Time expectedEnd = Simulator::Now () + timeout;
336  Task *current = m_current;
337  current->m_state = Task::BLOCKED;
338  if (!timeout.IsZero ())
339  {
340  m_waitQueue.push_back (Sleeper (current,timeout));
341  }
342  Schedule ();
343  current->m_waitTimer.Cancel ();
344  if (!timeout.IsZero ()
345  && Simulator::Now () <= expectedEnd)
346  {
347  return expectedEnd - Simulator::Now ();
348  }
349  return Seconds (0.0);
350 }
351 void
353 {
354  NS_LOG_FUNCTION (this << m_current);
355  NS_ASSERT (m_current != 0);
356  NS_ASSERT (m_current->m_state == Task::RUNNING);
357  // re-queue to make sure it will be handled.
359  m_scheduler->Enqueue (m_current);
360  Schedule ();
361 }
362 void
364 {
365  NS_LOG_FUNCTION (this << m_current);
366  NS_ASSERT (m_current != 0);
367  NS_ASSERT (m_current->m_state == Task::RUNNING);
368  Task *current = m_current;
369  current->m_state = Task::DEAD;
370  current->m_waitTimer.Cancel ();
371  m_deadTasks.push_back (current);
372  Schedule ();
373 }
374 
375 void
377 {
378  NS_LOG_FUNCTION (this << task << m_hightask);
379  NS_ASSERT (m_hightask == NULL);
380  m_hightask = task;
381 }
382 void
384 {
385  NS_LOG_FUNCTION (this << task << m_hightask);
386  NS_ASSERT (m_hightask == task);
387  m_hightask = NULL;
388 }
389 
390 Task *
392 {
393  if (m_hightask)
394  {
395  return m_hightask;
396  }
397  return m_current;
398 }
399 TaskManager *
401 {
402  uint32_t nodeId = Simulator::GetContext ();
403  if (nodeId == 0xffffffff)
404  {
405  return 0;
406  }
407  Ptr<Node> node = NodeList::GetNode (nodeId);
408  Ptr<TaskManager> manager = node->GetObject<TaskManager> ();
409  return PeekPointer (manager);
410 }
411 
412 void
414 {
415  NS_LOG_FUNCTION (this);
416  if (m_current == 0)
417  {
418  // we have nothing to schedule from
419  struct Task *next = m_scheduler->PeekNext ();
420  if (next != 0)
421  {
422  // and now, we have something to schedule to.
423  NS_LOG_DEBUG ("Leaving main, entering " << next);
424  m_scheduler->DequeueNext ();
425  m_current = next;
426  NS_ASSERT (next->m_state == Task::ACTIVE);
427  next->m_state = Task::RUNNING;
428  m_delayModel->RecordStart ();
429  if (next->m_switchNotifier != 0)
430  {
432  }
433 again:
434  if (next->m_fiber)
435  {
437  }
438  if (0 != m_todoOnMain)
439  {
440  m_current = next;
441  m_todoOnMain->Invoke ();
442  delete m_todoOnMain;
443  m_todoOnMain = 0;
444  goto again;
445  }
446  if (m_reSchedule)
447  {
448  NS_LOG_DEBUG ("Delayed schedule " << m_reScheduleTime);
449  m_reSchedule = false;
450  if (m_reScheduleTime > Time (0))
451  {
453  }
454  else
455  {
456  Simulator::ScheduleNow (&TaskManager::Schedule, this);
457  }
458  }
459  }
460  else
461  {
462  // but, we have nothing to schedule to.
463  }
464  while (m_waitQueue.size () > 0)
465  {
466  Sleeper s = m_waitQueue.front ();
468  m_waitQueue.pop_front ();
469  }
471  }
472  else
473  {
474  // we have something to schedule from.
475  // but, we have nothing to schedule to so, we go back to the main task.
476  Time delay = m_delayModel->RecordEnd ();
477  struct Task *next = m_scheduler->PeekNext ();
478  NS_LOG_DEBUG ("Leaving " << m_current << ", delay " << delay << " next = " << next << " entering main");
479  if (next != 0)
480  {
481  // but before leaving, we check if we have further processes active, and,
482  // if so, make sure we will schedule them later.
483  m_reSchedule = true;
484  m_reScheduleTime = delay;
485  }
486  struct Fiber *fiber = m_current->m_fiber;
487  if (m_current->m_switchNotifier != 0)
488  {
490  }
491  m_current = 0;
492  if (fiber)
493  {
495  }
496  }
497 }
498 
499 void
501 {
502  NS_LOG_FUNCTION (this << type);
503  switch (type)
504  {
507  break;
510  break;
511  default:
512  NS_ASSERT (false);
513  break;
514  }
517 }
518 
519 
520 void
522 {
523  if (task->m_state == Task::BLOCKED)
524  {
525  Wakeup (task);
526  }
527 }
528 
529 void
531 {
533 }
534 uint32_t
536 {
537  return m_fiberManager->GetStackSize (task->m_fiber);
538 }
539 void
541 {
542  if (m_current == 0)
543  {
544  e->Invoke ();
545  delete e;
546  }
547  else
548  {
549  m_todoOnMain = e;
550  struct Fiber *fiber = m_current->m_fiber;
551  m_current = 0;
552  m_noSignal = true;
554  }
555 }
556 EventId
557 TaskManager::ScheduleMain (Time const &time, EventImpl *e)
558 {
559  EventId ret;
560 
561  ExecOnMain (MakeEvent (&TaskManager::MainSchedule, &ret, time,e));
562 
563  return ret;
564 }
565 void
566 TaskManager::MainSchedule (EventId *res,Time const &time, EventImpl *e)
567 {
568  *res = Simulator::Schedule (time, e);
569 }
570 bool
572 {
573  bool ret = m_noSignal;
574  m_noSignal = false;
575  return ret;
576 }
577 } // namespace ns3