packagefs: Add caching for the package file heap reader
* ReaderImplBase: - Add virtual CreateCachedHeapReader() which can create a cached reader based on the given heap reader. - Rename HeapReader() to RawHeapReader() and add HeapReader() for the cached heap reader. - Add DetachHeapReader() to allow a clients to remove the heap reader(s) after deleting the ReaderImplBase object. * packagefs: - Add CachedDataReader class, which wraps a given BAbstractBufferedDataReader and provides caching for it using a VMCache. The implementation is based on the IOCache implementation. - Use CachedDataReader to wrap the heap reader. For file data that means they are cached twice -- in the heap reader cache and in the file cache -- but due to the heap reader using a VMCache as well, the pages will be recycled automatically anyway. For attribute data the cache should be very helpful, since they weren't cached at all before.
This commit is contained in:
parent
69a1f1f53f
commit
46122852f1
@ -40,7 +40,9 @@ public:
|
||||
uint64 HeapOffset() const;
|
||||
uint64 HeapSize() const;
|
||||
|
||||
PackageFileHeapReader* HeapReader() const
|
||||
PackageFileHeapReader* RawHeapReader() const
|
||||
{ return inherited::RawHeapReader(); }
|
||||
BAbstractBufferedDataReader* HeapReader() const
|
||||
{ return inherited::HeapReader(); }
|
||||
|
||||
protected:
|
||||
|
@ -26,6 +26,7 @@ namespace BPackageKit {
|
||||
namespace BHPKG {
|
||||
|
||||
|
||||
class BAbstractBufferedDataReader;
|
||||
class BErrorOutput;
|
||||
|
||||
|
||||
@ -71,8 +72,23 @@ protected:
|
||||
|
||||
BErrorOutput* ErrorOutput() const;
|
||||
|
||||
PackageFileHeapReader* HeapReader() const
|
||||
uint64 UncompressedHeapSize() const;
|
||||
|
||||
PackageFileHeapReader* RawHeapReader() const
|
||||
{ return fRawHeapReader; }
|
||||
BAbstractBufferedDataReader* HeapReader() const
|
||||
{ return fHeapReader; }
|
||||
// equals RawHeapReader(), if uncached
|
||||
|
||||
BAbstractBufferedDataReader* DetachHeapReader(
|
||||
PackageFileHeapReader** _rawHeapReader
|
||||
= NULL);
|
||||
// Detaches both raw and (if applicable)
|
||||
// cached heap reader. The called gains
|
||||
// ownership. The FD may need to be set on
|
||||
// the raw heap reader, if it shall be used
|
||||
// after destroying this object and Init()
|
||||
// has been called with keepFD == true.
|
||||
|
||||
protected:
|
||||
class AttributeHandlerContext;
|
||||
@ -95,6 +111,10 @@ protected:
|
||||
uint32 chunkSize, off_t offset,
|
||||
uint64 compressedSize,
|
||||
uint64 uncompressedSize);
|
||||
virtual status_t CreateCachedHeapReader(
|
||||
PackageFileHeapReader* heapReader,
|
||||
BAbstractBufferedDataReader*&
|
||||
_cachedReader);
|
||||
status_t InitSection(PackageFileSection& section,
|
||||
uint64 endOffset, uint64 length,
|
||||
uint64 maxSaneLength, uint64 stringsLength,
|
||||
@ -156,7 +176,8 @@ private:
|
||||
int fFD;
|
||||
bool fOwnsFD;
|
||||
|
||||
PackageFileHeapReader* fHeapReader;
|
||||
PackageFileHeapReader* fRawHeapReader;
|
||||
BAbstractBufferedDataReader* fHeapReader;
|
||||
|
||||
PackageFileSection* fCurrentSection;
|
||||
|
||||
|
@ -24,6 +24,7 @@ HAIKU_PACKAGE_FS_SOURCES =
|
||||
AttributeIndex.cpp
|
||||
AutoPackageAttributes.cpp
|
||||
BlockBufferPoolKernel.cpp
|
||||
CachedDataReader.cpp
|
||||
DebugSupport.cpp
|
||||
Dependency.cpp
|
||||
Directory.cpp
|
||||
|
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright 2010-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "CachedDataReader.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <package/hpkg/DataOutput.h>
|
||||
|
||||
#include <util/AutoLock.h>
|
||||
#include <vm/VMCache.h>
|
||||
#include <vm/vm_page.h>
|
||||
|
||||
#include "DebugSupport.h"
|
||||
|
||||
|
||||
using BPackageKit::BHPKG::BBufferDataReader;
|
||||
using BPackageKit::BHPKG::BBufferDataOutput;
|
||||
|
||||
|
||||
static inline bool
|
||||
page_physical_number_less(const vm_page* a, const vm_page* b)
|
||||
{
|
||||
return a->physical_page_number < b->physical_page_number;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - PagesDataOutput
|
||||
|
||||
|
||||
struct CachedDataReader::PagesDataOutput : public BDataOutput {
|
||||
PagesDataOutput(vm_page** pages, size_t pageCount)
|
||||
:
|
||||
fPages(pages),
|
||||
fPageCount(pageCount),
|
||||
fInPageOffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual status_t WriteData(const void* buffer, size_t size)
|
||||
{
|
||||
while (size > 0) {
|
||||
if (fPageCount == 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
size_t toCopy = std::min(size, B_PAGE_SIZE - fInPageOffset);
|
||||
status_t error = vm_memcpy_to_physical(
|
||||
fPages[0]->physical_page_number * B_PAGE_SIZE + fInPageOffset,
|
||||
buffer, toCopy, false);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
fInPageOffset += toCopy;
|
||||
if (fInPageOffset == B_PAGE_SIZE) {
|
||||
fInPageOffset = 0;
|
||||
fPages++;
|
||||
fPageCount--;
|
||||
}
|
||||
|
||||
buffer = (const char*)buffer + toCopy;
|
||||
size -= toCopy;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
vm_page** fPages;
|
||||
size_t fPageCount;
|
||||
size_t fInPageOffset;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - CachedDataReader
|
||||
|
||||
|
||||
CachedDataReader::CachedDataReader()
|
||||
:
|
||||
fReader(NULL),
|
||||
fCache(NULL),
|
||||
fCacheLineLockers()
|
||||
{
|
||||
mutex_init(&fLock, "packagefs cached reader");
|
||||
}
|
||||
|
||||
|
||||
CachedDataReader::~CachedDataReader()
|
||||
{
|
||||
if (fCache != NULL) {
|
||||
fCache->Lock();
|
||||
fCache->ReleaseRefAndUnlock();
|
||||
}
|
||||
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CachedDataReader::Init(BAbstractBufferedDataReader* reader, off_t size)
|
||||
{
|
||||
fReader = reader;
|
||||
|
||||
status_t error = fCacheLineLockers.Init();
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
error = VMCacheFactory::CreateNullCache(VM_PRIORITY_SYSTEM,
|
||||
fCache);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
AutoLocker<VMCache> locker(fCache);
|
||||
|
||||
error = fCache->Resize(size, VM_PRIORITY_SYSTEM);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CachedDataReader::ReadData(off_t offset, void* buffer, size_t size)
|
||||
{
|
||||
BBufferDataOutput output(buffer, size);
|
||||
return ReadDataToOutput(offset, size, &output);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CachedDataReader::ReadDataToOutput(off_t offset, size_t size,
|
||||
BDataOutput* output)
|
||||
{
|
||||
if (offset > fCache->virtual_end
|
||||
|| (off_t)size > fCache->virtual_end - offset) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
return B_OK;
|
||||
|
||||
while (size > 0) {
|
||||
// the start of the current cache line
|
||||
off_t lineOffset = (offset / kCacheLineSize) * kCacheLineSize;
|
||||
|
||||
// intersection of request and cache line
|
||||
off_t cacheLineEnd = std::min(lineOffset + (off_t)kCacheLineSize,
|
||||
fCache->virtual_end);
|
||||
size_t requestLineLength
|
||||
= std::min(cacheLineEnd - offset, (off_t)size);
|
||||
|
||||
// transfer the data of the cache line
|
||||
status_t error = _ReadCacheLine(lineOffset, cacheLineEnd - lineOffset,
|
||||
offset, requestLineLength, output);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
offset = cacheLineEnd;
|
||||
size -= requestLineLength;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CachedDataReader::_ReadCacheLine(off_t lineOffset, size_t lineSize,
|
||||
off_t requestOffset, size_t requestLength, BDataOutput* output)
|
||||
{
|
||||
PRINT("CachedDataReader::_ReadCacheLine(%" B_PRIdOFF ", %zu, %" B_PRIdOFF
|
||||
", %zu, %p\n", lineOffset, lineSize, requestOffset, requestLength,
|
||||
output);
|
||||
|
||||
CacheLineLocker cacheLineLocker(this, lineOffset);
|
||||
|
||||
// check whether there are pages of the cache line and the mark them used
|
||||
page_num_t firstPageOffset = lineOffset / B_PAGE_SIZE;
|
||||
page_num_t linePageCount = (lineSize + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
|
||||
vm_page* pages[kPagesPerCacheLine] = {};
|
||||
|
||||
AutoLocker<VMCache> cacheLocker(fCache);
|
||||
|
||||
page_num_t firstMissing = 0;
|
||||
page_num_t lastMissing = 0;
|
||||
page_num_t missingPages = 0;
|
||||
page_num_t pageOffset = firstPageOffset;
|
||||
|
||||
VMCachePagesTree::Iterator it = fCache->pages.GetIterator(pageOffset, true,
|
||||
true);
|
||||
while (pageOffset < firstPageOffset + linePageCount) {
|
||||
vm_page* page = it.Next();
|
||||
page_num_t currentPageOffset;
|
||||
if (page == NULL
|
||||
|| page->cache_offset >= firstPageOffset + linePageCount) {
|
||||
page = NULL;
|
||||
currentPageOffset = firstPageOffset + linePageCount;
|
||||
} else
|
||||
currentPageOffset = page->cache_offset;
|
||||
|
||||
if (pageOffset < currentPageOffset) {
|
||||
// pages are missing
|
||||
if (missingPages == 0)
|
||||
firstMissing = pageOffset;
|
||||
lastMissing = currentPageOffset - 1;
|
||||
missingPages += currentPageOffset - pageOffset;
|
||||
|
||||
for (; pageOffset < currentPageOffset; pageOffset++)
|
||||
pages[pageOffset - firstPageOffset] = NULL;
|
||||
}
|
||||
|
||||
if (page != NULL) {
|
||||
pages[pageOffset++ - firstPageOffset] = page;
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
vm_page_set_state(page, PAGE_STATE_UNUSED);
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
}
|
||||
}
|
||||
|
||||
cacheLocker.Unlock();
|
||||
|
||||
if (missingPages > 0) {
|
||||
// TODO: If the missing pages range doesn't intersect with the request, just
|
||||
// satisfy the request and don't read anything at all.
|
||||
// There are pages of the cache line missing. We have to allocate fresh
|
||||
// ones.
|
||||
|
||||
// reserve
|
||||
vm_page_reservation reservation;
|
||||
if (!vm_page_try_reserve_pages(&reservation, missingPages,
|
||||
VM_PRIORITY_SYSTEM)) {
|
||||
_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
|
||||
|
||||
// fall back to uncached transfer
|
||||
return fReader->ReadDataToOutput(requestOffset, requestLength,
|
||||
output);
|
||||
}
|
||||
|
||||
// Allocate the missing pages and remove the already existing pages in
|
||||
// the range from the cache. We're going to read/write the whole range
|
||||
// anyway.
|
||||
for (pageOffset = firstMissing; pageOffset <= lastMissing;
|
||||
pageOffset++) {
|
||||
page_num_t index = pageOffset - firstPageOffset;
|
||||
if (pages[index] == NULL) {
|
||||
pages[index] = vm_page_allocate_page(&reservation,
|
||||
PAGE_STATE_UNUSED);
|
||||
DEBUG_PAGE_ACCESS_END(pages[index]);
|
||||
} else {
|
||||
cacheLocker.Lock();
|
||||
fCache->RemovePage(pages[index]);
|
||||
cacheLocker.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
missingPages = lastMissing - firstMissing + 1;
|
||||
|
||||
// add the pages to the cache
|
||||
cacheLocker.Lock();
|
||||
|
||||
for (pageOffset = firstMissing; pageOffset <= lastMissing;
|
||||
pageOffset++) {
|
||||
page_num_t index = pageOffset - firstPageOffset;
|
||||
fCache->InsertPage(pages[index], (off_t)pageOffset * B_PAGE_SIZE);
|
||||
}
|
||||
|
||||
cacheLocker.Unlock();
|
||||
|
||||
// read in the missing pages
|
||||
status_t error = _ReadIntoPages(pages, firstMissing - firstPageOffset,
|
||||
missingPages);
|
||||
if (error != B_OK) {
|
||||
ERROR("CachedDataReader::_ReadCacheLine(): Failed to read into "
|
||||
"cache (offset: %" B_PRIdOFF ", length: %" B_PRIuSIZE "), "
|
||||
"trying uncached read (offset: %" B_PRIdOFF ", length: %"
|
||||
B_PRIuSIZE ")\n", (off_t)firstMissing * B_PAGE_SIZE,
|
||||
(size_t)missingPages * B_PAGE_SIZE, requestOffset,
|
||||
requestLength);
|
||||
|
||||
_DiscardPages(pages, firstMissing - firstPageOffset, missingPages);
|
||||
|
||||
// Try again using an uncached transfer
|
||||
return fReader->ReadDataToOutput(requestOffset, requestLength,
|
||||
output);
|
||||
}
|
||||
}
|
||||
|
||||
// write data to output
|
||||
status_t error = _WritePages(pages, requestOffset - lineOffset,
|
||||
requestLength, output);
|
||||
_CachePages(pages, 0, linePageCount);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/*! Frees all pages in given range of the \a pages array.
|
||||
\c NULL entries in the range are OK. All non \c NULL entries must refer
|
||||
to pages with \c PAGE_STATE_UNUSED. The pages may belong to \c fCache or
|
||||
may not have a cache.
|
||||
\c fCache must not be locked.
|
||||
*/
|
||||
void
|
||||
CachedDataReader::_DiscardPages(vm_page** pages, size_t firstPage,
|
||||
size_t pageCount)
|
||||
{
|
||||
PRINT("%p->CachedDataReader::_DiscardPages(%" B_PRIuSIZE ", %" B_PRIuSIZE
|
||||
")\n", this, firstPage, pageCount);
|
||||
|
||||
AutoLocker<VMCache> cacheLocker(fCache);
|
||||
|
||||
for (size_t i = firstPage; i < firstPage + pageCount; i++) {
|
||||
vm_page* page = pages[i];
|
||||
if (page == NULL)
|
||||
continue;
|
||||
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
|
||||
ASSERT_PRINT(page->State() == PAGE_STATE_UNUSED,
|
||||
"page: %p @! page -m %p", page, page);
|
||||
|
||||
if (page->Cache() != NULL)
|
||||
fCache->RemovePage(page);
|
||||
|
||||
vm_page_free(NULL, page);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Marks all pages in the given range of the \a pages array cached.
|
||||
There must not be any \c NULL entries in the given array range. All pages
|
||||
must belong to \c cache and have state \c PAGE_STATE_UNUSED.
|
||||
\c fCache must not be locked.
|
||||
*/
|
||||
void
|
||||
CachedDataReader::_CachePages(vm_page** pages, size_t firstPage,
|
||||
size_t pageCount)
|
||||
{
|
||||
PRINT("%p->CachedDataReader::_CachePages(%" B_PRIuSIZE ", %" B_PRIuSIZE
|
||||
")\n", this, firstPage, pageCount);
|
||||
|
||||
AutoLocker<VMCache> cacheLocker(fCache);
|
||||
|
||||
for (size_t i = firstPage; i < firstPage + pageCount; i++) {
|
||||
vm_page* page = pages[i];
|
||||
ASSERT(page != NULL);
|
||||
ASSERT_PRINT(page->State() == PAGE_STATE_UNUSED
|
||||
&& page->Cache() == fCache,
|
||||
"page: %p @! page -m %p", page, page);
|
||||
|
||||
DEBUG_PAGE_ACCESS_START(page);
|
||||
vm_page_set_state(page, PAGE_STATE_CACHED);
|
||||
DEBUG_PAGE_ACCESS_END(page);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Writes the contents of pages in \c pages to \a output.
|
||||
\param pages The pages array.
|
||||
\param pagesRelativeOffset The offset relative to \a pages[0] where to
|
||||
start writing from.
|
||||
\param requestLength The number of bytes to write.
|
||||
\param output The output to which the data shall be written.
|
||||
\return \c B_OK, if writing went fine, another error code otherwise.
|
||||
*/
|
||||
status_t
|
||||
CachedDataReader::_WritePages(vm_page** pages, size_t pagesRelativeOffset,
|
||||
size_t requestLength, BDataOutput* output)
|
||||
{
|
||||
PRINT("%p->CachedDataReader::_WritePages(%" B_PRIuSIZE ", %" B_PRIuSIZE
|
||||
", %p)\n", this, pagesRelativeOffset, requestLength, output);
|
||||
|
||||
size_t firstPage = pagesRelativeOffset / B_PAGE_SIZE;
|
||||
size_t endPage = (pagesRelativeOffset + requestLength + B_PAGE_SIZE - 1)
|
||||
/ B_PAGE_SIZE;
|
||||
|
||||
// fallback to copying individual pages
|
||||
size_t inPageOffset = pagesRelativeOffset % B_PAGE_SIZE;
|
||||
for (size_t i = firstPage; i < endPage; i++) {
|
||||
// map the page
|
||||
void* handle;
|
||||
addr_t address;
|
||||
status_t error = vm_get_physical_page(
|
||||
pages[i]->physical_page_number * B_PAGE_SIZE, &address,
|
||||
&handle);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// write the page's data
|
||||
size_t toCopy = std::min(B_PAGE_SIZE - inPageOffset, requestLength);
|
||||
error = output->WriteData((uint8*)(address + inPageOffset), toCopy);
|
||||
|
||||
// unmap the page
|
||||
vm_put_physical_page(address, handle);
|
||||
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
inPageOffset = 0;
|
||||
requestLength -= toCopy;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
CachedDataReader::_ReadIntoPages(vm_page** pages, size_t firstPage,
|
||||
size_t pageCount)
|
||||
{
|
||||
PagesDataOutput output(pages + firstPage, pageCount);
|
||||
|
||||
off_t firstPageOffset = (off_t)pages[firstPage]->cache_offset
|
||||
* B_PAGE_SIZE;
|
||||
generic_size_t requestLength = std::min(
|
||||
firstPageOffset + (off_t)pageCount * B_PAGE_SIZE,
|
||||
fCache->virtual_end)
|
||||
- firstPageOffset;
|
||||
|
||||
return fReader->ReadDataToOutput(firstPageOffset, requestLength, &output);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CachedDataReader::_LockCacheLine(CacheLineLocker* lineLocker)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
CacheLineLocker* otherLineLocker
|
||||
= fCacheLineLockers.Lookup(lineLocker->Offset());
|
||||
if (otherLineLocker == NULL) {
|
||||
fCacheLineLockers.Insert(lineLocker);
|
||||
return;
|
||||
}
|
||||
|
||||
// queue and wait
|
||||
otherLineLocker->Queue().Add(lineLocker);
|
||||
lineLocker->Wait(fLock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CachedDataReader::_UnlockCacheLine(CacheLineLocker* lineLocker)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
fCacheLineLockers.Remove(lineLocker);
|
||||
|
||||
if (CacheLineLocker* nextLineLocker = lineLocker->Queue().RemoveHead()) {
|
||||
nextLineLocker->Queue().MoveFrom(&lineLocker->Queue());
|
||||
fCacheLineLockers.Insert(nextLineLocker);
|
||||
nextLineLocker->WakeUp();
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef HEAP_CACHE_H
|
||||
#define HEAP_CACHE_H
|
||||
|
||||
|
||||
#include <package/hpkg/DataReader.h>
|
||||
|
||||
#include <condition_variable.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
#include <vm/vm_types.h>
|
||||
|
||||
|
||||
using BPackageKit::BHPKG::BAbstractBufferedDataReader;
|
||||
using BPackageKit::BHPKG::BDataOutput;
|
||||
using BPackageKit::BHPKG::BDataReader;
|
||||
|
||||
|
||||
class CachedDataReader : public BAbstractBufferedDataReader {
|
||||
public:
|
||||
CachedDataReader();
|
||||
virtual ~CachedDataReader();
|
||||
|
||||
status_t Init(BAbstractBufferedDataReader* reader,
|
||||
off_t size);
|
||||
|
||||
virtual status_t ReadData(off_t offset, void* buffer,
|
||||
size_t size);
|
||||
virtual status_t ReadDataToOutput(off_t offset, size_t size,
|
||||
BDataOutput* output);
|
||||
|
||||
private:
|
||||
class CacheLineLocker
|
||||
: public DoublyLinkedListLinkImpl<CacheLineLocker> {
|
||||
public:
|
||||
CacheLineLocker(CachedDataReader* reader, off_t cacheLineOffset)
|
||||
:
|
||||
fReader(reader),
|
||||
fOffset(cacheLineOffset)
|
||||
{
|
||||
fReader->_LockCacheLine(this);
|
||||
}
|
||||
|
||||
~CacheLineLocker()
|
||||
{
|
||||
fReader->_UnlockCacheLine(this);
|
||||
}
|
||||
|
||||
off_t Offset() const
|
||||
{
|
||||
return fOffset;
|
||||
}
|
||||
|
||||
CacheLineLocker*& HashNext()
|
||||
{
|
||||
return fHashNext;
|
||||
}
|
||||
|
||||
DoublyLinkedList<CacheLineLocker>& Queue()
|
||||
{
|
||||
return fQueue;
|
||||
}
|
||||
|
||||
void Wait(mutex& lock)
|
||||
{
|
||||
fWaitCondition.Init(this, "cached reader line locker");
|
||||
ConditionVariableEntry waitEntry;
|
||||
fWaitCondition.Add(&waitEntry);
|
||||
mutex_unlock(&lock);
|
||||
waitEntry.Wait();
|
||||
mutex_lock(&lock);
|
||||
}
|
||||
|
||||
void WakeUp()
|
||||
{
|
||||
fWaitCondition.NotifyOne();
|
||||
}
|
||||
|
||||
private:
|
||||
CachedDataReader* fReader;
|
||||
off_t fOffset;
|
||||
CacheLineLocker* fHashNext;
|
||||
DoublyLinkedList<CacheLineLocker> fQueue;
|
||||
ConditionVariable fWaitCondition;
|
||||
};
|
||||
|
||||
friend class CacheLineLocker;
|
||||
|
||||
struct LockerHashDefinition {
|
||||
typedef off_t KeyType;
|
||||
typedef CacheLineLocker ValueType;
|
||||
|
||||
size_t HashKey(off_t key) const
|
||||
{
|
||||
return size_t(key / kCacheLineSize);
|
||||
}
|
||||
|
||||
size_t Hash(const CacheLineLocker* value) const
|
||||
{
|
||||
return HashKey(value->Offset());
|
||||
}
|
||||
|
||||
bool Compare(off_t key, const CacheLineLocker* value) const
|
||||
{
|
||||
return value->Offset() == key;
|
||||
}
|
||||
|
||||
CacheLineLocker*& GetLink(CacheLineLocker* value) const
|
||||
{
|
||||
return value->HashNext();
|
||||
}
|
||||
};
|
||||
|
||||
typedef BOpenHashTable<LockerHashDefinition> LockerTable;
|
||||
|
||||
struct PagesDataOutput;
|
||||
|
||||
private:
|
||||
status_t _ReadCacheLine(off_t lineOffset,
|
||||
size_t lineSize, off_t requestOffset,
|
||||
size_t requestLength, BDataOutput* output);
|
||||
|
||||
void _DiscardPages(vm_page** pages, size_t firstPage,
|
||||
size_t pageCount);
|
||||
void _CachePages(vm_page** pages, size_t firstPage,
|
||||
size_t pageCount);
|
||||
status_t _WritePages(vm_page** pages,
|
||||
size_t pagesRelativeOffset,
|
||||
size_t requestLength, BDataOutput* output);
|
||||
status_t _ReadIntoPages(vm_page** pages,
|
||||
size_t firstPage, size_t pageCount);
|
||||
|
||||
void _LockCacheLine(CacheLineLocker* lineLocker);
|
||||
void _UnlockCacheLine(CacheLineLocker* lineLocker);
|
||||
|
||||
private:
|
||||
static const size_t kCacheLineSize = 64 * 1024;
|
||||
static const size_t kPagesPerCacheLine
|
||||
= kCacheLineSize / B_PAGE_SIZE;
|
||||
|
||||
private:
|
||||
mutex fLock;
|
||||
BAbstractBufferedDataReader* fReader;
|
||||
VMCache* fCache;
|
||||
LockerTable fCacheLineLockers;
|
||||
};
|
||||
|
||||
|
||||
#endif // HEAP_CACHE_H
|
@ -25,6 +25,7 @@
|
||||
#include <package/hpkg/v1/PackageReaderImpl.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "CachedDataReader.h"
|
||||
#include "DebugSupport.h"
|
||||
#include "GlobalFactory.h"
|
||||
#include "PackageDirectory.h"
|
||||
@ -645,8 +646,8 @@ private:
|
||||
// #pragma mark - HeapReaderV2
|
||||
|
||||
|
||||
struct Package::HeapReaderV2 : public HeapReader,
|
||||
private BAbstractBufferedDataReader, private BErrorOutput {
|
||||
struct Package::HeapReaderV2 : public HeapReader, public CachedDataReader,
|
||||
private BErrorOutput {
|
||||
public:
|
||||
HeapReaderV2()
|
||||
:
|
||||
@ -668,6 +669,11 @@ public:
|
||||
fHeapReader->SetErrorOutput(this);
|
||||
fHeapReader->SetFD(fd);
|
||||
|
||||
status_t error = CachedDataReader::Init(fHeapReader,
|
||||
fHeapReader->UncompressedHeapSize());
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -683,20 +689,6 @@ public:
|
||||
.CreatePackageDataReader(this, data.DataV2(), _reader);
|
||||
}
|
||||
|
||||
private:
|
||||
// BAbstractBufferedDataReader
|
||||
|
||||
virtual status_t ReadData(off_t offset, void* buffer, size_t size)
|
||||
{
|
||||
return fHeapReader->ReadData(offset, buffer, size);
|
||||
}
|
||||
|
||||
virtual status_t ReadDataToOutput(off_t offset, size_t size,
|
||||
BDataOutput* output)
|
||||
{
|
||||
return fHeapReader->ReadDataToOutput(offset, size, output);
|
||||
}
|
||||
|
||||
private:
|
||||
// BErrorOutput
|
||||
|
||||
@ -713,6 +705,52 @@ private:
|
||||
// #pragma mark - Package
|
||||
|
||||
|
||||
struct Package::CachingPackageReader : public PackageReaderImpl {
|
||||
CachingPackageReader(BErrorOutput* errorOutput)
|
||||
:
|
||||
PackageReaderImpl(errorOutput),
|
||||
fCachedHeapReader(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
~CachingPackageReader()
|
||||
{
|
||||
delete fCachedHeapReader;
|
||||
}
|
||||
|
||||
virtual status_t CreateCachedHeapReader(
|
||||
PackageFileHeapReader* rawHeapReader,
|
||||
BAbstractBufferedDataReader*& _cachedReader)
|
||||
{
|
||||
fCachedHeapReader = new(std::nothrow) HeapReaderV2;
|
||||
if (fCachedHeapReader == NULL)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
|
||||
status_t error = fCachedHeapReader->Init(rawHeapReader, FD());
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
_cachedReader = fCachedHeapReader;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
HeapReaderV2* DetachCachedHeapReader()
|
||||
{
|
||||
DetachHeapReader();
|
||||
|
||||
HeapReaderV2* cachedHeapReader = fCachedHeapReader;
|
||||
fCachedHeapReader = NULL;
|
||||
return cachedHeapReader;
|
||||
}
|
||||
|
||||
private:
|
||||
HeapReaderV2* fCachedHeapReader;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - Package
|
||||
|
||||
|
||||
Package::Package(::Volume* volume, dev_t deviceID, ino_t nodeID)
|
||||
:
|
||||
fVolume(volume),
|
||||
@ -775,7 +813,7 @@ Package::Load()
|
||||
|
||||
// try current package file format version
|
||||
{
|
||||
PackageReaderImpl packageReader(&errorOutput);
|
||||
CachingPackageReader packageReader(&errorOutput);
|
||||
status_t error = packageReader.Init(fd, false,
|
||||
BHPKG::B_HPKG_READER_DONT_PRINT_VERSION_MISMATCH_MESSAGE);
|
||||
if (error == B_OK) {
|
||||
@ -789,17 +827,8 @@ Package::Load()
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// create a heap reader
|
||||
HeapReaderV2* heapReader = new(std::nothrow) HeapReaderV2;
|
||||
if (heapReader == NULL)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
|
||||
error = heapReader->Init(packageReader.HeapReader(), fd);
|
||||
if (error != B_OK) {
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
fHeapReader = heapReader;
|
||||
// get the heap reader
|
||||
fHeapReader = packageReader.DetachCachedHeapReader();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,7 @@ private:
|
||||
struct HeapReader;
|
||||
struct HeapReaderV1;
|
||||
struct HeapReaderV2;
|
||||
struct CachingPackageReader;
|
||||
|
||||
private:
|
||||
mutex fLock;
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <package/hpkg/PackageData.h>
|
||||
#include <package/hpkg/PackageEntry.h>
|
||||
#include <package/hpkg/PackageEntryAttribute.h>
|
||||
#include <package/hpkg/PackageFileHeapReader.h>
|
||||
#include <package/hpkg/ZlibDecompressor.h>
|
||||
|
||||
|
||||
@ -340,7 +339,7 @@ PackageReaderImpl::Init(int fd, bool keepFD, uint32 flags)
|
||||
fd, keepFD, header, flags);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
fHeapSize = HeapReader()->UncompressedHeapSize();
|
||||
fHeapSize = UncompressedHeapSize();
|
||||
|
||||
// init package attributes section
|
||||
error = InitSection(fPackageAttributesSection, fHeapSize,
|
||||
|
@ -636,7 +636,7 @@ PackageWriterImpl::_Init(const char* fileName,
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
fHeapWriter->Reinit(packageReader.HeapReader());
|
||||
fHeapWriter->Reinit(packageReader.RawHeapReader());
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
|
@ -543,6 +543,7 @@ ReaderImplBase::ReaderImplBase(const char* fileType, BErrorOutput* errorOutput)
|
||||
fErrorOutput(errorOutput),
|
||||
fFD(-1),
|
||||
fOwnsFD(false),
|
||||
fRawHeapReader(NULL),
|
||||
fHeapReader(NULL),
|
||||
fCurrentSection(NULL),
|
||||
fScratchBuffer(NULL),
|
||||
@ -554,6 +555,8 @@ ReaderImplBase::ReaderImplBase(const char* fileType, BErrorOutput* errorOutput)
|
||||
ReaderImplBase::~ReaderImplBase()
|
||||
{
|
||||
delete fHeapReader;
|
||||
if (fRawHeapReader != fHeapReader)
|
||||
delete fRawHeapReader;
|
||||
|
||||
if (fOwnsFD && fFD >= 0)
|
||||
close(fFD);
|
||||
@ -562,21 +565,24 @@ ReaderImplBase::~ReaderImplBase()
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReaderImplBase::_Init(int fd, bool keepFD)
|
||||
uint64
|
||||
ReaderImplBase::UncompressedHeapSize() const
|
||||
{
|
||||
fFD = fd;
|
||||
fOwnsFD = keepFD;
|
||||
return fRawHeapReader->UncompressedHeapSize();
|
||||
}
|
||||
|
||||
// allocate a scratch buffer
|
||||
fScratchBuffer = new(std::nothrow) uint8[kScratchBufferSize];
|
||||
if (fScratchBuffer == NULL) {
|
||||
fErrorOutput->PrintError("Error: Out of memory!\n");
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
fScratchBufferSize = kScratchBufferSize;
|
||||
|
||||
return B_OK;
|
||||
BAbstractBufferedDataReader*
|
||||
ReaderImplBase::DetachHeapReader(PackageFileHeapReader** _rawHeapReader)
|
||||
{
|
||||
BAbstractBufferedDataReader* heapReader = fHeapReader;
|
||||
fHeapReader = NULL;
|
||||
|
||||
if (_rawHeapReader != NULL)
|
||||
*_rawHeapReader = fRawHeapReader;
|
||||
fRawHeapReader = NULL;
|
||||
|
||||
return heapReader;
|
||||
}
|
||||
|
||||
|
||||
@ -589,9 +595,32 @@ ReaderImplBase::InitHeapReader(uint32 compression, uint32 chunkSize,
|
||||
return B_BAD_DATA;
|
||||
}
|
||||
|
||||
fHeapReader = new(std::nothrow) PackageFileHeapReader(fErrorOutput, fFD,
|
||||
fRawHeapReader = new(std::nothrow) PackageFileHeapReader(fErrorOutput, fFD,
|
||||
offset, compressedSize, uncompressedSize);
|
||||
return fHeapReader->Init();
|
||||
if (fRawHeapReader == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t error = fRawHeapReader->Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
error = CreateCachedHeapReader(fRawHeapReader, fHeapReader);
|
||||
if (error != B_OK) {
|
||||
if (error != B_NOT_SUPPORTED)
|
||||
return error;
|
||||
|
||||
fHeapReader = fRawHeapReader;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReaderImplBase::CreateCachedHeapReader(PackageFileHeapReader* heapReader,
|
||||
BAbstractBufferedDataReader*& _cachedReader)
|
||||
{
|
||||
return B_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
@ -797,6 +826,24 @@ ReaderImplBase::ParseAttributeTree(AttributeHandlerContext* context,
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReaderImplBase::_Init(int fd, bool keepFD)
|
||||
{
|
||||
fFD = fd;
|
||||
fOwnsFD = keepFD;
|
||||
|
||||
// allocate a scratch buffer
|
||||
fScratchBuffer = new(std::nothrow) uint8[kScratchBufferSize];
|
||||
if (fScratchBuffer == NULL) {
|
||||
fErrorOutput->PrintError("Error: Out of memory!\n");
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
fScratchBufferSize = kScratchBufferSize;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
ReaderImplBase::_ParseAttributeTree(AttributeHandlerContext* context)
|
||||
{
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <Message.h>
|
||||
|
||||
#include <package/hpkg/HPKGDefsPrivate.h>
|
||||
#include <package/hpkg/PackageFileHeapReader.h>
|
||||
#include <package/hpkg/RepositoryContentHandler.h>
|
||||
|
||||
|
||||
@ -80,7 +79,7 @@ RepositoryReaderImpl::Init(int fd, bool keepFD)
|
||||
|
||||
// init package attributes section
|
||||
error = InitSection(fPackageAttributesSection,
|
||||
HeapReader()->UncompressedHeapSize(),
|
||||
UncompressedHeapSize(),
|
||||
B_BENDIAN_TO_HOST_INT64(header.packages_length),
|
||||
kMaxPackageAttributesSize,
|
||||
B_BENDIAN_TO_HOST_INT64(header.packages_strings_length),
|
||||
|
@ -769,7 +769,7 @@ packagefs_mount_file(int fd, ::Directory*& _mountedDirectory)
|
||||
return B_NO_MEMORY;
|
||||
BReference<PackageVolume> volumeReference(volume, true);
|
||||
|
||||
error = volume->Init(fd, packageReader.HeapReader());
|
||||
error = volume->Init(fd, packageReader.RawHeapReader());
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user