4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
24 #include "bcsignals.h"
26 #include "condition.h"
29 #include "edlsession.h"
31 #include "filesystem.h"
34 #include "preferences.h"
38 // edl came from a command which won't exist anymore
39 CICache::CICache(Preferences *preferences)
40 : Garbage("CICache"), List<CICacheItem>()
42 this->preferences = preferences;
44 check_out_lock = new Condition(0, "CICache::check_out_lock", 0);
45 total_lock = new Mutex("CICache::total_lock");
53 CICacheItem *item = last;
54 //printf("CICache::~CICache: %s\n", item->asset->path);
56 item->Garbage::remove_user();
58 delete check_out_lock;
67 File* CICache::check_out(Asset *asset, EDL *edl, int block)
69 CICacheItem *current = 0;
70 long tid = (long)Thread::get_self();
72 total_lock->lock("CICache::check_out");
77 // Scan directory for item
79 while(current && strcmp(current->asset->path, asset->path) != 0)
81 if(!current) { // Create new item
82 current = new CICacheItem(this, edl, asset);
83 append(current); current->checked_out = tid;
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");
90 remove_pointer(current);
92 current->Garbage::remove_user();
96 current->Garbage::add_user();
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;
106 else if( current->checked_out == tid )
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");
118 // cache deleted during checkout, destroy this
120 current->Garbage::remove_user();
121 total_lock->unlock();
125 //printf("users: %i \n", users );
127 current->Garbage::remove_user();
128 total_lock->unlock();
129 //printf("check out %p %lx %s\n", current, tid, asset->path);
130 return current ? current->file : 0;
133 int CICache::check_in(Asset *asset)
135 total_lock->lock("CICache::check_in");
136 CICacheItem *current = 0;
139 while(current && strcmp(current->asset->path, asset->path) != 0)
141 if(current && current->checked_out) {
142 current->checked_out = 0;
143 current->Garbage::remove_user();
148 total_lock->unlock();
150 // Release for blocking check_out operations
151 check_out_lock->unlock();
152 //printf("check in %p %lx %s\n", current, (long)Thread::get_self(), asset->path);
157 void CICache::remove_all()
159 List<CICacheItem> removed;
161 total_lock->lock("CICache::remove_all");
162 CICacheItem *current = first;
164 CICacheItem *next_item = current->next;
165 if( !current->checked_out ) {
166 remove_pointer(current);
167 removed.append(current);
171 total_lock->unlock();
172 while( removed.first ) {
173 CICacheItem *current = removed.first;
174 removed.remove_pointer(current);
175 current->Garbage::remove_user();
178 check_out_lock->lock();
182 int CICache::delete_entry(char *path)
184 CICacheItem *current = 0;
186 total_lock->lock("CICache::delete_entry");
188 while( current && strcmp(current->asset->path, path) !=0 )
190 if( !current ) break;
191 if( !current->checked_out ) {
192 remove_pointer(current);
195 total_lock->unlock();
196 check_out_lock->lock();
198 total_lock->unlock();
200 current->Garbage::remove_user();
204 int CICache::delete_entry(Asset *asset)
206 return delete_entry(asset->path);
211 // delete old assets if memory usage is exceeded
212 int64_t prev_memory_usage = 0;
215 int64_t memory_usage = get_memory_usage(1);
216 if( prev_memory_usage == memory_usage ) break;
217 if( preferences->cache_size >= memory_usage ) break;
218 //printf("CICache::age 3 %p %jd %jd\n", this, memory_usage, preferences->cache_size);
219 prev_memory_usage = memory_usage;
220 result = delete_oldest();
225 int64_t CICache::get_memory_usage(int use_lock)
227 CICacheItem *current;
229 if(use_lock) total_lock->lock("CICache::get_memory_usage");
230 for(current = first; current; current = NEXT)
232 File *file = current->file;
233 if(file) result += file->get_memory_usage();
235 if(use_lock) total_lock->unlock();
239 int CICache::get_oldest()
241 CICacheItem *current;
242 int oldest = 0x7fffffff;
243 total_lock->lock("CICache::get_oldest");
244 for(current = last; current; current = PREVIOUS)
246 if(current->age < oldest)
248 oldest = current->age;
251 total_lock->unlock();
256 int CICache::delete_oldest()
259 CICacheItem *oldest = 0;
260 total_lock->lock("CICache::delete_oldest");
261 if( first != last ) { // at least 2
262 CICacheItem *current = first;
263 for( ; !oldest && current; current=current->next )
264 if( !current->checked_out ) oldest = current;
265 for( ; current; current=current->next ) {
266 if( current->checked_out ) continue;
267 if( current->age < oldest->age )
270 // delete oldest of at least 2 cache files
272 remove_pointer(oldest);
276 // settle for just deleting one frame
277 else if( first && !first->checked_out )
278 result = first->file->delete_oldest();
279 total_lock->unlock();
281 oldest->Garbage::remove_user();
285 void CICache::dump(FILE *fp)
287 CICacheItem *current;
288 total_lock->lock("CICache::dump");
289 fprintf(fp, "CICache::dump total size %jd\n", get_memory_usage(0));
290 for(current = first; current; current = NEXT)
292 fprintf(fp, "cache item %p asset %p %s age=%d checked_out=%p\n",
293 current, current->asset, current->asset->path,
294 current->age, (void*)current->checked_out);
296 total_lock->unlock();
300 CICacheItem::CICacheItem()
301 : Garbage("CICacheItem"), ListItem<CICacheItem>()
306 CICacheItem::CICacheItem(CICache *cache, EDL *edl, Asset *asset)
307 : Garbage("CICacheItem"), ListItem<CICacheItem>()
309 age = EDL::next_id();
311 this->asset = new Asset;
313 item_lock = new Condition(1, "CICacheItem::item_lock", 0);
316 // Must copy Asset since this belongs to an EDL which won't exist forever.
317 this->asset->copy_from(asset, 1);
322 int cpus = cache->preferences->processors;
323 file->set_processors(cpus);
324 file->set_preload(edl->session->playback_preload);
325 file->set_subtitle(edl->session->decode_subtitles ?
326 edl->session->subtitle_number : -1);
327 file->set_program(edl->session->program_no);
328 file->set_interpolate_raw(edl->session->interpolate_raw);
329 file->set_white_balance_raw(edl->session->white_balance_raw);
334 CICacheItem::~CICacheItem()
336 if(file) delete file;
337 if(asset) asset->Garbage::remove_user();
338 if(item_lock) delete item_lock;