From 1529f584670ec421b6c6258698a8aefbbd658c3c Mon Sep 17 00:00:00 2001 From: Andrew Lewycky Date: Fri, 17 Mar 2000 15:08:25 +0000 Subject: [PATCH] Rewrite the BIGBLOCKFILE implementation for better performance. --- dlls/ole32/stg_bigblockfile.c | 843 +++++++++++++++++++----------------------- dlls/ole32/storage32.h | 6 +- 2 files changed, 383 insertions(+), 466 deletions(-) diff --git a/dlls/ole32/stg_bigblockfile.c b/dlls/ole32/stg_bigblockfile.c index d251738aef6..e02b076e8e3 100644 --- a/dlls/ole32/stg_bigblockfile.c +++ b/dlls/ole32/stg_bigblockfile.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "winbase.h" #include "winerror.h" @@ -39,66 +40,107 @@ DEFAULT_DEBUG_CHANNEL(storage) * class. */ -/*** - * Itdentifies a single big block and the related - * information - */ -struct BigBlock +/* We map in PAGE_SIZE-sized chunks. Must be a multiple of 4096. */ +#define PAGE_SIZE 131072 + +#define BLOCKS_PER_PAGE (PAGE_SIZE / BIG_BLOCK_SIZE) + +/* We keep a list of recently-discarded pages. This controls the + * size of that list. */ +#define MAX_VICTIM_PAGES 16 + +/* This structure provides one bit for each block in a page. + * Use BIGBLOCKFILE_{Test,Set,Clear}Bit to manipulate it. */ +typedef struct { - BigBlock * next; - DWORD index; - DWORD access_mode; - LPVOID lpBlock; -}; + unsigned int bits[BLOCKS_PER_PAGE / (CHAR_BIT * sizeof(unsigned int))]; +} BlockBits; /*** * This structure identifies the paged that are mapped * from the file and their position in memory. It is * also used to hold a reference count to those pages. + * + * page_index identifies which PAGE_SIZE chunk from the + * file this mapping represents. (The mappings are always + * PAGE_SIZE-aligned.) */ struct MappedPage { - MappedPage * next; - DWORD number; - int ref; - LPVOID lpBytes; -}; + MappedPage *next; + MappedPage *prev; -#define PAGE_SIZE 131072 -#define BLOCKS_PER_PAGE 256 + DWORD page_index; + LPVOID lpBytes; + LONG refcnt; -#define NUMBER_OF_MAPPED_PAGES 100 + BlockBits readable_blocks; + BlockBits writable_blocks; +}; /*********************************************************** * Prototypes for private methods */ static void* BIGBLOCKFILE_GetMappedView(LPBIGBLOCKFILE This, - DWORD pagenum, - DWORD desired_access); + DWORD page_index); static void BIGBLOCKFILE_ReleaseMappedPage(LPBIGBLOCKFILE This, - DWORD pagenum, - DWORD access); + MappedPage *page); static void BIGBLOCKFILE_FreeAllMappedPages(LPBIGBLOCKFILE This); +static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This); +static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This); static void* BIGBLOCKFILE_GetBigBlockPointer(LPBIGBLOCKFILE This, ULONG index, DWORD desired_access); -static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer(LPBIGBLOCKFILE This, - void* pBlock); -static void BIGBLOCKFILE_RemoveBlock(LPBIGBLOCKFILE This, - ULONG index); -static BigBlock* BIGBLOCKFILE_AddBigBlock(LPBIGBLOCKFILE This, - ULONG index); -static BigBlock* BIGBLOCKFILE_CreateBlock(ULONG index); +static MappedPage* BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This, + void* pBlock); +static MappedPage* BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This, + ULONG page_index); static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags); static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile); static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt); -static void BIGBLOCKFILE_RemoveAllBlocks(LPBIGBLOCKFILE This); + +/* Note that this evaluates a and b multiple times, so don't + * pass expressions with side effects. */ +#define ROUND_UP(a, b) ((((a) + (b) - 1)/(b))*(b)) + +/*********************************************************** + * Blockbits functions. + */ +static inline BOOL BIGBLOCKFILE_TestBit(const BlockBits *bb, + unsigned int index) +{ + unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int)); + unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int)); + + return bb->bits[array_index] & (1 << bit_index); +} + +static inline void BIGBLOCKFILE_SetBit(BlockBits *bb, unsigned int index) +{ + unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int)); + unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int)); + + bb->bits[array_index] |= (1 << bit_index); +} + +static inline void BIGBLOCKFILE_ClearBit(BlockBits *bb, unsigned int index) +{ + unsigned int array_index = index / (CHAR_BIT * sizeof(unsigned int)); + unsigned int bit_index = index % (CHAR_BIT * sizeof(unsigned int)); + + bb->bits[array_index] &= ~(1 << bit_index); +} + +static inline void BIGBLOCKFILE_Zero(BlockBits *bb) +{ + memset(bb->bits, 0, sizeof(bb->bits)); +} /****************************************************************************** * BIGBLOCKFILE_Construct * * Construct a big block file. Create the file mapping object. - * Create the read only mapped pages list, the writeable mapped page list + * Create the read only mapped pages list, the writable mapped page list * and the blocks in use list. */ BigBlockFile * BIGBLOCKFILE_Construct( @@ -121,9 +163,10 @@ BigBlockFile * BIGBLOCKFILE_Construct( This->blocksize = blocksize; - /* initialize the block list - */ - This->headblock = NULL; + This->maplist = NULL; + This->victimhead = NULL; + This->victimtail = NULL; + This->num_victim_pages = 0; if (This->fileBased) { @@ -175,20 +218,12 @@ static BOOL BIGBLOCKFILE_FileInit(LPBIGBLOCKFILE This, HANDLE hFile) return FALSE; } - This->filesize.s.LowPart = GetFileSize(This->hfile, NULL); + This->filesize.s.LowPart = GetFileSize(This->hfile, + &This->filesize.s.HighPart); - /* create the mapped pages list - */ - This->maplisthead = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage)); + This->maplist = NULL; - if (This->maplisthead == NULL) - { - CloseHandle(This->hfilemap); - CloseHandle(This->hfile); - return FALSE; - } - - This->maplisthead->next = NULL; + TRACE("file len %lu\n", This->filesize.s.LowPart); return TRUE; } @@ -202,7 +237,6 @@ static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt) { This->hfile = 0; This->hfilemap = NULL; - This->maplisthead = NULL; /* * Retrieve the handle to the byte array from the LockByte object. @@ -226,6 +260,8 @@ static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt) This->pbytearray = GlobalLock(This->hbytearray); + TRACE("mem on %p len %lu\n", This->pbytearray, This->filesize.s.LowPart); + return TRUE; } @@ -237,15 +273,10 @@ static BOOL BIGBLOCKFILE_MemInit(LPBIGBLOCKFILE This, ILockBytes* plkbyt) void BIGBLOCKFILE_Destructor( LPBIGBLOCKFILE This) { + BIGBLOCKFILE_FreeAllMappedPages(This); + if (This->fileBased) { - /* unmap all views and destroy the mapped page list - */ - BIGBLOCKFILE_FreeAllMappedPages(This); - HeapFree(GetProcessHeap(), 0, This->maplisthead); - - /* close all open handles - */ CloseHandle(This->hfilemap); CloseHandle(This->hfile); } @@ -255,11 +286,6 @@ void BIGBLOCKFILE_Destructor( ILockBytes_Release(This->pLkbyt); } - /* - * Destroy the blocks list. - */ - BIGBLOCKFILE_RemoveAllBlocks(This); - /* destroy this */ HeapFree(GetProcessHeap(), 0, This); @@ -288,10 +314,13 @@ void* BIGBLOCKFILE_GetROBigBlock( * validate the block index * */ - if ((This->blocksize * (index + 1)) > - (This->filesize.s.LowPart + - (This->blocksize - (This->filesize.s.LowPart % This->blocksize)))) - return 0; + if (This->blocksize * (index + 1) + > ROUND_UP(This->filesize.s.LowPart, This->blocksize)) + { + TRACE("out of range %lu vs %lu\n", This->blocksize * (index + 1), + This->filesize.s.LowPart); + return NULL; + } return BIGBLOCKFILE_GetBigBlockPointer(This, index, FILE_MAP_READ); } @@ -336,37 +365,17 @@ void* BIGBLOCKFILE_GetBigBlock(LPBIGBLOCKFILE This, ULONG index) */ void BIGBLOCKFILE_ReleaseBigBlock(LPBIGBLOCKFILE This, void *pBlock) { - DWORD page_num; - BigBlock* theBigBlock; + MappedPage *page; - if (pBlock == NULL) - return; - - /* - * get the block from the block list - */ - theBigBlock = BIGBLOCKFILE_GetBigBlockFromPointer(This, pBlock); + if (pBlock == NULL) + return; - if (theBigBlock == NULL) - return; + page = BIGBLOCKFILE_GetPageFromPointer(This, pBlock); - if (This->fileBased) - { - /* - * find out which page this block is in - */ - page_num = theBigBlock->index / BLOCKS_PER_PAGE; - - /* - * release this page - */ - BIGBLOCKFILE_ReleaseMappedPage(This, page_num, theBigBlock->access_mode); - } + if (page == NULL) + return; - /* - * remove block from list - */ - BIGBLOCKFILE_RemoveBlock(This, theBigBlock->index); + BIGBLOCKFILE_ReleaseMappedPage(This, page); } /****************************************************************************** @@ -380,19 +389,41 @@ void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize) if (This->filesize.s.LowPart == newSize.s.LowPart) return; + TRACE("from %lu to %lu\n", This->filesize.s.LowPart, newSize.s.LowPart); + /* + * unmap all views, must be done before call to SetEndFile + */ + BIGBLOCKFILE_UnmapAllMappedPages(This); + if (This->fileBased) { - /* - * unmap all views, must be done before call to SetEndFile - */ - BIGBLOCKFILE_FreeAllMappedPages(This); - + char buf[10]; + /* * close file-mapping object, must be done before call to SetEndFile */ CloseHandle(This->hfilemap); This->hfilemap = NULL; - + + /* + * BEGIN HACK + * This fixes a bug when saving through smbfs. + * smbmount a Windows shared directory, save a structured storage file + * to that dir: crash. + * + * The problem is that the SetFilePointer-SetEndOfFile combo below + * doesn't always succeed. The file is not grown. It seems like the + * operation is cached. By doing the WriteFile, the file is actually + * grown on disk. + * This hack is only needed when saving to smbfs. + */ + memset(buf, '0', 10); + SetFilePointer(This->hfile, newSize.s.LowPart, NULL, FILE_BEGIN); + WriteFile(This->hfile, buf, 10, NULL, NULL); + /* + * END HACK + */ + /* * set the new end of file */ @@ -424,13 +455,10 @@ void BIGBLOCKFILE_SetSize(LPBIGBLOCKFILE This, ULARGE_INTEGER newSize) This->pbytearray = GlobalLock(This->hbytearray); } - /* - * empty the blocks list. - */ - BIGBLOCKFILE_RemoveAllBlocks(This); - This->filesize.s.LowPart = newSize.s.LowPart; This->filesize.s.HighPart = newSize.s.HighPart; + + BIGBLOCKFILE_RemapAllMappedPages(This); } /****************************************************************************** @@ -445,287 +473,116 @@ ULARGE_INTEGER BIGBLOCKFILE_GetSize(LPBIGBLOCKFILE This) } /****************************************************************************** - * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE] + * BIGBLOCKFILE_AccessCheck [PRIVATE] * - * Returns a pointer to the specified block. + * block_index is the index within the page. */ -static void* BIGBLOCKFILE_GetBigBlockPointer( - LPBIGBLOCKFILE This, - ULONG index, - DWORD desired_access) +static BOOL BIGBLOCKFILE_AccessCheck(MappedPage *page, ULONG block_index, + DWORD desired_access) { - DWORD block_num; - void * pBytes; - BigBlock *aBigBlock; - - /* get the big block from the list or add it to the list - */ - aBigBlock = BIGBLOCKFILE_AddBigBlock(This, index); + assert(block_index < BLOCKS_PER_PAGE); - if (aBigBlock == NULL) - return NULL; + if (desired_access == FILE_MAP_READ) + { + if (BIGBLOCKFILE_TestBit(&page->writable_blocks, block_index)) + return FALSE; - /* we already have an address for this block - */ - if (aBigBlock->lpBlock != NULL) - { - /* make sure the desired access matches what we already have - */ - if (aBigBlock->access_mode == desired_access) - return aBigBlock->lpBlock; + BIGBLOCKFILE_SetBit(&page->readable_blocks, block_index); + } else - return NULL; - } - - /* - * else aBigBlock->lpBigBlock == NULL, it's a new block - */ - - if (This->fileBased) - { - DWORD page_num; - - /* find out which page this block is in - */ - page_num = index / BLOCKS_PER_PAGE; - - /* offset of the block in the page - */ - block_num = index % BLOCKS_PER_PAGE; - - /* get a pointer to the first byte in the page - */ - pBytes = BIGBLOCKFILE_GetMappedView(This, page_num, desired_access); - } - else - { - pBytes = This->pbytearray; - block_num = index; - } + { + assert(desired_access == FILE_MAP_WRITE); - if (pBytes == NULL) - return NULL; + if (BIGBLOCKFILE_TestBit(&page->readable_blocks, block_index)) + return FALSE; - /* initialize block - */ - aBigBlock->lpBlock = ((BYTE*)pBytes + (block_num*This->blocksize)); - aBigBlock->access_mode = desired_access; + BIGBLOCKFILE_SetBit(&page->writable_blocks, block_index); + } - return aBigBlock->lpBlock; + return TRUE; } /****************************************************************************** - * BIGBLOCKFILE_CreateBlock [PRIVATE] + * BIGBLOCKFILE_GetBigBlockPointer [PRIVATE] * - * Creates a node of the blocks list. + * Returns a pointer to the specified block. */ -static BigBlock* BIGBLOCKFILE_CreateBlock( - ULONG index) +static void* BIGBLOCKFILE_GetBigBlockPointer( + LPBIGBLOCKFILE This, + ULONG block_index, + DWORD desired_access) { - BigBlock *newBigBlock; + DWORD page_index = block_index / BLOCKS_PER_PAGE; + DWORD block_on_page = block_index % BLOCKS_PER_PAGE; - /* create new list node - */ - newBigBlock = HeapAlloc(GetProcessHeap(), 0, sizeof(BigBlock)); - - if (newBigBlock == NULL) - return NULL; + MappedPage *page = BIGBLOCKFILE_GetMappedView(This, page_index); + if (!page || !page->lpBytes) return NULL; - /* initialize node - */ - newBigBlock->index = index; - newBigBlock->lpBlock = NULL; + if (!BIGBLOCKFILE_AccessCheck(page, block_on_page, desired_access)) + { + BIGBLOCKFILE_ReleaseMappedPage(This, page); + return NULL; + } - return newBigBlock; + return (LPBYTE)page->lpBytes + (block_on_page * This->blocksize); } /****************************************************************************** - * BIGBLOCKFILE_AddBigBlock [PRIVATE] + * BIGBLOCKFILE_GetMappedPageFromPointer [PRIVATE] + * + * pBlock is a pointer to a block on a page. + * The page has to be on the in-use list. (As oppsed to the victim list.) * - * Returns the specified block from the blocks list. - * If the block is not found in the list, we will create it and add it to the - * list. + * Does not increment the usage count. */ -static BigBlock* BIGBLOCKFILE_AddBigBlock( - LPBIGBLOCKFILE This, - ULONG index) +static MappedPage *BIGBLOCKFILE_GetPageFromPointer(LPBIGBLOCKFILE This, + void *pBlock) { - BigBlock *current = This->headblock; - BigBlock *newBigBlock; - - if (current == NULL) /* empty list */ - { - newBigBlock = BIGBLOCKFILE_CreateBlock(index); - - if (newBigBlock != NULL) - { - newBigBlock->next = NULL; - This->headblock = newBigBlock; - } - - return newBigBlock; - } - else - { - /* - * special handling for head of the list - */ - - if (current->index == index) /* it's already here */ - return current; - else if (current->index > index) /* insertion at head of the list */ - { - newBigBlock = BIGBLOCKFILE_CreateBlock(index); - - if (newBigBlock != NULL) - { - newBigBlock->next = current; - This->headblock = newBigBlock; - } - - return newBigBlock; - } - } + MappedPage *page; - /* iterate through rest the list - */ - while (current->next != NULL) - { - if (current->next->index == index) /* found it */ + for (page = This->maplist; page != NULL; page = page->next) { - return current->next; - } - else if (current->next->index > index) /* it's not in the list */ - { - newBigBlock = BIGBLOCKFILE_CreateBlock(index); - - if (newBigBlock != NULL) - { - newBigBlock->next = current->next; - current->next = newBigBlock; - } + if ((LPBYTE)pBlock >= (LPBYTE)page->lpBytes + && (LPBYTE)pBlock <= (LPBYTE)page->lpBytes + PAGE_SIZE) + break; - return newBigBlock; } - else - current = current->next; - } - /* - * insertion at end of the list - */ - if (current->next == NULL) - { - newBigBlock = BIGBLOCKFILE_CreateBlock(index); - - if (newBigBlock != NULL) - { - newBigBlock->next = NULL; - current->next = newBigBlock; - } - - return newBigBlock; - } - - return NULL; + return page; } /****************************************************************************** - * BIGBLOCKFILE_RemoveAllBlocks [PRIVATE] + * BIGBLOCKFILE_FindPageInList [PRIVATE] * - * Removes all blocks from the blocks list. */ -static void BIGBLOCKFILE_RemoveAllBlocks( - LPBIGBLOCKFILE This) +static MappedPage *BIGBLOCKFILE_FindPageInList(MappedPage *head, + ULONG page_index) { - BigBlock *current; + for (; head != NULL; head = head->next) + { + if (head->page_index == page_index) + { + InterlockedIncrement(&head->refcnt); + break; + } + } + + return head; - while (This->headblock != NULL) - { - current = This->headblock; - This->headblock = current->next; - HeapFree(GetProcessHeap(), 0, current); - } } -/****************************************************************************** - * BIGBLOCKFILE_RemoveBlock [PRIVATE] - * - * Removes the specified block from the blocks list. - */ -static void BIGBLOCKFILE_RemoveBlock( - LPBIGBLOCKFILE This, - ULONG index) +static void BIGBLOCKFILE_UnlinkPage(MappedPage *page) { - BigBlock *current = This->headblock; - - /* - * empty list - */ - if (current == NULL) - return; - - /* - *special case: removing head of list - */ - if (current->index == index) - { - /* - * set new head free the old one - */ - This->headblock = current->next; - HeapFree(GetProcessHeap(), 0, current); - - return; - } - - /* - * iterate through rest of the list - */ - while (current->next != NULL) - { - if (current->next->index == index) /* found it */ - { - /* - * unlink the block and free the block - */ - current->next = current->next->next; - HeapFree(GetProcessHeap(), 0, current->next); - - return; - } - else - { - /* next node - */ - current = current->next; - } - } + if (page->next) page->next->prev = page->prev; + if (page->prev) page->prev->next = page->next; } -/****************************************************************************** - * BIGBLOCKFILE_GetBigBlockFromPointer [PRIVATE] - * - * Given a block pointer, this will return the corresponding block - * from the blocks list. - */ -static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer( - LPBIGBLOCKFILE This, - void* pBlock) +static void BIGBLOCKFILE_LinkHeadPage(MappedPage **head, MappedPage *page) { - BigBlock *current = This->headblock; - - while (current != NULL) - { - if (current->lpBlock == pBlock) - { - break; - } - else - current = current->next; - } - - return current; + if (*head) (*head)->prev = page; + page->next = *head; + page->prev = NULL; + *head = page; } /****************************************************************************** @@ -736,107 +593,120 @@ static BigBlock* BIGBLOCKFILE_GetBigBlockFromPointer( */ static void * BIGBLOCKFILE_GetMappedView( LPBIGBLOCKFILE This, - DWORD pagenum, - DWORD desired_access) + DWORD page_index) { - MappedPage* current = This->maplisthead; - ULONG count = 1; - BOOL found = FALSE; + MappedPage *page; - assert(This->maplisthead != NULL); + page = BIGBLOCKFILE_FindPageInList(This->maplist, page_index); + if (!page) + { + page = BIGBLOCKFILE_FindPageInList(This->victimhead, page_index); + if (page) + { + This->num_victim_pages--; + + BIGBLOCKFILE_Zero(&page->readable_blocks); + BIGBLOCKFILE_Zero(&page->writable_blocks); + } + } - /* - * Search for the page in the list. - */ - while ((found == FALSE) && (current->next != NULL)) - { - if (current->next->number == pagenum) + if (page) { - found = TRUE; + /* If the page is not already at the head of the list, move + * it there. (Also moves pages from victim to main list.) */ + if (This->maplist != page) + { + if (This->victimhead == page) This->victimhead = page->next; + if (This->victimtail == page) This->victimtail = page->prev; - /* - * If it's not already at the head of the list - * move it there. - */ - if (current != This->maplisthead) - { - MappedPage* temp = current->next; + BIGBLOCKFILE_UnlinkPage(page); - current->next = current->next->next; + BIGBLOCKFILE_LinkHeadPage(&This->maplist, page); + } - temp->next = This->maplisthead->next; - This->maplisthead->next = temp; - } + return page; } - /* - * The list is full and we haven't found it. - * Free the last element of the list because we'll add a new - * one at the head. - */ - if ((found == FALSE) && - (count >= NUMBER_OF_MAPPED_PAGES) && - (current->next != NULL)) + page = BIGBLOCKFILE_CreatePage(This, page_index); + if (!page) return NULL; + + BIGBLOCKFILE_LinkHeadPage(&This->maplist, page); + + return page; +} + +static BOOL BIGBLOCKFILE_MapPage(LPBIGBLOCKFILE This, MappedPage *page) +{ + DWORD lowoffset = PAGE_SIZE * page->page_index; + + if (This->fileBased) { - UnmapViewOfFile(current->next->lpBytes); + DWORD numBytesToMap; + DWORD desired_access; - HeapFree(GetProcessHeap(), 0, current->next); - current->next = NULL; - } + if (lowoffset + PAGE_SIZE > This->filesize.s.LowPart) + numBytesToMap = This->filesize.s.LowPart - lowoffset; + else + numBytesToMap = PAGE_SIZE; - if (current->next != NULL) - current = current->next; + if (This->flProtect == PAGE_READONLY) + desired_access = FILE_MAP_READ; + else + desired_access = FILE_MAP_WRITE; - count++; - } + page->lpBytes = MapViewOfFile(This->hfilemap, desired_access, 0, + lowoffset, numBytesToMap); + } + else + { + page->lpBytes = (LPBYTE)This->pbytearray + lowoffset; + } - /* - * Add the page at the head of the list. - */ - if (found == FALSE) - { - MappedPage* newMappedPage; - DWORD numBytesToMap; - DWORD hioffset = 0; - DWORD lowoffset = PAGE_SIZE * pagenum; + TRACE("mapped page %lu to %p\n", page->page_index, page->lpBytes); - newMappedPage = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage)); + return page->lpBytes != NULL; +} - if (newMappedPage == NULL) +static MappedPage *BIGBLOCKFILE_CreatePage(LPBIGBLOCKFILE This, + ULONG page_index) +{ + MappedPage *page; + + page = HeapAlloc(GetProcessHeap(), 0, sizeof(MappedPage)); + if (page == NULL) return NULL; - newMappedPage->number = pagenum; - newMappedPage->ref = 0; + page->page_index = page_index; + page->refcnt = 1; - newMappedPage->next = This->maplisthead->next; - This->maplisthead->next = newMappedPage; + page->next = NULL; + page->prev = NULL; - if (((pagenum + 1) * PAGE_SIZE) > This->filesize.s.LowPart) - numBytesToMap = This->filesize.s.LowPart - (pagenum * PAGE_SIZE); - else - numBytesToMap = PAGE_SIZE; + BIGBLOCKFILE_MapPage(This, page); - if (This->flProtect == PAGE_READONLY) - desired_access = FILE_MAP_READ; - else - desired_access = FILE_MAP_WRITE; + BIGBLOCKFILE_Zero(&page->readable_blocks); + BIGBLOCKFILE_Zero(&page->writable_blocks); - newMappedPage->lpBytes = MapViewOfFile(This->hfilemap, - desired_access, - hioffset, - lowoffset, - numBytesToMap); - } + return page; +} - /* - * The page we want should now be at the head of the list. - */ - assert(This->maplisthead->next != NULL); +static void BIGBLOCKFILE_UnmapPage(LPBIGBLOCKFILE This, MappedPage *page) +{ + TRACE("%ld at %p\n", page->page_index, page->lpBytes); + if (page->refcnt > 0) + ERR("unmapping inuse page %p\n", page->lpBytes); - current = This->maplisthead->next; - current->ref++; + if (This->fileBased && page->lpBytes) + UnmapViewOfFile(page->lpBytes); + + page->lpBytes = NULL; +} + +static void BIGBLOCKFILE_DeletePage(LPBIGBLOCKFILE This, MappedPage *page) +{ + BIGBLOCKFILE_UnmapPage(This, page); - return current->lpBytes; + HeapFree(GetProcessHeap(), 0, page); } /****************************************************************************** @@ -846,33 +716,54 @@ static void * BIGBLOCKFILE_GetMappedView( */ static void BIGBLOCKFILE_ReleaseMappedPage( LPBIGBLOCKFILE This, - DWORD pagenum, - DWORD access) + MappedPage *page) { - MappedPage* previous = This->maplisthead; - MappedPage* current; + assert(This != NULL); + assert(page != NULL); - assert(This->maplisthead->next != NULL); - - current = previous->next; - - /* search for the page in the list - */ - while (current != NULL) - { - if (current->number == pagenum) + /* If the page is no longer refenced, move it to the victim list. + * If the victim list is too long, kick somebody off. */ + if (!InterlockedDecrement(&page->refcnt)) { - /* decrement the reference count - */ - current->ref--; - return; + if (This->maplist == page) This->maplist = page->next; + + BIGBLOCKFILE_UnlinkPage(page); + + if (MAX_VICTIM_PAGES > 0) + { + if (This->num_victim_pages >= MAX_VICTIM_PAGES) + { + MappedPage *victim = This->victimtail; + if (victim) + { + This->victimtail = victim->prev; + if (This->victimhead == victim) + This->victimhead = victim->next; + + BIGBLOCKFILE_UnlinkPage(victim); + BIGBLOCKFILE_DeletePage(This, victim); + } + } + else This->num_victim_pages++; + + BIGBLOCKFILE_LinkHeadPage(&This->victimhead, page); + if (This->victimtail == NULL) This->victimtail = page; + } + else + BIGBLOCKFILE_DeletePage(This, page); } - else +} + +static void BIGBLOCKFILE_DeleteList(LPBIGBLOCKFILE This, MappedPage *list) +{ + while (list != NULL) { - previous = current; - current = current->next; + MappedPage *next = list->next; + + BIGBLOCKFILE_DeletePage(This, list); + + list = next; } - } } /****************************************************************************** @@ -884,21 +775,57 @@ static void BIGBLOCKFILE_ReleaseMappedPage( static void BIGBLOCKFILE_FreeAllMappedPages( LPBIGBLOCKFILE This) { - MappedPage * current = This->maplisthead->next; + BIGBLOCKFILE_DeleteList(This, This->maplist); + BIGBLOCKFILE_DeleteList(This, This->victimhead); - while (current != NULL) - { - /* Unmap views. - */ - UnmapViewOfFile(current->lpBytes); + This->maplist = NULL; + This->victimhead = NULL; + This->victimtail = NULL; + This->num_victim_pages = 0; +} - /* Free the nodes. - */ - This->maplisthead->next = current->next; - HeapFree(GetProcessHeap(), 0, current); +static void BIGBLOCKFILE_UnmapList(LPBIGBLOCKFILE This, MappedPage *list) +{ + for (; list != NULL; list = list->next) + { + BIGBLOCKFILE_UnmapPage(This, list); + } +} - current = This->maplisthead->next; - } +static void BIGBLOCKFILE_UnmapAllMappedPages(LPBIGBLOCKFILE This) +{ + BIGBLOCKFILE_UnmapList(This, This->maplist); + BIGBLOCKFILE_UnmapList(This, This->victimhead); +} + +static void BIGBLOCKFILE_RemapList(LPBIGBLOCKFILE This, MappedPage *list) +{ + while (list != NULL) + { + MappedPage *next = list->next; + + if (list->page_index * PAGE_SIZE > This->filesize.s.LowPart) + { + TRACE("discarding %lu\n", list->page_index); + + /* page is entirely outside of the file, delete it */ + BIGBLOCKFILE_UnlinkPage(list); + BIGBLOCKFILE_DeletePage(This, list); + } + else + { + /* otherwise, remap it */ + BIGBLOCKFILE_MapPage(This, list); + } + + list = next; + } +} + +static void BIGBLOCKFILE_RemapAllMappedPages(LPBIGBLOCKFILE This) +{ + BIGBLOCKFILE_RemapList(This, This->maplist); + BIGBLOCKFILE_RemapList(This, This->victimhead); } /**************************************************************************** @@ -909,18 +836,8 @@ static void BIGBLOCKFILE_FreeAllMappedPages( */ static DWORD BIGBLOCKFILE_GetProtectMode(DWORD openFlags) { - DWORD flProtect = PAGE_READONLY; - BOOL bSTGM_WRITE = ((openFlags & STGM_WRITE) == STGM_WRITE); - BOOL bSTGM_READWRITE = ((openFlags & STGM_READWRITE) == STGM_READWRITE); - BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE); - - if (bSTGM_READ) - flProtect = PAGE_READONLY; - - if ((bSTGM_WRITE) || (bSTGM_READWRITE)) - flProtect = PAGE_READWRITE; - - return flProtect; + if (openFlags & (STGM_WRITE | STGM_READWRITE)) + return PAGE_READWRITE; + else + return PAGE_READONLY; } - - diff --git a/dlls/ole32/storage32.h b/dlls/ole32/storage32.h index 2b4a616fed2..a4fe7a9cfa4 100644 --- a/dlls/ole32/storage32.h +++ b/dlls/ole32/storage32.h @@ -135,7 +135,6 @@ struct StgProperty */ typedef struct BigBlockFile BigBlockFile,*LPBIGBLOCKFILE; typedef struct MappedPage MappedPage,*LPMAPPEDPAGE; -typedef struct BigBlock BigBlock,*LPBIGBLOCK; struct BigBlockFile { @@ -145,11 +144,12 @@ struct BigBlockFile HANDLE hfile; HANDLE hfilemap; DWORD flProtect; - MappedPage *maplisthead; + MappedPage *maplist; + MappedPage *victimhead, *victimtail; + ULONG num_victim_pages; ILockBytes *pLkbyt; HGLOBAL hbytearray; LPVOID pbytearray; - BigBlock *headblock; }; /* -- 2.11.4.GIT