1 /***************************************************************************
2 * Copyright (C) 2007 by www.databasecache.com *
3 * Contact: praba_tuty@databasecache.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 ***************************************************************************/
21 #include<CatalogTables.h>
23 // sets the size of the Chunk allocator for fixed size
25 // we need one integer to store book keeping information
26 // whether the storage allocation unit is used of not
27 // when it is deleted this flag is only set to unused
28 void Chunk::setSize(size_t size
)
31 size_t needSize
= size
+ sizeof(int);
32 size_t multiple
= (size_t) os::floor(needSize
/ sizeof(size_t));
33 size_t rem
= needSize
% sizeof(size_t);
35 allocSize_
= needSize
;
37 allocSize_
= (multiple
+ 1) * sizeof(size_t);
40 void* Chunk::allocateForLargeDataSize(Database
*db
)
42 PageInfo
* pageInfo
= ((PageInfo
*)curPage_
);
43 DbRetVal ret
= db
->getAllocDatabaseMutex();
46 printError(ErrLockTimeOut
,"Unable to acquire alloc database Mutex");
50 //check whether we have space in curPage
51 if (pageInfo
->hasFreeSpace_
== 1)
53 char *data
= ((char*)curPage_
) + sizeof(PageInfo
);
54 pageInfo
->hasFreeSpace_
=0;
56 db
->releaseAllocDatabaseMutex();
57 return data
+ sizeof(int);
61 //no space in curpage , get new page from database
62 pageInfo
= (PageInfo
*)db
->getFreePage(allocSize_
);
65 db
->releaseAllocDatabaseMutex();
66 printError(ErrNoMemory
,"No more free pages in the database");
69 printDebug(DM_Alloc
, "Chunk ID:%d Large Data Item newPage:%x",
71 int multiple
= os::floor(allocSize_
/ PAGE_SIZE
);
72 int offset
= ((multiple
+ 1) * PAGE_SIZE
);
74 pageInfo
->setPageAsUsed(offset
);
77 ((PageInfo
*)curPage_
)->nextPage_
= (Page
*) pageInfo
;
78 //Make this as current page
79 curPage_
= (Page
*) pageInfo
;
80 char* data
= ((char*)curPage_
) + sizeof(PageInfo
);
81 //TODO::check whether it is locked
83 pageInfo
->hasFreeSpace_
=0;
84 db
->releaseAllocDatabaseMutex();
85 return data
+ sizeof(int);
89 void* Chunk::allocateFromFirstPage(Database
*db
, int noOfDataNodes
)
91 PageInfo
*pageIter
= ((PageInfo
*)firstPage_
);
92 printDebug(DM_Alloc
, "Chunk ID:%d. No free page in database",
94 printDebug(DM_Alloc
, "Scan from firstPage:%x for free nodes",
98 //scan from first page to locate a free node available
99 while(NULL
!= pageIter
)
101 data
= ((char*)pageIter
) + sizeof(PageInfo
);
102 if (pageIter
->hasFreeSpace_
== 1)
104 for (i
= 0; i
< noOfDataNodes
-1; i
++)
106 if (1 == *((int*)data
))
107 data
= data
+ allocSize_
;
110 if (i
!= noOfDataNodes
-1) break;
112 printDebug(DM_Alloc
, "Chunk ID: %d Page :%x does not have free nodes",
114 pageIter
= (PageInfo
*)((PageInfo
*) pageIter
)->nextPage_
;
116 if (NULL
== pageIter
) return NULL
;
117 printDebug(DM_Alloc
,"ChunkID:%d Scan for free node End:Page :%x",
120 return data
+ sizeof(int);
124 void* Chunk::allocateFromNewPage(Database
*db
)
126 DbRetVal ret
= db
->getAllocDatabaseMutex();
129 printError(ErrLockTimeOut
,"Unable to acquire alloc database Mutex");
132 //get a new page from db
133 Page
*page
= db
->getFreePage();
136 db
->releaseAllocDatabaseMutex();
139 printDebug(DM_Alloc
, "ChunkID:%d Normal Data Item newPage:%x",
141 //Initialize pageInfo for this new page
142 PageInfo
*pInfo
= (PageInfo
*)page
;
143 pInfo
->setPageAsUsed(0);
145 //create the link between old page and the newly created page
146 PageInfo
* pageInfo
= ((PageInfo
*)curPage_
);
147 pageInfo
->nextPage_
= page
;
149 //make this new page as the current page
152 char* data
= ((char*)page
) + sizeof(PageInfo
);
154 db
->releaseAllocDatabaseMutex();
155 return data
+ sizeof(int);
158 //Allocates memory to store data
159 //TODO::check whether it is locked before allocating.
160 //delete tuple will set the usedflag to true, but locks will be held
161 //till commit and it shall be rolledback.So make sure that it does not
162 //allocate deleted tuple which is yet to be commited.
164 void* Chunk::allocate(Database
*db
, DbRetVal
*status
)
166 PageInfo
* pageInfo
= ((PageInfo
*)curPage_
);
168 int noOfDataNodes
=os::floor((PAGE_SIZE
- sizeof(PageInfo
))/allocSize_
);
169 char *data
= ((char*)curPage_
) + sizeof(PageInfo
);
170 printDebug(DM_Alloc
, "Chunk::allocate id:%d curPage:%x noOfDataNodes:%d",
171 chunkID_
, curPage_
, noOfDataNodes
);
173 //1.scan through data list and find if any is free to use in current page
174 //2.If there is none then
175 // a) get new free page from db. set the prev->next to point
177 //4. b) initialize the free page to zero and get first data ptr.
178 //5.If there is one, return that
180 //For allocation more than PAGE_SIZE
181 if (0 == noOfDataNodes
)
183 data
= (char*) allocateForLargeDataSize(db
);
187 int ret
= getChunkMutex(db
->procSlot
);
190 if (status
!= NULL
) *status
= ErrLockTimeOut
;
191 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
194 int i
= noOfDataNodes
;
195 if (pageInfo
->hasFreeSpace_
== 1)
197 for (i
= 1; i
< noOfDataNodes
; i
++)
199 if (*((int*)data
) == 1) data
= data
+ allocSize_
;
204 printDebug(DM_Alloc
, "ChunkID:%d Node which might be free is %d",
206 //It comes here if the pageInfo->hasFreeSpace ==0
207 //or there are no free data space in this page
208 if (i
== noOfDataNodes
&& *((int*)data
) == 1)
211 printDebug(DM_Alloc
, "ChunkID:%d curPage does not have free nodes.", chunkID_
);
212 //there are no free data space in this page
213 pageInfo
->hasFreeSpace_
= 0;
214 if (chunkID_
== LockTableId
|| chunkID_
== TransHasTableId
)
216 data
= (char*) allocateFromFirstPage(db
, noOfDataNodes
);
219 data
= (char*) allocateFromNewPage(db
);
222 printError(ErrNoMemory
, "No memory in any of the pages:Increase db size");
223 if (status
!= NULL
) *status
= ErrNoMemory
;
229 data
= (char*) allocateFromNewPage(db
);
232 data
= (char*) allocateFromFirstPage(db
, noOfDataNodes
);
235 printError(ErrNoMemory
, "No memory in any of the pages:Increase db size");
236 if (status
!= NULL
) *status
= ErrNoMemory
;
240 releaseChunkMutex(db
->procSlot
);
244 releaseChunkMutex(db
->procSlot
);
245 return data
+ sizeof(int);
249 void* Chunk::allocateForLargeDataSize(Database
*db
, size_t size
)
251 //no need to take chunk mutexes for this, as we are taking alloc database mutex
252 int multiple
= os::floor(size
/ PAGE_SIZE
);
253 int offset
= ((multiple
+ 1) * PAGE_SIZE
);
254 PageInfo
* pageInfo
= ((PageInfo
*)curPage_
);
255 DbRetVal ret
= db
->getAllocDatabaseMutex();
258 printError(ErrLockTimeOut
,"Unable to acquire alloc database Mutex");
261 pageInfo
= (PageInfo
*)db
->getFreePage(allocSize_
);
262 if (NULL
== pageInfo
)
264 db
->releaseAllocDatabaseMutex();
265 printError(ErrNoMemory
,"No more free pages in the database:Increase db size");
268 printDebug(DM_VarAlloc
,"Chunk::allocate Large Data Item id:%d Size:%d curPage:%x ",
269 chunkID_
, size
, curPage_
);
270 //TODO:: logic pending
273 //REDESIGN MAY BE REQUIRED:Lets us live with this for now.
274 //what happens to the space lets say 10000 bytes is allocated
275 //it needs 2 pages,= 16000 bytes, 6000 bytes should not be wasted
276 //in this case.So need to take of this.
277 //Will be coded at later stage as this is developed to support
278 //undo logging and currently we shall assume that the logs generated
279 //wont be greater than PAGE_SIZE.
280 db
->releaseAllocDatabaseMutex();
287 void* Chunk::allocFromNewPageForVarSize(Database
*db
, size_t size
)
289 //Should be called only for data items <PAGE_SIZE
290 DbRetVal ret
= db
->getAllocDatabaseMutex();
293 printError(ErrLockTimeOut
,"Unable to acquire alloc database Mutex");
297 void *vnode
= varSizeFirstFitAllocate(size
);
300 db
->releaseAllocDatabaseMutex();
304 Page
*newPage
= db
->getFreePage();
305 db
->releaseAllocDatabaseMutex();
311 printDebug(DM_VarAlloc
, "ChunkID:%d New Page: %x ", chunkID_
, newPage
);
312 PageInfo
*pInfo
= (PageInfo
*) newPage
;
313 pInfo
->setPageAsUsed(0);
314 createDataBucket(newPage
, PAGE_SIZE
, size
);
316 ((PageInfo
*)curPage_
)->nextPage_
= newPage
;
318 char *data
= ((char*)newPage
) + sizeof(PageInfo
) + sizeof(VarSizeInfo
);
322 //Allocates from the current page of the chunk.
323 //Scans through the VarSizeInfo objects in the page and gets the free slot
324 void* Chunk::allocateFromCurPageForVarSize(size_t size
)
326 //Should be called only for data items <PAGE_SIZE
327 Page
*page
= ((PageInfo
*)curPage_
);
328 printDebug(DM_VarAlloc
, "Chunk::allocate Normal Data Item id:%d Size:%d curPage:%x ",
329 chunkID_
, size
, curPage_
);
330 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)page
) +
332 while ((char*) varInfo
< ((char*)page
+ PAGE_SIZE
))
334 if (0 == varInfo
->isUsed_
)
336 if( size
+ sizeof(VarSizeInfo
) < varInfo
->size_
)
338 splitDataBucket(varInfo
, size
);
339 printDebug(DM_VarAlloc
, "Chunkid:%d splitDataBucket: Size: %d Item:%x ",
340 chunkID_
, size
, varInfo
);
341 return (char*)varInfo
+ sizeof(VarSizeInfo
);
343 else if (size
== varInfo
->size_
) {
344 varInfo
->isUsed_
= 1;
345 return (char *) varInfo
+ sizeof(VarSizeInfo
);
349 varInfo
= (VarSizeInfo
*)((char*)varInfo
+ sizeof(VarSizeInfo
)
355 //Allocates memory to store data of variable size
356 void* Chunk::allocate(Database
*db
, size_t size
, DbRetVal
*status
)
358 if (0 == size
) return NULL
;
359 //check if the size is more than PAGE_SIZE
360 //if it is more than the PAGE_SIZE, then allocate new
361 //page using database and then link the curPage to the
362 //newly allocated page
363 //if it is less than PAGE_SIZE, then check the curpage for
364 //free memory of specified size
365 //if not available, then scan from the firstPage for the free
368 //TODO::During the scan, merge nearby nodes if both are free
369 //if not available then allocate new page
371 size_t alignedSize
= os::align(size
);
373 int ret
= getChunkMutex(db
->procSlot
);
376 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
377 *status
= ErrLockTimeOut
;
380 if (alignedSize
> PAGE_SIZE
)
382 data
= allocateForLargeDataSize(db
, alignedSize
);
386 data
= allocateFromCurPageForVarSize(alignedSize
);
388 //No available spaces in the current page.
390 data
= allocFromNewPageForVarSize(db
, alignedSize
);
392 printError(ErrNoMemory
, "No memory in any of the pages:Increase db size");
393 if (status
!= NULL
) *status
= ErrNoMemory
;
397 releaseChunkMutex(db
->procSlot
);
401 //Assumes chunk mutex is already taken, before calling this
402 void* Chunk::varSizeFirstFitAllocate(size_t size
)
404 printDebug(DM_VarAlloc
, "Chunk::varSizeFirstFitAllocate size:%d firstPage:%x",
407 Page
*page
= ((PageInfo
*)firstPage_
);
408 size_t alignedSize
= os::align(size
);
411 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)page
) + sizeof(PageInfo
));
412 while ((char*) varInfo
< ((char*)page
+ PAGE_SIZE
))
414 if (0 == varInfo
->isUsed_
)
416 if( alignedSize
+sizeof(VarSizeInfo
) < varInfo
->size_
)
418 splitDataBucket(varInfo
, alignedSize
);
419 return ((char*)varInfo
) + sizeof(VarSizeInfo
);
421 else if (alignedSize
== varInfo
->size_
) {
422 varInfo
->isUsed_
= 1;
423 printDebug(DM_VarAlloc
, "VarSizeFirstFitAllocate returning %x", ((char*)varInfo
) +sizeof(VarSizeInfo
));
424 return ((char *) varInfo
) + sizeof(VarSizeInfo
);
427 varInfo
= (VarSizeInfo
*)((char*)varInfo
+ sizeof(VarSizeInfo
)
430 printDebug(DM_VarAlloc
, "Chunk:This page does not have free data nodes page:%x", page
);
431 page
= ((PageInfo
*) page
)->nextPage_
;
436 void Chunk::freeForVarSizeAllocator(void *ptr
, int pslot
)
438 int ret
= getChunkMutex(pslot
);
441 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
444 VarSizeInfo
*varInfo
= (VarSizeInfo
*)((char*)ptr
- sizeof(VarSizeInfo
));
445 varInfo
->isUsed_
= 0;
446 printDebug(DM_VarAlloc
,"chunkID:%d Unset isUsed for %x", chunkID_
, varInfo
);
447 releaseChunkMutex(pslot
);
452 void Chunk::freeForLargeAllocator(void *ptr
, int pslot
)
454 //There will be max only one data element in a page.
455 //PageInfo is stored just before the data.
456 int ret
= getChunkMutex(pslot
);
459 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
462 PageInfo
*pageInfo
= (PageInfo
*)(((char*)
463 ptr
) - (sizeof(PageInfo
) + sizeof(int)));
464 PageInfo
*pInfo
= (PageInfo
*)firstPage_
, *prev
= (PageInfo
*)firstPage_
;
468 if (pInfo
== pageInfo
) {found
= true; break; }
470 pInfo
= (PageInfo
*)pInfo
->nextPage_
;
474 printError(ErrSysFatal
,"Page %x not found in page list:Logical error", pageInfo
);
475 releaseChunkMutex(pslot
);
478 prev
->nextPage_
= pageInfo
->nextPage_
;
479 pageInfo
->nextPageAfterMerge_
= NULL
;
480 pageInfo
->isUsed_
= 0;
481 os::memset(pageInfo
, 0 , allocSize_
);
482 pageInfo
->hasFreeSpace_
= 1;
483 releaseChunkMutex(pslot
);
487 //Frees the memory pointed by ptr
488 void Chunk::free(Database
*db
, void *ptr
)
492 freeForVarSizeAllocator(ptr
, db
->procSlot
);
495 int noOfDataNodes
=os::floor((PAGE_SIZE
- sizeof(PageInfo
)) / allocSize_
);
497 if (0 == noOfDataNodes
)
499 freeForLargeAllocator(ptr
, db
->procSlot
);
502 int ret
= getChunkMutex(db
->procSlot
);
505 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
508 //below is the code for freeing in fixed size allocator
510 //unset the used flag
511 *((int*)ptr
-1 ) = 0;
513 pageInfo
= getPageInfo(db
, ptr
);
514 if (NULL
== pageInfo
)
516 printError(ErrSysFatal
,"Probable Data corruption: pageInfo is NULL", pageInfo
);
517 releaseChunkMutex(db
->procSlot
);
520 //set the pageinfo where this ptr points
521 pageInfo
->hasFreeSpace_
= 1;
522 releaseChunkMutex(db
->procSlot
);
526 //returns the pageInfo of the page where this ptr points
527 //This works only if the data size is less than PAGE_SIZE
528 //If ptr points to data which is more than PAGE_SIZE,then
529 //calling this might lead to memory corruption
530 //Note:IMPORTANT::assumes db lock is taken before calling this
531 PageInfo
* Chunk::getPageInfo(Database
*db
, void *ptr
)
533 if (allocSize_
< PAGE_SIZE
- sizeof(PageInfo
)) {
534 int rem
= (long) ptr
% PAGE_SIZE
;
535 return (PageInfo
*)(((char*)ptr
) - rem
);
537 //large size allocator
538 char *inPtr
= (char*)ptr
;
539 PageInfo
* pageInfo
= ((PageInfo
*)firstPage_
);
541 while( pageInfo
!= NULL
)
543 if (inPtr
> (char*) pageInfo
&& pageInfo
->nextPageAfterMerge_
>inPtr
)
545 pageInfo
= (PageInfo
*)pageInfo
->nextPage_
;
551 //If called on chunk used to store tuples, it returns the total number of rows
552 //present in the table
553 long Chunk::getTotalDataNodes()
556 if (0 == allocSize_
) //->variable size allocator
558 Page
*page
= ((PageInfo
*)firstPage_
);
561 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)page
) + sizeof(PageInfo
));
562 while ((char*) varInfo
< ((char*)page
+ PAGE_SIZE
))
564 if (1 == varInfo
->isUsed_
) totalNodes
++;
565 varInfo
= (VarSizeInfo
*)((char*)varInfo
+ sizeof(VarSizeInfo
)
568 page
= ((PageInfo
*) page
)->nextPage_
;
573 //TODO::for large size allocator
574 if (allocSize_
>PAGE_SIZE
)//->each page has only one data node
576 Page
*page
= ((PageInfo
*)firstPage_
);
579 //current it page wise later this will done
580 if(1==*(int*)(((char*)page
)+sizeof(PageInfo
)))
582 page
= ((PageInfo
*) page
)->nextPage_
;
587 int noOfDataNodes
=os::floor((PAGE_SIZE
- sizeof(PageInfo
))/allocSize_
);
588 PageInfo
* pageInfo
= ((PageInfo
*)firstPage_
);
589 char *data
= ((char*)firstPage_
) + sizeof(PageInfo
);
591 while( pageInfo
!= NULL
)
593 data
= ((char*)pageInfo
) + sizeof(PageInfo
);
594 for (i
= 0; i
< noOfDataNodes
; i
++)
596 if (*((int*)data
) == 1) { totalNodes
++;}
597 data
= data
+ allocSize_
;
599 pageInfo
= (PageInfo
*)(((PageInfo
*)pageInfo
)->nextPage_
) ;
604 //TODO::for other type of allocators
607 PageInfo
* pageInfo
= ((PageInfo
*)firstPage_
);
608 PageInfo
* prevPage
= pageInfo
;
609 if (NULL
== pageInfo
)
613 pageInfo
= (PageInfo
*)pageInfo
->nextPage_
;
616 while( pageInfo
!= NULL
)
619 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)pageInfo
) +
621 while ((char*) varInfo
< ((char*)pageInfo
+ PAGE_SIZE
))
623 if (1 == varInfo
->isUsed_
) {flag
=true; break;}
624 varInfo
= (VarSizeInfo
*)((char*)varInfo
+ sizeof(VarSizeInfo
)
628 printDebug(DM_VarAlloc
,"Freeing unused page in varsize allocator %x\n", pageInfo
);
629 prevPage
->nextPage_
= pageInfo
->nextPage_
;
630 pageInfo
->isUsed_
= 0;
633 pageInfo
= (PageInfo
*)(((PageInfo
*)pageInfo
)->nextPage_
) ;
634 printDebug(DM_VarAlloc
,"compact iter %x\n", pageInfo
);
636 }else if (allocSize_
< PAGE_SIZE
)
638 while( pageInfo
!= NULL
)
641 int noOfDataNodes
=os::floor((PAGE_SIZE
- sizeof(PageInfo
))/allocSize_
);
642 char *data
= ((char*)pageInfo
) + sizeof(PageInfo
);
643 for (int i
= 0; i
< noOfDataNodes
-1; i
++)
645 if (1 == *((int*)data
)) { flag
= true; break; }
646 data
= data
+allocSize_
;
649 printDebug(DM_Alloc
,"Freeing unused page in fixed allocator %x\n", pageInfo
);
650 prevPage
->nextPage_
= pageInfo
->nextPage_
;
651 pageInfo
->isUsed_
= 0;
654 pageInfo
= (PageInfo
*)(((PageInfo
*)pageInfo
)->nextPage_
) ;
655 printDebug(DM_Alloc
,"compact iter %x\n", pageInfo
);
661 int Chunk::totalPages()
663 //logic is same for variable size and for large data node allocator.
664 PageInfo
* pageInfo
= ((PageInfo
*)firstPage_
);
666 while( pageInfo
!= NULL
)
669 pageInfo
= (PageInfo
*)(((PageInfo
*)pageInfo
)->nextPage_
) ;
674 int Chunk::initMutex()
676 return chunkMutex_
.init("Chunk");
678 int Chunk::getChunkMutex(int procSlot
)
680 return chunkMutex_
.getLock(procSlot
);
682 int Chunk::releaseChunkMutex(int procSlot
)
684 return chunkMutex_
.releaseLock(procSlot
);
686 int Chunk::destroyMutex()
688 return chunkMutex_
.destroy();
690 void Chunk::splitDataBucket(VarSizeInfo
*varInfo
, size_t needSize
)
692 int remSpace
= varInfo
->size_
- sizeof(VarSizeInfo
) - needSize
;
693 varInfo
->isUsed_
= 1;
694 varInfo
->size_
= needSize
;
695 varInfo
= (VarSizeInfo
*)((char*)varInfo
+
696 sizeof(VarSizeInfo
) + varInfo
->size_
);
697 varInfo
->isUsed_
= 0;
698 varInfo
->size_
= remSpace
;
699 printDebug(DM_VarAlloc
, "Remaining space is %d\n", remSpace
);
704 void Chunk::createDataBucket(Page
*page
, size_t totalSize
, size_t needSize
)
706 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)page
) + sizeof(PageInfo
));
707 varInfo
->isUsed_
= 0;
708 varInfo
->size_
= PAGE_SIZE
- sizeof(PageInfo
) - sizeof(VarSizeInfo
);
709 splitDataBucket(varInfo
, needSize
);
712 void Chunk::setChunkNameForSystemDB(int id
)
714 strcpy(chunkName
,ChunkName
[id
]);
719 printf(" <Chunk Id> %d </Chunk Id> \n",chunkID_
);
720 printf(" <TotalPages> %d </TotalPages> \n",totalPages());
721 printf(" <ChunkName > %s </ChunkName> \n",getChunkName());
722 printf(" <TotalDataNodes> %d </TotalDataNodes> \n",getTotalDataNodes());
723 printf(" <SizeOfDataNodes> %d </SizeOfDataNodes> \n",getSize());
724 printf(" <Allocation Type> ");
727 printf("FixedSizeAllocator ");
728 }else if(allocType_
==1)
730 printf("VariableSizeAllocator ");
733 printf("UnknownAllocator ");
736 printf("</Allocation Type>\n");