root/src/add-ons/kernel/file_systems/packagefs/package/CachedDataReader.h
/*
 * Copyright 2013-2014, 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::BDataReader;


class CachedDataReader : public BAbstractBufferedDataReader {
public:
                                                                CachedDataReader();
        virtual                                         ~CachedDataReader();

                        status_t                        Init(BAbstractBufferedDataReader* reader,
                                                                        off_t size);

        virtual status_t                        ReadDataToOutput(off_t offset, size_t size,
                                                                        BDataIO* 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, BDataIO* 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, BDataIO* 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