CancelPacketList

Files CCore/inc/CancelPacketList.h CCore/src/CancelPacketList.cpp

The class CancelPacketList implements the packet cancellation for packets, stored in some PacketList. It is also a "reference design" for the packet cancellation in a packet processing class.


class CancelPacketList : public Funchor_nocopy
 {
  public:
  
   enum Event
    {
     Event_cancel,
     Event_cancel_on_put,
     
     EventLim
    };
    
   friend const char * GetTextDesc(Event ev); 

   using StatInfo = Counters<Event,EventLim> ;
 
  private:
 
   Mutex mutex;
   
   StatInfo stat;
   
  private:
  
   void cancel(PacketHeader *packet);
   
   PacketFunction function_cancel() { return FunctionOf(this,&CancelPacketList::cancel); }
 
  public:
   
   // constructors
  
   CancelPacketList();
   
   explicit CancelPacketList(TextLabel name);
   
   ~CancelPacketList();
   
   // methods
   
   void getStat(StatInfo &ret);
   
   bool try_put(PacketList &list,PacketHeader *packet);
   
   template <class P>
   bool try_put(PacketList &list,P packet) { return try_put(list,GetPacketHeader(packet)); }
   
   void put(PacketList &list,PacketHeader *packet);
   
   template <class P>
   void put(PacketList &list,P packet) { put(list,GetPacketHeader(packet)); }
   
   PacketHeader * get(PacketList &list);
   
   void get(PacketList &list,PacketList &dst);
   
   void complete(PacketList &list);
 };

The constructors argument name is used to name the internal mutex.

The class counts some events and the event statistic is available through the method getStat().


void CancelPacketList::getStat(StatInfo &ret)
 {
  Mutex::Lock lock(mutex); // mutex protected region
  
  ret=stat;
 }

The class can serve multiple packet lists. To put a packet to the list there are two main methods (and two variants): try_put() and put().

put() is a simple upgrade over try_put():


void CancelPacketList::put(PacketList &list,PacketHeader *packet)
 {
  if( !try_put(list,packet) ) packet->complete(); // if the packet is cancelled, complete it
 }

try_put() puts the packet into the packet list and assigns the cancel function to it. So the packet can be removed from the list upon a cancel request. But if the packet comes in the canceled state (or the cancellation happens during the try_put() processing), the packet is not placed into the list and must be completed (or handled by another way). put() simply complete the packet in this case.


bool CancelPacketList::try_put(PacketList &list,PacketHeader *packet)
 {
  Mutex::Lock lock(mutex); // mutex protected region

  if( packet->setCancelFunction(function_cancel())==Packet_NoCancelFunction )
    {
     packet->pushExt<PacketList *>(&list);
      
     list.put(packet);
       
     return true;
    }
    
  stat.count(Event_cancel_on_put);
  
  return false;
 }

try_put() is working under the mutex protection. It starts with the setting the cancel function to the packet. If the packet is clean, then try_put() extends it with the packet list pointer and put the packet into the list. Now packet has the assigned cancel function and it remembers from which packet list it must be removed.

The cancel function also is working under the mutex protection.


void CancelPacketList::cancel(PacketHeader *packet)
 {
  {
   Mutex::Lock lock(mutex); // mutex protected region
  
   if( PacketList *list=*packet->getExt<PacketList *>() ) list->del(packet);
   
   stat.count(Event_cancel);
  } 
  
  packet->popExt<PacketList *>();
  
  packet->complete();
 }

It extracts the packet list pointer and if it is not null, removes the packet from the packet list. Then in the lock-free context the packet extension is popped and the packet is completed. The packet list pointer may be null, because it is nullified by the method get().

To extract a packet from the list the method get() is used.


PacketHeader * CancelPacketList::get(PacketList &list)
 {
  for(;;)
    {
     Mutex::Lock lock(mutex); // mutex protected region
 
     PacketHeader *packet=list.get();
  
     if( !packet ) return 0;
  
     if( packet->clearCancelFunction()!=Packet_Cancelled ) 
       {
        packet->popExt<PacketList *>();
       
        return packet;
       }
     else
       {
        *packet->getExt<PacketList *>()=0;
       }
    } 
 }

To get a packet, the mutex is locked, then the first packet is extracted from the list. If the list is empty, the method is finished and the null pointer is returned. Otherwise the cancel function is cleared. If the packet was not canceled, the packet extension is popped and the packet is returned. Otherwise the packet extension pointer is nullified and the packet is dropped. That is because the packet originator has already taken the cancel function and going to call it or may did it already. In the last case the cancel function execution will be delayed on the mutex lock and continues as soon as we release the mutex. The packet is extracted from the list, so we have to nullify the packet list pointer to prevent the second attempt to remove the packet from the list. And the packet will be completed by the cancel function.

The last two methods are helpers.

get(PacketList &list,PacketList &dst) moves all not canceled packets from the list to another list for a further processing.

complete() completes all packets from the list. The list becomes empty.