summaryrefslogtreecommitdiffstats
path: root/src/track/globaltrackcache.h
blob: e7fb049ad42a5631187786460688fd3592b56e9d (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
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
#pragma once


#include <map>
#include <unordered_map>

#include "track/track.h"
#include "track/trackref.h"


// forward declaration(s)
class GlobalTrackCache;

enum class GlobalTrackCacheLookupResult {
    NONE,
    HIT,
    MISS
};

// Find the updated location of a track in the database when
// the canonical location is no longer valid or accessible.
class /*interface*/ GlobalTrackCacheRelocator {
private:
    friend class GlobalTrackCache;
    // Try to determine and return the relocated file info
    // or otherwise return just the provided file info.
    virtual TrackFile relocateCachedTrack(
            TrackId trackId,
            TrackFile fileInfo) = 0;

protected:
    virtual ~GlobalTrackCacheRelocator() {}
};

typedef void (*deleteTrackFn_t)(Track*);

class GlobalTrackCacheEntry final {
    // We need to hold two shared pointers, the deletingPtr is
    // responsible for the lifetime of the Track object itself.
    // The second one counts the references outside Mixxx, if it
    // is not longer referenced, the track is saved and evicted
    // from the cache.
  public:
    class TrackDeleter {
    public:
        explicit TrackDeleter(deleteTrackFn_t deleteTrackFn = nullptr)
                : m_deleteTrackFn(deleteTrackFn) {
        }

        void operator()(Track* pTrack) const;

    private:
        deleteTrackFn_t m_deleteTrackFn;
    };

    explicit GlobalTrackCacheEntry(
            std::unique_ptr<Track, TrackDeleter> deletingPtr)
        : m_deletingPtr(std::move(deletingPtr)) {
    }
    GlobalTrackCacheEntry(const GlobalTrackCacheEntry& other) = delete;
    GlobalTrackCacheEntry(GlobalTrackCacheEntry&&) = default;

    void init(TrackWeakPointer savingWeakPtr) {
        // Uninitialized or expired
        DEBUG_ASSERT(!m_savingWeakPtr.lock());
        m_savingWeakPtr = std::move(savingWeakPtr);
    }

    Track* getPlainPtr() const {
        return m_deletingPtr.get();
    }

    TrackPointer lock() const {
        return m_savingWeakPtr.lock();
    }
    bool expired() const {
        return m_savingWeakPtr.expired();
    }

  private:
    std::unique_ptr<Track, TrackDeleter> m_deletingPtr;
    TrackWeakPointer m_savingWeakPtr;
};

typedef std::shared_ptr<GlobalTrackCacheEntry> GlobalTrackCacheEntryPointer;

class GlobalTrackCacheLocker {
public:
    GlobalTrackCacheLocker();
    GlobalTrackCacheLocker(const GlobalTrackCacheLocker&) = delete;
    GlobalTrackCacheLocker(GlobalTrackCacheLocker&&);
    virtual ~GlobalTrackCacheLocker();

    GlobalTrackCacheLocker& operator=(const GlobalTrackCacheLocker&) = delete;
    GlobalTrackCacheLocker& operator=(GlobalTrackCacheLocker&&) = delete;

    void unlockCache();

    void relocateCachedTracks(
            GlobalTrackCacheRelocator* /*nullable*/ pRelocator) const;

    void purgeTrackId(const TrackId& trackId);

    // Enforces the eviction of all cached tracks including invocation
    // of the callback and disables the cache permanently.
    void deactivateCache() const;

    bool isEmpty() const;

    // Lookup an existing Track object in the cache
    TrackPointer lookupTrackById(
            const TrackId& trackId) const;
    TrackPointer lookupTrackByRef(
            const TrackRef& trackRef) const;
    QSet<TrackId> getCachedTrackIds() const;

  private:
    friend class GlobalTrackCache;

    void lockCache();

protected:
    GlobalTrackCacheLocker(
            GlobalTrackCacheLocker&& moveable,
            GlobalTrackCacheLookupResult lookupResult,
            TrackPointer&& strongPtr,
            TrackRef&& trackRef);

    GlobalTrackCache* m_pInstance;
};

class GlobalTrackCacheResolver final: public GlobalTrackCacheLocker {
public:
    GlobalTrackCacheResolver(
                TrackFile fileInfo,
                SecurityTokenPointer pSecurityToken = SecurityTokenPointer());
    GlobalTrackCacheResolver(
                TrackFile fileInfo,
                TrackId trackId,
                SecurityTokenPointer pSecurityToken = SecurityTokenPointer());
    GlobalTrackCacheResolver(const GlobalTrackCacheResolver&) = delete;
    GlobalTrackCacheResolver(GlobalTrackCacheResolver&&) = default;

    GlobalTrackCacheLookupResult getLookupResult() const {
        return m_lookupResult;
    }

    const TrackPointer& getTrack() const {
        return m_strongPtr;
    }

    const TrackRef& getTrackRef() const {
        return m_trackRef;
    }

    void initTrackIdAndUnlockCache(TrackId trackId);

    GlobalTrackCacheResolver& operator=(const GlobalTrackCacheResolver&) = delete;
    GlobalTrackCacheResolver& operator=(GlobalTrackCacheResolver&&) = delete;

private:
    friend class GlobalTrackCache;
    GlobalTrackCacheResolver();

    void initLookupResult(
            GlobalTrackCacheLookupResult lookupResult,
            TrackPointer&& strongPtr,
            TrackRef&& trackRef);

    GlobalTrackCacheLookupResult m_lookupResult;

    TrackPointer m_strongPtr;

    TrackRef m_trackRef;
};

class /*interface*/ GlobalTrackCacheSaver {
private:
    friend class GlobalTrackCache;
    virtual void saveEvictedTrack(Track* pEvictedTrack) noexcept = 0;

protected:
    virtual ~GlobalTrackCacheSaver() {}
};

class GlobalTrackCache : public QObject {
    Q_OBJECT

public:
    static void createInstance(
            GlobalTrackCacheSaver* pSaver,
            // A custom deleter is only needed for tests without an event loop!
            deleteTrackFn_t deleteTrackFn = nullptr);
    // NOTE(uklotzde, 2018-02-20): We decided not to destroy the singular
    // instance during shutdown, because we are not able to guarantee that
    // all track references have been released before. Instead the singular
    // instance is only deactivated. The following function has only been
    // preserved for completeness.
    // See also: GlobalTrackCacheLocker::deactivateCache()
    static void destroyInstance();

    // Deleter callbacks for the smart-pointer
    static void evictAndSaveCachedTrack(GlobalTrackCacheEntryPointer cacheEntryPtr);

private slots:
    void evictAndSave(GlobalTrackCacheEntryPointer cacheEntryPtr);

private:
    friend class GlobalTrackCacheLocker;
    friend class GlobalTrackCacheResolver;

    GlobalTrackCache(
            GlobalTrackCacheSaver* pSaver,
            deleteTrackFn_t deleteTrackFn);
    ~GlobalTrackCache();

    void relocateTracks(
            GlobalTrackCacheRelocator* /*nullable*/ pRelocator);

    TrackPointer lookupById(
            const TrackId& trackId);
    TrackPointer lookupByRef(
            const TrackRef& trackRef);
    QSet<TrackId> getCachedTrackIds() const;

    TrackPointer revive(GlobalTrackCacheEntryPointer entryPtr);

    void resolve(
            GlobalTrackCacheResolver* /*in/out*/ pCacheResolver,
            TrackFile /*in*/ fileInfo,
            TrackId /*in*/ trackId,
            SecurityTokenPointer /*in*/ pSecurityToken);

    TrackRef initTrackId(
            const TrackPointer& strongPtr,
            TrackRef trackRef,
            TrackId trackId);

    void purgeTrackId(TrackId trackId);

    bool tryEvict(Track* plainPtr);
    bool isCached(Track* plainPtr) const;

    bool isEmpty() const;

    void deactivate();

    void saveEvictedTrack(Track* pEvictedTrack) const;

    // Managed by GlobalTrackCacheLocker
    mutable QMutex m_mutex;

    GlobalTrackCacheSaver* m_pSaver;

    deleteTrackFn_t m_deleteTrackFn;

    // This caches the unsaved Tracks by ID
    typedef std::unordered_map<TrackId, GlobalTrackCacheEntryPointer, TrackId::hash_fun_t> TracksById;
    TracksById m_tracksById;

    // This caches the unsaved Tracks by location
    typedef std::map<QString, GlobalTrackCacheEntryPointer> TracksByCanonicalLocation;
    TracksByCanonicalLocation m_tracksByCanonicalLocation;
};