batchrender cleanup, bd/dvd create upgrades, remote ctrl booby fix
[goodguy/cinelerra.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         check_outs = 0;
47 }
48
49 CICache::~CICache()
50 {
51         while(last)
52         {
53                 CICacheItem *item = last;
54 //printf("CICache::~CICache: %s\n", item->asset->path);
55                 remove_pointer(item);
56                 item->Garbage::remove_user();
57         }
58         delete check_out_lock;
59         delete total_lock;
60 }
61
62
63
64
65
66
67 File* CICache::check_out(Asset *asset, EDL *edl, int block)
68 {
69         CICacheItem *current = 0;
70         long tid = (long)Thread::get_self();
71         if( !tid ) tid = 1;
72
73         while(1)
74         {
75                 File *file = 0;
76                 total_lock->lock("CICache::check_out");
77 // Scan directory for item
78                 current = first;
79                 while(current && strcmp(current->asset->path, asset->path) != 0)
80                         current = NEXT;
81                 if(!current) { // Create new item
82                         current = new CICacheItem(this, edl, asset);
83                         append(current);  current->checked_out = tid;
84                         file = current->file;
85                         total_lock->unlock();
86                         int result = file->open_file(preferences, asset, 1, 0);
87                         if( result ) {
88 SET_TRACE
89                                 delete file;
90                                 file = 0;
91                         }
92                         total_lock->lock("CICache::check_out 2");
93                         if( !file ) {
94                                 remove_pointer(current);
95                                 current->file = 0;
96                                 current->Garbage::remove_user();
97                                 current = 0;
98                         }
99                         else
100                                 current->Garbage::add_user();
101                 }
102                 else {
103                         file = current->file;
104                         if(!current->checked_out) {
105 // Return existing/new item
106                                 current->Garbage::add_user();
107                                 current->age = EDL::next_id();
108                                 current->checked_out = tid;
109                         }
110                         else if( current->checked_out == tid )
111                                 ++check_outs;
112                         else
113                                 current = 0;
114                 }
115                 total_lock->unlock();
116                 if(current || !file || !block) break;
117 // Try again after blocking
118                 check_out_lock->lock("CICache::check_out");
119         }
120
121 //printf("check out %p %lx %s\n", current, tid, asset->path);
122         return current ? current->file : 0;
123 }
124
125 int CICache::check_in(Asset *asset)
126 {
127         total_lock->lock("CICache::check_in");
128         if( !check_outs ) {
129                 CICacheItem *current = first;
130                 while(current && strcmp(current->asset->path, asset->path) != 0)
131                         current = NEXT;
132                 if(current && current->checked_out) {
133                         current->checked_out = 0;
134                         current->Garbage::remove_user();
135                 }
136         }
137         else
138                 --check_outs;
139         total_lock->unlock();
140
141 // Release for blocking check_out operations
142         check_out_lock->unlock();
143 //printf("check  in %p %lx %s\n", current, (long)Thread::get_self(), asset->path);
144         age();
145         return 0;
146 }
147
148 void CICache::remove_all()
149 {
150         CICacheItem *current, *temp;
151         List<CICacheItem> removed;
152         total_lock->lock("CICache::remove_all");
153         for(current=first; current; current=temp)
154         {
155                 temp = NEXT;
156 // Must not be checked out because we need the pointer to check back in.
157 // Really need to give the user the CacheItem.
158                 if(!current->checked_out)
159                 {
160 //printf("CICache::remove_all: %s\n", current->asset->path);
161                         remove_pointer(current);
162                         removed.append(current);
163                 }
164         }
165         total_lock->unlock();
166         while( (current=removed.first) != 0 )
167         {
168                 removed.remove_pointer(current);
169                 current->Garbage::remove_user();
170         }
171 }
172
173 int CICache::delete_entry(char *path)
174 {
175         total_lock->lock("CICache::delete_entry");
176         CICacheItem *current = first;
177         while( current && strcmp(current->asset->path, path) !=0 )
178                 current = NEXT;
179         if(current && !current->checked_out)
180                 remove_pointer(current);
181         else
182                 current = 0;
183 //printf("CICache::delete_entry: %s\n", current->asset->path);
184         total_lock->unlock();
185         if(current)
186                 current->Garbage::remove_user();
187         return 0;
188 }
189
190 int CICache::delete_entry(Asset *asset)
191 {
192         return delete_entry(asset->path);
193 }
194
195 int CICache::age()
196 {
197 // delete old assets if memory usage is exceeded
198         int64_t prev_memory_usage = 0;
199         int result = 0;
200         while( !result ) {
201                 int64_t memory_usage = get_memory_usage(1);
202                 if( prev_memory_usage == memory_usage ) break;
203                 if( preferences->cache_size >= memory_usage ) break;
204 //printf("CICache::age 3 %p %jd %jd\n", this, memory_usage, preferences->cache_size);
205                 prev_memory_usage = memory_usage;
206                 result = delete_oldest();
207         }
208         return result;
209 }
210
211 int64_t CICache::get_memory_usage(int use_lock)
212 {
213         CICacheItem *current;
214         int64_t result = 0;
215         if(use_lock) total_lock->lock("CICache::get_memory_usage");
216         for(current = first; current; current = NEXT)
217         {
218                 File *file = current->file;
219                 if(file) result += file->get_memory_usage();
220         }
221         if(use_lock) total_lock->unlock();
222         return result;
223 }
224
225 int CICache::get_oldest()
226 {
227         CICacheItem *current;
228         int oldest = 0x7fffffff;
229         total_lock->lock("CICache::get_oldest");
230         for(current = last; current; current = PREVIOUS)
231         {
232                 if(current->age < oldest)
233                 {
234                         oldest = current->age;
235                 }
236         }
237         total_lock->unlock();
238
239         return oldest;
240 }
241
242 int CICache::delete_oldest()
243 {
244         int result = 0;
245         total_lock->lock("CICache::delete_oldest");
246         CICacheItem *oldest = 0;
247 // at least 2
248         if( first != last ) {
249                 CICacheItem *current = first;
250                 oldest = current;
251                 while( (current=NEXT) != 0 ) {
252                         if( current->age < oldest->age )
253                                 oldest = current;
254                 }
255 // Got the oldest file.  Try requesting cache purge from it.
256                 if( oldest->file )
257                         oldest->file->purge_cache();
258 // Delete the file if cache already empty and not checked out.
259                 if( !oldest->checked_out )
260                         remove_pointer(oldest);
261                 else
262                         oldest = 0;
263         }
264 // settle for just deleting one frame
265         else if( first )
266                 result = first->file->delete_oldest();
267         total_lock->unlock();
268         if( oldest ) {
269                 oldest->Garbage::remove_user();
270                 result = 1;
271         }
272         return result;
273 }
274
275 void CICache::dump()
276 {
277         CICacheItem *current;
278         total_lock->lock("CICache::dump");
279         printf("CICache::dump total size %jd\n", get_memory_usage(0));
280         for(current = first; current; current = NEXT)
281         {
282                 printf("cache item %p asset %p %s age=%d\n",
283                         current, current->asset,
284                         current->asset->path, current->age);
285         }
286         total_lock->unlock();
287 }
288
289
290 CICacheItem::CICacheItem()
291 : Garbage("CICacheItem"), ListItem<CICacheItem>()
292 {
293 }
294
295
296 CICacheItem::CICacheItem(CICache *cache, EDL *edl, Asset *asset)
297  : Garbage("CICacheItem"), ListItem<CICacheItem>()
298 {
299         age = EDL::next_id();
300
301         this->asset = new Asset;
302
303         item_lock = new Condition(1, "CICacheItem::item_lock", 0);
304
305
306 // Must copy Asset since this belongs to an EDL which won't exist forever.
307         this->asset->copy_from(asset, 1);
308         this->cache = cache;
309         checked_out = 0;
310
311         file = new File;
312         int cpus = cache->preferences->processors;
313         file->set_processors(cpus);
314         file->set_preload(edl->session->playback_preload);
315         file->set_subtitle(edl->session->decode_subtitles ?
316                 edl->session->subtitle_number : -1);
317         file->set_program(edl->session->program_no);
318         file->set_interpolate_raw(edl->session->interpolate_raw);
319         file->set_white_balance_raw(edl->session->white_balance_raw);
320
321 SET_TRACE
322 }
323
324 CICacheItem::~CICacheItem()
325 {
326         if(file) delete file;
327         if(asset) asset->Garbage::remove_user();
328         if(item_lock) delete item_lock;
329 }