1 /* cache vips operations
2 *
3 * 20/6/12
4 * - try to make it compile on centos5
5 * 7/7/12
6 * - add a lock so we can run operations from many threads
7 * 28/11/19 [MaxKellermann]
8 * - make invalidate advisory rather than immediate
9 */
10
11 /*
12
13 This file is part of VIPS.
14
15 VIPS is free software; you can redistribute it and/or modify
16 it under the terms of the GNU Lesser General Public License as published by
17 the Free Software Foundation; either version 2 of the License, or
18 (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU Lesser General Public License for more details.
24
25 You should have received a copy of the GNU Lesser General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 02110-1301 USA
29
30 */
31
32 /*
33
34 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
35
36 */
37
38 /*
39
40 TODO
41
42 what about delayed writes ... do we ever write in close? we shouldn't,
43 should do in evalend or written or somesuch
44
45 use g_param_values_cmp() instead of value_equal()?
46
47 */
48
49 /*
50 #define VIPS_DEBUG
51 #define DEBUG
52 */
53
54 #ifdef HAVE_CONFIG_H
55 #include <config.h>
56 #endif /*HAVE_CONFIG_H*/
57 #include <vips/intl.h>
58
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #ifdef HAVE_UNISTD_H
63 #include <unistd.h>
64 #endif /*HAVE_UNISTD_H*/
65 #include <ctype.h>
66
67 #include <vips/vips.h>
68 #include <vips/internal.h>
69 #include <vips/debug.h>
70
71 /* Set by GOption from the command line, eg. "12m".
72 */
73 gboolean vips__cache_dump = FALSE;
74 gboolean vips__cache_trace = FALSE;
75
76 /* Max number of cached operations.
77 *
78 * It was 10,000, but this was too high for batch-style applications with
79 * little reuse.
80 */
81 static int vips_cache_max = 100;
82
83 /* How many tracked open files we allow before we start dropping cache.
84 */
85 static int vips_cache_max_files = 100;
86
87 /* How much RAM we spend on caches before we start dropping cached operations
88 * ... default 100mb.
89 *
90 * It was 1gb, but that's a lot of memory for things like vipsthumbnail where
91 * there will be (almost) no reuse. Default low and let apps raise it if it'd
92 * be useful.
93 */
94 static size_t vips_cache_max_mem = 100 * 1024 * 1024;
95
96 /* Hold a ref to all "recent" operations.
97 */
98 static GHashTable *vips_cache_table = NULL;
99
100 /* A 'time' counter: increment on all cache ops. Use this to detect LRU.
101 */
102 static int vips_cache_time = 0;
103
104 /* Protect cache access with this.
105 */
106 static GMutex *vips_cache_lock = NULL;
107
108 /* Old versions of glib are missing these. When we abandon centos 5, switch to
109 * g_int64_hash() and g_double_hash().
110 */
111 #define INT64_HASH(X) (g_direct_hash(X))
112 #define DOUBLE_HASH(X) (g_direct_hash(X))
113
114 /* A cache entry.
115 */
116 typedef struct _VipsOperationCacheEntry {
117 VipsOperation *operation;
118
119 /* When we added this operation to cache .. used to find LRU for
120 * flush.
121 */
122 int time;
123
124 /* We listen for "invalidate" from the operation. Track the id here so
125 * we can disconnect when we drop an operation.
126 */
127 gulong invalidate_id;
128
129 /* Set if someone thinks this cache entry should be dropped.
130 */
131 gboolean invalid;
132
133 } VipsOperationCacheEntry;
134
135 /* Pass in the pspec so we can get the generic type. For example, a
136 * held in a GParamSpec allowing OBJECT, but the value could be of type
137 * VipsImage. generics are much faster to compare.
138 */
139 static unsigned int
vips_value_hash(GParamSpec * pspec,GValue * value)140 vips_value_hash( GParamSpec *pspec, GValue *value )
141 {
142 GType generic = G_PARAM_SPEC_TYPE( pspec );
143
144 /* Not compile-time constants, so we have to use a set of if()s. Could
145 * make a table at run time I guess.
146 */
147
148 if( generic == G_TYPE_PARAM_BOOLEAN )
149 return( (unsigned int) g_value_get_boolean( value ) );
150 else if( generic == G_TYPE_PARAM_CHAR )
151 return( (unsigned int) g_value_get_schar( value ) );
152 else if( generic == G_TYPE_PARAM_UCHAR )
153 return( (unsigned int) g_value_get_uchar( value ) );
154 else if( generic == G_TYPE_PARAM_INT )
155 return( (unsigned int) g_value_get_int( value ) );
156 else if( generic == G_TYPE_PARAM_UINT )
157 return( (unsigned int) g_value_get_uint( value ) );
158 else if( generic == G_TYPE_PARAM_LONG )
159 return( (unsigned int) g_value_get_long( value ) );
160 else if( generic == G_TYPE_PARAM_ULONG )
161 return( (unsigned int) g_value_get_ulong( value ) );
162 else if( generic == G_TYPE_PARAM_ENUM )
163 return( (unsigned int) g_value_get_enum( value ) );
164 else if( generic == G_TYPE_PARAM_FLAGS )
165 return( (unsigned int) g_value_get_flags( value ) );
166 else if( generic == G_TYPE_PARAM_UINT64 ) {
167 guint64 i = g_value_get_uint64( value );
168
169 return( INT64_HASH( (gint64 *) &i ) );
170 }
171 else if( generic == G_TYPE_PARAM_INT64 ) {
172 gint64 i = g_value_get_int64( value );
173
174 return( INT64_HASH( &i ) );
175 }
176 else if( generic == G_TYPE_PARAM_FLOAT ) {
177 float f = g_value_get_float( value );
178
179 return( g_direct_hash( (void *) &f ) );
180 }
181 else if( generic == G_TYPE_PARAM_DOUBLE ) {
182 double d = g_value_get_double( value );
183
184 return( DOUBLE_HASH( &d ) );
185 }
186 else if( generic == G_TYPE_PARAM_STRING ) {
187 const char *s = g_value_get_string( value );
188
189 return( s ? g_str_hash( s ) : 0 );
190 }
191 else if( generic == G_TYPE_PARAM_BOXED ) {
192 void *p = g_value_get_boxed( value );
193
194 return( p ? g_direct_hash( p ) : 0 );
195 }
196 else if( generic == G_TYPE_PARAM_POINTER ) {
197 void *p = g_value_get_pointer( value );
198
199 return( p ? g_direct_hash( p ) : 0 );
200 }
201 else if( generic == G_TYPE_PARAM_OBJECT ) {
202 void *p = g_value_get_object( value );
203
204 return( p ? g_direct_hash( p ) : 0 );
205 }
206 else {
207 /* Fallback: convert to a string and hash that.
208 * This is very slow, print a warning if we use it
209 * so we can add another case.
210 */
211 char *s;
212 unsigned int hash;
213
214 s = g_strdup_value_contents( value );
215 hash = g_str_hash( s );
216
217 printf( "vips_value_hash: no case for %s\n", s );
218 printf( "\ttype %d, %s\n",
219 (int) G_VALUE_TYPE( value ),
220 g_type_name( G_VALUE_TYPE( value ) ) );
221 printf( "\tgeneric %d, %s\n",
222 (int) G_VALUE_TYPE( generic ),
223 g_type_name( generic ) );
224
225 g_free( s );
226
227 return( hash );
228 }
229 }
230
231 /* Pass in the pspec so we can get the generic type. For example, a
232 * value could be held in a GParamSpec allowing OBJECT, but the value
233 * could be of type VipsImage. generics are much faster to compare.
234 */
235 static gboolean
vips_value_equal(GParamSpec * pspec,GValue * v1,GValue * v2)236 vips_value_equal( GParamSpec *pspec, GValue *v1, GValue *v2 )
237 {
238 GType generic = G_PARAM_SPEC_TYPE( pspec );
239 GType t1 = G_VALUE_TYPE( v1 );
240 GType t2 = G_VALUE_TYPE( v2 );
241
242 if( t1 != t2 )
243 return( FALSE );
244
245 /* Not compile-time constants, so we have to use a set of if()s. Could
246 * make a table at run time I guess.
247 */
248
249 if( generic == G_TYPE_PARAM_BOOLEAN )
250 return( g_value_get_boolean( v1 ) ==
251 g_value_get_boolean( v2 ) );
252 else if( generic == G_TYPE_PARAM_CHAR )
253 return( g_value_get_schar( v1 ) ==
254 g_value_get_schar( v2 ) );
255 if( generic == G_TYPE_PARAM_UCHAR )
256 return( g_value_get_uchar( v1 ) ==
257 g_value_get_uchar( v2 ) );
258 if( generic == G_TYPE_PARAM_INT )
259 return( g_value_get_int( v1 ) ==
260 g_value_get_int( v2 ) );
261 if( generic == G_TYPE_PARAM_UINT )
262 return( g_value_get_uint( v1 ) ==
263 g_value_get_uint( v2 ) );
264 if( generic == G_TYPE_PARAM_LONG )
265 return( g_value_get_long( v1 ) ==
266 g_value_get_long( v2 ) );
267 if( generic == G_TYPE_PARAM_ULONG )
268 return( g_value_get_ulong( v1 ) ==
269 g_value_get_ulong( v2 ) );
270 if( generic == G_TYPE_PARAM_ENUM )
271 return( g_value_get_enum( v1 ) ==
272 g_value_get_enum( v2 ) );
273 if( generic == G_TYPE_PARAM_FLAGS )
274 return( g_value_get_flags( v1 ) ==
275 g_value_get_flags( v2 ) );
276 if( generic == G_TYPE_PARAM_UINT64 )
277 return( g_value_get_uint64( v1 ) ==
278 g_value_get_uint64( v2 ) );
279 if( generic == G_TYPE_PARAM_INT64 )
280 return( g_value_get_int64( v1 ) ==
281 g_value_get_int64( v2 ) );
282 if( generic == G_TYPE_PARAM_FLOAT )
283 return( g_value_get_float( v1 ) ==
284 g_value_get_float( v2 ) );
285 if( generic == G_TYPE_PARAM_DOUBLE )
286 return( g_value_get_double( v1 ) ==
287 g_value_get_double( v2 ) );
288 if( generic == G_TYPE_PARAM_STRING ) {
289 const char *s1 = g_value_get_string( v1 );
290 const char *s2 = g_value_get_string( v2 );
291
292 if( s1 == s2 )
293 return( TRUE );
294 else
295 return( s1 && s2 && strcmp( s1, s2 ) == 0 );
296 }
297 if( generic == G_TYPE_PARAM_BOXED )
298 return( g_value_get_boxed( v1 ) ==
299 g_value_get_boxed( v2 ) );
300 if( generic == G_TYPE_PARAM_POINTER )
301 return( g_value_get_pointer( v1 ) ==
302 g_value_get_pointer( v2 ) );
303 if( generic == G_TYPE_PARAM_OBJECT )
304 return( g_value_get_object( v1 ) ==
305 g_value_get_object( v2 ) );
306 else {
307 /* Fallback: convert to a string and compare that.
308 * This is very slow, print a warning if we use it
309 * so we can add another case.
310 */
311 char *s1;
312 char *s2;
313 gboolean equal;
314
315 s1 = g_strdup_value_contents( v1 );
316 s2 = g_strdup_value_contents( v2 );
317 equal = strcmp( s1, s2 ) == 0;
318
319 printf( "vips_value_equal: no case for %s, %s\n",
320 s1, s2 );
321 printf( "\tt1 %d, %s\n", (int) t1, g_type_name( t1 ) );
322 printf( "\tt2 %d, %s\n", (int) t2, g_type_name( t2 ) );
323 printf( "\tgeneric %d, %s\n",
324 (int) G_VALUE_TYPE( generic ),
325 g_type_name( generic ) );
326
327 g_free( s1 );
328 g_free( s2 );
329
330 return( equal );
331 }
332 }
333
334 static void *
vips_object_hash_arg(VipsObject * object,GParamSpec * pspec,VipsArgumentClass * argument_class,VipsArgumentInstance * argument_instance,void * a,void * b)335 vips_object_hash_arg( VipsObject *object,
336 GParamSpec *pspec,
337 VipsArgumentClass *argument_class,
338 VipsArgumentInstance *argument_instance,
339 void *a, void *b )
340 {
341 unsigned int *hash = (unsigned int *) a;
342
343 if( (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
344 (argument_class->flags & VIPS_ARGUMENT_INPUT) &&
345 argument_instance->assigned ) {
346 const char *name = g_param_spec_get_name( pspec );
347 GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
348 GValue value = { 0, };
349
350 g_value_init( &value, type );
351 g_object_get_property( G_OBJECT( object ), name, &value );
352 *hash = (*hash << 1) ^ vips_value_hash( pspec, &value );
353 g_value_unset( &value );
354 }
355
356 return( NULL );
357 }
358
359 /* Find a hash from the input arguments to a VipsOperstion.
360 */
361 static unsigned int
vips_operation_hash(VipsOperation * operation)362 vips_operation_hash( VipsOperation *operation )
363 {
364 if( !operation->found_hash ) {
365 guint hash;
366
367 /* Include the operation type in the hash.
368 */
369 hash = (guint) G_OBJECT_TYPE( operation );
370 (void) vips_argument_map( VIPS_OBJECT( operation ),
371 vips_object_hash_arg, &hash, NULL );
372
373 /* Make sure we can't have a zero hash value.
374 */
375 hash |= 1;
376
377 operation->hash = hash;
378 operation->found_hash = TRUE;
379 }
380
381 return( operation->hash );
382 }
383
384 static void *
vips_object_equal_arg(VipsObject * object,GParamSpec * pspec,VipsArgumentClass * argument_class,VipsArgumentInstance * argument_instance,void * a,void * b)385 vips_object_equal_arg( VipsObject *object,
386 GParamSpec *pspec,
387 VipsArgumentClass *argument_class,
388 VipsArgumentInstance *argument_instance,
389 void *a, void *b )
390 {
391 VipsObject *other = (VipsObject *) a;
392
393 const char *name = g_param_spec_get_name( pspec );
394 GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
395 GValue v1 = { 0, };
396 GValue v2 = { 0, };
397
398 gboolean equal;
399
400 /* Only test assigned input constructor args.
401 */
402 if( !(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) ||
403 !(argument_class->flags & VIPS_ARGUMENT_INPUT) ||
404 !argument_instance->assigned )
405 return( NULL );
406
407 /* If this is an optional arg, we need to check that this was
408 * assigned on @other as well.
409 */
410 if( !(argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
411 !vips_object_argument_isset( other, name ) )
412 /* Optional and was not set on other ... we've found a
413 * difference!
414 */
415 return( object );
416
417 g_value_init( &v1, type );
418 g_value_init( &v2, type );
419 g_object_get_property( G_OBJECT( object ), name, &v1 );
420 g_object_get_property( G_OBJECT( other ), name, &v2 );
421 equal = vips_value_equal( pspec, &v1, &v2 );
422 g_value_unset( &v1 );
423 g_value_unset( &v2 );
424
425 /* Stop (return non-NULL) if we've found a difference.
426 */
427 return( !equal ? object : NULL );
428 }
429
430 /* Are two objects equal, ie. have the same inputs.
431 */
432 static gboolean
vips_operation_equal(VipsOperation * a,VipsOperation * b)433 vips_operation_equal( VipsOperation *a, VipsOperation *b )
434 {
435 if( a == b )
436 return( TRUE );
437
438 if( G_OBJECT_TYPE( a ) == G_OBJECT_TYPE( b ) &&
439 vips_operation_hash( a ) == vips_operation_hash( b ) &&
440 !vips_argument_map( VIPS_OBJECT( a ),
441 vips_object_equal_arg, b, NULL ) )
442 return( TRUE );
443
444 return( FALSE );
445 }
446
447 void *
vips__cache_once_init(void * data)448 vips__cache_once_init( void *data )
449 {
450 vips_cache_lock = vips_g_mutex_new();
451
452 vips_cache_table = g_hash_table_new(
453 (GHashFunc) vips_operation_hash,
454 (GEqualFunc) vips_operation_equal );
455
456 return( NULL );
457 }
458
459 void
vips__cache_init(void)460 vips__cache_init( void )
461 {
462 static GOnce once = G_ONCE_INIT;
463
464 VIPS_ONCE( &once, vips__cache_once_init, NULL );
465 }
466
467 static void *
vips_cache_print_fn(void * value,void * a,void * b)468 vips_cache_print_fn( void *value, void *a, void *b )
469 {
470 VipsOperationCacheEntry *entry = value;
471
472 char str[32768];
473 VipsBuf buf = VIPS_BUF_STATIC( str );
474
475 vips_object_to_string( VIPS_OBJECT( entry->operation ), &buf );
476
477 printf( "%p - %s\n", value, vips_buf_all( &buf ) );
478
479 return( NULL );
480 }
481
482 static void
vips_cache_print_nolock(void)483 vips_cache_print_nolock( void )
484 {
485 if( vips_cache_table ) {
486 printf( "Operation cache:\n" );
487 vips_hash_table_map( vips_cache_table,
488 vips_cache_print_fn, NULL, NULL );
489 }
490 }
491
492 /**
493 * vips_cache_print:
494 *
495 * Print the whole operation cache to stdout. Handy for debugging.
496 */
497 void
vips_cache_print(void)498 vips_cache_print( void )
499 {
500 g_mutex_lock( vips_cache_lock );
501
502 vips_cache_print_nolock();
503
504 g_mutex_unlock( vips_cache_lock );
505 }
506
507 static void *
vips_object_unref_arg(VipsObject * object,GParamSpec * pspec,VipsArgumentClass * argument_class,VipsArgumentInstance * argument_instance,void * a,void * b)508 vips_object_unref_arg( VipsObject *object,
509 GParamSpec *pspec,
510 VipsArgumentClass *argument_class,
511 VipsArgumentInstance *argument_instance,
512 void *a, void *b )
513 {
514 if( (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
515 (argument_class->flags & VIPS_ARGUMENT_OUTPUT) &&
516 argument_instance->assigned &&
517 G_IS_PARAM_SPEC_OBJECT( pspec ) ) {
518 GObject *value;
519
520 /* This will up the ref count for us.
521 */
522 g_object_get( G_OBJECT( object ),
523 g_param_spec_get_name( pspec ), &value, NULL );
524
525 /* Drop the ref we just got, then drop the ref we make when we
526 * added to the cache.
527 */
528 g_object_unref( value );
529 g_object_unref( value );
530 }
531
532 return( NULL );
533 }
534
535 static void
vips_cache_unref(VipsOperation * operation)536 vips_cache_unref( VipsOperation *operation )
537 {
538 #ifdef DEBUG
539 printf( "vips_cache_unref: " );
540 vips_object_print_summary( VIPS_OBJECT( operation ) );
541 #endif /*DEBUG*/
542
543 (void) vips_argument_map( VIPS_OBJECT( operation ),
544 vips_object_unref_arg, NULL, NULL );
545 g_object_unref( operation );
546 }
547
548 /* Remove an operation from the cache.
549 */
550 static void
vips_cache_remove(VipsOperation * operation)551 vips_cache_remove( VipsOperation *operation )
552 {
553 VipsOperationCacheEntry *entry = (VipsOperationCacheEntry *)
554 g_hash_table_lookup( vips_cache_table, operation );
555
556 #ifdef DEBUG
557 printf( "vips_cache_remove: " );
558 vips_object_print_summary( VIPS_OBJECT( operation ) );
559 #endif /*DEBUG*/
560
561 g_assert( entry );
562
563 if( entry->invalidate_id ) {
564 g_signal_handler_disconnect( operation, entry->invalidate_id );
565 entry->invalidate_id = 0;
566 }
567
568 g_hash_table_remove( vips_cache_table, operation );
569 vips_cache_unref( operation );
570
571 g_free( entry );
572 }
573
574 static void *
vips_object_ref_arg(VipsObject * object,GParamSpec * pspec,VipsArgumentClass * argument_class,VipsArgumentInstance * argument_instance,void * a,void * b)575 vips_object_ref_arg( VipsObject *object,
576 GParamSpec *pspec,
577 VipsArgumentClass *argument_class,
578 VipsArgumentInstance *argument_instance,
579 void *a, void *b )
580 {
581 if( (argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
582 (argument_class->flags & VIPS_ARGUMENT_OUTPUT) &&
583 argument_instance->assigned &&
584 G_IS_PARAM_SPEC_OBJECT( pspec ) ) {
585 GObject *value;
586
587 /* This will up the ref count for us.
588 */
589 g_object_get( G_OBJECT( object ),
590 g_param_spec_get_name( pspec ), &value, NULL );
591 }
592
593 return( NULL );
594 }
595
596 static void
vips_operation_touch(VipsOperation * operation)597 vips_operation_touch( VipsOperation *operation )
598 {
599 VipsOperationCacheEntry *entry = (VipsOperationCacheEntry *)
600 g_hash_table_lookup( vips_cache_table, operation );
601
602 vips_cache_time += 1;
603
604 /* Don't up the time for invalid items -- we want them to fall out of
605 * cache.
606 */
607 if( !entry->invalid )
608 entry->time = vips_cache_time;
609 }
610
611 /* Ref an operation for the cache. The operation itself, plus all the output
612 * objects it makes.
613 */
614 static void
vips_cache_ref(VipsOperation * operation)615 vips_cache_ref( VipsOperation *operation )
616 {
617 #ifdef DEBUG
618 printf( "vips_cache_ref: " );
619 vips_object_print_summary( VIPS_OBJECT( operation ) );
620 #endif /*DEBUG*/
621
622 g_object_ref( operation );
623 (void) vips_argument_map( VIPS_OBJECT( operation ),
624 vips_object_ref_arg, NULL, NULL );
625 vips_operation_touch( operation );
626 }
627
628 static void
vips_cache_invalidate_cb(VipsOperation * operation,VipsOperationCacheEntry * entry)629 vips_cache_invalidate_cb( VipsOperation *operation,
630 VipsOperationCacheEntry *entry )
631 {
632 #ifdef DEBUG
633 printf( "vips_cache_invalidate_cb: " );
634 vips_object_print_summary( VIPS_OBJECT( operation ) );
635 #endif /*DEBUG*/
636
637 entry->invalid = TRUE;
638 }
639
640 static void
vips_cache_insert(VipsOperation * operation)641 vips_cache_insert( VipsOperation *operation )
642 {
643 VipsOperationCacheEntry *entry = g_new( VipsOperationCacheEntry, 1 );
644
645 #ifdef VIPS_DEBUG
646 printf( "vips_cache_insert: adding to cache" );
647 vips_object_print_dump( VIPS_OBJECT( operation ) );
648 #endif /*VIPS_DEBUG*/
649
650 entry->operation = operation;
651 entry->time = 0;
652 entry->invalidate_id = 0;
653 entry->invalid = FALSE;
654
655 g_hash_table_insert( vips_cache_table, operation, entry );
656 vips_cache_ref( operation );
657
658 /* If the operation signals "invalidate", we must tag this cache entry
659 * for removal.
660 */
661 entry->invalidate_id = g_signal_connect( operation, "invalidate",
662 G_CALLBACK( vips_cache_invalidate_cb ), entry );
663 }
664
665 static void *
vips_cache_get_first_fn(void * value,void * a,void * b)666 vips_cache_get_first_fn( void *value, void *a, void *b )
667 {
668 return( value );
669 }
670
671 /* Return the first item.
672 */
673 static VipsOperation *
vips_cache_get_first(void)674 vips_cache_get_first( void )
675 {
676 VipsOperationCacheEntry *entry;
677
678 if( vips_cache_table &&
679 (entry = vips_hash_table_map( vips_cache_table,
680 vips_cache_get_first_fn, NULL, NULL )) )
681 return( VIPS_OPERATION( entry->operation ) );
682
683 return( NULL );
684 }
685
686 /**
687 * vips_cache_drop_all:
688 *
689 * Drop the whole operation cache, handy for leak tracking. Also called
690 * automatically on vips_shutdown().
691 */
692 void
vips_cache_drop_all(void)693 vips_cache_drop_all( void )
694 {
695 #ifdef VIPS_DEBUG
696 printf( "vips_cache_drop_all:\n" );
697 #endif /*VIPS_DEBUG*/
698
699 g_mutex_lock( vips_cache_lock );
700
701 if( vips_cache_table ) {
702 VipsOperation *operation;
703
704 if( vips__cache_dump )
705 vips_cache_print_nolock();
706
707 /* We can't modify the hash in the callback from
708 * g_hash_table_foreach() and friends. Repeatedly drop the
709 * first item instead.
710 */
711 while( (operation = vips_cache_get_first()) )
712 vips_cache_remove( operation );
713
714 VIPS_FREEF( g_hash_table_unref, vips_cache_table );
715 }
716
717 g_mutex_unlock( vips_cache_lock );
718 }
719
720 static void
vips_cache_get_lru_cb(VipsOperation * key,VipsOperationCacheEntry * value,VipsOperationCacheEntry ** best)721 vips_cache_get_lru_cb( VipsOperation *key, VipsOperationCacheEntry *value,
722 VipsOperationCacheEntry **best )
723 {
724 if( !*best ||
725 (*best)->time > value->time )
726 *best = value;
727 }
728
729 /* Get the least-recently-used cache item.
730 *
731 * TODO ... will this be too expensive? probably not
732 */
733 static VipsOperation *
vips_cache_get_lru(void)734 vips_cache_get_lru( void )
735 {
736 VipsOperationCacheEntry *entry;
737
738 entry = NULL;
739 g_hash_table_foreach( vips_cache_table,
740 (GHFunc) vips_cache_get_lru_cb, &entry );
741
742 if( entry )
743 return( entry->operation );
744
745 return( NULL );
746 }
747
748 /* Is the cache full? Drop until it's not.
749 */
750 static void
vips_cache_trim(void)751 vips_cache_trim( void )
752 {
753 VipsOperation *operation;
754
755 g_mutex_lock( vips_cache_lock );
756
757 while( vips_cache_table &&
758 (g_hash_table_size( vips_cache_table ) > vips_cache_max ||
759 vips_tracked_get_files() > vips_cache_max_files ||
760 vips_tracked_get_mem() > vips_cache_max_mem) &&
761 (operation = vips_cache_get_lru()) ) {
762 #ifdef DEBUG
763 printf( "vips_cache_trim: trimming " );
764 vips_object_print_summary( VIPS_OBJECT( operation ) );
765 #endif /*DEBUG*/
766
767 vips_cache_remove( operation );
768 }
769
770 g_mutex_unlock( vips_cache_lock );
771 }
772
773 /**
774 * vips_cache_operation_lookup:
775 * @operation: (transfer none): pointer to operation to lookup
776 *
777 * Look up an unbuilt @operation in the cache. If we get a hit, ref and
778 * return the old operation. If there's no hit, return NULL.
779 *
780 * Returns: (transfer full): the cache hit, if any.
781 */
782 VipsOperation *
vips_cache_operation_lookup(VipsOperation * operation)783 vips_cache_operation_lookup( VipsOperation *operation )
784 {
785 VipsOperationCacheEntry *hit;
786 VipsOperation *result;
787
788 g_assert( VIPS_IS_OPERATION( operation ) );
789 g_assert( !VIPS_OBJECT( operation )->constructed );
790
791 #ifdef VIPS_DEBUG
792 printf( "vips_cache_operation_lookup: " );
793 vips_object_print_dump( VIPS_OBJECT( operation ) );
794 #endif /*VIPS_DEBUG*/
795
796 g_mutex_lock( vips_cache_lock );
797
798 result = NULL;
799
800 if( (hit = g_hash_table_lookup( vips_cache_table, operation )) ) {
801 if( hit->invalid ) {
802 /* There but has been tagged for removal.
803 */
804 vips_cache_remove( hit->operation );
805 hit = NULL;
806 }
807 else {
808 if( vips__cache_trace ) {
809 printf( "vips cache*: " );
810 vips_object_print_summary(
811 VIPS_OBJECT( operation ) );
812 }
813
814 result = hit->operation;
815 vips_cache_ref( result );
816 }
817 }
818
819 g_mutex_unlock( vips_cache_lock );
820
821 #ifdef VIPS_DEBUG
822 printf( "vips_cache_operation_lookup: result = %p\n", result );
823 #endif /*VIPS_DEBUG*/
824
825 return( result );
826 }
827
828 /**
829 * vips_cache_operation_add:
830 * @operation: (transfer none): pointer to operation to add
831 *
832 * Add a built operation to the cache. The cache will ref the operation.
833 */
834 void
vips_cache_operation_add(VipsOperation * operation)835 vips_cache_operation_add( VipsOperation *operation )
836 {
837 g_assert( VIPS_OBJECT( operation )->constructed );
838
839 g_mutex_lock( vips_cache_lock );
840
841 #ifdef VIPS_DEBUG
842 printf( "vips_cache_operation_add: adding " );
843 vips_object_print_dump( VIPS_OBJECT( operation ) );
844 #endif /*VIPS_DEBUG*/
845
846 /* If two threads call the same operation at the same time,
847 * we can get multiple adds. Let the first one win. See
848 * https://github.com/libvips/libvips/pull/181
849 */
850 if( !g_hash_table_lookup( vips_cache_table, operation ) ) {
851 VipsOperationFlags flags =
852 vips_operation_get_flags( operation );
853 gboolean nocache = flags & VIPS_OPERATION_NOCACHE;
854
855 /* Has to be after _build() so we can see output args.
856 */
857 if( vips__cache_trace ) {
858 if( nocache )
859 printf( "vips cache : " );
860 else
861 printf( "vips cache+: " );
862 vips_object_print_summary( VIPS_OBJECT( operation ) );
863 }
864
865 if( !nocache )
866 vips_cache_insert( operation );
867 }
868
869 g_mutex_unlock( vips_cache_lock );
870
871 vips_cache_trim();
872 }
873
874 /**
875 * vips_cache_operation_buildp: (skip)
876 * @operation: pointer to operation to lookup
877 *
878 * Look up @operation in the cache. If we get a hit, unref @operation, ref the
879 * old one and return that through the argument pointer.
880 *
881 * If we miss, build and add @operation.
882 *
883 * Returns: 0 on success, or -1 on error.
884 */
885 int
vips_cache_operation_buildp(VipsOperation ** operation)886 vips_cache_operation_buildp( VipsOperation **operation )
887 {
888 VipsOperation *hit;
889
890 g_assert( VIPS_IS_OPERATION( *operation ) );
891
892 #ifdef VIPS_DEBUG
893 printf( "vips_cache_operation_buildp: " );
894 vips_object_print_dump( VIPS_OBJECT( *operation ) );
895 #endif /*VIPS_DEBUG*/
896
897 if( (hit = vips_cache_operation_lookup( *operation )) ) {
898 #ifdef VIPS_DEBUG
899 printf( "vips_cache_operation_buildp: cache hit %p\n", hit );
900 #endif /*VIPS_DEBUG*/
901
902 g_object_unref( *operation );
903 *operation = hit;
904 }
905 else {
906 #ifdef VIPS_DEBUG
907 printf( "vips_cache_operation_buildp: cache miss, building\n" );
908 #endif /*VIPS_DEBUG*/
909
910 if( vips_object_build( VIPS_OBJECT( *operation ) ) )
911 return( -1 );
912
913 vips_cache_operation_add( *operation );
914 }
915
916 return( 0 );
917 }
918
919 /**
920 * vips_cache_operation_build:
921 * @operation: (transfer none): operation to lookup
922 *
923 * A binding-friendly version of vips_cache_operation_buildp().
924 *
925 * After calling this, @operation has the same ref count as when it went in,
926 * and the result must be freed with vips_object_unref_outputs() and
927 * g_object_unref().
928 *
929 * Returns: (transfer full): The built operation.
930 */
931 VipsOperation *
vips_cache_operation_build(VipsOperation * operation)932 vips_cache_operation_build( VipsOperation *operation )
933 {
934 VipsOperation *orig_operation = operation;
935
936 /* Stop it being unreffed for us on hit.
937 */
938 g_object_ref( orig_operation );
939
940 if( vips_cache_operation_buildp( &operation ) ) {
941 g_object_unref( orig_operation );
942
943 return( NULL );
944 }
945
946 return( operation );
947 }
948
949 /**
950 * vips_cache_set_max:
951 * @max: maximum number of operation to cache
952 *
953 * Set the maximum number of operations we keep in cache.
954 */
955 void
vips_cache_set_max(int max)956 vips_cache_set_max( int max )
957 {
958 vips_cache_max = max;
959 vips_cache_trim();
960 }
961
962 /**
963 * vips_cache_set_max_mem:
964 * @max_mem: maximum amount of tracked memory we use
965 *
966 * Set the maximum amount of tracked memory we allow before we start dropping
967 * cached operations. See vips_tracked_get_mem().
968 *
969 * libvips only tracks memory it allocates, it can't track memory allocated by
970 * external libraries. If you use an operation like vips_magickload(), most of
971 * the memory it uses won't be included.
972 *
973 * See also: vips_tracked_get_mem().
974 */
975 void
vips_cache_set_max_mem(size_t max_mem)976 vips_cache_set_max_mem( size_t max_mem )
977 {
978 vips_cache_max_mem = max_mem;
979 vips_cache_trim();
980 }
981
982 /**
983 * vips_cache_get_max:
984 *
985 * Get the maximum number of operations we keep in cache.
986 *
987 * Returns: the maximum number of operations we keep in cache
988 */
989 int
vips_cache_get_max(void)990 vips_cache_get_max( void )
991 {
992 return( vips_cache_max );
993 }
994
995 /**
996 * vips_cache_get_size:
997 *
998 * Get the current number of operations in cache.
999 *
1000 * Returns: get the current number of operations in cache.
1001 */
1002 int
vips_cache_get_size(void)1003 vips_cache_get_size( void )
1004 {
1005 guint size;
1006
1007 g_mutex_lock( vips_cache_lock );
1008
1009 size = 0;
1010 if( vips_cache_table )
1011 size = g_hash_table_size( vips_cache_table );
1012
1013 g_mutex_unlock( vips_cache_lock );
1014
1015 return( size );
1016 }
1017
1018 /**
1019 * vips_cache_get_max_mem:
1020 *
1021 * Get the maximum amount of tracked memory we allow before we start dropping
1022 * cached operations. See vips_tracked_get_mem().
1023 *
1024 * See also: vips_tracked_get_mem().
1025 *
1026 * Returns: the maximum amount of tracked memory we allow
1027 */
1028 size_t
vips_cache_get_max_mem(void)1029 vips_cache_get_max_mem( void )
1030 {
1031 return( vips_cache_max_mem );
1032 }
1033
1034 /**
1035 * vips_cache_get_max_files:
1036 *
1037 * Get the maximum number of tracked files we allow before we start dropping
1038 * cached operations. See vips_tracked_get_files().
1039 *
1040 * libvips only tracks file descriptors it allocates, it can't track ones
1041 * allocated by external libraries. If you use an operation like
1042 * vips_magickload(), most of the descriptors it uses won't be included.
1043 *
1044 * See also: vips_tracked_get_files().
1045 *
1046 * Returns: the maximum number of tracked files we allow
1047 */
1048 int
vips_cache_get_max_files(void)1049 vips_cache_get_max_files( void )
1050 {
1051 return( vips_cache_max_files );
1052 }
1053
1054 /**
1055 * vips_cache_set_max_files:
1056 * @max_files: max open files we allow
1057 *
1058 * Set the maximum number of tracked files we allow before we start dropping
1059 * cached operations. See vips_tracked_get_files().
1060 *
1061 * See also: vips_tracked_get_files().
1062 */
1063 void
vips_cache_set_max_files(int max_files)1064 vips_cache_set_max_files( int max_files )
1065 {
1066 vips_cache_max_files = max_files;
1067 vips_cache_trim();
1068 }
1069
1070 /**
1071 * vips_cache_set_dump:
1072 * @dump: if %TRUE, dump the operation cache on exit
1073 *
1074 * Handy for debugging. Print the operation cache to stdout just before exit.
1075 *
1076 * See also: vips_cache_set_trace().
1077 */
1078 void
vips_cache_set_dump(gboolean dump)1079 vips_cache_set_dump( gboolean dump )
1080 {
1081 vips__cache_dump = dump;
1082 }
1083
1084 /**
1085 * vips_cache_set_trace:
1086 * @trace: if %TRUE, trace the operation cache
1087 *
1088 * Handy for debugging. Print operation cache actions to stdout as we run.
1089 *
1090 * You can set the environment variable `VIPS_TRACE` to turn this option on, or
1091 * use the command-line flag `--vips-cache-trace`.
1092 *
1093 * See also: vips_cache_set_dump().
1094 */
1095 void
vips_cache_set_trace(gboolean trace)1096 vips_cache_set_trace( gboolean trace )
1097 {
1098 vips__cache_trace = trace;
1099 }
1100