root/Footagehead/trunk/FHImageLoader.m

Revision 4875, 11.8 kB (checked in by morris, 1 year ago)

Fix thread sync when loading data instead of images

  • Property svn:keywords set to Id Rev
Line 
1 /* $Id$ */
2
3 /*
4  *  Copyright (c) 2007 Axel Andersson
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *  1. Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  *  2. Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import "FHCache.h"
30 #import "FHFile.h"
31 #import "FHImage.h"
32 #import "FHImageLoader.h"
33
34 #define FHImagesToKeepBehind                    2
35
36 @interface FHImageLoader(Private)
37
38 - (void)_asynchronouslyLoadDataForFile:(FHFile *)file;
39 - (void)_loadDataForFile:(FHFile *)file;
40 - (void)_loadImageForFile:(FHFile *)file;
41
42 @end
43
44
45 @implementation FHImageLoader(Private)
46
47 - (void)_asynchronouslyLoadDataForFile:(FHFile *)file {
48         NSURLConnection *connection;
49         WIURL                   *url;
50        
51         url = [file URL];
52        
53         [_asynchronousRequest setURL:[url URL]];
54         [_asynchronousRequest setValue:[[url URLByDeletingLastPathComponent] string] forHTTPHeaderField:@"Referer"];
55        
56         connection = [NSURLConnection connectionWithRequest:_asynchronousRequest delegate:self];
57
58         _asynchronousDone = NO;
59         _asynchronousFile = file;
60         [_asynchronousData setLength:0];
61        
62         do {
63                 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
64                                                                  beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]];
65         } while(!_asynchronousDone);
66 }
67
68
69
70 - (void)_loadDataForFile:(FHFile *)file {
71         NSData          *data;
72         WIURL           *url;
73        
74         [_notificationCenter mainThreadPostNotificationName:FHImageLoaderWillLoadFile object:file];
75
76         url = [file URL];
77
78         if([url isFileURL]) {
79                 data = [[NSData alloc] initWithContentsOfFile:[url path]];
80                 [file setData:data];
81                 [data release];
82         } else {
83                 [_asynchronousLock lock];
84                
85                 _asynchronousImages = NO;
86
87                 [self _asynchronouslyLoadDataForFile:file];
88                
89                 [file setData:_asynchronousData];
90                
91                 [_asynchronousData release];
92                 _asynchronousData = [[NSMutableData alloc] init];
93                
94                 [_asynchronousLock unlock];
95         }
96        
97         [_notificationCenter mainThreadPostNotificationName:FHImageLoaderDidLoadFile object:file waitUntilDone:YES];
98 }
99
100
101
102 - (void)_loadImageForFile:(FHFile *)file {
103         NSData          *data;
104         WIURL           *url;
105         FHImage         *image, *thumbnail;
106        
107         [_notificationCenter mainThreadPostNotificationName:FHImageLoaderWillLoadImage object:file];
108
109         url = [file URL];
110
111         if([url isFileURL]) {
112                 data = [[NSData alloc] initWithContentsOfFile:[url path]];
113                 image = [[FHImage alloc] initImageWithData:data];
114                 thumbnail = NULL;
115                 [data release];
116         } else {
117                 [_asynchronousLock lock];
118                
119                 _asynchronousImages = YES;
120                
121                 [self _asynchronouslyLoadDataForFile:file];
122                
123                 image = [[FHImage alloc] initImageWithData:_asynchronousData];
124                 thumbnail = [[FHCache cache] thumbnailForURL:url withData:_asynchronousData];
125
126                 [_asynchronousData setLength:0];
127                 [_asynchronousLock unlock];
128         }
129        
130         [file setImage:image];
131         [file setLoaded:YES];
132         _pixels += [image pixels];
133         [image release];
134        
135         [_notificationCenter mainThreadPostNotificationName:FHImageLoaderDidLoadImage object:file];
136        
137         if(thumbnail) {
138                 [file setThumbnail:thumbnail];
139
140                 [_notificationCenter mainThreadPostNotificationName:FHImageLoaderDidLoadThumbnail object:file];
141         }
142 }
143
144 @end
145
146
147
148 @implementation FHImageLoader
149
150 - (id)init {
151         self = [super init];
152        
153         _notificationCenter             = [[NSNotificationCenter alloc] init];
154        
155         _imageLock                              = [[NSConditionLock alloc] initWithCondition:0];
156         _thumbnailLock                  = [[NSConditionLock alloc] initWithCondition:0];
157         _dataLock                               = [[NSConditionLock alloc] initWithCondition:0];
158
159         _asynchronousLock               = [[NSLock alloc] init];
160         _asynchronousData               = [[NSMutableData alloc] init];
161        
162         _asynchronousRequest    = [[NSMutableURLRequest alloc] init];
163
164 #ifdef DEBUG
165         [_asynchronousRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
166 #endif
167
168         _maxPixels                              = ((double) [[NSProcessInfo processInfo] amountOfMemory] / 10) / 5;
169        
170         [NSThread detachNewThreadSelector:@selector(imageThread:) toTarget:self withObject:NULL];
171         [NSThread detachNewThreadSelector:@selector(thumbnailsThread:) toTarget:self withObject:NULL];
172         [NSThread detachNewThreadSelector:@selector(dataThread:) toTarget:self withObject:NULL];
173
174         [NSThread setThreadPriority:0.75];
175        
176         return self;
177 }
178
179
180
181 - (void)dealloc {
182         [_notificationCenter release];
183        
184         [_imageLock release];
185         [_thumbnailLock release];
186         [_dataLock release];
187        
188         [_asynchronousData release];
189         [_asynchronousLock release];
190        
191         [super dealloc];
192 }
193
194
195
196 #pragma mark -
197
198 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
199         _asynchronousLength = [response expectedContentLength];
200 }
201
202
203
204 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
205         NSString        *notification;
206         double          percent;
207        
208         [_asynchronousData appendData:data];
209
210         if(_asynchronousLength > 0) {
211                 percent = (double) [_asynchronousData length] / (double) _asynchronousLength;
212                
213                 if(percent > [_asynchronousFile percentReceived] + 0.05 || [_asynchronousFile percentReceived] == 0.0) {
214                         [_asynchronousFile setPercentReceived:percent];
215        
216                         notification = _asynchronousImages ? FHImageLoaderReceivedImageData : FHImageLoaderReceivedFileData;
217                        
218                         [_notificationCenter mainThreadPostNotificationName:notification object:_asynchronousFile];
219                 }
220         }
221 }
222
223
224
225 - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
226         _asynchronousDone = YES;
227 }
228
229
230
231 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
232         _asynchronousDone = YES;
233 }
234
235
236
237 #pragma mark -
238
239 - (void)imageThread:(id)object {
240         NSAutoreleasePool               *pool, *loopPool;
241         NSArray                                 *files;
242         FHFile                                  *file;
243         NSUInteger                              i, count, index;
244         NSUInteger                              counter, lastCounter = 0;
245         BOOL                                    pause, stop;
246        
247         [NSThread setThreadPriority:0.5];
248        
249         pool = [[NSAutoreleasePool alloc] init];
250
251         while(YES) {
252                 loopPool = [[NSAutoreleasePool alloc] init];
253
254                 [_imageLock lockWhenCondition:1];
255                 index = _index;
256                 files = [[_files retain] autorelease];
257                 counter = _imageCounter;
258                 stop = _imageStop;
259                 [_imageLock unlockWithCondition:0];
260                
261                 if(stop)
262                         break;
263                
264                 if(counter != lastCounter) {
265                         count = [files count];
266                        
267                         if(_pixels >= _maxPixels && index > FHImagesToKeepBehind) {
268                                 for(i = 0; i < count && i < index - FHImagesToKeepBehind; i++) {
269                                         file = [files objectAtIndex:i];
270                                        
271                                         if([file isDirectory])
272                                                 continue;
273                                        
274                                         if(![file isLoaded])
275                                                 continue;
276                                        
277                                         _pixels -= [[file image] pixels];
278                                        
279                                         [file setImage:NULL];
280                                         [file setLoaded:NO];
281                                        
282                                         if(_pixels < _maxPixels)
283                                                 break;
284                                 }
285                         }
286                        
287                         for(i = index; i < count; i++) {
288                                 file = [files objectAtIndex:i];
289                                
290                                 if(![file isDirectory] && ![file image]) {
291                                         if(i > index + 1 && _pixels >= _maxPixels)
292                                                 break;
293                                        
294                                         [self _loadImageForFile:file];
295                                 }
296
297                                 [_imageLock lock];
298                                 if(counter != _imageCounter)
299                                         i = count;
300                                 pause = _imagePause;
301                                 _imagePause = NO;
302                                 stop = _imageStop;
303                                 [_imageLock unlockWithCondition:counter == _imageCounter ? 0 : 1];
304                                
305                                 if(pause || stop)
306                                         break;
307                         }
308                        
309                         lastCounter = counter;
310                 }
311
312                 [loopPool release];
313                 loopPool = NULL;
314                
315                 if(stop)
316                         break;
317         }
318        
319         [pool release];
320 }
321
322
323
324 - (void)thumbnailsThread:(id)object {
325         NSAutoreleasePool       *pool;
326         NSArray                         *files;
327         FHImage                         *image;
328         FHFile                          *file;
329         WIURL                           *url;
330         NSUInteger                      i, count, counter, images;
331         BOOL                            pause, stop;
332        
333         [NSThread setThreadPriority:0.25];
334
335         while(YES) {
336                 pool = [[NSAutoreleasePool alloc] init];
337
338                 [_thumbnailLock lockWhenCondition:1];
339                 files = [[_files retain] autorelease];
340                 counter = _thumbnailCounter;
341                 stop = _thumbnailStop;
342                 [_thumbnailLock unlockWithCondition:0];
343                
344                 if(stop)
345                         break;
346                
347                 count = [files count];
348
349                 for(i = images = 0; i < count; i++) {
350                         file = [files objectAtIndex:i];
351                         url = [file URL];
352                        
353                         if(![file isDirectory] && ![file thumbnail] && [url isFileURL]) {
354                                 image = [[FHCache cache] thumbnailForURL:url];
355                                
356                                 if(image) {
357                                         [file setThumbnail:image];
358                                        
359                                         [_notificationCenter mainThreadPostNotificationName:FHImageLoaderDidLoadThumbnail object:file];
360                                 }
361                         }
362                                
363                         if(++images % 5 == 0) {
364                                 [_thumbnailLock lock];
365                                 if(counter != _thumbnailCounter)
366                                         i = count;
367                                 pause = _thumbnailPause;
368                                 stop = _thumbnailStop;
369                                 [_thumbnailLock unlockWithCondition:counter == _thumbnailCounter ? 0 : 1];
370                                
371                                 if(pause || stop)
372                                         break;
373                         }
374                 }
375                
376                 [pool release];
377                 pool = NULL;
378                
379                 if(stop)
380                         break;
381         }
382        
383         [pool release];
384 }
385
386
387
388 - (void)dataThread:(id)sender {
389         NSAutoreleasePool               *pool, *loopPool;
390         NSArray                                 *files;
391         FHFile                                  *file;
392         NSUInteger                              i, count;
393         NSUInteger                              counter, lastCounter = 0;
394         BOOL                                    pause, stop;
395        
396         [NSThread setThreadPriority:0.5];
397        
398         pool = [[NSAutoreleasePool alloc] init];
399
400         while(YES) {
401                 loopPool = [[NSAutoreleasePool alloc] init];
402
403                 [_dataLock lockWhenCondition:1];
404                 files = [[_files retain] autorelease];
405                 counter = _dataCounter;
406                 stop = _dataStop;
407                 [_dataLock unlockWithCondition:0];
408                
409                 if(stop)
410                         break;
411                
412                 if(counter != lastCounter) {
413                         count = [files count];
414                        
415                         for(i = 0; i < count; i++) {
416                                 file = [files objectAtIndex:i];
417                                
418                                 if(![file isDirectory] && ![file data])
419                                         [self _loadDataForFile:file];
420                                
421                                 [_dataLock lock];
422                                 if(counter != _dataCounter)
423                                         i = count;
424                                 pause = _dataPause;
425                                 _dataPause = NO;
426                                 stop = _dataStop;
427                                 [_dataLock unlockWithCondition:counter == _dataCounter ? 0 : 1];
428                                
429                                 if(pause || stop)
430                                         break;
431                         }
432                        
433                         lastCounter = counter;
434
435                         if(!pause && !stop)
436                                 [_notificationCenter mainThreadPostNotificationName:FHImageLoaderDidLoadAllFiles];
437                 }
438
439                 [loopPool release];
440                 loopPool = NULL;
441                
442                 if(stop)
443                         break;
444         }
445        
446         [pool release];
447 }
448
449
450
451 #pragma mark -
452
453 - (NSNotificationCenter *)notificationCenter {
454         return _notificationCenter;
455 }
456
457
458
459 - (void)setFiles:(NSArray *)files {
460         [_imageLock lock];
461        
462         _pixels = 0;
463        
464         [files retain];
465         [_files release];
466        
467         _files = files;
468        
469         [_imageLock unlockWithCondition:0];
470 }
471
472
473
474 - (NSArray *)files {
475         return _files;
476 }
477
478
479
480 #pragma mark -
481
482 - (void)startLoadingImageAtIndex:(NSUInteger)index {
483         [_imageLock lock];
484         _index = index;
485         _imageCounter++;
486         [_imageLock unlockWithCondition:1];
487 }
488
489
490
491 - (void)startLoadingImages {
492         [_imageLock lock];
493         _imageCounter++;
494         [_imageLock unlockWithCondition:1];
495 }
496
497
498
499 - (void)startLoadingThumbnails {
500         [_thumbnailLock lock];
501         _thumbnailCounter++;
502         [_thumbnailLock unlockWithCondition:1];
503 }
504
505
506
507 - (void)pauseLoadingImagesAndThumbnails {
508         [_imageLock lock];
509         _imagePause = YES;
510         [_imageLock unlockWithCondition:0];
511
512         [_thumbnailLock lock];
513         _thumbnailPause = YES;
514         [_thumbnailLock unlockWithCondition:0];
515 }
516
517
518
519 - (void)stopLoadingImagesAndThumbnails {
520         [_imageLock lock];
521         _imageStop = YES;
522         [_imageLock unlockWithCondition:0];
523
524         [_thumbnailLock lock];
525         _thumbnailStop = YES;
526         [_thumbnailLock unlockWithCondition:0];
527 }
528
529
530
531 - (void)startLoadingData {
532         [_dataLock lock];
533         _dataCounter++;
534         [_dataLock unlockWithCondition:1];
535 }
536
537
538
539 - (void)pauseLoadingData {
540         [_dataLock lock];
541         _dataPause = YES;
542         [_dataLock unlockWithCondition:0];
543 }
544
545
546
547 - (void)stopLoadingData {
548         [_dataLock lock];
549         _dataStop = YES;
550         [_dataLock unlockWithCondition:0];
551 }
552
553 @end
Note: See TracBrowser for help on using the browser.