/* ** Unlink the chunk at index i from ** whatever list is currently a member of. */ static void memsys3Unlink(int i){ int size, hash; assert( sqlite3_mutex_held(mem.mutex) ); size = mem.aPool[i-1].u.hdr.size; assert( size==mem.aPool[i+size-1].u.hdr.prevSize ); assert( size>=2 ); if( size <= MX_SMALL ){ memsys3UnlinkFromList(i, &mem.aiSmall[size-2]); }else{ hash = size % N_HASH; memsys3UnlinkFromList(i, &mem.aiHash[hash]); } }
/* ** Unlink the chunk at index i from ** whatever list is currently a member of. */ static void memsys3Unlink(u32 i){ u32 size, hash; assert( sqlite4_mutex_held(mem3.mutex) ); assert( (mem3.aPool[i-1].u.hdr.size4x & 1)==0 ); assert( i>=1 ); size = mem3.aPool[i-1].u.hdr.size4x/4; assert( size==mem3.aPool[i+size-1].u.hdr.prevSize ); assert( size>=2 ); if( size <= MX_SMALL ){ memsys3UnlinkFromList(i, &mem3.aiSmall[size-2]); }else{ hash = size % N_HASH; memsys3UnlinkFromList(i, &mem3.aiHash[hash]); } }
/* ** *pRoot is the head of a list of free chunks of the same size ** or same size hash. In other words, *pRoot is an entry in either ** mem3.aiSmall[] or mem3.aiHash[]. ** ** This routine examines all entries on the given list and tries ** to coalesce each entries with adjacent free chunks. ** ** If it sees a chunk that is larger than mem3.iMaster, it replaces ** the current mem3.iMaster with the new larger chunk. In order for ** this mem3.iMaster replacement to work, the master chunk must be ** linked into the hash tables. That is not the normal state of ** affairs, of course. The calling routine must link the master ** chunk before invoking this routine, then must unlink the (possibly ** changed) master chunk once this routine has finished. */ static void memsys3Merge(u32 *pRoot){ u32 iNext, prev, size, i, x; assert( sqlite4_mutex_held(mem3.mutex) ); for(i=*pRoot; i>0; i=iNext){ iNext = mem3.aPool[i].u.list.next; size = mem3.aPool[i-1].u.hdr.size4x; assert( (size&1)==0 ); if( (size&2)==0 ){ memsys3UnlinkFromList(i, pRoot); assert( i > mem3.aPool[i-1].u.hdr.prevSize ); prev = i - mem3.aPool[i-1].u.hdr.prevSize; if( prev==iNext ){ iNext = mem3.aPool[prev].u.list.next; } memsys3Unlink(prev); size = i + size/4 - prev; x = mem3.aPool[prev-1].u.hdr.size4x & 2; mem3.aPool[prev-1].u.hdr.size4x = size*4 | x; mem3.aPool[prev+size-1].u.hdr.prevSize = size; memsys3Link(prev); i = prev; }else{ size /= 4; } if( size>mem3.szMaster ){ mem3.iMaster = i; mem3.szMaster = size; } } }
/* ** *pRoot is the head of a list of free chunks of the same size ** or same size hash. In other words, *pRoot is an entry in either ** mem.aiSmall[] or mem.aiHash[]. ** ** This routine examines all entries on the given list and tries ** to coalesce each entries with adjacent free chunks. ** ** If it sees a chunk that is larger than mem.iMaster, it replaces ** the current mem.iMaster with the new larger chunk. In order for ** this mem.iMaster replacement to work, the master chunk must be ** linked into the hash tables. That is not the normal state of ** affairs, of course. The calling routine must link the master ** chunk before invoking this routine, then must unlink the (possibly ** changed) master chunk once this routine has finished. */ static void memsys3Merge(int *pRoot){ int iNext, prev, size, i; assert( sqlite3_mutex_held(mem.mutex) ); for(i=*pRoot; i>0; i=iNext){ iNext = mem.aPool[i].u.list.next; size = mem.aPool[i-1].u.hdr.size; assert( size>0 ); if( mem.aPool[i-1].u.hdr.prevSize>0 ){ memsys3UnlinkFromList(i, pRoot); prev = i - mem.aPool[i-1].u.hdr.prevSize; assert( prev>=0 ); if( prev==iNext ){ iNext = mem.aPool[prev].u.list.next; } memsys3Unlink(prev); size = i + size - prev; mem.aPool[prev-1].u.hdr.size = size; mem.aPool[prev+size-1].u.hdr.prevSize = size; memsys3Link(prev); i = prev; } if( size>mem.szMaster ){ mem.iMaster = i; mem.szMaster = size; } } }
/* ** Return a block of memory of at least nBytes in size. ** Return NULL if unable. ** ** This function assumes that the necessary mutexes, if any, are ** already held by the caller. Hence "Unsafe". */ static void *memsys3MallocUnsafe(int nByte){ u32 i; u32 nBlock; u32 toFree; assert( sqlite4_mutex_held(mem3.mutex) ); assert( sizeof(Mem3Block)==8 ); if( nByte<=12 ){ nBlock = 2; }else{ nBlock = (nByte + 11)/8; } assert( nBlock>=2 ); /* STEP 1: ** Look for an entry of the correct size in either the small ** chunk table or in the large chunk hash table. This is ** successful most of the time (about 9 times out of 10). */ if( nBlock <= MX_SMALL ){ i = mem3.aiSmall[nBlock-2]; if( i>0 ){ memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]); return memsys3Checkout(i, nBlock); } }else{ int hash = nBlock % N_HASH; for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next){ if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ){ memsys3UnlinkFromList(i, &mem3.aiHash[hash]); return memsys3Checkout(i, nBlock); } } } /* STEP 2: ** Try to satisfy the allocation by carving a piece off of the end ** of the master chunk. This step usually works if step 1 fails. */ if( mem3.szMaster>=nBlock ){ return memsys3FromMaster(nBlock); } /* STEP 3: ** Loop through the entire memory pool. Coalesce adjacent free ** chunks. Recompute the master chunk as the largest free chunk. ** Then try again to satisfy the allocation by carving a piece off ** of the end of the master chunk. This step happens very ** rarely (we hope!) */ for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2){ memsys3OutOfMemory(toFree); if( mem3.iMaster ){ memsys3Link(mem3.iMaster); mem3.iMaster = 0; mem3.szMaster = 0; } for(i=0; i<N_HASH; i++){ memsys3Merge(&mem3.aiHash[i]); } for(i=0; i<MX_SMALL-1; i++){ memsys3Merge(&mem3.aiSmall[i]); } if( mem3.szMaster ){ memsys3Unlink(mem3.iMaster); if( mem3.szMaster>=nBlock ){ return memsys3FromMaster(nBlock); } } } /* If none of the above worked, then we fail. */ return 0; }
//给用户分配n字节的空间,返回该空间的地址。 //该函数返回至少n字节大小的block,没有则返回null。该函数假设所有必要的互斥锁都上了,所以不安全 static void *memsys3MallocUnsafe(int nByte) { u32 i; u32 nBlock; u32 toFree; assert( sqlite3_mutex_held(mem3.mutex) ); //如果不能加锁,则终止程序 assert( sizeof(Mem3Block)==8 ); //若Mem3Block大小为8,继续往下执行 if( nByte<=12 ) { //给nBlock赋值 nBlock = 2; } else { nBlock = (nByte + 11)/8; } assert( nBlock>=2 ); /* STEP 1: ** Look for an entry of the correct size in either the small ** chunk table or in the large chunk hash table. This is ** successful most of the time (about 9 times out of 10). */ //首先在小chunk或者大chunk中寻找正确大小块的入口,一般都会成功 if( nBlock <= MX_SMALL ) { //nBlock小于MX_SMALL,则在小chunk中找 i = mem3.aiSmall[nBlock-2]; if( i>0 ) { memsys3UnlinkFromList(i, &mem3.aiSmall[nBlock-2]); return memsys3Checkout(i, nBlock); //返回找到的满足的chunk } } else { //若nBlock大于MX_SMALL,则在大chunk中找 int hash = nBlock % N_HASH; for(i=mem3.aiHash[hash]; i>0; i=mem3.aPool[i].u.list.next) { if( mem3.aPool[i-1].u.hdr.size4x/4==nBlock ) { memsys3UnlinkFromList(i, &mem3.aiHash[hash]); return memsys3Checkout(i, nBlock); //返回找到的chunk } } } /* STEP 2: ** Try to satisfy the allocation by carving a piece off of the end ** of the master chunk. This step usually works if step 1 fails. */ //尝试从master chunk中分裂出合适的空间,第一步失败才执行 if( mem3.szMaster>=nBlock ) { return memsys3FromMaster(nBlock); //从master chunk中获取chunk } /* STEP 3: ** Loop through the entire memory pool. Coalesce adjacent free ** chunks. Recompute the master chunk as the largest free chunk. ** Then try again to satisfy the allocation by carving a piece off ** of the end of the master chunk. This step happens very ** rarely (we hope!) */ //遍历整个内存池,合并相邻空闲chunk,重新计算主要的chunk大小, //再次尝试从master chunk中分裂出满足分配条件的chunk。前面都不行才执行该步骤。 for(toFree=nBlock*16; toFree<(mem3.nPool*16); toFree *= 2) { //遍历内存池 memsys3OutOfMemory(toFree); //不够分配则释放 if( mem3.iMaster ) { //master chunk存在,将其链接到相应块索引表中 memsys3Link(mem3.iMaster); mem3.iMaster = 0; mem3.szMaster = 0; } for(i=0; i<N_HASH; i++) { memsys3Merge(&mem3.aiHash[i]); //链接相邻空chunk到aiHash中 } for(i=0; i<MX_SMALL-1; i++) { memsys3Merge(&mem3.aiSmall[i]); //链接相邻空chunk到aiSmall中 } if( mem3.szMaster ) { //当前master chunk不为0,则从索引表中断开 memsys3Unlink(mem3.iMaster); if( mem3.szMaster>=nBlock ) { return memsys3FromMaster(nBlock); //返回得到的内存空间 } } } /* If none of the above worked, then we fail. */ return 0; //若上面三步都失败了,那就失败了,返回0 }