From 46122852f1dfbbf4bac6f5a634452ff90c5635ce Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 21 May 2013 19:42:57 +0200 Subject: [PATCH] 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. --- .../private/package/hpkg/PackageReaderImpl.h | 4 +- headers/private/package/hpkg/ReaderImplBase.h | 25 +- .../kernel/file_systems/packagefs/Jamfile | 1 + .../packagefs/package/CachedDataReader.cpp | 455 ++++++++++++++++++ .../packagefs/package/CachedDataReader.h | 152 ++++++ .../packagefs/package/Package.cpp | 85 ++-- .../file_systems/packagefs/package/Package.h | 1 + src/kits/package/hpkg/PackageReaderImpl.cpp | 3 +- src/kits/package/hpkg/PackageWriterImpl.cpp | 2 +- src/kits/package/hpkg/ReaderImplBase.cpp | 75 ++- .../package/hpkg/RepositoryReaderImpl.cpp | 3 +- .../file_systems/packagefs/packagefs.cpp | 2 +- 12 files changed, 757 insertions(+), 51 deletions(-) create mode 100644 src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.cpp create mode 100644 src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.h diff --git a/headers/private/package/hpkg/PackageReaderImpl.h b/headers/private/package/hpkg/PackageReaderImpl.h index d506b39ded..5ef4e085c0 100644 --- a/headers/private/package/hpkg/PackageReaderImpl.h +++ b/headers/private/package/hpkg/PackageReaderImpl.h @@ -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: diff --git a/headers/private/package/hpkg/ReaderImplBase.h b/headers/private/package/hpkg/ReaderImplBase.h index 07fc8796b6..53248127ea 100644 --- a/headers/private/package/hpkg/ReaderImplBase.h +++ b/headers/private/package/hpkg/ReaderImplBase.h @@ -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; diff --git a/src/add-ons/kernel/file_systems/packagefs/Jamfile b/src/add-ons/kernel/file_systems/packagefs/Jamfile index e2061a8a75..5102cd4be9 100644 --- a/src/add-ons/kernel/file_systems/packagefs/Jamfile +++ b/src/add-ons/kernel/file_systems/packagefs/Jamfile @@ -24,6 +24,7 @@ HAIKU_PACKAGE_FS_SOURCES = AttributeIndex.cpp AutoPackageAttributes.cpp BlockBufferPoolKernel.cpp + CachedDataReader.cpp DebugSupport.cpp Dependency.cpp Directory.cpp diff --git a/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.cpp b/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.cpp new file mode 100644 index 0000000000..fa6f3fc990 --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.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 + +#include + +#include +#include +#include + +#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 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 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 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 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(); + } +} diff --git a/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.h b/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.h new file mode 100644 index 0000000000..7cba87ca4e --- /dev/null +++ b/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.h @@ -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 + +#include +#include +#include +#include + + +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 { + 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& 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 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 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 diff --git a/src/add-ons/kernel/file_systems/packagefs/package/Package.cpp b/src/add-ons/kernel/file_systems/packagefs/package/Package.cpp index 13d5be71b6..541ec3f50f 100644 --- a/src/add-ons/kernel/file_systems/packagefs/package/Package.cpp +++ b/src/add-ons/kernel/file_systems/packagefs/package/Package.cpp @@ -25,6 +25,7 @@ #include #include +#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; } diff --git a/src/add-ons/kernel/file_systems/packagefs/package/Package.h b/src/add-ons/kernel/file_systems/packagefs/package/Package.h index b75996bf04..caf2fb4097 100644 --- a/src/add-ons/kernel/file_systems/packagefs/package/Package.h +++ b/src/add-ons/kernel/file_systems/packagefs/package/Package.h @@ -100,6 +100,7 @@ private: struct HeapReader; struct HeapReaderV1; struct HeapReaderV2; + struct CachingPackageReader; private: mutex fLock; diff --git a/src/kits/package/hpkg/PackageReaderImpl.cpp b/src/kits/package/hpkg/PackageReaderImpl.cpp index acee5c17f2..83ff096de7 100644 --- a/src/kits/package/hpkg/PackageReaderImpl.cpp +++ b/src/kits/package/hpkg/PackageReaderImpl.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include @@ -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, diff --git a/src/kits/package/hpkg/PackageWriterImpl.cpp b/src/kits/package/hpkg/PackageWriterImpl.cpp index 7c519c310b..b7fc37a80f 100644 --- a/src/kits/package/hpkg/PackageWriterImpl.cpp +++ b/src/kits/package/hpkg/PackageWriterImpl.cpp @@ -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; diff --git a/src/kits/package/hpkg/ReaderImplBase.cpp b/src/kits/package/hpkg/ReaderImplBase.cpp index ad83419906..ba4b0b56cd 100644 --- a/src/kits/package/hpkg/ReaderImplBase.cpp +++ b/src/kits/package/hpkg/ReaderImplBase.cpp @@ -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) { diff --git a/src/kits/package/hpkg/RepositoryReaderImpl.cpp b/src/kits/package/hpkg/RepositoryReaderImpl.cpp index f59cb8f494..98867ab81e 100644 --- a/src/kits/package/hpkg/RepositoryReaderImpl.cpp +++ b/src/kits/package/hpkg/RepositoryReaderImpl.cpp @@ -19,7 +19,6 @@ #include #include -#include #include @@ -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), diff --git a/src/system/boot/loader/file_systems/packagefs/packagefs.cpp b/src/system/boot/loader/file_systems/packagefs/packagefs.cpp index 6257d53a01..3ca86a4030 100644 --- a/src/system/boot/loader/file_systems/packagefs/packagefs.cpp +++ b/src/system/boot/loader/file_systems/packagefs/packagefs.cpp @@ -769,7 +769,7 @@ packagefs_mount_file(int fd, ::Directory*& _mountedDirectory) return B_NO_MEMORY; BReference volumeReference(volume, true); - error = volume->Init(fd, packageReader.HeapReader()); + error = volume->Init(fd, packageReader.RawHeapReader()); if (error != B_OK) RETURN_ERROR(error);