summaryrefslogtreecommitdiff
path: root/view/sharedcache/core/FileAccessorCache.h
blob: bb2f8084f99075710f954126759b9b90567b89e7 (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
#pragma once

#include <shared_mutex>

#include "MappedFileAccessor.h"

typedef uint32_t CacheAccessorID;

// TODO: We might want to make this more than just the path, for example
// TODO: We might want to make it unique to a view session (session id).
// Get a unique entry id for the given file path.
CacheAccessorID GetCacheAccessorID(const std::string& filePath);

class WeakFileAccessor;

class FileAccessorCache
{
	size_t m_cacheSize;
	std::mutex m_mutex;
	// NOTE: If we end up wanting to handle 1000's of files we should consider std::list.
	std::deque<CacheAccessorID> m_cache;
	std::unordered_map<CacheAccessorID, std::shared_ptr<MappedFileAccessor>> m_accessors;

	explicit FileAccessorCache(size_t cacheSize = 8);

	void EvictLastUsed();

public:
	static FileAccessorCache& Global();

	// Get a weak reference to a file accessor, the reference at this point is alive.
	// The reference is always alive at this point either because it is in the cache or it has been inserted in.
	// Subsequent calls to this might kill the backing file accessor resulting in the weak ref recreating the file
	// accessor and inserting itself back into its related cache.
	WeakFileAccessor Open(const std::string& filePath);

	// Adjust the cache size limit.
	// This will NOT evict current cache entries, as they are already available.
	// Any subsequent call to `Open` will assume this cache size, evicting until the size is equal to the cache size.
	void SetCacheSize(uint64_t size) { m_cacheSize = size; };
};

// Write log to be used in conjunction with `WeakFileAccessor` to re-apply written data to a "revived" file.
struct FileAccessorWriteLog
{
	// To persist writes to a file accessor being revived (within the lock() function)
	// we keep a list of writes that will be re-applied in the lock function.
	std::shared_mutex m_persistedMutex;
	std::unordered_map<size_t, uint64_t> m_persistedPointers;

	FileAccessorWriteLog() = default;

	// Add the pointer to the persisted pointers.
	void AddPointer(size_t address, size_t pointer);

	// Apply all logged writes to the given accessor.
	void ApplyWrites(MappedFileAccessor& accessor);
};

class WeakFileAccessor
{
	// Weak pointer to the mapped file accessor, once this is expired we will re-open.
	std::weak_ptr<MappedFileAccessor> m_weakPtr;
	// File path for re-opening if needed
	std::string m_filePath;

	// Used to re-add writes once the file accessor is "revived".
	std::shared_ptr<FileAccessorWriteLog> m_writeLog;

	// TODO: Store a weak_ptr/shared_ptr to FileAccessorCache? That way we dont access Global()
	// TODO: Only need to do the above if we want multiple caches.

public:
	explicit WeakFileAccessor(std::weak_ptr<MappedFileAccessor> weakPtr, std::string filePath) :
		m_weakPtr(std::move(weakPtr)), m_filePath(std::move(filePath)),
		m_writeLog(std::make_shared<FileAccessorWriteLog>())
	{}

	std::shared_ptr<MappedFileAccessor> lock();

	// Persists the written pointer within this weak file accessor.
	// This works as we expect the weak file accessor to be stored per virtual memory region.
	void WritePointer(size_t address, size_t pointer);
};