1/*
2 * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5#ifndef HEAP_CACHE_H
6#define HEAP_CACHE_H
7
8
9#include <package/hpkg/DataReader.h>
10
11#include <condition_variable.h>
12#include <util/DoublyLinkedList.h>
13#include <util/OpenHashTable.h>
14#include <vm/vm_types.h>
15
16
17using BPackageKit::BHPKG::BAbstractBufferedDataReader;
18using BPackageKit::BHPKG::BDataReader;
19
20
21class CachedDataReader : public BAbstractBufferedDataReader {
22public:
23								CachedDataReader();
24	virtual						~CachedDataReader();
25
26			status_t			Init(BAbstractBufferedDataReader* reader,
27									off_t size);
28
29	virtual	status_t			ReadDataToOutput(off_t offset, size_t size,
30									BDataIO* output);
31
32private:
33			class CacheLineLocker
34				: public DoublyLinkedListLinkImpl<CacheLineLocker> {
35			public:
36				CacheLineLocker(CachedDataReader* reader, off_t cacheLineOffset)
37					:
38					fReader(reader),
39					fOffset(cacheLineOffset)
40				{
41					fReader->_LockCacheLine(this);
42				}
43
44				~CacheLineLocker()
45				{
46					fReader->_UnlockCacheLine(this);
47				}
48
49				off_t Offset() const
50				{
51					return fOffset;
52				}
53
54				CacheLineLocker*& HashNext()
55				{
56					return fHashNext;
57				}
58
59				DoublyLinkedList<CacheLineLocker>& Queue()
60				{
61					return fQueue;
62				}
63
64				void Wait(mutex& lock)
65				{
66					fWaitCondition.Init(this, "cached reader line locker");
67					ConditionVariableEntry waitEntry;
68					fWaitCondition.Add(&waitEntry);
69					mutex_unlock(&lock);
70					waitEntry.Wait();
71					mutex_lock(&lock);
72				}
73
74				void WakeUp()
75				{
76					fWaitCondition.NotifyOne();
77				}
78
79			private:
80				CachedDataReader*					fReader;
81				off_t								fOffset;
82				CacheLineLocker*					fHashNext;
83				DoublyLinkedList<CacheLineLocker>	fQueue;
84				ConditionVariable					fWaitCondition;
85			};
86
87			friend class CacheLineLocker;
88
89			struct LockerHashDefinition {
90				typedef off_t			KeyType;
91				typedef	CacheLineLocker	ValueType;
92
93				size_t HashKey(off_t key) const
94				{
95					return size_t(key / kCacheLineSize);
96				}
97
98				size_t Hash(const CacheLineLocker* value) const
99				{
100					return HashKey(value->Offset());
101				}
102
103				bool Compare(off_t key, const CacheLineLocker* value) const
104				{
105					return value->Offset() == key;
106				}
107
108				CacheLineLocker*& GetLink(CacheLineLocker* value) const
109				{
110					return value->HashNext();
111				}
112			};
113
114			typedef BOpenHashTable<LockerHashDefinition> LockerTable;
115
116			struct PagesDataOutput;
117
118private:
119			status_t			_ReadCacheLine(off_t lineOffset,
120									size_t lineSize, off_t requestOffset,
121							 		size_t requestLength, BDataIO* output);
122
123			void				_DiscardPages(vm_page** pages, size_t firstPage,
124									size_t pageCount);
125			void				_CachePages(vm_page** pages, size_t firstPage,
126									size_t pageCount);
127			status_t			_WritePages(vm_page** pages,
128									size_t pagesRelativeOffset,
129									size_t requestLength, BDataIO* output);
130			status_t			_ReadIntoPages(vm_page** pages,
131									size_t firstPage, size_t pageCount);
132
133			void				_LockCacheLine(CacheLineLocker* lineLocker);
134			void				_UnlockCacheLine(CacheLineLocker* lineLocker);
135
136private:
137			static const size_t kCacheLineSize = 64 * 1024;
138			static const size_t kPagesPerCacheLine
139				= kCacheLineSize / B_PAGE_SIZE;
140
141private:
142			mutex				fLock;
143			BAbstractBufferedDataReader* fReader;
144			VMCache*			fCache;
145			LockerTable			fCacheLineLockers;
146};
147
148
149#endif	// HEAP_CACHE_H
150