diff options
Diffstat (limited to 'src/H5HL.c')
-rw-r--r-- | src/H5HL.c | 524 |
1 files changed, 334 insertions, 190 deletions
@@ -61,7 +61,10 @@ static void *H5HL_read(H5F_t *f, hid_t dxpl_id, haddr_t addr, size_t offset, siz static herr_t H5HL_write(H5F_t *f, hid_t dxpl_id, haddr_t addr, size_t offset, size_t size, const void *buf); #endif /* NOT_YET */ -static H5HL_free_t * H5HL_remove_free(H5HL_t *heap, H5HL_free_t *fl); + +static herr_t H5HL_serialize(H5F_t *f, H5HL_t *heap, uint8_t *buf); +static H5HL_free_t *H5HL_remove_free(H5HL_t *heap, H5HL_free_t *fl); +static herr_t H5HL_minimize_heap_space(H5F_t *f, hid_t dxpl_id, H5HL_t *heap); /* Metadata cache callbacks */ static H5HL_t *H5HL_load(H5F_t *f, hid_t dxpl_id, haddr_t addr, const void *udata1, @@ -305,6 +308,211 @@ done: /*------------------------------------------------------------------------- + * Function: H5HL_minimize_heap_space + * + * Purpose: Go through the heap's freelist and determine if we can + * eliminate the free blocks at the tail of the buffer. + * + * Return: Success: SUCCEED + * Failure: FAIL + * + * Programmer: Bill Wendling + * wendling@ncsa.uiuc.edu + * Sept. 16, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +H5HL_minimize_heap_space(H5F_t *f, hid_t dxpl_id, H5HL_t *heap) +{ + herr_t ret_value = SUCCEED; + size_t sizeof_hdr; + + FUNC_ENTER_NOAPI(H5HL_minimize_heap_space, FAIL) + + /* check args */ + assert(f); + assert(heap); + + sizeof_hdr = H5HL_SIZEOF_HDR(f); /* cache H5HL header size for file */ + + /* + * Check to see if we can reduce the size of the heap in memory by + * eliminating free blocks at the tail of the buffer before flushing the + * buffer out. + */ + if (heap->freelist) { + H5HL_free_t *tmp_fl; + H5HL_free_t *last_fl = NULL; + + /* Search for a free block at the end of the buffer */ + for (tmp_fl = heap->freelist; tmp_fl; tmp_fl = tmp_fl->next) + /* Check if the end of this free block is at the end of the buffer */ + if (tmp_fl->offset + tmp_fl->size == heap->mem_alloc) { + last_fl = tmp_fl; + break; + } + + /* + * Found free block at the end of the buffer, decide what to do + * about it + */ + if (last_fl) { + size_t new_mem_size = heap->mem_alloc; /* New size of memory buffer */ + + /* + * If the last free block's size is more than half the memory + * buffer size (and the memory buffer is larger than the + * minimum size), reduce or eliminate it. + */ + if (last_fl->size >= (heap->mem_alloc / 2) && heap->mem_alloc > H5HL_MIN_HEAP) { + /* + * Reduce size of buffer until it's too small or would + * eliminate the free block + */ + while (new_mem_size > H5HL_MIN_HEAP && + new_mem_size >= (last_fl->offset + H5HL_SIZEOF_FREE(f))) + new_mem_size /= 2; + + /* + * Check if reducing the memory buffer size would + * eliminate the free list + */ + if (new_mem_size < (last_fl->offset + H5HL_SIZEOF_FREE(f))) { + /* Check if this is the only block on the free list */ + if (last_fl->prev == NULL && last_fl->next == NULL) { + /* Double the new memory size */ + new_mem_size *= 2; + + /* Truncate the free block */ + last_fl->size = H5HL_ALIGN(new_mem_size - last_fl->offset); + new_mem_size = last_fl->offset + last_fl->size; + assert(last_fl->size >= H5HL_SIZEOF_FREE(f)); + } else { + /* + * Set the size of the memory buffer to the start + * of the free list + */ + new_mem_size = last_fl->offset; + + /* Eliminate the free block from the list */ + last_fl = H5HL_remove_free(heap, last_fl); + } + } else { + /* Truncate the free block */ + last_fl->size = H5HL_ALIGN(new_mem_size - last_fl->offset); + new_mem_size = last_fl->offset + last_fl->size; + assert(last_fl->size >= H5HL_SIZEOF_FREE(f)); + assert(last_fl->size == H5HL_ALIGN(last_fl->size)); + } + + /* Resize the memory buffer */ + if (new_mem_size != heap->mem_alloc) { + heap->mem_alloc = new_mem_size; + heap->chunk = H5FL_BLK_REALLOC(heap_chunk, heap->chunk, (sizeof_hdr + new_mem_size)); + + if (!heap->chunk) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") + } + } + } + } + + /* + * If the heap grew larger or smaller than disk storage then move the + * data segment of the heap to another contiguous block of disk + * storage. + */ + if (heap->mem_alloc != heap->disk_alloc) { + haddr_t old_addr = heap->addr, new_addr; + + /* Release old space on disk */ + H5_CHECK_OVERFLOW(heap->disk_alloc, size_t, hsize_t); + H5MF_xfree(f, H5FD_MEM_LHEAP, dxpl_id, old_addr, (hsize_t)heap->disk_alloc); + H5E_clear(NULL); /* don't really care if the free failed */ + + /* Allocate new space on disk */ + H5_CHECK_OVERFLOW(heap->mem_alloc, size_t, hsize_t); + + if (HADDR_UNDEF == (new_addr = H5MF_alloc(f, H5FD_MEM_LHEAP, dxpl_id, (hsize_t)heap->mem_alloc))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate file space for heap") + + heap->addr = new_addr; + + /* Set new size of block on disk */ + heap->disk_alloc = heap->mem_alloc; + } + +done: + FUNC_LEAVE_NOAPI(ret_value) +} + + +/*------------------------------------------------------------------------- + * Function: H5HL_serialize + * + * Purpose: Serialize the heap. This function will eliminate free + * blocks at the tail of the heap and also split the block + * if it needs to be split for the file. This is so that we + * can serialize it correctly. + * + * Return: Non-negative on success/Negative on failure + * + * Programmer: Bill Wendling + * wendling@ncsa.uiuc.edu + * Sept. 16, 2003 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static herr_t +H5HL_serialize(H5F_t *f, H5HL_t *heap, uint8_t *buf) +{ + H5HL_free_t *fl; + uint8_t *p; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOINIT(H5HL_serialize) + + /* check args */ + assert(buf); + assert(heap); + + /* serialize the header */ + p = buf; + fl = heap->freelist; + HDmemcpy(p, H5HL_MAGIC, H5HL_SIZEOF_MAGIC); + p += H5HL_SIZEOF_MAGIC; + *p++ = H5HL_VERSION; + *p++ = 0; /*reserved*/ + *p++ = 0; /*reserved*/ + *p++ = 0; /*reserved*/ + H5F_ENCODE_LENGTH(f, p, heap->mem_alloc); + H5F_ENCODE_LENGTH(f, p, fl ? fl->offset : H5HL_FREE_NULL); + H5F_addr_encode(f, &p, heap->addr); + + /* serialize the free list */ + for (; fl; fl = fl->next) { + assert (fl->offset == H5HL_ALIGN (fl->offset)); + p = heap->chunk + H5HL_SIZEOF_HDR(f) + fl->offset; + + if (fl->next) { + H5F_ENCODE_LENGTH(f, p, fl->next->offset); + } else { + H5F_ENCODE_LENGTH(f, p, H5HL_FREE_NULL); + } + + H5F_ENCODE_LENGTH(f, p, fl->size); + } + + FUNC_LEAVE_NOAPI(ret_value) +} + + +/*------------------------------------------------------------------------- * Function: H5HL_flush * * Purpose: Flushes a heap from memory to disk if it's dirty. Optionally @@ -317,25 +525,24 @@ done: * Jul 17 1997 * * Modifications: - * rky, 1998-08-28 - * Only p0 writes metadata to disk. + * rky, 1998-08-28 + * Only p0 writes metadata to disk. * - * Robb Matzke, 1999-07-28 - * The ADDR argument is passed by value. + * Robb Matzke, 1999-07-28 + * The ADDR argument is passed by value. * * Quincey Koziol, 2002-7-180 * Added dxpl parameter to allow more control over I/O from metadata * cache. + * + * Bill Wendling, 2003-09-16 + * Separated out the bit that serializes the heap. *------------------------------------------------------------------------- */ static herr_t H5HL_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5HL_t *heap) { - uint8_t *p; - H5HL_free_t *fl; - haddr_t hdr_end_addr; - size_t sizeof_hdr; /* Cache H5HL header size for file */ - herr_t ret_value=SUCCEED; /* Return value */ + herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(H5HL_flush, FAIL); @@ -345,169 +552,45 @@ H5HL_flush(H5F_t *f, hid_t dxpl_id, hbool_t destroy, haddr_t addr, H5HL_t *heap) assert(heap); if (heap->cache_info.dirty) { - /* Cache this for later */ - sizeof_hdr= H5HL_SIZEOF_HDR(f); + haddr_t hdr_end_addr; + size_t sizeof_hdr = H5HL_SIZEOF_HDR(f); /* cache H5HL header size for file */ - /* - * Check to see if we can reduce the size of the heap in memory by - * eliminating free blocks at the tail of the buffer before flushing the - * buffer out. - */ - if(heap->freelist) { - H5HL_free_t *tmp_fl=heap->freelist; - H5HL_free_t *last_fl=NULL; - - /* Search for a free block at the end of the buffer */ - while(tmp_fl!=NULL) { - /* Check if the end of this free block is at the end of the buffer */ - if(tmp_fl->offset + tmp_fl->size == heap->mem_alloc) { - last_fl=tmp_fl; - break; - } /* end if */ - tmp_fl=tmp_fl->next; - } /* end while */ - - /* Found free block at the end of the buffer, decide what to do about it */ - if(last_fl) { - size_t new_mem_size=heap->mem_alloc; /* New size of memory buffer */ + /* Minimize the heap space size if possible */ + if (H5HL_minimize_heap_space(f, dxpl_id, heap) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to minimize local heap space") - /* - *If the last free block's size is more than half the memory - * buffer size (and the memory buffer is larger than the minimum - * size), reduce or eliminate it. - */ - if(last_fl->size>=(heap->mem_alloc/2) && heap->mem_alloc>H5HL_MIN_HEAP) { - /* Reduce size of buffer until it's too small or would eliminate the free block */ - while(new_mem_size>H5HL_MIN_HEAP && - new_mem_size>=(last_fl->offset+H5HL_SIZEOF_FREE(f))) - new_mem_size /= 2; - - /* Check if reducing the memory buffer size would eliminate the free list */ - if(new_mem_size<(last_fl->offset+H5HL_SIZEOF_FREE(f))) { - /* Check if this is the only block on the free list */ - if(last_fl->prev==NULL && last_fl->next==NULL) { - /* Double the new memory size */ - new_mem_size *=2; - - /* Truncate the free block */ - last_fl->size=H5HL_ALIGN(new_mem_size-last_fl->offset); - new_mem_size=last_fl->offset+last_fl->size; - assert(last_fl->size>=H5HL_SIZEOF_FREE(f)); - } /* end if */ - else { - /* Set the size of the memory buffer to the start of the free list */ - new_mem_size=last_fl->offset; - - /* Eliminate the free block from the list */ - last_fl = H5HL_remove_free(heap, last_fl); - } /* end else */ - } /* end if */ - else { - /* Truncate the free block */ - last_fl->size=H5HL_ALIGN(new_mem_size-last_fl->offset); - new_mem_size=last_fl->offset+last_fl->size; - assert(last_fl->size>=H5HL_SIZEOF_FREE(f)); - assert(last_fl->size==H5HL_ALIGN(last_fl->size)); - } /* end else */ - - /* Resize the memory buffer */ - if(new_mem_size!=heap->mem_alloc) { - heap->mem_alloc=new_mem_size; - heap->chunk = H5FL_BLK_REALLOC(heap_chunk,heap->chunk, - (sizeof_hdr + new_mem_size)); - if (NULL==heap->chunk) - HGOTO_ERROR (H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed"); - } /* end if */ - } /* end if */ - } /* end if */ - } /* end if */ - - /* - * If the heap grew larger or smaller than disk storage then move the - * data segment of the heap to another contiguous block of - * disk storage. - */ - if (heap->mem_alloc != heap->disk_alloc) { - haddr_t old_addr = heap->addr, new_addr; - - /* Release old space on disk */ - H5_CHECK_OVERFLOW(heap->disk_alloc,size_t,hsize_t); - H5MF_xfree(f, H5FD_MEM_LHEAP, dxpl_id, old_addr, (hsize_t)heap->disk_alloc); - H5E_clear(NULL); /*don't really care if the free failed */ - - /* Allocate new space on disk */ - H5_CHECK_OVERFLOW(heap->mem_alloc,size_t,hsize_t); - if (HADDR_UNDEF==(new_addr=H5MF_alloc(f, H5FD_MEM_LHEAP, dxpl_id, - (hsize_t)heap->mem_alloc))) - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate file space for heap"); - heap->addr = new_addr; - - /* Set new size of block on disk */ - heap->disk_alloc = heap->mem_alloc; - } - - /* - * Write the header. - */ - p = heap->chunk; - fl=heap->freelist; - HDmemcpy(p, H5HL_MAGIC, H5HL_SIZEOF_MAGIC); - p += H5HL_SIZEOF_MAGIC; - *p++ = H5HL_VERSION; - *p++ = 0; /*reserved*/ - *p++ = 0; /*reserved*/ - *p++ = 0; /*reserved*/ - H5F_ENCODE_LENGTH(f, p, heap->mem_alloc); - H5F_ENCODE_LENGTH(f, p, fl ? fl->offset : H5HL_FREE_NULL); - H5F_addr_encode(f, &p, heap->addr); - - /* - * Write the free list. - */ - while (fl) { - assert (fl->offset == H5HL_ALIGN (fl->offset)); - p = heap->chunk + sizeof_hdr + fl->offset; - if (fl->next) { - H5F_ENCODE_LENGTH(f, p, fl->next->offset); - } else { - H5F_ENCODE_LENGTH(f, p, H5HL_FREE_NULL); - } - H5F_ENCODE_LENGTH(f, p, fl->size); - fl = fl->next; - } + /* Write the header */ + if (H5HL_serialize(f, heap, heap->chunk) < 0) + HGOTO_ERROR(H5E_BTREE, H5E_CANTSERIALIZE, FAIL, "unable to serialize local heap") - /* - * Copy buffer to disk. - */ + /* Copy buffer to disk */ hdr_end_addr = addr + (hsize_t)sizeof_hdr; + if (H5F_addr_eq(heap->addr, hdr_end_addr)) { /* The header and data are contiguous */ - if (H5F_block_write(f, H5FD_MEM_LHEAP, addr, - (sizeof_hdr+heap->disk_alloc), + if (H5F_block_write(f, H5FD_MEM_LHEAP, addr, (sizeof_hdr + heap->disk_alloc), dxpl_id, heap->chunk) < 0) - HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "unable to write heap header and data to file"); + HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "unable to write heap header and data to file") } else { - if (H5F_block_write(f, H5FD_MEM_LHEAP, addr, sizeof_hdr, - dxpl_id, heap->chunk)<0) - HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "unable to write heap header to file"); + if (H5F_block_write(f, H5FD_MEM_LHEAP, addr, sizeof_hdr, dxpl_id, heap->chunk) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "unable to write heap header to file") + if (H5F_block_write(f, H5FD_MEM_LHEAP, heap->addr, heap->disk_alloc, dxpl_id, heap->chunk + sizeof_hdr) < 0) - HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "unable to write heap data to file"); + HGOTO_ERROR(H5E_HEAP, H5E_WRITEERROR, FAIL, "unable to write heap data to file") } heap->cache_info.dirty = 0; } - /* - * Should we destroy the memory version? - */ + /* Should we destroy the memory version? */ if (destroy) { - if(H5HL_dest(f,heap)<0) - HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy local heap collection"); + if (H5HL_dest(f,heap) < 0) + HGOTO_ERROR(H5E_HEAP, H5E_CANTFREE, FAIL, "unable to destroy local heap collection") } done: - FUNC_LEAVE_NOAPI(ret_value); + FUNC_LEAVE_NOAPI(ret_value) } @@ -652,60 +735,121 @@ done: /*------------------------------------------------------------------------- - * Function: H5HL_peek + * Function: H5HL_protect + * + * Purpose: This function is a wrapper for the H5AC_protect call. The + * old H5HL_peek call (which this once was) wasn't "safe" + * for FPHDF5. (It'd get a read lock on an object but once + * it got that object, it'd release it keeping the old + * pointer value, which is no longer valid. This won't work + * since the pointer into some metdata block can become + * invalid.) + * + * N.B.: This function is always called in conjunction with + * the H5HL_offset_into function. The return from that + * function is the proper pointer to the heap's object. This + * is done so that the return from this function can be sent + * to H5HL_unprotect. + * + * Return: Success: Ptr to the object. The pointer points to a + * chunk of memory large enough to hold the + * object from the specified offset (usually the + * beginning of the object) to the end of the + * object. Do not attempt to read past the end + * of the object. + * Failure: NULL + * + * Programmer: Bill Wendling + * wendling@ncsa.uiuc.edu + * Sept. 17, 2003 * - * Purpose: This function is a more efficient version of H5HL_read. - * Instead of copying a heap object into a caller-supplied - * buffer, this function returns a pointer directly into the - * cache where the heap is being held. Thus, the return pointer - * is valid only until the next call to the cache. + * Modifications: * - * The address of the heap is ADDR in file F. OFFSET is the - * byte offset of the object from the beginning of the heap and - * may include an offset into the interior of the object. + *------------------------------------------------------------------------- + */ +const H5HL_t * +H5HL_protect(H5F_t *f, hid_t dxpl_id, haddr_t addr) +{ + H5HL_t *ret_value = NULL; + + FUNC_ENTER_NOAPI(H5HL_protect, NULL); + + /* check arguments */ + assert(f); + assert(H5F_addr_defined(addr)); + + if (NULL == (ret_value = H5AC_protect(f, dxpl_id, H5AC_LHEAP, addr, NULL, NULL, H5AC_READ))) + HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, NULL, "unable to load heap"); + +done: + FUNC_LEAVE_NOAPI(ret_value); +} + + +/*------------------------------------------------------------------------- + * Function: H5HL_offset_into * - * Return: Success: Ptr to the object. The pointer points to - * a chunk of memory large enough to hold the - * object from the specified offset (usually - * the beginning of the object) to the end - * of the object. Do not attempt to read past - * the end of the object. + * Purpose: Called directly after the call to H5HL_protect so that + * a pointer to the object in the heap can be got. * - * Failure: NULL + * Return: Success: Valid pointer. + * Failure: NULL * - * Programmer: Robb Matzke - * matzke@llnl.gov - * Jul 16 1997 + * Programmer: Bill Wendling + * wendling@ncsa.uiuc.edu + * Sept. 17, 2003 * * Modifications: - * Robb Matzke, 1999-07-28 - * The ADDR argument is passed by value. + * + *------------------------------------------------------------------------- + */ +void * +H5HL_offset_into(H5F_t *f, const H5HL_t *heap, size_t offset) +{ + /* + * We need to have called some other function before this to get a + * valid heap pointer. So, this can remain "FUNC_ENTER_NOINIT" + */ + FUNC_ENTER_NOINIT(H5HL_offset_into) + assert(f); + assert(heap); + assert(offset < heap->mem_alloc); + FUNC_LEAVE_NOAPI(heap->chunk + H5HL_SIZEOF_HDR(f) + offset) +} + + +/*------------------------------------------------------------------------- + * Function: H5HL_unprotect + * + * Purpose: Unprotect the data retrieved by the H5HL_protect call. + * + * Return: Success: SUCCEED + * Failure: FAIL + * + * Programmer: Bill Wendling + * wendling@ncsa.uiuc.edu + * Sept. 17, 2003 + * + * Modifications: + * *------------------------------------------------------------------------- */ -const void * -H5HL_peek(H5F_t *f, hid_t dxpl_id, haddr_t addr, size_t offset) +herr_t +H5HL_unprotect(H5F_t *f, hid_t dxpl_id, const H5HL_t *heap, haddr_t addr) { - H5HL_t *heap; - const void *ret_value; + herr_t ret_value = SUCCEED; - FUNC_ENTER_NOAPI(H5HL_peek, NULL); + FUNC_ENTER_NOAPI(H5HL_peek, FAIL); /* check arguments */ assert(f); + assert(heap); assert(H5F_addr_defined(addr)); - if (NULL == (heap = H5AC_protect(f, dxpl_id, H5AC_LHEAP, addr, NULL, NULL, H5AC_READ))) - HGOTO_ERROR(H5E_HEAP, H5E_CANTLOAD, NULL, "unable to load heap"); - - assert(offset < heap->mem_alloc); - - /* Set return value */ - ret_value = heap->chunk + H5HL_SIZEOF_HDR(f) + offset; + if (H5AC_unprotect(f, dxpl_id, H5AC_LHEAP, addr, heap, FALSE) != SUCCEED) + HDONE_ERROR(H5E_HEAP, H5E_PROTECT, FAIL, "unable to release object header"); done: - if (heap && H5AC_unprotect(f, dxpl_id, H5AC_LHEAP, addr, heap, FALSE) != SUCCEED) - HDONE_ERROR(H5E_HEAP, H5E_PROTECT, NULL, "unable to release object header"); - FUNC_LEAVE_NOAPI(ret_value); } |