prevent popup deactivation while button_down
[goodguy/history.git] / cinelerra-5.0 / 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 "format.inc"
33 #include "mutex.h"
34 #include "thread.h"
35 #include "preferences.h"
36
37 #include <string.h>
38
39 // edl came from a command which won't exist anymore
40 CICache::CICache(Preferences *preferences)
41  : List<CICacheItem>()
42 {
43         this->preferences = preferences;
44         edl = 0;
45         check_out_lock = new Condition(0, "CICache::check_out_lock", 0);
46         total_lock = new Mutex("CICache::total_lock");
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
111                                 current = 0;
112                 }
113                 total_lock->unlock();
114                 if(current || !file || !block) break;
115 // Try again after blocking
116                 check_out_lock->lock("CICache::check_out");
117         }
118
119 //printf("check out %p %lx %s\n", current, tid, asset->path);
120         return current ? current->file : 0;
121 }
122
123 int CICache::check_in(Asset *asset)
124 {
125         total_lock->lock("CICache::check_in");
126         CICacheItem *current = first;
127         while(current && strcmp(current->asset->path, asset->path) != 0)
128                 current = NEXT;
129         if(current && current->checked_out) {
130                 current->checked_out = 0;
131                 current->Garbage::remove_user();
132         }
133         total_lock->unlock();
134
135 // Release for blocking check_out operations
136         check_out_lock->unlock();
137 //printf("check  in %p %lx %s\n", current, (long)Thread::get_self(), asset->path);
138         age();
139         return 0;
140 }
141
142 void CICache::remove_all()
143 {
144         CICacheItem *current, *temp;
145         List<CICacheItem> removed;
146         total_lock->lock("CICache::remove_all");
147         for(current=first; current; current=temp)
148         {
149                 temp = NEXT;
150 // Must not be checked out because we need the pointer to check back in.
151 // Really need to give the user the CacheItem.
152                 if(!current->checked_out)
153                 {
154 //printf("CICache::remove_all: %s\n", current->asset->path);
155                         remove_pointer(current);
156                         removed.append(current);
157                 }
158         }
159         total_lock->unlock();
160         while( (current=removed.first) != 0 )
161         {
162                 removed.remove_pointer(current);
163                 current->Garbage::remove_user();
164         }
165 }
166
167 int CICache::delete_entry(char *path)
168 {
169         total_lock->lock("CICache::delete_entry");
170         CICacheItem *current = first;
171         while( current && strcmp(current->asset->path, path) !=0 )
172                 current = NEXT;
173         if(current && !current->checked_out)
174                 remove_pointer(current);
175         else
176                 current = 0;
177 //printf("CICache::delete_entry: %s\n", current->asset->path);
178         total_lock->unlock();
179         if(current)
180                 current->Garbage::remove_user();
181         return 0;
182 }
183
184 int CICache::delete_entry(Asset *asset)
185 {
186         return delete_entry(asset->path);
187 }
188
189 int CICache::age()
190 {
191 // delete old assets if memory usage is exceeded
192         int64_t prev_memory_usage = 0;
193         int result = 0;
194         while( !result ) {
195                 int64_t memory_usage = get_memory_usage(1);
196                 if( prev_memory_usage == memory_usage ) break;
197                 if( preferences->cache_size >= memory_usage ) break;
198 //printf("CICache::age 3 %p " _LD " " _LD "\n", this, memory_usage, preferences->cache_size);
199                 prev_memory_usage = memory_usage;
200                 result = delete_oldest();
201         }
202         return result;
203 }
204
205 int64_t CICache::get_memory_usage(int use_lock)
206 {
207         CICacheItem *current;
208         int64_t result = 0;
209         if(use_lock) total_lock->lock("CICache::get_memory_usage");
210         for(current = first; current; current = NEXT)
211         {
212                 File *file = current->file;
213                 if(file) result += file->get_memory_usage();
214         }
215         if(use_lock) total_lock->unlock();
216         return result;
217 }
218
219 int CICache::get_oldest()
220 {
221         CICacheItem *current;
222         int oldest = 0x7fffffff;
223         total_lock->lock("CICache::get_oldest");
224         for(current = last; current; current = PREVIOUS)
225         {
226                 if(current->age < oldest)
227                 {
228                         oldest = current->age;
229                 }
230         }
231         total_lock->unlock();
232
233         return oldest;
234 }
235
236 int CICache::delete_oldest()
237 {
238         int result = 0;
239         total_lock->lock("CICache::delete_oldest");
240         CICacheItem *oldest = 0;
241 // at least 2
242         if( first != last ) {
243                 CICacheItem *current = first;
244                 oldest = current;
245                 while( (current=NEXT) != 0 ) {
246                         if( current->age < oldest->age )
247                                 oldest = current;
248                 }
249 // Got the oldest file.  Try requesting cache purge from it.
250                 if( oldest->file )
251                         oldest->file->purge_cache();
252 // Delete the file if cache already empty and not checked out.
253                 if( !oldest->checked_out )
254                         remove_pointer(oldest);
255                 else
256                         oldest = 0;
257         }
258 // settle for just deleting one frame
259         else if( first )
260                 result = first->file->delete_oldest();
261         total_lock->unlock();
262         if( oldest ) {
263                 oldest->Garbage::remove_user();
264                 result = 1;
265         }
266         return result;    
267 }
268
269 int CICache::dump()
270 {
271         CICacheItem *current;
272         total_lock->lock("CICache::dump");
273         printf("CICache::dump total size " _LD "\n", get_memory_usage(0));
274         for(current = first; current; current = NEXT)
275         {
276                 printf("cache item %p asset %p %s age=%d\n", 
277                         current, current->asset,
278                         current->asset->path, current->age);
279         }
280         total_lock->unlock();
281         return 0;
282 }
283
284
285
286
287
288
289
290
291
292 CICacheItem::CICacheItem()
293 : Garbage("CICacheItem"), ListItem<CICacheItem>()
294 {
295 }
296
297
298 CICacheItem::CICacheItem(CICache *cache, EDL *edl, Asset *asset)
299  : Garbage("CICacheItem"), ListItem<CICacheItem>()
300 {
301         age = EDL::next_id();
302
303         this->asset = new Asset;
304
305         item_lock = new Condition(1, "CICacheItem::item_lock", 0);
306         
307
308 // Must copy Asset since this belongs to an EDL which won't exist forever.
309         this->asset->copy_from(asset, 1);
310         this->cache = cache;
311         checked_out = 0;
312
313
314         file = new File;
315         int cpus = cache->preferences->processors;
316         file->set_processors(cpus);
317         file->set_preload(edl->session->playback_preload);
318         file->set_subtitle(edl->session->decode_subtitles ? 
319                 edl->session->subtitle_number : -1);
320         file->set_program(edl->session->program_no);
321         file->set_interpolate_raw(edl->session->interpolate_raw);
322         file->set_white_balance_raw(edl->session->white_balance_raw);
323
324 SET_TRACE
325 }
326
327 CICacheItem::~CICacheItem()
328 {
329         if(file) delete file;
330         if(asset) asset->Garbage::remove_user();
331         if(item_lock) delete item_lock;
332 }