A Discrete-Event Network Simulator
API
wifi-mac-queue.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2005, 2009 INRIA
3  * Copyright (c) 2009 MIRKO BANCHI
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
19  * Mirko Banchi <mk.banchi@gmail.com>
20  * Stefano Avallone <stavallo@unina.it>
21  */
22 
23 #include "wifi-mac-queue.h"
24 
26 
27 #include "ns3/simulator.h"
28 
29 #include <functional>
30 #include <optional>
31 
32 namespace ns3
33 {
34 
35 NS_LOG_COMPONENT_DEFINE("WifiMacQueue");
36 
37 NS_OBJECT_ENSURE_REGISTERED(WifiMacQueue);
38 NS_OBJECT_TEMPLATE_CLASS_TWO_DEFINE(Queue, WifiMpdu, WifiMacQueueContainer);
39 
40 TypeId
42 {
43  static TypeId tid =
44  TypeId("ns3::WifiMacQueue")
46  .SetGroupName("Wifi")
47  .AddConstructor<WifiMacQueue>()
48  .AddAttribute("MaxSize",
49  "The max queue size",
50  QueueSizeValue(QueueSize("500p")),
51  MakeQueueSizeAccessor(&QueueBase::SetMaxSize, &QueueBase::GetMaxSize),
52  MakeQueueSizeChecker())
53  .AddAttribute("MaxDelay",
54  "If a packet stays longer than this delay in the queue, it is dropped.",
55  TimeValue(MilliSeconds(500)),
58  .AddTraceSource("Expired",
59  "MPDU dropped because its lifetime expired.",
61  "ns3::WifiMpdu::TracedCallback");
62  return tid;
63 }
64 
66  : m_ac(ac),
67  NS_LOG_TEMPLATE_DEFINE("WifiMacQueue")
68 {
69 }
70 
72 {
74 }
75 
76 void
78 {
79  NS_LOG_FUNCTION(this);
80  m_scheduler = nullptr;
82 }
83 
84 AcIndex
86 {
87  return m_ac;
88 }
89 
92 {
93  NS_ASSERT(mpdu->IsQueued());
94  return mpdu->GetQueueIt(WmqIteratorTag());
95 }
96 
99 {
100  return GetIt(mpdu)->mpdu;
101 }
102 
105 {
106  if (!mpdu->IsQueued())
107  {
108  return nullptr;
109  }
110  if (auto aliasIt = GetIt(mpdu)->inflights.find(linkId);
111  aliasIt != GetIt(mpdu)->inflights.cend())
112  {
113  return aliasIt->second;
114  }
115  return nullptr;
116 }
117 
118 void
120 {
121  NS_LOG_FUNCTION(this);
122 
123  std::list<Ptr<WifiMpdu>> mpdus;
124  auto [first, last] = GetContainer().ExtractExpiredMpdus(queueId);
125 
126  for (auto it = first; it != last; it++)
127  {
128  mpdus.push_back(it->mpdu);
129  }
130  for (const auto& mpdu : mpdus)
131  {
132  // fire the Expired trace
134  }
135  // notify the scheduler
136  if (!mpdus.empty())
137  {
138  m_scheduler->NotifyRemove(m_ac, mpdus);
139  }
140 }
141 
142 void
144 {
145  NS_LOG_FUNCTION(this);
146 
147  std::list<Ptr<WifiMpdu>> mpdus;
148  auto [first, last] = GetContainer().ExtractAllExpiredMpdus();
149 
150  for (auto it = first; it != last; it++)
151  {
152  mpdus.push_back(it->mpdu);
153  }
154  for (const auto& mpdu : mpdus)
155  {
156  // fire the Expired trace
158  }
159  // notify the scheduler
160  if (!mpdus.empty())
161  {
162  m_scheduler->NotifyRemove(m_ac, mpdus);
163  }
164 }
165 
166 void
168 {
169  NS_LOG_FUNCTION(this);
170 
172 
173  auto [first, last] = GetContainer().GetAllExpiredMpdus();
174 
175  for (auto it = first; it != last;)
176  {
177  // the scheduler has been notified and the Expired trace has been fired
178  // when the MPDU was extracted from its queue. The only thing left to do
179  // is to update the Queue base class statistics by calling Queue::DoRemove
180  auto curr = it++;
182  }
183 }
184 
185 bool
187 {
188  NS_ASSERT(item && item->IsQueued());
189  auto it = GetIt(item);
190  if (now > it->expiryTime)
191  {
192  NS_LOG_DEBUG("Removing packet that stayed in the queue for too long (queuing time="
193  << now - it->expiryTime + m_maxDelay << ")");
194  // Trace the expired MPDU first and then remove it from the queue (if still in the queue).
195  // Indeed, the Expired traced source is connected to BlockAckManager::NotifyDiscardedMpdu,
196  // which checks if the expired MPDU is in-flight or is a retransmission to determine
197  // whether a BlockAckReq frame must be sent to advance the recipient window. If the
198  // expired MPDU is removed from the queue before tracing the expiration, it is no longer
199  // in-flight and NotifyDiscardedMpdu wrongfully assumes that a BlockAckReq is not needed.
200  m_traceExpired(item);
201  if (item->IsQueued())
202  {
203  DoRemove(it);
204  }
205  return true;
206  }
207  return false;
208 }
209 
210 void
212 {
213  NS_LOG_FUNCTION(this << scheduler);
214  m_scheduler = scheduler;
215 }
216 
217 void
219 {
220  NS_LOG_FUNCTION(this << delay);
221  m_maxDelay = delay;
222 }
223 
224 Time
226 {
227  return m_maxDelay;
228 }
229 
230 bool
232 {
233  NS_LOG_FUNCTION(this << *item);
234 
235  auto queueId = WifiMacQueueContainer::GetQueueId(item);
236  return Insert(GetContainer().GetQueue(queueId).cend(), item);
237 }
238 
239 bool
240 WifiMacQueue::Insert(ConstIterator pos, Ptr<WifiMpdu> item)
241 {
242  NS_LOG_FUNCTION(this << *item);
244  "WifiMacQueues must be in packet mode");
245 
246  // insert the item if the queue is not full
247  if (QueueBase::GetNPackets() < GetMaxSize().GetValue())
248  {
249  return DoEnqueue(pos, item);
250  }
251 
252  // the queue is full; try to make some room by removing stale packets
253  auto queueId = WifiMacQueueContainer::GetQueueId(item);
254 
255  if (pos != GetContainer().GetQueue(queueId).cend())
256  {
258  "pos must point to an element in the same container queue as item");
259  if (pos->expiryTime <= Simulator::Now())
260  {
261  // the element pointed to by pos is stale and will be removed along with all of
262  // its predecessors; the new item will be enqueued at the front of the queue
263  pos = GetContainer().GetQueue(queueId).cbegin();
264  }
265  }
266 
268 
269  return DoEnqueue(pos, item);
270 }
271 
274 {
275  // An MPDU is dequeued when either is acknowledged or is dropped, hence a Dequeue
276  // method without an argument makes no sense.
277  NS_ABORT_MSG("Not implemented by WifiMacQueue");
278  return nullptr;
279 }
280 
281 void
283 {
284  NS_LOG_FUNCTION(this);
285 
286  std::list<ConstIterator> iterators;
287 
288  for (const auto& mpdu : mpdus)
289  {
290  if (mpdu->IsQueued())
291  {
292  auto it = GetIt(mpdu);
293  NS_ASSERT(it->ac == m_ac);
294  NS_ASSERT(it->mpdu == mpdu->GetOriginal());
295  iterators.emplace_back(it);
296  }
297  }
298 
299  DoDequeue(iterators);
300 }
301 
304 {
305  return Peek(std::nullopt);
306 }
307 
309 WifiMacQueue::Peek(std::optional<uint8_t> linkId) const
310 {
311  NS_LOG_FUNCTION(this);
312 
313  auto queueId = m_scheduler->GetNext(m_ac, linkId);
314 
315  if (!queueId.has_value())
316  {
317  NS_LOG_DEBUG("The queue is empty");
318  return nullptr;
319  }
320 
321  return GetContainer().GetQueue(queueId.value()).cbegin()->mpdu;
322 }
323 
326 {
327  NS_LOG_FUNCTION(this << +tid << dest << item);
328  NS_ABORT_IF(dest.IsGroup());
330  return PeekByQueueId(queueId, item);
331 }
332 
335 {
336  NS_LOG_FUNCTION(this << item);
337  NS_ASSERT(!item || (item->IsQueued() && WifiMacQueueContainer::GetQueueId(item) == queueId));
338 
339  // Remove MPDUs with expired lifetime if we are looking for the first MPDU in the queue
340  if (!item)
341  {
342  ExtractExpiredMpdus(queueId);
343  }
344 
345  auto it = (item ? std::next(GetIt(item)) : GetContainer().GetQueue(queueId).cbegin());
346 
347  if (it == GetContainer().GetQueue(queueId).cend())
348  {
349  NS_LOG_DEBUG("The queue is empty");
350  return nullptr;
351  }
352 
353  return it->mpdu;
354 }
355 
358 {
359  NS_LOG_FUNCTION(this << +linkId << item);
360  NS_ASSERT(!item || item->IsQueued());
361 
362  if (item)
363  {
364  // check if there are other MPDUs in the same container queue as item
365  auto mpdu = PeekByQueueId(WifiMacQueueContainer::GetQueueId(item), item);
366 
367  if (mpdu)
368  {
369  return mpdu;
370  }
371  }
372 
373  std::optional<WifiContainerQueueId> queueId;
374 
375  if (item)
376  {
377  queueId = m_scheduler->GetNext(m_ac, linkId, WifiMacQueueContainer::GetQueueId(item));
378  }
379  else
380  {
381  queueId = m_scheduler->GetNext(m_ac, linkId);
382  }
383 
384  if (!queueId.has_value())
385  {
386  NS_LOG_DEBUG("The queue is empty");
387  return nullptr;
388  }
389 
390  return GetContainer().GetQueue(queueId.value()).cbegin()->mpdu;
391 }
392 
395 {
396  return Remove(Peek());
397 }
398 
401 {
402  NS_LOG_FUNCTION(this << mpdu);
403  NS_ASSERT(mpdu && mpdu->IsQueued());
404  auto it = GetIt(mpdu);
405  NS_ASSERT(it->ac == m_ac);
406  NS_ASSERT(it->mpdu == mpdu->GetOriginal());
407 
408  return DoRemove(it);
409 }
410 
411 void
413 {
414  NS_LOG_FUNCTION(this);
415 
416  // there may be some expired MPDUs in the container queue storing MPDUs with expired lifetime,
417  // which will not be flushed by the Flush() method of the base class.
420 }
421 
422 void
424 {
425  NS_LOG_FUNCTION(this << *currentItem << *newItem);
426  NS_ASSERT(currentItem->IsQueued());
427  auto currentIt = GetIt(currentItem);
428  NS_ASSERT(currentIt->ac == m_ac);
429  NS_ASSERT(currentIt->mpdu == currentItem->GetOriginal());
430  NS_ASSERT(!newItem->IsQueued());
431 
432  Time expiryTime = currentIt->expiryTime;
433  auto pos = std::next(currentIt);
434  DoDequeue({currentIt});
435  bool ret = Insert(pos, newItem);
436  GetIt(newItem)->expiryTime = expiryTime;
437  // The size of a WifiMacQueue is measured as number of packets. We dequeued
438  // one packet, so there is certainly room for inserting one packet
439  NS_ABORT_IF(!ret);
440 }
441 
442 uint32_t
444 {
445  return GetContainer().GetQueue(queueId).size();
446 }
447 
448 uint32_t
450 {
451  return GetContainer().GetNBytes(queueId);
452 }
453 
454 bool
455 WifiMacQueue::DoEnqueue(ConstIterator pos, Ptr<WifiMpdu> item)
456 {
457  NS_LOG_FUNCTION(this << *item);
458 
459  auto currSize = GetMaxSize();
460  // control frames should not consume room in the MAC queue, so increase queue size
461  // if we are trying to enqueue a control frame
462  if (item->GetHeader().IsCtl())
463  {
464  SetMaxSize(currSize + item);
465  }
466  auto mpdu = m_scheduler->HasToDropBeforeEnqueue(m_ac, item);
467 
468  if (mpdu == item)
469  {
470  // the given item must be dropped
471  SetMaxSize(currSize);
472  return false;
473  }
474 
475  auto queueId = WifiMacQueueContainer::GetQueueId(item);
476  if (pos != GetContainer().GetQueue(queueId).cend() && mpdu && pos->mpdu == mpdu->GetOriginal())
477  {
478  // the element pointed to by pos must be dropped; update insert position
479  pos = std::next(pos);
480  }
481  if (mpdu)
482  {
483  DoRemove(GetIt(mpdu));
484  }
485 
486  Iterator ret;
488  {
489  // set item's information about its position in the queue
490  item->SetQueueIt(ret, {});
491  ret->ac = m_ac;
492  ret->expiryTime = item->GetHeader().IsCtl() ? Time::Max() : Simulator::Now() + m_maxDelay;
493  WmqIteratorTag tag;
494  ret->deleter = [tag](auto mpdu) { mpdu->SetQueueIt(std::nullopt, tag); };
495 
496  m_scheduler->NotifyEnqueue(m_ac, item);
497  return true;
498  }
499  SetMaxSize(currSize);
500  return false;
501 }
502 
503 void
504 WifiMacQueue::DoDequeue(const std::list<ConstIterator>& iterators)
505 {
506  NS_LOG_FUNCTION(this);
507 
508  std::list<Ptr<WifiMpdu>> items;
509 
510  // First, dequeue all the items
511  for (auto& it : iterators)
512  {
514  {
515  items.push_back(item);
516  if (item->GetHeader().IsCtl())
517  {
518  SetMaxSize(GetMaxSize() - item);
519  }
520  }
521  }
522 
523  // Then, notify the scheduler
524  if (!items.empty())
525  {
526  m_scheduler->NotifyDequeue(m_ac, items);
527  }
528 }
529 
531 WifiMacQueue::DoRemove(ConstIterator pos)
532 {
533  NS_LOG_FUNCTION(this);
534 
536 
537  if (item)
538  {
539  if (item->GetHeader().IsCtl())
540  {
541  SetMaxSize(GetMaxSize() - item);
542  }
543  m_scheduler->NotifyRemove(m_ac, {item});
544  }
545 
546  return item;
547 }
548 
549 } // namespace ns3
an EUI-48 address
Definition: mac48-address.h:46
bool IsGroup() const
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
QueueSize GetMaxSize() const
Definition: queue.cc:217
uint32_t GetNBytes() const
Definition: queue.cc:98
uint32_t GetNPackets() const
Definition: queue.cc:90
void SetMaxSize(QueueSize size)
Set the maximum size of this queue.
Definition: queue.cc:200
Template class for packet Queues.
Definition: queue.h:268
Ptr< Item > DoRemove(ConstIterator pos)
Pull the item to drop from the queue.
Definition: queue.h:576
void Flush()
Flush the queue by calling Remove() on each item enqueued.
Definition: queue.h:608
void DoDispose() override
Destructor implementation.
Definition: queue.h:619
const ns3::WifiMacQueueContainer & GetContainer() const
Get a const reference to the container of queue items.
Definition: queue.h:504
Container::iterator Iterator
Iterator.
Definition: queue.h:321
Class for representing queue sizes.
Definition: queue-size.h:96
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition: simulator.h:605
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
static Time Max()
Maximum representable Time Not to be confused with Max(Time,Time).
Definition: nstime.h:297
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
const ContainerQueue & GetQueue(const WifiContainerQueueId &queueId) const
Get a const reference to the container queue identified by the given QueueId.
static WifiContainerQueueId GetQueueId(Ptr< const WifiMpdu > mpdu)
Return the QueueId identifying the container queue in which the given MPDU is (or is to be) enqueued.
uint32_t GetNBytes(const WifiContainerQueueId &queueId) const
Get the total size of the MPDUs stored in the queue identified by the given QueueId.
std::pair< iterator, iterator > ExtractAllExpiredMpdus() const
Transfer non-inflight MPDUs with expired lifetime in all the container queues to the container queue ...
std::pair< iterator, iterator > GetAllExpiredMpdus() const
Get the range [first, last) of iterators pointing to all the MPDUs queued in the container queue stor...
std::pair< iterator, iterator > ExtractExpiredMpdus(const WifiContainerQueueId &queueId) const
Transfer non-inflight MPDUs with expired lifetime in the container queue identified by the given Queu...
This queue implements the timeout procedure described in (Section 9.19.2.6 "Retransmit procedures" pa...
Time m_maxDelay
Time to live for packets in the queue.
void Replace(Ptr< const WifiMpdu > currentItem, Ptr< WifiMpdu > newItem)
Replace the given current item with the given new item.
Ptr< WifiMpdu > PeekByQueueId(const WifiContainerQueueId &queueId, Ptr< const WifiMpdu > item=nullptr) const
Search and return the first packet present in the container queue identified by the given queue ID.
Ptr< WifiMpdu > Remove() override
Remove the packet in the front of the queue.
Ptr< WifiMacQueueScheduler > m_scheduler
the MAC queue scheduler
AcIndex GetAc() const
Get the Access Category of the packets stored in this queue.
bool Insert(ConstIterator pos, Ptr< WifiMpdu > item)
Enqueue the given Wifi MAC queue item before the given position.
void ExtractExpiredMpdus(const WifiContainerQueueId &queueId) const
Move MPDUs with expired lifetime from the container queue identified by the given queue ID to the con...
bool Enqueue(Ptr< WifiMpdu > item) override
Enqueue the given Wifi MAC queue item at the end of the queue.
Ptr< const WifiMpdu > Peek() const override
Peek the packet in the front of the queue.
Iterator GetIt(Ptr< const WifiMpdu > mpdu) const
bool TtlExceeded(Ptr< const WifiMpdu > item, const Time &now)
Remove the given item if it has been in the queue for too long.
void WipeAllExpiredMpdus()
Remove all MPDUs with expired lifetime from this WifiMacQueue object.
Ptr< WifiMpdu > Dequeue() override
Dequeue the packet in the front of the queue.
void SetScheduler(Ptr< WifiMacQueueScheduler > scheduler)
Set the wifi MAC queue scheduler.
void SetMaxDelay(Time delay)
Set the maximum delay before the packet is discarded.
void DequeueIfQueued(const std::list< Ptr< const WifiMpdu >> &mpdus)
Dequeue the given MPDUs if they are stored in this queue.
void DoDispose() override
Destructor implementation.
~WifiMacQueue() override
Ptr< WifiMpdu > GetAlias(Ptr< const WifiMpdu > mpdu, uint8_t linkId)
Ptr< WifiMpdu > DoRemove(ConstIterator pos)
Wrapper for the DoRemove method provided by the base class that additionally resets the iterator fiel...
Ptr< WifiMpdu > GetOriginal(Ptr< WifiMpdu > mpdu)
Unlike the GetOriginal() method of WifiMpdu, this method returns a non-const pointer to the original ...
WifiMacQueue(AcIndex ac=AC_UNDEF)
Constructor.
Ptr< WifiMpdu > PeekFirstAvailable(uint8_t linkId, Ptr< const WifiMpdu > item=nullptr) const
Return first available packet for transmission on the given link.
TracedCallback< Ptr< const WifiMpdu > > m_traceExpired
Traced callback: fired when a packet is dropped due to lifetime expiration.
bool DoEnqueue(ConstIterator pos, Ptr< WifiMpdu > item)
Wrapper for the DoEnqueue method provided by the base class that additionally sets the iterator field...
AcIndex m_ac
the access category
Ptr< WifiMpdu > PeekByTidAndAddress(uint8_t tid, Mac48Address dest, Ptr< const WifiMpdu > item=nullptr) const
Search and return, if present in the queue, the first packet having the receiver address equal to des...
void Flush()
Flush the queue.
void DoDequeue(const std::list< ConstIterator > &iterators)
Wrapper for the DoDequeue method provided by the base class that additionally resets the iterator fie...
void ExtractAllExpiredMpdus() const
Move MPDUs with expired lifetime from all the container queues to the container queue storing MPDUs w...
static TypeId GetTypeId()
Get the type ID.
Time GetMaxDelay() const
Return the maximum delay before the packet is discarded.
Tag used to allow (only) WifiMacQueue to access the queue iterator stored by a WifiMpdu.
Definition: wifi-mpdu.h:48
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition: abort.h:76
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_TEMPLATE_DEFINE(name)
Initialize a reference to a Log component.
Definition: log.h:236
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_TEMPLATE_CLASS_TWO_DEFINE(type, param1, param2)
Explicitly instantiate a template class with two template parameters and register the resulting insta...
Definition: object-base.h:116
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
@ PACKETS
Use number of packets for queue size.
Definition: queue-size.h:45
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:73
Definition: first.py:1
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1414
std::tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:533
#define list