~ubuntu-branches/ubuntu/maverick/gimp/maverick-updates

« back to all changes in this revision

Viewing changes to app/base/tile-cache.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Holbach
  • Date: 2005-12-09 19:44:52 UTC
  • Revision ID: james.westby@ubuntu.com-20051209194452-yggpemjlofpjqyf4
Tags: upstream-2.2.9
ImportĀ upstreamĀ versionĀ 2.2.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* The GIMP -- an image manipulation program
 
2
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
17
 */
 
18
 
 
19
#include "config.h"
 
20
 
 
21
#ifdef USE_PTHREADS
 
22
#include <pthread.h>
 
23
#endif
 
24
 
 
25
#include <glib-object.h>
 
26
 
 
27
#include "base-types.h"
 
28
 
 
29
#include "tile.h"
 
30
#include "tile-cache.h"
 
31
#include "tile-swap.h"
 
32
#include "tile-private.h"
 
33
 
 
34
 
 
35
/*  This is the percentage of the maximum cache size that should be cleared
 
36
 *   from the cache when an eviction is necessary
 
37
 */
 
38
#define FREE_QUANTUM          0.1
 
39
 
 
40
#define IDLE_SWAPPER_TIMEOUT  250
 
41
 
 
42
 
 
43
static gboolean  tile_cache_zorch_next     (void);
 
44
static void      tile_cache_flush_internal (Tile     *tile);
 
45
 
 
46
#ifdef USE_PTHREADS
 
47
static gpointer  tile_idle_thread          (gpointer  data);
 
48
#else
 
49
static gboolean  tile_idle_preswap         (gpointer  data);
 
50
#endif
 
51
 
 
52
 
 
53
static gboolean initialize = TRUE;
 
54
 
 
55
typedef struct _TileList
 
56
{
 
57
  Tile *first;
 
58
  Tile *last;
 
59
} TileList;
 
60
 
 
61
static gulong   max_tile_size   = TILE_WIDTH * TILE_HEIGHT * 4;
 
62
static gulong   cur_cache_size  = 0;
 
63
static gulong   max_cache_size  = 0;
 
64
static gulong   cur_cache_dirty = 0;
 
65
static TileList clean_list      = { NULL, NULL };
 
66
static TileList dirty_list      = { NULL, NULL };
 
67
 
 
68
#ifdef USE_PTHREADS
 
69
static pthread_t       preswap_thread;
 
70
static pthread_mutex_t dirty_mutex  = PTHREAD_MUTEX_INITIALIZER;
 
71
static pthread_cond_t  dirty_signal = PTHREAD_COND_INITIALIZER;
 
72
static pthread_mutex_t tile_mutex   = PTHREAD_MUTEX_INITIALIZER;
 
73
#define CACHE_LOCK   pthread_mutex_lock (&tile_mutex)
 
74
#define CACHE_UNLOCK pthread_mutex_unlock (&tile_mutex)
 
75
#else
 
76
static guint           idle_swapper = 0;
 
77
#define CACHE_LOCK   /* nothing */
 
78
#define CACHE_UNLOCK /* nothing */
 
79
#endif
 
80
 
 
81
 
 
82
void
 
83
tile_cache_init (gulong tile_cache_size)
 
84
{
 
85
  if (initialize)
 
86
    {
 
87
      initialize = FALSE;
 
88
 
 
89
      clean_list.first = clean_list.last = NULL;
 
90
      dirty_list.first = dirty_list.last = NULL;
 
91
 
 
92
      max_cache_size = tile_cache_size;
 
93
 
 
94
#ifdef USE_PTHREADS
 
95
      pthread_create (&preswap_thread, NULL, &tile_idle_thread, NULL);
 
96
#else
 
97
      idle_swapper = g_timeout_add (IDLE_SWAPPER_TIMEOUT,
 
98
                                    tile_idle_preswap,
 
99
                                    NULL);
 
100
#endif
 
101
    }
 
102
}
 
103
 
 
104
void
 
105
tile_cache_exit (void)
 
106
{
 
107
  tile_cache_set_size (0);
 
108
}
 
109
 
 
110
void
 
111
tile_cache_insert (Tile *tile)
 
112
{
 
113
  TileList *list;
 
114
  TileList *newlist;
 
115
 
 
116
  CACHE_LOCK;
 
117
 
 
118
  if (! tile->data)
 
119
    goto out;
 
120
 
 
121
  /* First check and see if the tile is already
 
122
   *  in the cache. In that case we will simply place
 
123
   *  it at the end of the tile list to indicate that
 
124
   *  it was the most recently accessed tile.
 
125
   */
 
126
 
 
127
  list = (TileList *) tile->listhead;
 
128
 
 
129
  newlist = ((tile->dirty || tile->swap_offset == -1) ?
 
130
             &dirty_list : &clean_list);
 
131
 
 
132
  /* if list is NULL, the tile is not in the cache */
 
133
 
 
134
  if (list)
 
135
    {
 
136
      /* Tile is in the cache.  Remove it from its current list and
 
137
         put it at the tail of the proper list (clean or dirty) */
 
138
 
 
139
      if (tile->next)
 
140
        tile->next->prev = tile->prev;
 
141
      else
 
142
        list->last = tile->prev;
 
143
 
 
144
      if (tile->prev)
 
145
        tile->prev->next = tile->next;
 
146
      else
 
147
        list->first = tile->next;
 
148
 
 
149
      tile->listhead = NULL;
 
150
 
 
151
      if (list == &dirty_list)
 
152
        cur_cache_dirty -= tile_size_inline (tile);
 
153
    }
 
154
  else
 
155
    {
 
156
      /* The tile was not in the cache. First check and see
 
157
       *  if there is room in the cache. If not then we'll have
 
158
       *  to make room first. Note: it might be the case that the
 
159
       *  cache is smaller than the size of a tile in which case
 
160
       *  it won't be possible to put it in the cache.
 
161
       */
 
162
      while ((cur_cache_size + max_tile_size) > max_cache_size)
 
163
        {
 
164
          if (! tile_cache_zorch_next ())
 
165
            {
 
166
              g_warning ("cache: unable to find room for a tile");
 
167
              goto out;
 
168
            }
 
169
        }
 
170
 
 
171
      cur_cache_size += tile_size_inline (tile);
 
172
    }
 
173
 
 
174
  /* Put the tile at the end of the proper list */
 
175
 
 
176
  tile->next = NULL;
 
177
  tile->prev = newlist->last;
 
178
  tile->listhead = newlist;
 
179
 
 
180
  if (newlist->last)
 
181
    newlist->last->next = tile;
 
182
  else
 
183
    newlist->first = tile;
 
184
 
 
185
  newlist->last = tile;
 
186
 
 
187
  /* gosgood@idt.net 1999-12-04                                  */
 
188
  /* bytes on cur_cache_dirty miscounted in CVS 1.12:            */
 
189
  /* Invariant: test for selecting dirty list should be the same */
 
190
  /* as counting files dirty.                                    */
 
191
 
 
192
  if (tile->dirty || (tile->swap_offset == -1))
 
193
    {
 
194
      cur_cache_dirty += tile_size_inline (tile);
 
195
 
 
196
#ifdef USE_PTHREADS
 
197
      pthread_mutex_lock (&dirty_mutex);
 
198
      pthread_cond_signal (&dirty_signal);
 
199
      pthread_mutex_unlock (&dirty_mutex);
 
200
#endif
 
201
    }
 
202
 
 
203
out:
 
204
  CACHE_UNLOCK;
 
205
}
 
206
 
 
207
void
 
208
tile_cache_flush (Tile *tile)
 
209
{
 
210
  CACHE_LOCK;
 
211
 
 
212
  tile_cache_flush_internal (tile);
 
213
 
 
214
  CACHE_UNLOCK;
 
215
}
 
216
 
 
217
static void
 
218
tile_cache_flush_internal (Tile *tile)
 
219
{
 
220
  TileList *list;
 
221
 
 
222
  /* Find where the tile is in the cache.
 
223
   */
 
224
 
 
225
  list = (TileList *) tile->listhead;
 
226
 
 
227
  if (list)
 
228
    {
 
229
      cur_cache_size -= tile_size_inline (tile);
 
230
 
 
231
      if (list == &dirty_list)
 
232
        cur_cache_dirty -= tile_size_inline (tile);
 
233
 
 
234
      if (tile->next)
 
235
        tile->next->prev = tile->prev;
 
236
      else
 
237
        list->last = tile->prev;
 
238
 
 
239
      if (tile->prev)
 
240
        tile->prev->next = tile->next;
 
241
      else
 
242
        list->first = tile->next;
 
243
 
 
244
      tile->listhead = NULL;
 
245
    }
 
246
}
 
247
 
 
248
 
 
249
void
 
250
tile_cache_set_size (gulong cache_size)
 
251
{
 
252
  CACHE_LOCK;
 
253
 
 
254
  max_cache_size = cache_size;
 
255
 
 
256
  while (cur_cache_size > max_cache_size)
 
257
    {
 
258
      if (!tile_cache_zorch_next ())
 
259
        break;
 
260
    }
 
261
 
 
262
  CACHE_UNLOCK;
 
263
}
 
264
 
 
265
 
 
266
static gboolean
 
267
tile_cache_zorch_next (void)
 
268
{
 
269
  Tile *tile;
 
270
 
 
271
  if (clean_list.first)
 
272
    tile = clean_list.first;
 
273
  else if (dirty_list.first)
 
274
    tile = dirty_list.first;
 
275
  else
 
276
    return FALSE;
 
277
 
 
278
  CACHE_UNLOCK;
 
279
  TILE_MUTEX_LOCK (tile);
 
280
  CACHE_LOCK;
 
281
 
 
282
  tile_cache_flush_internal (tile);
 
283
 
 
284
  if (tile->dirty || tile->swap_offset == -1)
 
285
    {
 
286
      tile_swap_out (tile);
 
287
    }
 
288
 
 
289
  if (! tile->dirty)
 
290
    {
 
291
      g_free (tile->data);
 
292
      tile->data = NULL;
 
293
      TILE_MUTEX_UNLOCK (tile);
 
294
 
 
295
      return TRUE;
 
296
    }
 
297
 
 
298
  /* unable to swap out tile for some reason */
 
299
  TILE_MUTEX_UNLOCK (tile);
 
300
 
 
301
  return FALSE;
 
302
}
 
303
 
 
304
 
 
305
#if USE_PTHREADS
 
306
 
 
307
static gpointer
 
308
tile_idle_thread (gpointer data)
 
309
{
 
310
  Tile     *tile;
 
311
  TileList *list;
 
312
  gint      count;
 
313
 
 
314
  g_printerr ("starting tile preswapper thread\n");
 
315
 
 
316
  count = 0;
 
317
  while (TRUE)
 
318
    {
 
319
      CACHE_LOCK;
 
320
 
 
321
      if (count > 5 || dirty_list.first == NULL)
 
322
        {
 
323
          CACHE_UNLOCK;
 
324
 
 
325
          count = 0;
 
326
 
 
327
          pthread_mutex_lock (&dirty_mutex);
 
328
          pthread_cond_wait (&dirty_signal, &dirty_mutex);
 
329
          pthread_mutex_unlock (&dirty_mutex);
 
330
 
 
331
          CACHE_LOCK;
 
332
        }
 
333
 
 
334
      if ((tile = dirty_list.first))
 
335
        {
 
336
          CACHE_UNLOCK;
 
337
          TILE_MUTEX_LOCK (tile);
 
338
          CACHE_LOCK;
 
339
 
 
340
          if (tile->dirty || tile->swap_offset == -1)
 
341
            {
 
342
              list = tile->listhead;
 
343
 
 
344
              if (list == &dirty_list)
 
345
                cur_cache_dirty -= tile_size_inline (tile);
 
346
 
 
347
              if (tile->next)
 
348
                tile->next->prev = tile->prev;
 
349
              else
 
350
                list->last = tile->prev;
 
351
 
 
352
              if (tile->prev)
 
353
                tile->prev->next = tile->next;
 
354
              else
 
355
                list->first = tile->next;
 
356
 
 
357
              tile->next = NULL;
 
358
              tile->prev = clean_list.last;
 
359
              tile->listhead = &clean_list;
 
360
 
 
361
              if (clean_list.last)
 
362
                clean_list.last->next = tile;
 
363
              else
 
364
                clean_list.first = tile;
 
365
 
 
366
              clean_list.last = tile;
 
367
 
 
368
              CACHE_UNLOCK;
 
369
 
 
370
              tile_swap_out (tile);
 
371
            }
 
372
          else
 
373
            {
 
374
              CACHE_UNLOCK;
 
375
            }
 
376
 
 
377
          TILE_MUTEX_UNLOCK (tile);
 
378
        }
 
379
      else
 
380
        {
 
381
          CACHE_UNLOCK;
 
382
        }
 
383
 
 
384
      count++;
 
385
    }
 
386
}
 
387
 
 
388
#else  /* !USE_PTHREADS */
 
389
 
 
390
static gboolean
 
391
tile_idle_preswap (gpointer data)
 
392
{
 
393
  Tile *tile;
 
394
 
 
395
  if (cur_cache_dirty * 2 < max_cache_size)
 
396
    return TRUE;
 
397
 
 
398
  if ((tile = dirty_list.first))
 
399
    {
 
400
      tile_swap_out (tile);
 
401
 
 
402
      dirty_list.first = tile->next;
 
403
 
 
404
      if (tile->next)
 
405
        tile->next->prev = NULL;
 
406
      else
 
407
        dirty_list.last = NULL;
 
408
 
 
409
      tile->next = NULL;
 
410
      tile->prev = clean_list.last;
 
411
      tile->listhead = &clean_list;
 
412
 
 
413
      if (clean_list.last)
 
414
        clean_list.last->next = tile;
 
415
      else
 
416
        clean_list.first = tile;
 
417
 
 
418
      clean_list.last = tile;
 
419
      cur_cache_dirty -= tile_size_inline (tile);
 
420
    }
 
421
 
 
422
  return TRUE;
 
423
}
 
424
 
 
425
#endif  /* !USE_PTHREADS */