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