Coverage Report

Created: 2026-08-11 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvips/libvips/iofuncs/buffer.c
Line
Count
Source
1
/* Manage sets of pixel buffers on an image.
2
 *
3
 * 30/10/06
4
 *  - from window.c
5
 * 2/2/07
6
 *  - speed up the search, use our own lock (thanks Christian)
7
 * 5/2/07
8
 *  - split to many buffer lists per image
9
 * 11/2/07
10
 *  - split to a buffer hash per thread
11
 *  - reuse buffer mallocs when we can
12
 * 20/2/07
13
 *  - add VipsBufferCacheList and we can avoid some hash ops on
14
 *    done/undone
15
 * 5/3/10
16
 *  - move invalid stuff to region
17
 *  - move link maintenance to im_demand_hint
18
 * 21/9/11
19
 *  - switch to vips_tracked_malloc()
20
 * 18/12/13
21
 *  - keep a few buffers in reserve per image, stops malloc/free
22
 *    cycling when sharing is repeatedly discovered
23
 * 6/6/16
24
 *  - free buffers on image close as well as thread exit, so main thread
25
 *    buffers don't clog up the system
26
 * 13/10/16
27
 *  - better solution: don't keep a buffercache for non-workers
28
 */
29
30
/*
31
32
  This file is part of VIPS.
33
34
  VIPS is free software; you can redistribute it and/or modify
35
  it under the terms of the GNU Lesser General Public License as published by
36
  the Free Software Foundation; either version 2 of the License, or
37
  (at your option) any later version.
38
39
  This program is distributed in the hope that it will be useful,
40
  but WITHOUT ANY WARRANTY; without even the implied warranty of
41
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42
  GNU Lesser General Public License for more details.
43
44
  You should have received a copy of the GNU Lesser General Public License
45
  along with this program; if not, write to the Free Software
46
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
47
  02110-1301  USA
48
49
 */
50
51
/*
52
53
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
54
55
 */
56
57
/*
58
#define DEBUG_VERBOSE
59
#define DEBUG_CREATE
60
#define DEBUG
61
 */
62
63
#ifdef HAVE_CONFIG_H
64
#include <config.h>
65
#endif /*HAVE_CONFIG_H*/
66
#include <glib/gi18n-lib.h>
67
68
#include <stdio.h>
69
#include <stdlib.h>
70
71
#include <vips/vips.h>
72
#include <vips/internal.h>
73
74
#ifdef DEBUG
75
/* Track all buffers here for debugging.
76
 */
77
static GSList *vips__buffer_all = NULL;
78
#endif /*DEBUG*/
79
80
#ifdef DEBUG_CREATE
81
static GSList *vips__buffer_cache_all = NULL;
82
#endif /*DEBUG_CREATE*/
83
84
/* The maximum numbers of buffers we hold in reserve per image.
85
 */
86
static const int buffer_cache_max_reserve = 2;
87
88
/* Workers have a BufferThread (and BufferCache) in a GPrivate they have
89
 * exclusive access to.
90
 */
91
static void buffer_thread_destroy_notify(gpointer data);
92
static GPrivate buffer_thread_key =
93
  G_PRIVATE_INIT(buffer_thread_destroy_notify);
94
95
void
96
vips_buffer_print(VipsBuffer *buffer)
97
0
{
98
0
  printf("VipsBuffer: %p ref_count = %d, ", buffer, buffer->ref_count);
99
0
  printf("im = %p, ", buffer->im);
100
0
  printf("area.left = %d, ", buffer->area.left);
101
0
  printf("area.top = %d, ", buffer->area.top);
102
0
  printf("area.width = %d, ", buffer->area.width);
103
0
  printf("area.height = %d, ", buffer->area.height);
104
0
  printf("done = %d, ", buffer->done);
105
0
  printf("cache = %p, ", buffer->cache);
106
0
  printf("buf = %p, ", buffer->buf);
107
0
  printf("bsize = %zd\n", buffer->bsize);
108
0
}
109
110
#ifdef DEBUG
111
static void *
112
vips_buffer_dump(VipsBuffer *buffer, size_t *reserve, size_t *alive)
113
{
114
  vips_buffer_print(buffer);
115
116
  g_assert(buffer->im);
117
  g_assert(buffer->buf);
118
119
  if (!buffer->cache &&
120
    !buffer->done) {
121
    /* Global buffer, not linked to any cache.
122
     */
123
    printf("global buffer %p, %.3g MB\n",
124
      buffer, buffer->bsize / (1024 * 1024.0));
125
    *alive += buffer->bsize;
126
  }
127
128
  else if (buffer->cache &&
129
    buffer->done &&
130
    !vips_rect_isempty(&buffer->area) &&
131
    g_slist_find(buffer->cache->buffers, buffer)) {
132
    /* Published on a thread.
133
     */
134
    printf("thread buffer %p, %.3g MB\n",
135
      buffer, buffer->bsize / (1024 * 1024.0));
136
    *alive += buffer->bsize;
137
  }
138
139
  else if (buffer->ref_count == 0 &&
140
    buffer->cache &&
141
    !buffer->done &&
142
    vips_rect_isempty(&buffer->area) &&
143
    g_slist_find(buffer->cache->reserve, buffer))
144
    /* Held in reserve.
145
     */
146
    *reserve += buffer->bsize;
147
148
  else
149
    printf("buffer craziness!\n");
150
151
  return NULL;
152
}
153
#endif /*DEBUG*/
154
155
#ifdef DEBUG_CREATE
156
static void *
157
vips_buffer_cache_dump(VipsBufferCache *cache, void *a, void *b)
158
{
159
  printf("VipsBufferCache: %p\n", cache);
160
  printf("\t%d buffers\n", g_slist_length(cache->buffers));
161
  printf("\tthread %p\n", cache->thread);
162
  printf("\timage %p\n", cache->im);
163
  printf("\tbuffer_thread %p\n", cache->buffer_thread);
164
  printf("\t%d in reserve\n", g_slist_length(cache->reserve));
165
166
  return NULL;
167
}
168
#endif /*DEBUG_CREATE*/
169
170
void
171
vips_buffer_dump_all(void)
172
0
{
173
#ifdef DEBUG
174
  if (vips__buffer_all) {
175
    size_t reserve;
176
    size_t alive;
177
178
    printf("buffers:\n");
179
180
    reserve = 0;
181
    alive = 0;
182
    vips_slist_map2(vips__buffer_all,
183
      (VipsSListMap2Fn) vips_buffer_dump, &reserve, &alive);
184
    printf("%.3g MB alive\n", alive / (1024 * 1024.0));
185
    printf("%.3g MB in reserve\n", reserve / (1024 * 1024.0));
186
  }
187
188
#ifdef DEBUG_CREATE
189
  if (vips__buffer_cache_all) {
190
    printf("buffers: %d buffer cache still alive\n",
191
      g_slist_length(vips__buffer_cache_all));
192
    vips_slist_map2(vips__buffer_cache_all,
193
      (VipsSListMap2Fn) vips_buffer_cache_dump, NULL, NULL);
194
    printf("g_thread_self() == %p\n", g_thread_self());
195
  }
196
#endif /*DEBUG_CREATE*/
197
#endif /*DEBUG*/
198
0
}
199
200
static void
201
vips_buffer_free(VipsBuffer *buffer)
202
9.75M
{
203
9.75M
  VIPS_FREEF(vips_tracked_aligned_free, buffer->buf);
204
9.75M
  buffer->bsize = 0;
205
9.75M
  g_free(buffer);
206
207
#ifdef DEBUG
208
  g_mutex_lock(&vips__global_lock);
209
210
  g_assert(g_slist_find(vips__buffer_all, buffer));
211
  vips__buffer_all = g_slist_remove(vips__buffer_all, buffer);
212
213
  g_mutex_unlock(&vips__global_lock);
214
#endif /*DEBUG*/
215
216
#ifdef DEBUG_VERBOSE
217
  printf("vips_buffer_free: freeing buffer %p\n", buffer);
218
#endif /*DEBUG_VERBOSE*/
219
9.75M
}
220
221
static void
222
buffer_thread_free(VipsBufferThread *buffer_thread)
223
1.03M
{
224
1.03M
  VIPS_FREEF(g_hash_table_destroy, buffer_thread->hash);
225
1.03M
  VIPS_FREE(buffer_thread);
226
1.03M
}
227
228
/* Run for GDestroyNotify on the VipsBufferThread hash.
229
 */
230
static void
231
buffer_cache_free(VipsBufferCache *cache)
232
6.38M
{
233
6.38M
  GSList *p;
234
235
#ifdef DEBUG_CREATE
236
  g_mutex_lock(&vips__global_lock);
237
  vips__buffer_cache_all =
238
    g_slist_remove(vips__buffer_cache_all, cache);
239
  g_mutex_unlock(&vips__global_lock);
240
241
  printf("buffer_cache_free: freeing cache %p on thread %p\n",
242
    cache, g_thread_self());
243
  printf("\t(%d caches left)\n",
244
    g_slist_length(vips__buffer_cache_all));
245
#endif /*DEBUG_CREATE*/
246
247
  /* Need to mark undone so we don't try and take them off this cache on
248
   * unref.
249
   */
250
6.38M
  for (p = cache->buffers; p; p = p->next) {
251
0
    VipsBuffer *buffer = (VipsBuffer *) p->data;
252
253
0
    g_assert(buffer->done);
254
0
    g_assert(buffer->cache == cache);
255
256
0
    buffer->done = FALSE;
257
0
    buffer->cache = NULL;
258
0
  }
259
6.38M
  VIPS_FREEF(g_slist_free, cache->buffers);
260
261
12.6M
  for (p = cache->reserve; p; p = p->next) {
262
6.24M
    VipsBuffer *buffer = (VipsBuffer *) p->data;
263
264
6.24M
    vips_buffer_free(buffer);
265
6.24M
  }
266
6.38M
  VIPS_FREEF(g_slist_free, cache->reserve);
267
268
6.38M
  g_free(cache);
269
6.38M
}
270
271
static VipsBufferCache *
272
buffer_cache_new(VipsBufferThread *buffer_thread, VipsImage *im)
273
6.38M
{
274
6.38M
  VipsBufferCache *cache;
275
276
6.38M
  cache = g_new(VipsBufferCache, 1);
277
6.38M
  cache->buffers = NULL;
278
6.38M
  cache->thread = g_thread_self();
279
6.38M
  cache->im = im;
280
6.38M
  cache->buffer_thread = buffer_thread;
281
6.38M
  cache->reserve = NULL;
282
6.38M
  cache->n_reserve = 0;
283
284
#ifdef DEBUG_CREATE
285
  g_mutex_lock(&vips__global_lock);
286
  vips__buffer_cache_all =
287
    g_slist_prepend(vips__buffer_cache_all, cache);
288
  g_mutex_unlock(&vips__global_lock);
289
290
  printf("buffer_cache_new: new cache %p for thread %p on image %p\n",
291
    cache, g_thread_self(), im);
292
  printf("\t(%d caches now)\n",
293
    g_slist_length(vips__buffer_cache_all));
294
#endif /*DEBUG_CREATE*/
295
296
6.38M
  return cache;
297
6.38M
}
298
299
static VipsBufferThread *
300
buffer_thread_new(void)
301
1.03M
{
302
1.03M
  VipsBufferThread *buffer_thread;
303
304
1.03M
  buffer_thread = g_new(VipsBufferThread, 1);
305
1.03M
  buffer_thread->hash = g_hash_table_new_full(
306
1.03M
    g_direct_hash, g_direct_equal,
307
1.03M
    NULL, (GDestroyNotify) buffer_cache_free);
308
1.03M
  buffer_thread->thread = g_thread_self();
309
310
1.03M
  return buffer_thread;
311
1.03M
}
312
313
/* Get our private VipsBufferThread. NULL for non-worker threads.
314
 */
315
static VipsBufferThread *
316
buffer_thread_get(void)
317
40.4M
{
318
40.4M
  VipsBufferThread *buffer_thread;
319
320
40.4M
  if (vips_thread_isvips()) {
321
    /* Our threads get a set of private buffers, since we know we
322
     * will be calling vips_thread_shutdown() on thread
323
     * termination.
324
     */
325
38.5M
    if (!(buffer_thread = g_private_get(&buffer_thread_key))) {
326
1.03M
      buffer_thread = buffer_thread_new();
327
1.03M
      g_private_set(&buffer_thread_key, buffer_thread);
328
1.03M
    }
329
330
38.5M
    g_assert(buffer_thread->thread == g_thread_self());
331
38.5M
  }
332
1.91M
  else
333
    /* Non-vips threads don't have one.
334
     */
335
1.91M
    buffer_thread = NULL;
336
337
40.4M
  return buffer_thread;
338
40.4M
}
339
340
/* Get the VipsBufferCache for this image, or NULL for a non-worker.
341
 */
342
static VipsBufferCache *
343
buffer_cache_get(VipsImage *im)
344
37.0M
{
345
37.0M
  VipsBufferThread *buffer_thread;
346
37.0M
  VipsBufferCache *cache;
347
348
37.0M
  if ((buffer_thread = buffer_thread_get())) {
349
35.1M
    if (!(cache = (VipsBufferCache *)
350
35.1M
          g_hash_table_lookup(buffer_thread->hash, im))) {
351
6.38M
      cache = buffer_cache_new(buffer_thread, im);
352
6.38M
      g_hash_table_insert(buffer_thread->hash, im, cache);
353
6.38M
    }
354
355
35.1M
    g_assert(cache->thread == g_thread_self());
356
35.1M
  }
357
1.91M
  else
358
1.91M
    cache = NULL;
359
360
37.0M
  return cache;
361
37.0M
}
362
363
/* Pixels have been calculated: publish for other parts of this thread to see.
364
 */
365
void
366
vips_buffer_done(VipsBuffer *buffer)
367
1.68M
{
368
1.68M
  VipsImage *im = buffer->im;
369
1.68M
  VipsBufferCache *cache;
370
371
1.68M
  if (!buffer->done &&
372
1.68M
    (cache = buffer_cache_get(im))) {
373
1.68M
    g_assert(!g_slist_find(cache->buffers, buffer));
374
1.68M
    g_assert(!buffer->cache);
375
376
1.68M
    buffer->done = TRUE;
377
1.68M
    buffer->cache = cache;
378
379
1.68M
    cache->buffers = g_slist_prepend(cache->buffers, buffer);
380
381
#ifdef DEBUG_VERBOSE
382
    printf("vips_buffer_done: thread %p adding buffer %p to cache %p\n",
383
      g_thread_self(), buffer, cache);
384
    vips_buffer_print(buffer);
385
#endif /*DEBUG_VERBOSE*/
386
1.68M
  }
387
1.68M
}
388
389
/* Take off the public 'done' list. Make sure it has no calculated pixels in.
390
 */
391
void
392
vips_buffer_undone(VipsBuffer *buffer)
393
23.8M
{
394
23.8M
  if (buffer->done) {
395
1.68M
    VipsBufferCache *cache = buffer->cache;
396
397
#ifdef DEBUG_VERBOSE
398
    printf("vips_buffer_undone: thread %p removing "
399
         "buffer %p from cache %p\n",
400
      g_thread_self(), buffer, cache);
401
#endif /*DEBUG_VERBOSE*/
402
403
1.68M
    g_assert(cache->thread == g_thread_self());
404
1.68M
    g_assert(cache->buffer_thread->thread == cache->thread);
405
1.68M
    g_assert(g_slist_find(cache->buffers, buffer));
406
1.68M
    g_assert(buffer_thread_get());
407
1.68M
    g_assert(cache->buffer_thread == buffer_thread_get());
408
409
1.68M
    cache->buffers = g_slist_remove(cache->buffers, buffer);
410
1.68M
    buffer->done = FALSE;
411
412
#ifdef DEBUG_VERBOSE
413
    printf("vips_buffer_undone: %d buffers left\n",
414
      g_slist_length(cache->buffers));
415
#endif /*DEBUG_VERBOSE*/
416
1.68M
  }
417
418
23.8M
  buffer->cache = NULL;
419
23.8M
  buffer->area.width = 0;
420
23.8M
  buffer->area.height = 0;
421
23.8M
}
422
423
void
424
vips_buffer_unref(VipsBuffer *buffer)
425
11.7M
{
426
#ifdef DEBUG_VERBOSE
427
  printf("** vips_buffer_unref: left = %d, top = %d, "
428
       "width = %d, height = %d (%p)\n",
429
    buffer->area.left, buffer->area.top,
430
    buffer->area.width, buffer->area.height,
431
    buffer);
432
#endif /*DEBUG_VERBOSE*/
433
434
11.7M
  g_assert(buffer->ref_count > 0);
435
436
11.7M
  buffer->ref_count -= 1;
437
438
11.7M
  if (buffer->ref_count == 0) {
439
11.5M
    VipsBufferCache *cache;
440
441
#ifdef DEBUG_VERBOSE
442
    if (!buffer->done)
443
      printf("vips_buffer_unref: buffer was not done\n");
444
#endif /*DEBUG_VERBOSE*/
445
446
11.5M
    vips_buffer_undone(buffer);
447
448
    /* Place on this thread's reserve list for reuse.
449
     */
450
11.5M
    if ((cache = buffer_cache_get(buffer->im)) &&
451
10.1M
      cache->n_reserve < buffer_cache_max_reserve) {
452
8.04M
      g_assert(!buffer->cache);
453
454
8.04M
      cache->reserve =
455
8.04M
        g_slist_prepend(cache->reserve, buffer);
456
8.04M
      cache->n_reserve += 1;
457
458
8.04M
      buffer->cache = cache;
459
8.04M
      buffer->area.width = 0;
460
8.04M
      buffer->area.height = 0;
461
8.04M
    }
462
3.50M
    else
463
3.50M
      vips_buffer_free(buffer);
464
11.5M
  }
465
11.7M
}
466
467
static int
468
buffer_move(VipsBuffer *buffer, VipsRect *area)
469
12.0M
{
470
12.0M
  VipsImage *im = buffer->im;
471
12.0M
  size_t new_bsize;
472
12.0M
  size_t align;
473
474
12.0M
  g_assert(buffer->ref_count == 1);
475
476
12.0M
  vips_buffer_undone(buffer);
477
12.0M
  g_assert(!buffer->done);
478
479
12.0M
  buffer->area = *area;
480
481
12.0M
  new_bsize = (size_t) VIPS_IMAGE_SIZEOF_PEL(im) *
482
12.0M
    area->width * area->height;
483
484
  /* Need to pad buffer size to be aligned-up to
485
   * 64 bytes for the highway paths.
486
   */
487
12.0M
#ifdef HAVE_HWY
488
12.0M
  if (im->BandFmt == VIPS_FORMAT_UCHAR) {
489
7.75M
    new_bsize += /*HWY_ALIGNMENT*/ 64 - 1;
490
7.75M
    align = /*HWY_ALIGNMENT*/ 64;
491
7.75M
  }
492
4.31M
  else
493
4.31M
#endif /*HAVE_HWY*/
494
4.31M
    align = 16;
495
496
12.0M
  if (buffer->bsize < new_bsize ||
497
9.78M
    !buffer->buf) {
498
9.78M
    buffer->bsize = new_bsize;
499
9.78M
    VIPS_FREEF(vips_tracked_aligned_free, buffer->buf);
500
9.78M
    if (!(buffer->buf = vips_tracked_aligned_alloc(buffer->bsize, align)))
501
0
      return -1;
502
9.78M
  }
503
504
12.0M
  return 0;
505
12.0M
}
506
507
/* Make a new buffer.
508
 */
509
VipsBuffer *
510
vips_buffer_new(VipsImage *im, VipsRect *area)
511
11.5M
{
512
11.5M
  VipsBufferCache *cache;
513
11.5M
  VipsBuffer *buffer;
514
515
11.5M
  if ((cache = buffer_cache_get(im)) &&
516
11.3M
    cache->reserve) {
517
1.79M
    buffer = (VipsBuffer *) cache->reserve->data;
518
1.79M
    cache->reserve = g_slist_remove(cache->reserve, buffer);
519
1.79M
    cache->n_reserve -= 1;
520
521
1.79M
    g_assert(buffer->im == im);
522
1.79M
    g_assert(buffer->done == FALSE);
523
1.79M
    g_assert(buffer->cache);
524
525
1.79M
    buffer->ref_count = 1;
526
1.79M
    buffer->done = FALSE;
527
1.79M
    buffer->cache = NULL;
528
1.79M
  }
529
9.75M
  else {
530
9.75M
    buffer = g_new0(VipsBuffer, 1);
531
9.75M
    buffer->ref_count = 1;
532
9.75M
    buffer->im = im;
533
9.75M
    buffer->done = FALSE;
534
9.75M
    buffer->cache = NULL;
535
9.75M
    buffer->buf = NULL;
536
9.75M
    buffer->bsize = 0;
537
538
#ifdef DEBUG
539
    g_mutex_lock(&vips__global_lock);
540
    vips__buffer_all =
541
      g_slist_prepend(vips__buffer_all, buffer);
542
    g_mutex_unlock(&vips__global_lock);
543
#endif /*DEBUG*/
544
9.75M
  }
545
546
11.5M
  if (buffer_move(buffer, area)) {
547
0
    vips_buffer_free(buffer);
548
0
    return NULL;
549
0
  }
550
551
11.5M
  return buffer;
552
11.5M
}
553
554
/* Find an existing buffer that encloses area and return a ref. Or NULL for no
555
 * existing buffer.
556
 */
557
static VipsBuffer *
558
buffer_find(VipsImage *im, VipsRect *r)
559
12.3M
{
560
12.3M
  VipsBufferCache *cache;
561
12.3M
  VipsBuffer *buffer;
562
12.3M
  GSList *p;
563
12.3M
  VipsRect *area;
564
565
12.3M
  if (!(cache = buffer_cache_get(im)))
566
238k
    return NULL;
567
568
  /* This needs to be quick :-( don't use
569
   * vips_slist_map2()/vips_rect_includesrect(), do the search
570
   * inline.
571
   *
572
   * FIXME we return the first enclosing buffer, perhaps we should
573
   * search for the largest?
574
   */
575
12.6M
  for (p = cache->buffers; p; p = p->next) {
576
797k
    buffer = (VipsBuffer *) p->data;
577
797k
    area = &buffer->area;
578
579
797k
    if (area->left <= r->left &&
580
796k
      area->top <= r->top &&
581
795k
      area->left + area->width >= r->left + r->width &&
582
788k
      area->top + area->height >= r->top + r->height) {
583
246k
      buffer->ref_count += 1;
584
585
#ifdef DEBUG_VERBOSE
586
      printf("buffer_find: left = %d, top = %d, "
587
           "width = %d, height = %d, count = %d (%p)\n",
588
        buffer->area.left, buffer->area.top,
589
        buffer->area.width, buffer->area.height,
590
        buffer->ref_count,
591
        buffer);
592
#endif /*DEBUG_VERBOSE*/
593
594
246k
      return buffer;
595
246k
    }
596
797k
  }
597
598
11.8M
  return NULL;
599
12.0M
}
600
601
/* Return a ref to a buffer that encloses area. The buffer we return might be
602
 * done.
603
 */
604
VipsBuffer *
605
vips_buffer_ref(VipsImage *im, VipsRect *area)
606
0
{
607
0
  VipsBuffer *buffer;
608
609
0
  if ((buffer = buffer_find(im, area)))
610
0
    return buffer;
611
0
  else
612
0
    return vips_buffer_new(im, area);
613
0
}
614
615
/* Unref old, ref new, in a single operation. Reuse stuff if we can. The
616
 * buffer we return might or might not be done.
617
 */
618
VipsBuffer *
619
vips_buffer_unref_ref(VipsBuffer *old_buffer, VipsImage *im, VipsRect *area)
620
12.3M
{
621
12.3M
  VipsBuffer *buffer;
622
623
12.3M
  g_assert(!old_buffer ||
624
12.3M
    old_buffer->im == im);
625
626
  /* Is the current buffer OK?
627
   */
628
12.3M
  if (old_buffer &&
629
605k
    vips_rect_includesrect(&old_buffer->area, area))
630
7.99k
    return old_buffer;
631
632
  /* Does the new area already have a buffer?
633
   */
634
12.3M
  if ((buffer = buffer_find(im, area))) {
635
246k
    VIPS_FREEF(vips_buffer_unref, old_buffer);
636
246k
    return buffer;
637
246k
  }
638
639
  /* Is the current buffer unshared? We can just move it.
640
   */
641
12.0M
  if (old_buffer &&
642
551k
    old_buffer->ref_count == 1) {
643
525k
    if (buffer_move(old_buffer, area)) {
644
0
      vips_buffer_unref(old_buffer);
645
0
      return NULL;
646
0
    }
647
648
525k
    return old_buffer;
649
525k
  }
650
651
  /* Fallback ... unref the old one, make a new one.
652
   */
653
12.0M
  VIPS_FREEF(vips_buffer_unref, old_buffer);
654
11.5M
  if (!(buffer = vips_buffer_new(im, area)))
655
0
    return NULL;
656
657
11.5M
  return buffer;
658
11.5M
}
659
660
static void
661
buffer_thread_destroy_notify(gpointer data)
662
0
{
663
  /* We only come here if vips_thread_shutdown() was not called for this
664
   * thread. Do our best to clean up.
665
   *
666
   * GPrivate has stopped working by this point in destruction, be
667
   * careful not to touch that.
668
   */
669
0
  buffer_thread_free(data);
670
0
}
671
672
/* Init the buffer cache system. This is called during vips_init.
673
 */
674
void
675
vips__buffer_init(void)
676
39
{
677
39
  if (buffer_cache_max_reserve < 1)
678
0
    printf("vips__buffer_init: buffer reserve disabled\n");
679
680
#ifdef DEBUG
681
  printf("vips__buffer_init: DEBUG enabled\n");
682
#endif /*DEBUG*/
683
684
#ifdef DEBUG_CREATE
685
  printf("vips__buffer_init: DEBUG_CREATE enabled\n");
686
#endif /*DEBUG_CREATE*/
687
39
}
688
689
void
690
vips__buffer_shutdown(void)
691
1.52M
{
692
1.52M
  VipsBufferThread *buffer_thread;
693
694
1.52M
  if ((buffer_thread = g_private_get(&buffer_thread_key))) {
695
1.03M
    buffer_thread_free(buffer_thread);
696
    g_private_set(&buffer_thread_key, NULL);
697
1.03M
  }
698
1.52M
}