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