File: CacheMemory.cpp

package info (click to toggle)
libopenshot 0.5.0%2Bdfsg1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 31,228 kB
  • sloc: cpp: 32,692; python: 92; sh: 67; makefile: 21; ruby: 5
file content (343 lines) | stat: -rw-r--r-- 8,750 bytes parent folder | download | duplicates (2)
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
/**
 * @file
 * @brief Source file for Cache class
 * @author Jonathan Thomas <jonathan@openshot.org>
 *
 * @ref License
 */

// Copyright (c) 2008-2019 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later

#include "CacheMemory.h"
#include "Exceptions.h"
#include "Frame.h"
#include "MemoryTrim.h"

using namespace std;
using namespace openshot;

// Default constructor, no max bytes
CacheMemory::CacheMemory() : CacheBase(0), bytes_freed_since_trim(0) {
	// Set cache type name
	cache_type = "CacheMemory";
	range_version = 0;
	needs_range_processing = false;
}

// Constructor that sets the max bytes to cache
CacheMemory::CacheMemory(int64_t max_bytes) : CacheBase(max_bytes), bytes_freed_since_trim(0) {
	// Set cache type name
	cache_type = "CacheMemory";
	range_version = 0;
	needs_range_processing = false;
}

// Default destructor
CacheMemory::~CacheMemory()
{
	Clear();

	// remove mutex
	delete cacheMutex;
}

// Add a Frame to the cache
void CacheMemory::Add(std::shared_ptr<Frame> frame)
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
	int64_t frame_number = frame->number;

	// Freshen frame if it already exists
	if (frames.count(frame_number))
		// Move frame to front of queue
		Touch(frame_number);

	else
	{
		// Add frame to queue and map
		frames[frame_number] = frame;
		frame_numbers.push_front(frame_number);
		ordered_frame_numbers.push_back(frame_number);
		needs_range_processing = true;

		// Clean up old frames
		CleanUp();
	}
}

// Check if frame is already contained in cache
bool CacheMemory::Contains(int64_t frame_number) {
	if (frames.count(frame_number) > 0) {
		return true;
	} else {
		return false;
	}
}

// Get a frame from the cache (or NULL shared_ptr if no frame is found)
std::shared_ptr<Frame> CacheMemory::GetFrame(int64_t frame_number)
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

	// Does frame exists in cache?
	if (frames.count(frame_number))
		// return the Frame object
		return frames[frame_number];

	else
		// no Frame found
		return std::shared_ptr<Frame>();
}

// @brief Get an array of all Frames
std::vector<std::shared_ptr<openshot::Frame>> CacheMemory::GetFrames()
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

	std::vector<std::shared_ptr<openshot::Frame>> all_frames;
	std::vector<int64_t>::iterator itr_ordered;
	for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered)
	{
		int64_t frame_number = *itr_ordered;
		all_frames.push_back(GetFrame(frame_number));
	}

	return all_frames;
}

// Get the smallest frame number (or NULL shared_ptr if no frame is found)
std::shared_ptr<Frame> CacheMemory::GetSmallestFrame()
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

	// Loop through frame numbers
	std::deque<int64_t>::iterator itr;
	int64_t smallest_frame = -1;
	for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
	{
		if (*itr < smallest_frame || smallest_frame == -1)
			smallest_frame = *itr;
	}

	// Return frame (if any)
	if (smallest_frame != -1) {
		return frames[smallest_frame];
	} else {
		return NULL;
	}
}

// Gets the maximum bytes value
int64_t CacheMemory::GetBytes()
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

	int64_t total_bytes = 0;

	// Loop through frames, and calculate total bytes
	std::deque<int64_t>::reverse_iterator itr;
	for(itr = frame_numbers.rbegin(); itr != frame_numbers.rend(); ++itr)
	{
		total_bytes += frames[*itr]->GetBytes();
	}

	return total_bytes;
}

// Remove a specific frame
void CacheMemory::Remove(int64_t frame_number)
{
	Remove(frame_number, frame_number);
}

// Remove range of frames
void CacheMemory::Remove(int64_t start_frame_number, int64_t end_frame_number)
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
	int64_t removed_bytes = 0;

	// Loop through frame numbers
	std::deque<int64_t>::iterator itr;
	for(itr = frame_numbers.begin(); itr != frame_numbers.end();)
	{
		if (*itr >= start_frame_number && *itr <= end_frame_number)
		{
			// erase frame number
			itr = frame_numbers.erase(itr);
		}else
			itr++;
	}

	// Loop through ordered frame numbers
	std::vector<int64_t>::iterator itr_ordered;
	for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end();)
	{
		if (*itr_ordered >= start_frame_number && *itr_ordered <= end_frame_number)
		{
			// Count bytes freed before erasing the frame
			if (frames.count(*itr_ordered))
				removed_bytes += frames[*itr_ordered]->GetBytes();

			// erase frame number
			frames.erase(*itr_ordered);
			itr_ordered = ordered_frame_numbers.erase(itr_ordered);
		}else
			itr_ordered++;
	}

	if (removed_bytes > 0)
	{
		bytes_freed_since_trim += removed_bytes;
		if (bytes_freed_since_trim >= TRIM_THRESHOLD_BYTES)
		{
			// Periodically return freed arenas to the OS
			if (TrimMemoryToOS())
				bytes_freed_since_trim = 0;
		}
	}

	// Needs range processing (since cache has changed)
	needs_range_processing = true;
}

// Move frame to front of queue (so it lasts longer)
void CacheMemory::Touch(int64_t frame_number)
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

	// Does frame exists in cache?
	if (frames.count(frame_number))
	{
		// Loop through frame numbers
		std::deque<int64_t>::iterator itr;
		for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
		{
			if (*itr == frame_number)
			{
				// erase frame number
				frame_numbers.erase(itr);

				// add frame number to 'front' of queue
				frame_numbers.push_front(frame_number);
				break;
			}
		}
	}
}

// Clear the cache of all frames
void CacheMemory::Clear()
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

	frames.clear();
	frame_numbers.clear();
	frame_numbers.shrink_to_fit();
	ordered_frame_numbers.clear();
	ordered_frame_numbers.shrink_to_fit();
	needs_range_processing = true;
	bytes_freed_since_trim = 0;

	// Trim freed arenas back to OS after large clears
	TrimMemoryToOS(true);
}

// Count the frames in the queue
int64_t CacheMemory::Count()
{
	// Create a scoped lock, to protect the cache from multiple threads
	const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

	// Return the number of frames in the cache
	return frames.size();
}

// Clean up cached frames that exceed the number in our max_bytes variable
void CacheMemory::CleanUp()
{
	// Do we auto clean up?
	if (max_bytes > 0)
	{
		// Create a scoped lock, to protect the cache from multiple threads
		const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);

		while (GetBytes() > max_bytes && frame_numbers.size() > 20)
		{
			// Get the oldest frame number.
			int64_t frame_to_remove = frame_numbers.back();

			// Remove frame_number and frame
			Remove(frame_to_remove);
		}
	}
}


// Generate JSON string of this object
std::string CacheMemory::Json() {

	// Return formatted string
	return JsonValue().toStyledString();
}

// Generate Json::Value for this object
Json::Value CacheMemory::JsonValue() {

	// Process range data (if anything has changed)
	CalculateRanges();

	// Create root json object
	Json::Value root = CacheBase::JsonValue(); // get parent properties
	root["type"] = cache_type;

	root["version"] = std::to_string(range_version);

	// Parse and append range data (if any)
	try {
		const Json::Value ranges = openshot::stringToJson(json_ranges);
		root["ranges"] = ranges;
	} catch (...) { }

	// return JsonValue
	return root;
}

// Load JSON string into this object
void CacheMemory::SetJson(const std::string value) {

	try
	{
		// Parse string to Json::Value
		const Json::Value root = openshot::stringToJson(value);
		// Set all values that match
		SetJsonValue(root);
	}
	catch (const std::exception& e)
	{
		// Error parsing JSON (or missing keys)
		throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
	}
}

// Load Json::Value into this object
void CacheMemory::SetJsonValue(const Json::Value root) {

	// Close timeline before we do anything (this also removes all open and closing clips)
	Clear();

	// Set parent data
	CacheBase::SetJsonValue(root);

	if (!root["type"].isNull())
		cache_type = root["type"].asString();
}