Developers.ThePacketPool
The Packet Pool
The Shareaza code uses something it calls a packet pool to express peer-to-peer networking packets in memory. This system of pools is used for each network, but this document will describe how it works for Gnutella packets.
How the code creates the packet pool
There is just one packet pool for each network. First, here is the line of code at the top of G1Packet.cpp that creates the packet pool for Gnutella packets.
[code Top of G1Packet.cpp] CG1Packet::CG1PacketPool CG1Packet::POOL; </source>
This looks confusing, but is just like declaring a global integer named i with a line at the top like int i;, The type here isn't int, but rather is CG1Packet::CG1PacketPool. This type is defined in G1Packet.h, in the middle of class G1Packet:
[code G1Packet.h] static CG1PacketPool POOL; </source>
POOL is defined as static within the CG1Packet class. This is why the name of the global is CG1Packet::POOL and not just POOL. The type CG1PacketPool is a nested class. This is why it has to be written CG1Packet::CG1PacketPool and not just CG1PacketPool.
Hopefully, all this fancy object oriented C++ is necessary. The packet pool does let Shareaza make a lot of packets quickly and without having to allocate memory each time.
Inheritance of packet pool classes
Packet.h is mostly about the class CPacket, and G1Packet.h is mostly about the class CG1Packet. But, at the bottom of Packet.h is another class: CPacketPool. And, within CG1Packet is a nested class named CG1PacketPool. Just like CG1Packet inherits from CPacket, CG1PacketPool inherits from CPacket. Here is what the four classes do:
CPacket - a packet on a peer-to-peer network CG1Packet - specifically, a Gnutella network packet
CPacketPool - a big list of blank packets we can grab to use quickly CG1PacketPool - the list specalized for Gnutella packets
What the packet pool looks like
class CPacketPool is defined at the bottom of Packet.h. Here is a summary of the member variables:
[code Bottom of Packet.h] class CPacketPool {
CPtrArray m_pPools; CPacket* m_pFree; DWORD m_nFree;
</source>
The first member variable, n_pPools is a CPtrArray. This is a class defined by MFC. It's an array of pointers. If you have a bunch of objects of the same type, you can keep them in a list with this. Just make a CPtrArray, and then store a pointer to each object in it. CPtrArray is MFC's collection class.
So, m_pPools is a list of pointers. But, the pointers don't point to packets. That would be too simple. Instead, each pointer points to an array of 256 packets. The Shareaza code calls an array of 256 packets a single packet pool. That's why the member variable is called Pools. Here's a diagram of what the packet pool looks like:
The packet pool holds packets in a grid, like a matrix. Since the CPtrArray can grow to any size, the pool can hold any number of packets.
Allocating memory is slow. If Shareaza called new CG1Packet each time it needed a new packet, it would run slowly. This is why each allocated block of memory holds 256 packets. With the packet pool design, Shareaza can access a lot of new packets quickly, without having to perform memory allocation.
Code that makes the packet pool
Here's the code that makes a new pool of 256 packets:
[code G1Packet.h] // Takes nSize, the number of CG1Packet objects we want // Sets nPitch to the size of each one, and points pPool at a new array of that many of them inline void CG1Packet::CG1PacketPool::NewPoolImpl(int nSize, CPacket*& pPool, int& nPitch) {
// Set nPitch to the size in bytes of each CG1Packet object nPitch = sizeof(CG1Packet);
// Allocate a new array of nSize CG1Packet objects, and point pPool at it pPool = new CG1Packet[ nSize ];
} </source>
NewPoolImpl creates a new array of 256 CG1Packet objects. The number of array elements is nSize. The code new CG1Packet[ nSize ]; allocates a new array of 256 CG1Packet objects. They are right next to each other in a single big block of allocated memory. The new operator returns a pointer to this block, and the method saves it as pPool.
The code that calls NewPoolImpl needs to know how big each packet in the array is. This is just the size of a CG1Packet object. The method gets the size, and saves it in nPitch, where the caller can read it.
Here's the code that calls NewPoolImpl to make a new pool, and then adds it to the list of them:
<source lang="c"> // Create a new array of 256 packets, called a packet pool, and add it to the list of them void CPacketPool::NewPool() {
// Allocate an array of 256 packets, this is a new packet pool CPacket* pPool = NULL; int nPitch = 0, nSize = 256; NewPoolImpl( // NewPoolImpl allocates an array of packets for this packet pool object nSize, // The number of packets the array will hold, we want 256 pPool, // NewPoolImpl will save a pointer to the allocated array here nPitch ); // NewPoolImpl will save the size of each packet here
// Add the new packet pool to m_pPools, this CPacketPool object's list of them m_pPools.Add( pPool );
</source>
How the linked list of free packets works
Each packet object has a pointer inside it called m_pNext. This lets us link them into a list. In the packet pool object, two member variables have the word free in them:
[code CPacketPool]
CPacket* m_pFree; DWORD m_nFree;
</source>
When Shareaza makes a new packet pool of 256 packets, none of them are in use yet. They are all free. m_pFree points to the last one in the pool. That packet's m_pNext pointer points to the one right before it, which is also free. All the next pointers hop one back, until we get to the first packet in the array. If there is more than one packet pool, it points to the last packet in the previous pool. If this is the first or only pool, it points to NULL.
So, if you create several packet pools and don't use any packets in any of them, they will all be linked together in a big list. The list starts with m_pFree, and continues on through m_pNext pointers until you finally ready NULL. Here's the code in CPacketPool::NewPool that does that:
[code NewPool]
// Link the packets in the new pool so m_pFree points at the last one, they each point to the... BYTE* pBytes = (BYTE*)pPool; // Start the pBytes pointer at the start of our new packet pool while ( nSize-- > 0 ) // Loop 256 times, once for each packet in the new pool { // Link this packet to the one before it pPool = (CPacket*)pBytes; // Point pPool at the new packet pool we just created and added... pBytes += nPitch; // Move pBytes to the next packet in the pool pPool->m_pNext = m_pFree; // Set the next pointer in the first packet in the pool m_pFree = pPool; // Point m_pFree at the next packet m_nFree++; // Record one more packet is linked into the list }
</source>
Here's some code from CG1Neighbour.cpp that shows Shareaza getting a new packet:
[code CG1Neighbour.cpp] CG1Packet* pReply = CG1Packet::New( pPacket->m_nType, 1, &pPacket->m_pGUID ); </source>
The New method could just call new CG1Packet to allocate the new packet. But, that would be slow. Instead, the new packet comes from the packet pool.
Here's the source code for CG1Packet::New:
[code G1Packet.cpp] CG1Packet* CG1Packet::New(int nType, DWORD nTTL, GGUID* pGUID) {
// Get a pointer to a packet in the single global Gnutella packet pool CG1Packet* pPacket = (CG1Packet*)POOL.New(); // Calls CPacketPool::New, defined in Packet.h
</source>
It calls POOL.New(). POOL is the single global packet pool object for Gnutella packets. Control goes to CPacketPool::New. Here is the important code:
[code CPacketPool New in Packet.h] // Remove the last linked packet in the most recently added packet pool from the linked list of... CPacket* pPacket = m_pFree; // Point pPacket at the last packet in the newest pool m_pFree = m_pFree->m_pNext; // Move m_pFree to the second to last packet in the newest pool m_nFree--; // Record that there is one fewer packet linked together in all the... </source>
The code unlinks a packet from the free list, and returns a pointer to it. It also decrements m_nFree, the number of free packets in the pool and linked into the list.
When Shareaza is done with a packet, it isn't deleted. It's just linked back into the list of free packets.