Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / cache.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "asset.h"
23 #include "assets.h"
24 #include "bcsignals.h"
25 #include "cache.h"
26 #include "condition.h"
27 #include "datatype.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "file.h"
31 #include "filesystem.h"
32 #include "mutex.h"
33 #include "thread.h"
34 #include "preferences.h"
35
36 #include <string.h>
37
38 // edl came from a command which won't exist anymore
39 CICache::CICache(Preferences *preferences)
40  : List<CICacheItem>()
41 {
42         this->preferences = preferences;
43         edl = 0;
44         check_out_lock = new Condition(0, "CICache::check_out_lock", 0);
45         total_lock = new Mutex("CICache::total_lock");
46 }
47
48 CICache::~CICache()
49 {
50         while(last)
51         {
52                 CICacheItem *item = last;
53 //printf("CICache::~CICache: %s\n", item->asset->path);
54                 remove_pointer(item);
55                 item->Garbage::remove_user();
56         }
57         delete check_out_lock;
58         delete total_lock;
59 }
60
61
62
63
64
65
66 File* CICache::check_out(Asset *asset, EDL *edl, int block)
67 {
68         CICacheItem *current = 0;
69         long tid = (long)Thread::get_self();
70         if( !tid ) tid = 1;
71
72         while(1)
73         {
74                 File *file = 0;
75                 total_lock->lock("CICache::check_out");
76 // Scan directory for item
77                 current = first;
78                 while(current && strcmp(current->asset->path, asset->path) != 0)
79                         current = NEXT;
80                 if(!current) { // Create new item
81                         current = new CICacheItem(this, edl, asset);
82                         append(current);  current->checked_out = tid;
83                         file = current->file;
84                         total_lock->unlock();
85                         int result = file->open_file(preferences, asset, 1, 0);
86                         if( result ) {
87 SET_TRACE
88                                 delete file;
89                                 file = 0;
90                         }
91                         total_lock->lock("CICache::check_out 2");
92                         if( !file ) {
93                                 remove_pointer(current);
94                                 current->file = 0;
95                                 current->Garbage::remove_user();
96                                 current = 0;
97                         }
98                         else
99                                 current->Garbage::add_user();
100                 }
101                 else {
102                         file = current->file;
103                         if(!current->checked_out) {
104 // Return existing/new item
105                                 current->Garbage::add_user();
106                                 current->age = EDL::next_id();
107                                 current->checked_out = tid;
108                         }
109                         else
110                                 current = 0;
111                 }
112                 total_lock->unlock();
113                 if(current || !file || !block) break;
114 // Try again after blocking
115                 check_out_lock->lock("CICache::check_out");
116         }
117
118 //printf("check out %p %lx %s\n", current, tid, asset->path);
119         return current ? current->file : 0;
120 }
121
122 int CICache::check_in(Asset *asset)
123 {
124         total_lock->lock("CICache::check_in");
125         CICacheItem *current = first;
126         while(current && strcmp(current->asset->path, asset->path) != 0)
127                 current = NEXT;
128         if(current && current->checked_out) {
129                 current->checked_out = 0;
130                 current->Garbage::remove_user();
131         }
132         total_lock->unlock();
133
134 // Release for blocking check_out operations
135         check_out_lock->unlock();
136 //printf("check  in %p %lx %s\n", current, (long)Thread::get_self(), asset->path);
137         age();
138         return 0;
139 }
140
141 void CICache::remove_all()
142 {
143         CICacheItem *current, *temp;
144         List<CICacheItem> removed;
145         total_lock->lock("CICache::remove_all");
146         for(current=first; current; current=temp)
147         {
148                 temp = NEXT;
149 // Must not be checked out because we need the pointer to check back in.
150 // Really need to give the user the CacheItem.
151                 if(!current->checked_out)
152                 {
153 //printf("CICache::remove_all: %s\n", current->asset->path);
154                         remove_pointer(current);
155                         removed.append(current);
156                 }
157         }
158         total_lock->unlock();
159         while( (current=removed.first) != 0 )
160         {
161                 removed.remove_pointer(current);
162                 current->Garbage::remove_user();
163         }
164 }
165
166 int CICache::delete_entry(char *path)
167 {
168         total_lock->lock("CICache::delete_entry");
169         CICacheItem *current = first;
170         while( current && strcmp(current->asset->path, path) !=0 )
171                 current = NEXT;
172         if(current && !current->checked_out)
173                 remove_pointer(current);
174         else
175                 current = 0;
176 //printf("CICache::delete_entry: %s\n", current->asset->path);
177         total_lock->unlock();
178         if(current)
179                 current->Garbage::remove_user();
180         return 0;
181 }
182
183 int CICache::delete_entry(Asset *asset)
184 {
185         return delete_entry(asset->path);
186 }
187
188 int CICache::age()
189 {
190 // delete old assets if memory usage is exceeded
191         int64_t prev_memory_usage = 0;
192         int result = 0;
193         while( !result ) {
194                 int64_t memory_usage = get_memory_usage(1);
195                 if( prev_memory_usage == memory_usage ) break;
196                 if( preferences->cache_size >= memory_usage ) break;
197 //printf("CICache::age 3 %p %jd %jd\n", this, memory_usage, preferences->cache_size);
198                 prev_memory_usage = memory_usage;
199                 result = delete_oldest();
200         }
201         return result;
202 }
203
204 int64_t CICache::get_memory_usage(int use_lock)
205 {
206         CICacheItem *current;
207         int64_t result = 0;
208         if(use_lock) total_lock->lock("CICache::get_memory_usage");
209         for(current = first; current; current = NEXT)
210         {
211                 File *file = current->file;
212                 if(file) result += file->get_memory_usage();
213         }
214         if(use_lock) total_lock->unlock();
215         return result;
216 }
217
218 int CICache::get_oldest()
219 {
220         CICacheItem *current;
221         int oldest = 0x7fffffff;
222         total_lock->lock("CICache::get_oldest");
223         for(current = last; current; current = PREVIOUS)
224         {
225                 if(current->age < oldest)
226                 {
227                         oldest = current->age;
228                 }
229         }
230         total_lock->unlock();
231
232         return oldest;
233 }
234
235 int CICache::delete_oldest()
236 {
237         int result = 0;
238         total_lock->lock("CICache::delete_oldest");
239         CICacheItem *oldest = 0;
240 // at least 2
241         if( first != last ) {
242                 CICacheItem *current = first;
243                 oldest = current;
244                 while( (current=NEXT) != 0 ) {
245                         if( current->age < oldest->age )
246                                 oldest = current;
247                 }
248 // Got the oldest file.  Try requesting cache purge from it.
249                 if( oldest->file )
250                         oldest->file->purge_cache();
251 // Delete the file if cache already empty and not checked out.
252                 if( !oldest->checked_out )
253                         remove_pointer(oldest);
254                 else
255                         oldest = 0;
256         }
257 // settle for just deleting one frame
258         else if( first )
259                 result = first->file->delete_oldest();
260         total_lock->unlock();
261         if( oldest ) {
262                 oldest->Garbage::remove_user();
263                 result = 1;
264         }
265         return result;
266 }
267
268 void CICache::dump()
269 {
270         CICacheItem *current;
271         total_lock->lock("CICache::dump");
272         printf("CICache::dump total size %jd\n", get_memory_usage(0));
273         for(current = first; current; current = NEXT)
274         {
275                 printf("cache item %p asset %p %s age=%d\n",
276                         current, current->asset,
277                         current->asset->path, current->age);
278         }
279         total_lock->unlock();
280 }
281
282
283 CICacheItem::CICacheItem()
284 : Garbage("CICacheItem"), ListItem<CICacheItem>()
285 {
286 }
287
288
289 CICacheItem::CICacheItem(CICache *cache, EDL *edl, Asset *asset)
290  : Garbage("CICacheItem"), ListItem<CICacheItem>()
291 {
292         age = EDL::next_id();
293
294         this->asset = new Asset;
295
296         item_lock = new Condition(1, "CICacheItem::item_lock", 0);
297
298
299 // Must copy Asset since this belongs to an EDL which won't exist forever.
300         this->asset->copy_from(asset, 1);
301         this->cache = cache;
302         checked_out = 0;
303
304         file = new File;
305         int cpus = cache->preferences->processors;
306         file->set_processors(cpus);
307         file->set_preload(edl->session->playback_preload);
308         file->set_subtitle(edl->session->decode_subtitles ?
309                 edl->session->subtitle_number : -1);
310         file->set_program(edl->session->program_no);
311         file->set_interpolate_raw(edl->session->interpolate_raw);
312         file->set_white_balance_raw(edl->session->white_balance_raw);
313
314 SET_TRACE
315 }
316
317 CICacheItem::~CICacheItem()
318 {
319         if(file) delete file;
320         if(asset) asset->Garbage::remove_user();
321         if(item_lock) delete item_lock;
322 }