summaryrefslogtreecommitdiff
path: root/indra/llfilesystem/lldiskcache.h
blob: b60e74f8c9b9f7fab235e79cad5e53f6e1f17d1b (plain)
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
/**
 * @file lldiskcache.h
 * @brief The disk cache implementation declarations.
 *
 * @Description:
 * This code implements a disk cache using the following ideas:
 * 1/ The metadata for a file can be encapsulated in the filename.
      The filenames will be composed of the following fields:
        Prefix:     Used to identify the file as a part of the cache.
                    An additional reason for using a prefix is that it
                    might be possible, either accidentally or maliciously
                    to end up with the cache dir set to a non-cache
                    location such as your OS system dir or a work folder.
                    Purging files from that would obviously be a disaster
                    so this is an extra step to help avoid that scenario.
        ID:         Typically the asset ID (UUID) of the asset being
                    saved but can be anything valid for a filename
        Extra Info: A field for use in the future that can be used
                    to store extra identifiers - e.g. the discard
                    level of a JPEG2000 file
        Asset Type: A text string created from the LLAssetType enum
                    that identifies the type of asset being stored.
        .asset      A file extension of .asset is used to help
                    identify this as a Viewer asset file
 * 2/ The time of last access for a file can be updated instantly
 *    for file reads and automatically as part of the file writes.
 * 3/ The purge algorithm collects a list of all files in the
 *    directory, sorts them by date of last access (write) and then
 *    deletes any files based on age until the total size of all
 *    the files is less than the maximum size specified.
 * 4/ An LLSingleton idiom is used since there will only ever be
 *    a single cache and we want to access it from numerous places.
 * 5/ Performance on my modest system seems very acceptable. For
 *    example, in testing, I was able to purge a directory of
 *    10,000 files, deleting about half of them in ~ 1700ms. For
 *    the same sized directory of files, writing the last updated
 *    time to each took less than 600ms indicating that this
 *    important part of the mechanism has almost no overhead.
 *
 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2020, Linden Research, Inc.
 *
 * This library 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;
 * version 2.1 of the License only.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef _LLDISKCACHE
#define _LLDISKCACHE

#include "llsingleton.h"

class LLDiskCache :
    public LLParamSingleton<LLDiskCache>
{
    public:
        /**
         * Since this is using the LLSingleton pattern but we
         * want to allow the constructor to be called first
         * with various parameters, we also invoke the
         * LLParamSingleton idiom and use it to initialize
         * the class via a call in LLAppViewer.
         */
        LLSINGLETON(LLDiskCache,
                    /**
                     * The full name of the cache folder - typically a
                     * a child of the main Viewer cache directory. Defined
                     * by the setting at 'DiskCacheDirName'
                     */
                    const std::string cache_dir,
                    /**
                     * The maximum size of the cache in bytes - Based on the
                     * setting at 'CacheSize' and 'DiskCachePercentOfTotal'
                     */
                    const uintmax_t max_size_bytes,
                    /**
                     * A flag that enables extra cache debugging so that
                     * if there are bugs, we can ask uses to enable this
                     * setting and send us their logs
                     */
                    const bool enable_cache_debug_info);

        virtual ~LLDiskCache() = default;

    public:
        /**
         * Construct a filename and path to it based on the file meta data
         * (id, asset type, additional 'extra' info like discard level perhaps)
         * Worth pointing out that this function used to be in LLFileSystem but
         * so many things had to be pushed back there to accomodate it, that I
         * decided to move it here.  Still not sure that's completely right.
         */
        const std::string metaDataToFilepath(const std::string id,
                                             LLAssetType::EType at,
                                             const std::string extra_info);

        /**
         * Update the "last write time" of a file to "now". This must be called whenever a
         * file in the cache is read (not written) so that the last time the file was
         * accessed is up to date (This is used in the mechanism for purging the cache)
         */
        void updateFileAccessTime(const std::string file_path);

        /**
         * Purge the oldest items in the cache so that the combined size of all files
         * is no bigger than mMaxSizeBytes.
         *
         * WARNING: purge() is called by LLPurgeDiskCacheThread. As such it must
         * NOT touch any LLDiskCache data without introducing and locking a mutex!
         *
         * Purging the disk cache involves nontrivial work on the viewer's
         * filesystem. If called on the main thread, this causes a noticeable
         * freeze.
         */
        void purge();

        /**
         * Clear the cache by removing all the files in the specified cache
         * directory individually. Only the files that contain a prefix defined
         * by mCacheFilenamePrefix will be removed.
         */
        void clearCache();

        /**
         * Return some information about the cache for use in About Box etc.
         */
        const std::string getCacheInfo();

        void removeOldVFSFiles();

    private:
        /**
         * Utility function to gather the total size the files in a given
         * directory. Primarily used here to determine the directory size
         * before and after the cache purge
         */
        uintmax_t dirFileSize(const std::string dir);

        /**
         * Utility function to convert an LLAssetType enum into a
         * string that we use as part of the cache file filename
         */
        const std::string assetTypeToString(LLAssetType::EType at);

    private:
        /**
         * The maximum size of the cache in bytes. After purge is called, the
         * total size of the cache files in the cache directory will be
         * less than this value
         */
        uintmax_t mMaxSizeBytes;

        /**
         * The folder that holds the cached files. The consumer of this
         * class must avoid letting the user set this location as a malicious
         * setting could potentially point it at a non-cache directory (for example,
         * the Windows System dir) with disastrous results.
         */
        std::string mCacheDir;

        /**
         * The prefix inserted at the start of a cache file filename to
         * help identify it as a cache file. It's probably not required
         * (just the presence in the cache folder is enough) but I am
         * paranoid about the cache folder being set to something bad
         * like the users' OS system dir by mistake or maliciously and
         * this will help to offset any damage if that happens.
         */
        std::string mCacheFilenamePrefix;

        /**
         * When enabled, displays additional debugging information in
         * various parts of the code
         */
        bool mEnableCacheDebugInfo;
};

class LLPurgeDiskCacheThread : public LLThread
{
public:
    LLPurgeDiskCacheThread();

protected:
    void run() override;
};
#endif // _LLDISKCACHE