OpenShot Library | libopenshot  0.5.0
CacheMemory.cpp
Go to the documentation of this file.
1 
9 // Copyright (c) 2008-2019 OpenShot Studios, LLC
10 //
11 // SPDX-License-Identifier: LGPL-3.0-or-later
12 
13 #include "CacheMemory.h"
14 #include "Exceptions.h"
15 #include "Frame.h"
16 #include "MemoryTrim.h"
17 
18 using namespace std;
19 using namespace openshot;
20 
21 // Default constructor, no max bytes
22 CacheMemory::CacheMemory() : CacheBase(0), bytes_freed_since_trim(0) {
23  // Set cache type name
24  cache_type = "CacheMemory";
25  range_version = 0;
26  needs_range_processing = false;
27 }
28 
29 // Constructor that sets the max bytes to cache
30 CacheMemory::CacheMemory(int64_t max_bytes) : CacheBase(max_bytes), bytes_freed_since_trim(0) {
31  // Set cache type name
32  cache_type = "CacheMemory";
33  range_version = 0;
34  needs_range_processing = false;
35 }
36 
37 // Default destructor
39 {
40  Clear();
41 
42  // remove mutex
43  delete cacheMutex;
44 }
45 
46 // Add a Frame to the cache
47 void CacheMemory::Add(std::shared_ptr<Frame> frame)
48 {
49  // Create a scoped lock, to protect the cache from multiple threads
50  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
51  int64_t frame_number = frame->number;
52 
53  // Freshen frame if it already exists
54  if (frames.count(frame_number))
55  // Move frame to front of queue
56  Touch(frame_number);
57 
58  else
59  {
60  // Add frame to queue and map
61  frames[frame_number] = frame;
62  frame_numbers.push_front(frame_number);
63  ordered_frame_numbers.push_back(frame_number);
65 
66  // Clean up old frames
67  CleanUp();
68  }
69 }
70 
71 // Check if frame is already contained in cache
72 bool CacheMemory::Contains(int64_t frame_number) {
73  if (frames.count(frame_number) > 0) {
74  return true;
75  } else {
76  return false;
77  }
78 }
79 
80 // Get a frame from the cache (or NULL shared_ptr if no frame is found)
81 std::shared_ptr<Frame> CacheMemory::GetFrame(int64_t frame_number)
82 {
83  // Create a scoped lock, to protect the cache from multiple threads
84  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
85 
86  // Does frame exists in cache?
87  if (frames.count(frame_number))
88  // return the Frame object
89  return frames[frame_number];
90 
91  else
92  // no Frame found
93  return std::shared_ptr<Frame>();
94 }
95 
96 // @brief Get an array of all Frames
97 std::vector<std::shared_ptr<openshot::Frame>> CacheMemory::GetFrames()
98 {
99  // Create a scoped lock, to protect the cache from multiple threads
100  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
101 
102  std::vector<std::shared_ptr<openshot::Frame>> all_frames;
103  std::vector<int64_t>::iterator itr_ordered;
104  for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end(); ++itr_ordered)
105  {
106  int64_t frame_number = *itr_ordered;
107  all_frames.push_back(GetFrame(frame_number));
108  }
109 
110  return all_frames;
111 }
112 
113 // Get the smallest frame number (or NULL shared_ptr if no frame is found)
114 std::shared_ptr<Frame> CacheMemory::GetSmallestFrame()
115 {
116  // Create a scoped lock, to protect the cache from multiple threads
117  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
118 
119  // Loop through frame numbers
120  std::deque<int64_t>::iterator itr;
121  int64_t smallest_frame = -1;
122  for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
123  {
124  if (*itr < smallest_frame || smallest_frame == -1)
125  smallest_frame = *itr;
126  }
127 
128  // Return frame (if any)
129  if (smallest_frame != -1) {
130  return frames[smallest_frame];
131  } else {
132  return NULL;
133  }
134 }
135 
136 // Gets the maximum bytes value
138 {
139  // Create a scoped lock, to protect the cache from multiple threads
140  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
141 
142  int64_t total_bytes = 0;
143 
144  // Loop through frames, and calculate total bytes
145  std::deque<int64_t>::reverse_iterator itr;
146  for(itr = frame_numbers.rbegin(); itr != frame_numbers.rend(); ++itr)
147  {
148  total_bytes += frames[*itr]->GetBytes();
149  }
150 
151  return total_bytes;
152 }
153 
154 // Remove a specific frame
155 void CacheMemory::Remove(int64_t frame_number)
156 {
157  Remove(frame_number, frame_number);
158 }
159 
160 // Remove range of frames
161 void CacheMemory::Remove(int64_t start_frame_number, int64_t end_frame_number)
162 {
163  // Create a scoped lock, to protect the cache from multiple threads
164  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
165  int64_t removed_bytes = 0;
166 
167  // Loop through frame numbers
168  std::deque<int64_t>::iterator itr;
169  for(itr = frame_numbers.begin(); itr != frame_numbers.end();)
170  {
171  if (*itr >= start_frame_number && *itr <= end_frame_number)
172  {
173  // erase frame number
174  itr = frame_numbers.erase(itr);
175  }else
176  itr++;
177  }
178 
179  // Loop through ordered frame numbers
180  std::vector<int64_t>::iterator itr_ordered;
181  for(itr_ordered = ordered_frame_numbers.begin(); itr_ordered != ordered_frame_numbers.end();)
182  {
183  if (*itr_ordered >= start_frame_number && *itr_ordered <= end_frame_number)
184  {
185  // Count bytes freed before erasing the frame
186  if (frames.count(*itr_ordered))
187  removed_bytes += frames[*itr_ordered]->GetBytes();
188 
189  // erase frame number
190  frames.erase(*itr_ordered);
191  itr_ordered = ordered_frame_numbers.erase(itr_ordered);
192  }else
193  itr_ordered++;
194  }
195 
196  if (removed_bytes > 0)
197  {
198  bytes_freed_since_trim += removed_bytes;
199  if (bytes_freed_since_trim >= TRIM_THRESHOLD_BYTES)
200  {
201  // Periodically return freed arenas to the OS
202  if (TrimMemoryToOS())
203  bytes_freed_since_trim = 0;
204  }
205  }
206 
207  // Needs range processing (since cache has changed)
208  needs_range_processing = true;
209 }
210 
211 // Move frame to front of queue (so it lasts longer)
212 void CacheMemory::Touch(int64_t frame_number)
213 {
214  // Create a scoped lock, to protect the cache from multiple threads
215  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
216 
217  // Does frame exists in cache?
218  if (frames.count(frame_number))
219  {
220  // Loop through frame numbers
221  std::deque<int64_t>::iterator itr;
222  for(itr = frame_numbers.begin(); itr != frame_numbers.end(); ++itr)
223  {
224  if (*itr == frame_number)
225  {
226  // erase frame number
227  frame_numbers.erase(itr);
228 
229  // add frame number to 'front' of queue
230  frame_numbers.push_front(frame_number);
231  break;
232  }
233  }
234  }
235 }
236 
237 // Clear the cache of all frames
239 {
240  // Create a scoped lock, to protect the cache from multiple threads
241  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
242 
243  frames.clear();
244  frame_numbers.clear();
245  frame_numbers.shrink_to_fit();
246  ordered_frame_numbers.clear();
247  ordered_frame_numbers.shrink_to_fit();
248  needs_range_processing = true;
249  bytes_freed_since_trim = 0;
250 
251  // Trim freed arenas back to OS after large clears
252  TrimMemoryToOS(true);
253 }
254 
255 // Count the frames in the queue
257 {
258  // Create a scoped lock, to protect the cache from multiple threads
259  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
260 
261  // Return the number of frames in the cache
262  return frames.size();
263 }
264 
265 // Clean up cached frames that exceed the number in our max_bytes variable
266 void CacheMemory::CleanUp()
267 {
268  // Do we auto clean up?
269  if (max_bytes > 0)
270  {
271  // Create a scoped lock, to protect the cache from multiple threads
272  const std::lock_guard<std::recursive_mutex> lock(*cacheMutex);
273 
274  while (GetBytes() > max_bytes && frame_numbers.size() > 20)
275  {
276  // Get the oldest frame number.
277  int64_t frame_to_remove = frame_numbers.back();
278 
279  // Remove frame_number and frame
280  Remove(frame_to_remove);
281  }
282  }
283 }
284 
285 
286 // Generate JSON string of this object
287 std::string CacheMemory::Json() {
288 
289  // Return formatted string
290  return JsonValue().toStyledString();
291 }
292 
293 // Generate Json::Value for this object
294 Json::Value CacheMemory::JsonValue() {
295 
296  // Process range data (if anything has changed)
297  CalculateRanges();
298 
299  // Create root json object
300  Json::Value root = CacheBase::JsonValue(); // get parent properties
301  root["type"] = cache_type;
302 
303  root["version"] = std::to_string(range_version);
304 
305  // Parse and append range data (if any)
306  try {
307  const Json::Value ranges = openshot::stringToJson(json_ranges);
308  root["ranges"] = ranges;
309  } catch (...) { }
310 
311  // return JsonValue
312  return root;
313 }
314 
315 // Load JSON string into this object
316 void CacheMemory::SetJson(const std::string value) {
317 
318  try
319  {
320  // Parse string to Json::Value
321  const Json::Value root = openshot::stringToJson(value);
322  // Set all values that match
323  SetJsonValue(root);
324  }
325  catch (const std::exception& e)
326  {
327  // Error parsing JSON (or missing keys)
328  throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
329  }
330 }
331 
332 // Load Json::Value into this object
333 void CacheMemory::SetJsonValue(const Json::Value root) {
334 
335  // Close timeline before we do anything (this also removes all open and closing clips)
336  Clear();
337 
338  // Set parent data
340 
341  if (!root["type"].isNull())
342  cache_type = root["type"].asString();
343 }
openshot::stringToJson
const Json::Value stringToJson(const std::string value)
Definition: Json.cpp:16
openshot::CacheMemory::Clear
void Clear()
Clear the cache of all frames.
Definition: CacheMemory.cpp:238
openshot::CacheMemory::Count
int64_t Count()
Count the frames in the queue.
Definition: CacheMemory.cpp:256
openshot::CacheBase::needs_range_processing
bool needs_range_processing
Something has changed, and the range data needs to be re-calculated.
Definition: CacheBase.h:40
openshot::CacheMemory::GetBytes
int64_t GetBytes()
Gets the maximum bytes value.
Definition: CacheMemory.cpp:137
openshot::CacheMemory::GetFrame
std::shared_ptr< openshot::Frame > GetFrame(int64_t frame_number)
Get a frame from the cache.
Definition: CacheMemory.cpp:81
openshot::CacheBase::max_bytes
int64_t max_bytes
This is the max number of bytes to cache (0 = no limit)
Definition: CacheBase.h:38
openshot::CacheMemory::Add
void Add(std::shared_ptr< openshot::Frame > frame)
Add a Frame to the cache.
Definition: CacheMemory.cpp:47
openshot::CacheBase::ordered_frame_numbers
std::vector< int64_t > ordered_frame_numbers
Ordered list of frame numbers used by cache.
Definition: CacheBase.h:42
openshot
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:28
openshot::CacheBase::json_ranges
std::string json_ranges
JSON ranges of frame numbers.
Definition: CacheBase.h:41
MemoryTrim.h
Cross-platform helper to encourage returning freed memory to the OS.
openshot::CacheMemory::Contains
bool Contains(int64_t frame_number)
Check if frame is already contained in cache.
Definition: CacheMemory.cpp:72
openshot::CacheMemory::Remove
void Remove(int64_t frame_number)
Remove a specific frame.
Definition: CacheMemory.cpp:155
openshot::CacheMemory::JsonValue
Json::Value JsonValue()
Generate Json::Value for this object.
Definition: CacheMemory.cpp:294
openshot::CacheBase
All cache managers in libopenshot are based on this CacheBase class.
Definition: CacheBase.h:34
openshot::CacheBase::SetJsonValue
virtual void SetJsonValue(const Json::Value root)=0
Load Json::Value into this object.
Definition: CacheBase.cpp:111
openshot::CacheMemory::~CacheMemory
virtual ~CacheMemory()
Definition: CacheMemory.cpp:38
openshot::CacheMemory::Touch
void Touch(int64_t frame_number)
Move frame to front of queue (so it lasts longer)
Definition: CacheMemory.cpp:212
openshot::InvalidJSON
Exception for invalid JSON.
Definition: Exceptions.h:217
CacheMemory.h
Header file for CacheMemory class.
openshot::CacheMemory::Json
std::string Json()
Generate JSON string of this object.
Definition: CacheMemory.cpp:287
openshot::CacheBase::JsonValue
virtual Json::Value JsonValue()=0
Generate Json::Value for this object.
Definition: CacheBase.cpp:100
openshot::CacheBase::CalculateRanges
void CalculateRanges()
Calculate ranges of frames.
Definition: CacheBase.cpp:36
Frame.h
Header file for Frame class.
openshot::CacheMemory::GetSmallestFrame
std::shared_ptr< openshot::Frame > GetSmallestFrame()
Get the smallest frame number.
Definition: CacheMemory.cpp:114
openshot::CacheBase::cache_type
std::string cache_type
This is a friendly type name of the derived cache instance.
Definition: CacheBase.h:37
openshot::CacheMemory::CacheMemory
CacheMemory()
Default constructor, no max bytes.
Definition: CacheMemory.cpp:22
openshot::CacheMemory::SetJson
void SetJson(const std::string value)
Load JSON string into this object.
Definition: CacheMemory.cpp:316
openshot::CacheMemory::SetJsonValue
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
Definition: CacheMemory.cpp:333
openshot::CacheMemory::GetFrames
std::vector< std::shared_ptr< openshot::Frame > > GetFrames()
Get an array of all Frames.
Definition: CacheMemory.cpp:97
openshot::CacheBase::range_version
int64_t range_version
The version of the JSON range data (incremented with each change)
Definition: CacheBase.h:44
Exceptions.h
Header file for all Exception classes.
openshot::TrimMemoryToOS
bool TrimMemoryToOS(bool force) noexcept
Attempt to return unused heap memory to the operating system.
Definition: MemoryTrim.cpp:40
openshot::CacheBase::cacheMutex
std::recursive_mutex * cacheMutex
Mutex for multiple threads.
Definition: CacheBase.h:47