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 ***************************************************************************/
22 // sets the size of the Chunk allocator for fixed size
24 // we need one integer to store book keeping information
25 // whether the storage allocation unit is used of not
26 // when it is deleted this flag is only set to unused
27 void Chunk::setSize(size_t size
)
30 size_t needSize
= size
+ sizeof(int);
31 size_t multiple
= os::floor(needSize
/ sizeof(size_t));
32 size_t rem
= needSize
% sizeof(size_t);
34 allocSize_
= needSize
;
36 allocSize_
= (multiple
+ 1) * sizeof(size_t);
39 //Allocates memory to store data
40 //TODO::check whether it is locked before allocating.
41 //delete tuple will set the usedflag to true, but locks will be held
42 //till commit and it shall be rolledback.So make sure that it does not
43 //allocate deleted tuple which is yet to be commited.
45 void* Chunk::allocate(Database
*db
)
47 int ret
= getChunkMutex();
50 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
53 PageInfo
* pageInfo
= ((PageInfo
*)curPage_
);
55 int noOfDataNodes
=os::floor((PAGE_SIZE
- sizeof(PageInfo
))/allocSize_
);
56 char *data
= ((char*)curPage_
) + sizeof(PageInfo
);
57 printDebug(DM_Alloc
, "Chunk::allocate id:%d curPage:%x noOfDataNodes:%d",
58 chunkID_
, curPage_
, noOfDataNodes
);
60 //1.scan through data list and find if any is free to use in current page
61 //2.If there is none then
62 // a) get new free page from db. set the prev->next to point
64 //4. b) initialize the free page to zero and get first data ptr.
65 //5.If there is one, return that
67 //For allocation more than PAGE_SIZE
68 if (0 == noOfDataNodes
)
70 ret
= db
->getAllocDatabaseMutex();
73 printError(ErrLockTimeOut
,"Unable to acquire alloc database Mutex");
76 pageInfo
= (PageInfo
*)db
->getFreePage(allocSize_
);
80 db
->releaseAllocDatabaseMutex();
81 printError(ErrNoMemory
,"No more free pages in the database");
84 printDebug(DM_Alloc
, "Chunk ID:%d Large Data Item newPage:%x",
86 int multiple
= os::floor(allocSize_
/ PAGE_SIZE
);
87 int offset
= ((multiple
+ 1) * PAGE_SIZE
);
89 pageInfo
->setPageAsUsed(offset
);
92 ((PageInfo
*)curPage_
)->nextPage_
= (Page
*) pageInfo
;
93 //Make this as current page
94 curPage_
= (Page
*) pageInfo
;
95 data
= ((char*)curPage_
) + sizeof(PageInfo
);
96 //TODO::check whether it is locked
99 db
->releaseAllocDatabaseMutex();
100 return data
+ sizeof(int);
104 int i
= noOfDataNodes
;
105 if (pageInfo
->hasFreeSpace_
== 1)
107 for (i
= 1; i
< noOfDataNodes
; i
++)
109 if (*((int*)data
) == 1) data
= data
+ allocSize_
;
114 printDebug(DM_Alloc
, "ChunkID:%d Node which might be free is %d",
116 //It comes here if the pageInfo->hasFreeSpace ==0
117 //or there are no free data space in this page
118 if (i
== noOfDataNodes
&& *((int*)data
) == 1)
120 ret
= db
->getAllocDatabaseMutex();
123 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
127 //there are no free data space in this page
128 pageInfo
->hasFreeSpace_
= 0;
129 //get a new page from db
130 printDebug(DM_Alloc
, "ChunkID:%d curPage does not have free nodes.", chunkID_
);
131 Page
*page
= db
->getFreePage();
134 PageInfo
*pageIter
= ((PageInfo
*)firstPage_
);
135 printDebug(DM_Alloc
, "Chunk ID:%d. No free page in database",
137 printDebug(DM_Alloc
, "Scan from firstPage:%x for free nodes",
139 //scan from first page to locate a free node available
140 while(NULL
!= pageIter
)
142 data
= ((char*)pageIter
) + sizeof(PageInfo
);
143 if (pageIter
->hasFreeSpace_
== 1)
145 for (i
= 0; i
< noOfDataNodes
-1; i
++)
147 if (1 == *((int*)data
))
148 data
= data
+ allocSize_
;
151 if (i
!= noOfDataNodes
-1) break;
153 printDebug(DM_Alloc
, "Chunk ID: %d Page :%x does not have free nodes",
155 pageIter
= (PageInfo
*)((PageInfo
*) pageIter
)->nextPage_
;
157 if (NULL
== pageIter
)
160 db
->releaseAllocDatabaseMutex();
161 printError(ErrNoMemory
,"No more free pages in the database:Increase db size");
164 printDebug(DM_Alloc
,"ChunkID:%d Scan for free node End:Page :%x",
168 db
->releaseAllocDatabaseMutex();
169 return data
+ sizeof(int);
171 db
->releaseAllocDatabaseMutex();
172 //os::memset(page, 0, PAGE_SIZE);
173 printDebug(DM_Alloc
, "ChunkID:%d Normal Data Item newPage:%x",
175 //Initialize pageInfo for this new page
176 PageInfo
*pInfo
= (PageInfo
*)page
;
177 pInfo
->setPageAsUsed(0);
179 //create the link between old page and the newly created page
180 data
= ((char*)page
) + sizeof(PageInfo
);
181 pageInfo
->nextPage_
= page
;
183 //make this new page as the current page
189 return data
+ sizeof(int);
192 //Allocates memory to store data
193 void* Chunk::allocate(Database
*db
, size_t size
)
196 if (0 == size
) return NULL
;
197 //align the size first
198 //check if the size is more than PAGE_SIZE
199 //if it is more than the PAGE_SIZE, then allocate new
200 //page using database and then link the curPage to the
201 //newly allocated page
202 //if it is less than PAGE_SIZE, then check the curpage for
203 //free memory of specified size
204 //if not available, then scan from the firstPage for the free
207 //TODO::During the scan, merge nearby nodes if both are free
208 //if not available then allocate new page
209 int ret
= getChunkMutex();
212 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
215 size_t alignedSize
= os::align(size
);
217 if (size
> PAGE_SIZE
)
219 int multiple
= os::floor(alignedSize
/ PAGE_SIZE
);
220 int offset
= ((multiple
+ 1) * PAGE_SIZE
);
221 PageInfo
* pageInfo
= ((PageInfo
*)curPage_
);
222 ret
= db
->getAllocDatabaseMutex();
225 printError(ErrLockTimeOut
,"Unable to acquire alloc database Mutex");
228 pageInfo
= (PageInfo
*)db
->getFreePage(allocSize_
);
229 if (NULL
== pageInfo
)
232 db
->releaseAllocDatabaseMutex();
233 printError(ErrNoMemory
,"No more free pages in the database:Increase db size");
236 printDebug(DM_VarAlloc
,"Chunk::allocate Large Data Item id:%d Size:%d curPage:%x ",
237 chunkID_
, alignedSize
, curPage_
);
238 //TODO:: logic pending
239 //what happens to the space lets say 10000 bytes is allocated
240 //it needs 2 pages,= 16000 bytes, 6000 bytes should not be wasted
241 //in this case.So need to take of this.
242 //Will be coded at later stage as this is developed to support
243 //undo logging and currently we shall assume that the logs generated
244 //wont be greater than PAGE_SIZE.
245 printf("data size greater than page size\n");
247 db
->releaseAllocDatabaseMutex();
252 Page
*page
= ((PageInfo
*)curPage_
);
253 printDebug(DM_VarAlloc
, "Chunk::allocate Normal Data Item id:%d Size:%d curPage:%x ",
254 chunkID_
, alignedSize
, curPage_
);
255 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)page
) +
257 while ((char*) varInfo
< ((char*)page
+ PAGE_SIZE
))
259 if (0 == varInfo
->isUsed_
)
261 if( alignedSize
< varInfo
->size_
)
263 splitDataBucket(varInfo
, alignedSize
);
265 printDebug(DM_VarAlloc
, "Chunkid:%d splitDataBucket: Size: %d Item:%x ",
266 chunkID_
, alignedSize
, varInfo
);
267 return (char*)varInfo
+ sizeof(VarSizeInfo
);
270 varInfo
= (VarSizeInfo
*)((char*)varInfo
+ sizeof(VarSizeInfo
)
273 //No available spaces in the current page.
275 ret
= db
->getAllocDatabaseMutex();
278 printError(ErrLockTimeOut
,"Unable to acquire alloc database Mutex");
281 Page
*newPage
= db
->getFreePage();
284 void *data
= varSizeFirstFitAllocate(size
);
286 db
->releaseAllocDatabaseMutex();
288 printError(ErrNoMemory
,"No more free space in the database:Increase db size");
291 db
->releaseAllocDatabaseMutex();
293 printDebug(DM_VarAlloc
, "ChunkID:%d New Page: %x ", chunkID_
, newPage
);
294 PageInfo
*pInfo
= (PageInfo
*) newPage
;
295 pInfo
->setPageAsUsed(0);
296 createDataBucket(newPage
, PAGE_SIZE
, alignedSize
);
298 ((PageInfo
*)curPage_
)->nextPage_
= newPage
;
301 char *data
= ((char*)newPage
) + sizeof(PageInfo
) + sizeof(VarSizeInfo
);
306 //Assumes chunk mutex is already taken, before calling this
307 void* Chunk::varSizeFirstFitAllocate(size_t size
)
309 Page
*page
= ((PageInfo
*)firstPage_
);
310 size_t alignedSize
= os::align(size
);
313 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)page
) + sizeof(PageInfo
));
314 while ((char*) varInfo
< ((char*)page
+ PAGE_SIZE
))
316 if (0 == varInfo
->isUsed_
)
318 if( alignedSize
< varInfo
->size_
)
320 splitDataBucket(varInfo
, alignedSize
);
321 return (char*)varInfo
+ sizeof(VarSizeInfo
);
324 varInfo
= (VarSizeInfo
*)((char*)varInfo
+ sizeof(VarSizeInfo
)
327 page
= ((PageInfo
*) page
)->nextPage_
;
334 //Frees the memory pointed by ptr
335 void Chunk::free(Database
*db
, void *ptr
)
337 int ret
= getChunkMutex();
340 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
345 VarSizeInfo
*varInfo
= (VarSizeInfo
*)((char*)ptr
- sizeof(VarSizeInfo
));
346 varInfo
->isUsed_
= 0;
347 printDebug(DM_VarAlloc
,"chunkID:%d Unset isUsed for %x", chunkID_
, varInfo
);
348 //return from here for variable size allocator
352 //unset the used flag
353 *((int*)ptr
-1 ) = 0;
354 int noOfDataNodes
=os::floor((PAGE_SIZE
- sizeof(PageInfo
)) / allocSize_
);
356 if (0 == noOfDataNodes
)
358 //means tuple larger than PAGE_SIZE
359 PageInfo
*pageInfo
= (PageInfo
*)(((char*)
360 ptr
) - (sizeof(PageInfo
) + sizeof(int)));
361 PageInfo
*pInfo
= (PageInfo
*)firstPage_
, *prev
= (PageInfo
*)firstPage_
;
365 if (pInfo
== pageInfo
) {found
= true; break; }
367 pInfo
= (PageInfo
*)pInfo
->nextPage_
;
371 printError(ErrSysFatal
,"Page %x not found in page list:Logic error", pageInfo
);
375 prev
->nextPage_
= pageInfo
->nextPage_
;
376 pageInfo
->nextPageAfterMerge_
= NULL
;
377 pageInfo
->isUsed_
= 0;
378 os::memset(pageInfo
, 0 , allocSize_
);
379 pageInfo
->hasFreeSpace_
= 1;
384 pageInfo
= getPageInfo(db
, ptr
);
385 if (NULL
== pageInfo
)
387 printError(ErrSysFatal
,"Probable Data corruption: pageInfo is NULL", pageInfo
);
391 //set the pageinfo where this ptr points
392 pageInfo
->hasFreeSpace_
= 1;
397 //returns the pageInfo of the page where this ptr points
398 //This works only if the data size is less than PAGE_SIZE
399 //If ptr points to data which is more than PAGE_SIZE,then
400 //calling this might lead to memory corruption
401 //Note:IMPORTANT::assumes db lock is taken before calling this
402 PageInfo
* Chunk::getPageInfo(Database
*db
, void *ptr
)
404 char *inPtr
= (char*)ptr
;
405 PageInfo
* pageInfo
= ((PageInfo
*)firstPage_
);
407 while( pageInfo
!= NULL
)
409 if (inPtr
> (char*) pageInfo
&& ((char*)pageInfo
+ PAGE_SIZE
) >inPtr
)
411 pageInfo
= (PageInfo
*)(((PageInfo
*)pageInfo
)->nextPage_
) ;
416 long Chunk::getTotalDataNodes()
418 int ret
= getChunkMutex();
421 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
425 //TODO:: for variable size allocator
426 if (0 == allocSize_
) //->variable size allocator
429 //TODO::for large size allocator
430 if (allocSize_
>PAGE_SIZE
)//->each page has only one data node
433 int noOfDataNodes
=os::floor((PAGE_SIZE
- sizeof(PageInfo
))/allocSize_
);
434 PageInfo
* pageInfo
= ((PageInfo
*)firstPage_
);
435 char *data
= ((char*)firstPage_
) + sizeof(PageInfo
);
438 while( pageInfo
!= NULL
)
440 data
= ((char*)pageInfo
) + sizeof(PageInfo
);
441 for (i
= 0; i
< noOfDataNodes
; i
++)
443 if (*((int*)data
) == 1) { totalNodes
++;}
444 data
= data
+ allocSize_
;
446 pageInfo
= (PageInfo
*)(((PageInfo
*)pageInfo
)->nextPage_
) ;
452 int Chunk::totalPages()
454 int ret
= getChunkMutex();
457 printError(ErrLockTimeOut
,"Unable to acquire chunk Mutex");
460 //logic is same for variable size and for large data node allocator.
461 PageInfo
* pageInfo
= ((PageInfo
*)firstPage_
);
463 while( pageInfo
!= NULL
)
466 pageInfo
= (PageInfo
*)(((PageInfo
*)pageInfo
)->nextPage_
) ;
472 int Chunk::initMutex()
474 return chunkMutex_
.init();
476 int Chunk::getChunkMutex()
478 return chunkMutex_
.tryLock();
480 int Chunk::releaseChunkMutex()
482 return chunkMutex_
.releaseLock();
484 int Chunk::destroyMutex()
486 return chunkMutex_
.destroy();
488 void Chunk::splitDataBucket(VarSizeInfo
*varInfo
, size_t needSize
)
490 int remSpace
= varInfo
->size_
- sizeof(VarSizeInfo
) - needSize
;
491 varInfo
->isUsed_
= 1;
492 varInfo
->size_
= needSize
;
493 varInfo
= (VarSizeInfo
*)((char*)varInfo
+
494 sizeof(VarSizeInfo
) + varInfo
->size_
);
495 varInfo
->isUsed_
= 0;
496 varInfo
->size_
= remSpace
;
501 void Chunk::createDataBucket(Page
*page
, size_t totalSize
, size_t needSize
)
503 VarSizeInfo
*varInfo
= (VarSizeInfo
*)(((char*)page
) + sizeof(PageInfo
));
504 varInfo
->isUsed_
= 0;
505 varInfo
->size_
= PAGE_SIZE
- sizeof(PageInfo
) - sizeof(VarSizeInfo
);
506 splitDataBucket(varInfo
, needSize
);