/
bank.h
346 lines (297 loc) · 11.1 KB
/
bank.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/** @file bank.h Abstract data bank with multi-tiered caching.
*
* @authors Copyright © 2013 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/
#ifndef LIBDENG2_BANK_H
#define LIBDENG2_BANK_H
#include <QObject>
#include <set>
#include "../libdeng2.h"
#include "../DotPath"
#include "../PathTree"
#include "../ISerializable"
#include "../Observers"
#include "../Time"
namespace de {
/**
* Abstract data bank with multi-tiered caching. Bank has the following
* characteristics:
* - Organises a set of data using a PathTree into a logical structure
* (e.g., a set of images in the UI style).
* - Generic caching mechanism applicable to any data objects.
* - Supports use of serialization to move objects from memory to persistent
* disk cache (not intended to replicate what the OS virtual memory does,
* but rather as a way to avoid repetitive preprocessing tasks on source
* data, e.g., map format conversion).
* - Utilizes concurrency by running tasks in background thread(s).
*
* Data items are identified using Paths. Serializable data items can be stored
* in a persistent cache ("hot storage"), from where they can be deserialized
* quickly in the future. @note Bank uses DotPath, so the path separator is
* assumed to be "." unless explicitly specified in the arguments.
*
* Data is kept cached on multiple levels: in memory, in hot storage
* (serialized in a file), or in cold storage (unprocessed source data). When a
* cache level's maximum size is reached, the oldest items are moved to a lower
* level (age determined by latest time of access).
*
* Bank supports both synchronous and asynchronous usage. (The latter requires
* use of Bank::BackgroundThread.)
*
* @par Thread-safety
*
* When using BackgroundThread, the Bank will perform all heavy lifting in
* separate worker threads (loading from source and (de)serialization). However,
* audience notifications always occur in the main thread (where the
* application event loop is running).
*
* A user of the Bank does not need to worry about cached items disappearing
* suddenly from memory: purging items to lower cache levels only occurs on
* request (Bank::purge()). Also, when an item is being removed from memory, it
* will receive a notification beforehand (IData::aboutToUnload()).
*
* @ingroup data
*/
class DENG2_PUBLIC Bank
{
public:
/// Failed to load data from the source. @ingroup errors
DENG2_ERROR(LoadError);
enum Flag
{
/**
* Separate thread used for managing the bank's data (loading, caching
* data). Requires data items and sources to be thread-safe.
*/
BackgroundThread = 0x1,
/**
* Do not use the hot storage to keep serialized copies of data items.
* This is useful if recreating the data from source is trivial.
*/
DisableHotStorage = 0x2,
/**
* When the Bank instance is destroyed (e.g., when the application
* shuts down) the contents of the hot storage cache level are cleared.
* If not specified, the hot storage is reused when the Bank is
* recreated later (unless the source data is newer).
*/
ClearHotStorageWhenBankDestroyed = 0x4,
DefaultFlags = DisableHotStorage
};
Q_DECLARE_FLAGS(Flags, Flag)
enum CacheLevel
{
/**
* Data is in its original storage container (e.g., source file) and it
* has to be processed, parsed or decoded before it can be used.
*/
InColdStorage = 0,
/**
* Data is not in memory but can be restored to memory relatively
* quickly, for instance just be reading a file. Uses serialization to
* convert the data to/from bytes.
*/
InHotStorage = 1,
/**
* Data is in memory and available for use immediately.
*/
InMemory = 2
};
enum Importance
{
Immediately, ///< Request handled before any queued tasks.
AfterQueued ///< Request handled after any queued tasks.
};
enum { Unlimited = -1 };
/**
* Interface for specifying the source of a data item.
*/
class ISource
{
public:
virtual ~ISource() {}
/**
* Returns the timestamp of the source data, which determines when the
* source data has last been modified. If the source is newer/older than
* cached copies, the cached data is discarded. If the returned time is
* Time::invalidTime(), no time checks are performed and the source
* data is considered immutable.
*/
virtual Time modifiedAt() const {
return Time::invalidTime();
}
};
/**
* Interface for a data item kept in memory.
*/
class IData
{
public:
virtual ~IData() {}
/// Returns an ISerializable pointer to the object. Required
/// for putting the data in hot storage.
virtual ISerializable *asSerializable() { return 0; }
/// Returns the size of the data that it occupies in memory.
virtual duint sizeInMemory() const { return 0; }
/// Called to notify the data that it is leaving the memory cache.
virtual void aboutToUnload() {}
};
typedef std::set<String> Names; // alphabetical order
/**
* Notified when a data item has been loaded to memory (cache level
* InMemory). May be called from the background thread, if one is running.
*/
DENG2_DEFINE_AUDIENCE2(Load, void bankLoaded(DotPath const &path))
/**
* Notified when a data item's cache level changes (in addition to the Load
* notification).
*/
DENG2_DEFINE_AUDIENCE2(CacheLevel, void bankCacheLevelChanged(DotPath const &path, CacheLevel level))
public:
/**
* Constructs a data bank.
*
* @param flags Flags that determine the behavior of the bank.
* @param hotStorageLocation Location where the hot storage files are kept.
*/
Bank(Flags const &flags = DefaultFlags, String const &hotStorageLocation = "/home/cache");
virtual ~Bank();
Flags flags() const;
/**
* Sets the folder where the hot storage (serialized data) is kept. A
* subfolder structure is created to match the elements of the data items'
* paths.
*
* @param location Hot storage location.
*/
void setHotStorageCacheLocation(String const &location);
/**
* Sets the maximum amount of data to keep in the hot storage.
* Default is Unlimited.
*
* @param maxBytes Maximum number of bytes. May also be Unlimited.
*/
void setHotStorageSize(dint64 maxBytes);
/**
* Sets the maximum amount of data to keep in memory. Default is Unlimited.
*
* @param maxBytes Maximum number of bytes. May also be Unlimited.
*/
void setMemoryCacheSize(dint64 maxBytes);
String hotStorageCacheLocation() const;
dint64 hotStorageSize() const;
dint64 memoryCacheSize() const;
/**
* Removes all items and their source information from the bank. This is
* not the same as unloading the data to a lower cache level. The data in
* the hot storage is unaffected.
*/
void clear();
/**
* Adds a new data item to the bank.
*
* @param path Identifier of the data.
* @param source Source information that is required for loading the data to
* memory. Bank takes ownership.
*/
void add(DotPath const &path, ISource *source);
void remove(DotPath const &path);
/**
* Determines whether the Bank contains an item (not a folder).
*
* @param path Identifier of the data.
*
* @return @c true or @c false.
*/
bool has(DotPath const &path) const;
/**
* Collects a list of the paths of all items in the bank.
*
* @param names Names.
*
* @return Number of names returned in @a names.
*/
dint allItems(Names &names) const;
PathTree const &index() const;
/**
* Requests a data item to be loaded. When using BackgroundThread, this is
* an asynchronous operation. When the data is available, audienceForLoad
* is notified. Loading is done using the source information specified in
* the call to add().
*
* @param path Identifier of the data.
* @param importance When/how to carry out the load request (with BackgroundThread).
*/
void load(DotPath const &path, Importance importance = Immediately);
void loadAll();
/**
* Returns the data of an item.
*
* If the item is presently not in memory, it will first be loaded (using
* Immediately; blocks until complete). The data is automatically
* marked as used at the current time, so it will not leave the memory
* cache level until sometime in the future.
*
* If the caller retains the IData reference for a long time, it is
* obligated to join audienceForLevelChanged to be notified when the data
* is removed from the cache.
*
* @param path Identifier of the data.
*
* @return IData instance. Ownership kept by the Bank.
*/
IData &data(DotPath const &path) const;
/**
* Moves a data item to a lower cache level. When using BackgroundThread,
* this is an asynchronous operation. audienceForLevelChanged is notified
* when the data has been stored.
*
* @param path Identifier of the data.
* @param toLevel Destination level for the data.
*/
void unload(DotPath const &path, CacheLevel toLevel = InHotStorage);
/**
* Moves all data items to a lower cache level.
*
* @param maxLevel Maximum cache level for all items.
*/
void unloadAll(CacheLevel maxLevel = InColdStorage);
/**
* Removes an item's cached data from all cache levels.
*
* @param path Identifier of the data.
*/
void clearFromCache(DotPath const &path);
/**
* Moves excess items on each cache level to lower level(s).
*/
void purge();
protected:
virtual IData *loadFromSource(ISource &source) = 0;
/**
* Construct a new concrete instance of the data item. Called before
* deserialization. Default implementation just returns NULL (seriliazation
* not supported).
*
* @return IData instance. Ownership given to caller.
*/
virtual IData *newData();
private:
DENG2_PRIVATE(d)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Bank::Flags)
} // namespace de
#endif // LIBDENG2_BANK_H