tile-cache.c 7.98 KB
Newer Older
1
#include <gtk/gtkmain.h>
Elliot Lee's avatar
Elliot Lee committed
2
3
#include <glib.h>
#include "gimprc.h"
4
#include "tile.h"
Elliot Lee's avatar
Elliot Lee committed
5
6
#include "tile_cache.h"
#include "tile_swap.h"
7
8
9
10
#ifdef USE_PTHREADS
#include <pthread.h>
#endif

11
12
#include "tile_pvt.h"			/* ick. */

13
#include "stdio.h"
Elliot Lee's avatar
Elliot Lee committed
14
15
16
17
18
19

/*  This is the percentage of the maximum cache size that should be cleared
 *   from the cache when an eviction is necessary
 */
#define FREE_QUANTUM 0.1

20
21
22
static void  tile_cache_init           (void);
static gint  tile_cache_zorch_next     (void);
static void  tile_cache_flush_internal (Tile *tile);
Elliot Lee's avatar
Elliot Lee committed
23

24
25
26
27
28
#ifdef USE_PTHREADS
static void* tile_idle_thread          (void *);
#else
static gint  tile_idle_preswap         (gpointer);
#endif
Elliot Lee's avatar
Elliot Lee committed
29
30

static int initialize = TRUE;
31
32
33
34
35
36

typedef struct _TileList {
  Tile* first;
  Tile* last;
} TileList;

Elliot Lee's avatar
Elliot Lee committed
37
38
39
static unsigned long max_tile_size = TILE_WIDTH * TILE_HEIGHT * 4;
static unsigned long cur_cache_size = 0;
static unsigned long max_cache_size = 0;
40
41
42
43
44
45
46
47
48
static unsigned long cur_cache_dirty = 0;
static TileList clean_list = { NULL, NULL };
static TileList dirty_list = { NULL, NULL };

#ifdef USE_PTHREADS
static pthread_t preswap_thread;
static pthread_mutex_t dirty_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t dirty_signal = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t tile_mutex  = PTHREAD_MUTEX_INITIALIZER;
49
50
#define CACHE_LOCK pthread_mutex_lock(&tile_mutex)
#define CACHE_UNLOCK pthread_mutex_unlock(&tile_mutex)
51
52
#else
static gint idle_swapper = 0;
53
54
#define CACHE_LOCK /*nothing*/
#define CACHE_UNLOCK /*nothing*/
55
#endif
Elliot Lee's avatar
Elliot Lee committed
56
57
58
59
60


void
tile_cache_insert (Tile *tile)
{
61
62
  TileList *list;
  TileList *newlist;
Elliot Lee's avatar
Elliot Lee committed
63
64
65
66

  if (initialize)
    tile_cache_init ();

67
  CACHE_LOCK;
68
69
  if (tile->data == NULL) goto out;	

Elliot Lee's avatar
Elliot Lee committed
70
71
72
73
74
75
  /* First check and see if the tile is already
   *  in the cache. In that case we will simply place
   *  it at the end of the tile list to indicate that
   *  it was the most recently accessed tile.
   */

76
77
  list = (TileList*)(tile->listhead);
  newlist = (tile->dirty || tile->swap_offset == -1) ? &dirty_list : &clean_list;
Elliot Lee's avatar
Elliot Lee committed
78

79
  /* if list is NULL, the tile is not in the cache */
Elliot Lee's avatar
Elliot Lee committed
80

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  if (list) 
    {
      /* Tile is in the cache.  Remove it from its current list and
	 put it at the tail of the proper list (clean or dirty) */

      if (tile->next) 
	tile->next->prev = tile->prev;
      else
	list->last = tile->prev;
      
      if (tile->prev)
	tile->prev->next = tile->next;
      else
	list->first = tile->next;

      tile->listhead = NULL;
      if (list == &dirty_list) cur_cache_dirty -= tile_size (tile);
Elliot Lee's avatar
Elliot Lee committed
98
99
100
101
102
103
104
105
106
    }
  else
    {
      /* The tile was not in the cache. First check and see
       *  if there is room in the cache. If not then we'll have
       *  to make room first. Note: it might be the case that the
       *  cache is smaller than the size of a tile in which case
       *  it won't be possible to put it in the cache.
       */
107
      while ((cur_cache_size + max_tile_size) > max_cache_size)
Elliot Lee's avatar
Elliot Lee committed
108
	{
scott's avatar
scott committed
109
110
	  if (!tile_cache_zorch_next()) 
	    {
111
	      g_warning ("cache: unable to find room for a tile");
scott's avatar
scott committed
112
	      goto out;
113
	      /* goto grump;*/
scott's avatar
scott committed
114
	    }
Elliot Lee's avatar
Elliot Lee committed
115
	}
116
      
117
118
      /*grump:*/

Elliot Lee's avatar
Elliot Lee committed
119
120
121
122
123
      /* Note the increase in the number of bytes the cache
       *  is referencing.
       */
      cur_cache_size += tile_size (tile);
    }
124
125
126
127
128
129
130
131
132
133
134

  /* Put the tile at the end of the proper list */

  tile->next = NULL;
  tile->prev = newlist->last;
  tile->listhead = newlist;

  if (newlist->last) newlist->last->next = tile;
  else               newlist->first = tile;
  newlist->last = tile;

135
136
137
138
139
140
  /* gosgood@idt.net 1999-12-04                                  */
  /* bytes on cur_cache_dirty miscounted in CVS 1.12:            */
  /* Invariant: test for selecting dirty list should be the same */
  /* as counting files dirty.                                    */

  if ((tile->dirty) || ( tile->swap_offset == -1)) 
141
142
    {
      cur_cache_dirty += tile_size (tile);
scott's avatar
scott committed
143
144
      if (1)
	{
145
#ifdef USE_PTHREADS
scott's avatar
scott committed
146
147
148
	  pthread_mutex_lock(&dirty_mutex);
	  pthread_cond_signal(&dirty_signal);
	  pthread_mutex_unlock(&dirty_mutex);
149
#endif
scott's avatar
scott committed
150
	}
151
152
    }
out:
153
  CACHE_UNLOCK;
154

Elliot Lee's avatar
Elliot Lee committed
155
156
157
158
159
160
161
162
}

void
tile_cache_flush (Tile *tile)
{
  if (initialize)
    tile_cache_init ();

163
  CACHE_LOCK;
164
  tile_cache_flush_internal (tile);
165
  CACHE_UNLOCK;
166
167
168
169
170
171
172
}

static void
tile_cache_flush_internal (Tile *tile)
{
  TileList *list;

Elliot Lee's avatar
Elliot Lee committed
173
174
  /* Find where the tile is in the cache.
   */
175
176
  
  list = (TileList*)(tile->listhead);
Elliot Lee's avatar
Elliot Lee committed
177

178
  if (list) 
Elliot Lee's avatar
Elliot Lee committed
179
180
181
182
183
    {
      /* Note the decrease in the number of bytes the cache
       *  is referencing.
       */
      cur_cache_size -= tile_size (tile);
184
185
186
187
188
189
190
191
192
193
194
195
196
      if (list == &dirty_list) cur_cache_dirty -= tile_size (tile);

      if (tile->next) 
	tile->next->prev = tile->prev;
      else
	list->last = tile->prev;
      
      if (tile->prev)
	tile->prev->next = tile->next;
      else
	list->first = tile->next;

      tile->listhead = NULL;
Elliot Lee's avatar
Elliot Lee committed
197
198
199
    }
}

200
201

/* untested -- ADM */
Elliot Lee's avatar
Elliot Lee committed
202
203
204
205
206
207
208
void
tile_cache_set_size (unsigned long cache_size)
{
  if (initialize)
    tile_cache_init ();

  max_cache_size = cache_size;
209
  CACHE_LOCK;
210
  while (cur_cache_size > max_cache_size)
Elliot Lee's avatar
Elliot Lee committed
211
    {
212
213
      if (!tile_cache_zorch_next ())
	break;
Elliot Lee's avatar
Elliot Lee committed
214
    }
215
  CACHE_UNLOCK;
Elliot Lee's avatar
Elliot Lee committed
216
217
218
219
220
221
222
223
224
225
}


static void
tile_cache_init ()
{
  if (initialize)
    {
      initialize = FALSE;

226
227
      clean_list.first = clean_list.last = NULL;
      dirty_list.first = dirty_list.last = NULL;
Elliot Lee's avatar
Elliot Lee committed
228
229

      max_cache_size = tile_cache_size;
230
231
232
233
234
235
236
237

#ifdef USE_PTHREADS
      pthread_create (&preswap_thread, NULL, &tile_idle_thread, NULL);
#else
      idle_swapper = gtk_timeout_add (250,
				      (GtkFunction) tile_idle_preswap, 
				      (gpointer) 0);
#endif
Elliot Lee's avatar
Elliot Lee committed
238
239
240
    }
}

241
static gint
Elliot Lee's avatar
Elliot Lee committed
242
243
tile_cache_zorch_next ()
{
244
245
  Tile *tile;

246
  /*  fprintf(stderr, "cache zorch: %u/%u\n", cur_cache_size, cur_cache_dirty);*/
scott's avatar
scott committed
247

248
249
250
251
  if      (clean_list.first) tile = clean_list.first;
  else if (dirty_list.first) tile = dirty_list.first;
  else return FALSE;

252
253
254
  CACHE_UNLOCK;
  TILE_MUTEX_LOCK (tile);
  CACHE_LOCK;
255
  tile_cache_flush_internal (tile);
256
  if (tile->dirty || tile->swap_offset == -1)
scott's avatar
scott committed
257
258
259
    {
      tile_swap_out (tile);
    }
260
261
262
263
264
265
266
  if (! tile->dirty) {
    g_free (tile->data);
    tile->data = NULL;
    TILE_MUTEX_UNLOCK (tile);
    return TRUE;
  }
  /* unable to swap out tile for some reason */
267
  TILE_MUTEX_UNLOCK (tile);
268
  return FALSE;
269
270
271
272
273
274
275
276
}

#if USE_PTHREADS
static void *
tile_idle_thread (void *data)
{
  Tile *tile;
  TileList *list;
scott's avatar
scott committed
277
  int count;
278

279
  fprintf (stderr, "starting tile preswapper\n");
280

scott's avatar
scott committed
281
  count = 0;
282
  while (1)
283
    {
284
      CACHE_LOCK;
285
286
287
288
289
290
291
292
293
294
      if (count > 5 || dirty_list.first == NULL)
	{
	  CACHE_UNLOCK;
	  count = 0;
	  pthread_mutex_lock (&dirty_mutex);
	  pthread_cond_wait (&dirty_signal, &dirty_mutex);
	  pthread_mutex_unlock (&dirty_mutex);
	  CACHE_LOCK;
	}

295
      if ((tile = dirty_list.first)) 
296
	{
297
	  CACHE_UNLOCK;
298
299
	  TILE_MUTEX_LOCK (tile);
	  CACHE_LOCK;
300

301
	  if (tile->dirty || tile->swap_offset == -1) 
302
303
	    {
	      list = tile->listhead;
304

305
	      if (list == &dirty_list) cur_cache_dirty -= tile_size (tile);
306

307
308
309
310
	      if (tile->next) 
		tile->next->prev = tile->prev;
	      else
		list->last = tile->prev;
311

312
313
314
315
	      if (tile->prev)
		tile->prev->next = tile->next;
	      else
		list->first = tile->next;
316

317
318
319
	      tile->next = NULL;
	      tile->prev = clean_list.last;
	      tile->listhead = &clean_list;
320

321
322
323
324
325
326
	      if (clean_list.last) clean_list.last->next = tile;
	      else                 clean_list.first = tile;
	      clean_list.last = tile;

	      CACHE_UNLOCK;

327
	      tile_swap_out (tile);
328
329
330
331
332
	    }
	  else 
	    {
	      CACHE_UNLOCK;
	    }
333
334

	  TILE_MUTEX_UNLOCK (tile);
335
	}
336
337
338
339
      else 
	{
	  CACHE_UNLOCK;
	}
scott's avatar
scott committed
340
      count++;
341
    }
Elliot Lee's avatar
Elliot Lee committed
342
343
}

344
345
346
#else
static gint
tile_idle_preswap (gpointer data)
Elliot Lee's avatar
Elliot Lee committed
347
{
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
  Tile *tile;
  if (cur_cache_dirty*2 < max_cache_size) return TRUE;
  if ((tile = dirty_list.first)) 
    {
      tile_swap_out(tile);

      dirty_list.first = tile->next;
      if (tile->next) 
	tile->next->prev = NULL;
      else
	dirty_list.last = NULL;

      tile->next = NULL;
      tile->prev = clean_list.last;
      tile->listhead = &clean_list;
      if (clean_list.last) clean_list.last->next = tile;
      else                 clean_list.first = tile;
      clean_list.last = tile;
      cur_cache_dirty -= tile_size (tile);
    }
  
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
370
}
371
#endif
372