mirror of
https://gitlab.gnome.org/GNOME/gimp
synced 2024-10-23 21:11:43 +00:00
a60cd2f202
Thu Jan 6 18:39:05 GMT 2000 Adam D. Moss <adam@gimp.org> * app/tile_cache.c * app/tile_swap.c: Maybe fixed tile_cache_set_size() so that it actually works now. You can use this to set GIMP's tile cache size at runtime without restarting. If the prefs guys (Sven, Michael?) don't wire it in I'll look at it, but it can't be soon.
375 lines
8 KiB
C
375 lines
8 KiB
C
#include <gtk/gtkmain.h>
|
|
#include <glib.h>
|
|
#include "gimprc.h"
|
|
#include "tile.h"
|
|
#include "tile_cache.h"
|
|
#include "tile_swap.h"
|
|
#ifdef USE_PTHREADS
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#include "tile_pvt.h" /* ick. */
|
|
|
|
#include "libgimp/gimpintl.h"
|
|
|
|
#include "stdio.h"
|
|
|
|
/* 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
|
|
|
|
static void tile_cache_init (void);
|
|
static gint tile_cache_zorch_next (void);
|
|
static void tile_cache_flush_internal (Tile *tile);
|
|
|
|
#ifdef USE_PTHREADS
|
|
static void* tile_idle_thread (void *);
|
|
#else
|
|
static gint tile_idle_preswap (gpointer);
|
|
#endif
|
|
|
|
static int initialize = TRUE;
|
|
|
|
typedef struct _TileList {
|
|
Tile* first;
|
|
Tile* last;
|
|
} TileList;
|
|
|
|
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;
|
|
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;
|
|
#define CACHE_LOCK pthread_mutex_lock(&tile_mutex)
|
|
#define CACHE_UNLOCK pthread_mutex_unlock(&tile_mutex)
|
|
#else
|
|
static gint idle_swapper = 0;
|
|
#define CACHE_LOCK /*nothing*/
|
|
#define CACHE_UNLOCK /*nothing*/
|
|
#endif
|
|
|
|
|
|
void
|
|
tile_cache_insert (Tile *tile)
|
|
{
|
|
TileList *list;
|
|
TileList *newlist;
|
|
|
|
if (initialize)
|
|
tile_cache_init ();
|
|
|
|
CACHE_LOCK;
|
|
if (tile->data == NULL) goto out;
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
list = (TileList*)(tile->listhead);
|
|
newlist = (tile->dirty || tile->swap_offset == -1) ? &dirty_list : &clean_list;
|
|
|
|
/* if list is NULL, the tile is not in the cache */
|
|
|
|
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);
|
|
}
|
|
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.
|
|
*/
|
|
while ((cur_cache_size + max_tile_size) > max_cache_size)
|
|
{
|
|
if (!tile_cache_zorch_next())
|
|
{
|
|
g_warning ("cache: unable to find room for a tile");
|
|
goto out;
|
|
/* goto grump;*/
|
|
}
|
|
}
|
|
|
|
/*grump:*/
|
|
|
|
/* Note the increase in the number of bytes the cache
|
|
* is referencing.
|
|
*/
|
|
cur_cache_size += tile_size (tile);
|
|
}
|
|
|
|
/* 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;
|
|
|
|
/* 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))
|
|
{
|
|
cur_cache_dirty += tile_size (tile);
|
|
if (1)
|
|
{
|
|
#ifdef USE_PTHREADS
|
|
pthread_mutex_lock(&dirty_mutex);
|
|
pthread_cond_signal(&dirty_signal);
|
|
pthread_mutex_unlock(&dirty_mutex);
|
|
#endif
|
|
}
|
|
}
|
|
out:
|
|
CACHE_UNLOCK;
|
|
|
|
}
|
|
|
|
void
|
|
tile_cache_flush (Tile *tile)
|
|
{
|
|
if (initialize)
|
|
tile_cache_init ();
|
|
|
|
CACHE_LOCK;
|
|
tile_cache_flush_internal (tile);
|
|
CACHE_UNLOCK;
|
|
}
|
|
|
|
static void
|
|
tile_cache_flush_internal (Tile *tile)
|
|
{
|
|
TileList *list;
|
|
|
|
/* Find where the tile is in the cache.
|
|
*/
|
|
|
|
list = (TileList*)(tile->listhead);
|
|
|
|
if (list)
|
|
{
|
|
/* Note the decrease in the number of bytes the cache
|
|
* is referencing.
|
|
*/
|
|
cur_cache_size -= tile_size (tile);
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/* untested -- ADM */
|
|
void
|
|
tile_cache_set_size (unsigned long cache_size)
|
|
{
|
|
if (initialize)
|
|
tile_cache_init ();
|
|
|
|
max_cache_size = cache_size;
|
|
CACHE_LOCK;
|
|
while (cur_cache_size > max_cache_size)
|
|
{
|
|
if (!tile_cache_zorch_next ())
|
|
break;
|
|
}
|
|
CACHE_UNLOCK;
|
|
}
|
|
|
|
|
|
static void
|
|
tile_cache_init ()
|
|
{
|
|
if (initialize)
|
|
{
|
|
initialize = FALSE;
|
|
|
|
clean_list.first = clean_list.last = NULL;
|
|
dirty_list.first = dirty_list.last = NULL;
|
|
|
|
max_cache_size = tile_cache_size;
|
|
|
|
#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
|
|
}
|
|
}
|
|
|
|
static gint
|
|
tile_cache_zorch_next ()
|
|
{
|
|
Tile *tile;
|
|
|
|
/* fprintf(stderr, "cache zorch: %u/%u\n", cur_cache_size, cur_cache_dirty);*/
|
|
|
|
if (clean_list.first) tile = clean_list.first;
|
|
else if (dirty_list.first) tile = dirty_list.first;
|
|
else return FALSE;
|
|
|
|
CACHE_UNLOCK;
|
|
TILE_MUTEX_LOCK (tile);
|
|
CACHE_LOCK;
|
|
tile_cache_flush_internal (tile);
|
|
if (tile->dirty || tile->swap_offset == -1)
|
|
{
|
|
tile_swap_out (tile);
|
|
}
|
|
if (! tile->dirty) {
|
|
g_free (tile->data);
|
|
tile->data = NULL;
|
|
TILE_MUTEX_UNLOCK (tile);
|
|
return TRUE;
|
|
}
|
|
/* unable to swap out tile for some reason */
|
|
TILE_MUTEX_UNLOCK (tile);
|
|
return FALSE;
|
|
}
|
|
|
|
#if USE_PTHREADS
|
|
static void *
|
|
tile_idle_thread (void *data)
|
|
{
|
|
Tile *tile;
|
|
TileList *list;
|
|
int count;
|
|
|
|
fprintf (stderr, "starting tile preswapper\n");
|
|
|
|
count = 0;
|
|
while (1)
|
|
{
|
|
CACHE_LOCK;
|
|
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;
|
|
}
|
|
|
|
if ((tile = dirty_list.first))
|
|
{
|
|
CACHE_UNLOCK;
|
|
TILE_MUTEX_LOCK (tile);
|
|
CACHE_LOCK;
|
|
|
|
if (tile->dirty || tile->swap_offset == -1)
|
|
{
|
|
list = tile->listhead;
|
|
|
|
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->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;
|
|
|
|
CACHE_UNLOCK;
|
|
|
|
tile_swap_out (tile);
|
|
}
|
|
else
|
|
{
|
|
CACHE_UNLOCK;
|
|
}
|
|
|
|
TILE_MUTEX_UNLOCK (tile);
|
|
}
|
|
else
|
|
{
|
|
CACHE_UNLOCK;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
|
|
#else
|
|
static gint
|
|
tile_idle_preswap (gpointer data)
|
|
{
|
|
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;
|
|
}
|
|
#endif
|
|
|