home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / tile_cache.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-02-03  |  8.3 KB  |  373 lines

  1. #include <gtk/gtkmain.h>
  2. #include <glib.h>
  3. #include "gimprc.h"
  4. #include "tile.h"
  5. #include "tile_cache.h"
  6. #include "tile_swap.h"
  7. #ifdef USE_PTHREADS
  8. #include <pthread.h>
  9. #endif
  10.  
  11. #include "tile_pvt.h"            /* ick. */
  12.  
  13. #include "stdio.h"
  14.  
  15. /*  This is the percentage of the maximum cache size that should be cleared
  16.  *   from the cache when an eviction is necessary
  17.  */
  18. #define FREE_QUANTUM 0.1
  19.  
  20. static void  tile_cache_init           (void);
  21. static gint  tile_cache_zorch_next     (void);
  22. static void  tile_cache_flush_internal (Tile *tile);
  23.  
  24. #ifdef USE_PTHREADS
  25. static void* tile_idle_thread          (void *);
  26. #else
  27. static gint  tile_idle_preswap         (gpointer);
  28. #endif
  29.  
  30. static int initialize = TRUE;
  31.  
  32. typedef struct _TileList {
  33.   Tile* first;
  34.   Tile* last;
  35. } TileList;
  36.  
  37. static unsigned long max_tile_size = TILE_WIDTH * TILE_HEIGHT * 4;
  38. static unsigned long cur_cache_size = 0;
  39. static unsigned long max_cache_size = 0;
  40. static unsigned long cur_cache_dirty = 0;
  41. static TileList clean_list = { NULL, NULL };
  42. static TileList dirty_list = { NULL, NULL };
  43.  
  44. #ifdef USE_PTHREADS
  45. static pthread_t preswap_thread;
  46. static pthread_mutex_t dirty_mutex = PTHREAD_MUTEX_INITIALIZER;
  47. static pthread_cond_t dirty_signal = PTHREAD_COND_INITIALIZER;
  48. static pthread_mutex_t tile_mutex  = PTHREAD_MUTEX_INITIALIZER;
  49. #define CACHE_LOCK pthread_mutex_lock(&tile_mutex)
  50. #define CACHE_UNLOCK pthread_mutex_unlock(&tile_mutex)
  51. #else
  52. static gint idle_swapper = 0;
  53. #define CACHE_LOCK /*nothing*/
  54. #define CACHE_UNLOCK /*nothing*/
  55. #endif
  56.  
  57.  
  58. void
  59. tile_cache_insert (Tile *tile)
  60. {
  61.   TileList *list;
  62.   TileList *newlist;
  63.  
  64.   if (initialize)
  65.     tile_cache_init ();
  66.  
  67.   CACHE_LOCK;
  68.   if (tile->data == NULL) goto out;    
  69.  
  70.   /* First check and see if the tile is already
  71.    *  in the cache. In that case we will simply place
  72.    *  it at the end of the tile list to indicate that
  73.    *  it was the most recently accessed tile.
  74.    */
  75.  
  76.   list = (TileList*)(tile->listhead);
  77.   newlist = (tile->dirty || tile->swap_offset == -1) ? &dirty_list : &clean_list;
  78.  
  79.   /* if list is NULL, the tile is not in the cache */
  80.  
  81.   if (list) 
  82.     {
  83.       /* Tile is in the cache.  Remove it from its current list and
  84.      put it at the tail of the proper list (clean or dirty) */
  85.  
  86.       if (tile->next) 
  87.     tile->next->prev = tile->prev;
  88.       else
  89.     list->last = tile->prev;
  90.       
  91.       if (tile->prev)
  92.     tile->prev->next = tile->next;
  93.       else
  94.     list->first = tile->next;
  95.  
  96.       tile->listhead = NULL;
  97.       if (list == &dirty_list) cur_cache_dirty -= tile_size (tile);
  98.     }
  99.   else
  100.     {
  101.       /* The tile was not in the cache. First check and see
  102.        *  if there is room in the cache. If not then we'll have
  103.        *  to make room first. Note: it might be the case that the
  104.        *  cache is smaller than the size of a tile in which case
  105.        *  it won't be possible to put it in the cache.
  106.        */
  107.       while ((cur_cache_size + max_tile_size) > max_cache_size)
  108.     {
  109.       if (!tile_cache_zorch_next()) 
  110.         {
  111.           g_warning ("cache: unable to find room for a tile");
  112.           goto out;
  113.           /* goto grump;*/
  114.         }
  115.     }
  116.       
  117.       /*grump:*/
  118.  
  119.       /* Note the increase in the number of bytes the cache
  120.        *  is referencing.
  121.        */
  122.       cur_cache_size += tile_size (tile);
  123.     }
  124.  
  125.   /* Put the tile at the end of the proper list */
  126.  
  127.   tile->next = NULL;
  128.   tile->prev = newlist->last;
  129.   tile->listhead = newlist;
  130.  
  131.   if (newlist->last) newlist->last->next = tile;
  132.   else               newlist->first = tile;
  133.   newlist->last = tile;
  134.  
  135.   /* gosgood@idt.net 1999-12-04                                  */
  136.   /* bytes on cur_cache_dirty miscounted in CVS 1.12:            */
  137.   /* Invariant: test for selecting dirty list should be the same */
  138.   /* as counting files dirty.                                    */
  139.  
  140.   if ((tile->dirty) || ( tile->swap_offset == -1)) 
  141.     {
  142.       cur_cache_dirty += tile_size (tile);
  143.       if (1)
  144.     {
  145. #ifdef USE_PTHREADS
  146.       pthread_mutex_lock(&dirty_mutex);
  147.       pthread_cond_signal(&dirty_signal);
  148.       pthread_mutex_unlock(&dirty_mutex);
  149. #endif
  150.     }
  151.     }
  152. out:
  153.   CACHE_UNLOCK;
  154.  
  155. }
  156.  
  157. void
  158. tile_cache_flush (Tile *tile)
  159. {
  160.   if (initialize)
  161.     tile_cache_init ();
  162.  
  163.   CACHE_LOCK;
  164.   tile_cache_flush_internal (tile);
  165.   CACHE_UNLOCK;
  166. }
  167.  
  168. static void
  169. tile_cache_flush_internal (Tile *tile)
  170. {
  171.   TileList *list;
  172.  
  173.   /* Find where the tile is in the cache.
  174.    */
  175.   
  176.   list = (TileList*)(tile->listhead);
  177.  
  178.   if (list) 
  179.     {
  180.       /* Note the decrease in the number of bytes the cache
  181.        *  is referencing.
  182.        */
  183.       cur_cache_size -= tile_size (tile);
  184.       if (list == &dirty_list) cur_cache_dirty -= tile_size (tile);
  185.  
  186.       if (tile->next) 
  187.     tile->next->prev = tile->prev;
  188.       else
  189.     list->last = tile->prev;
  190.       
  191.       if (tile->prev)
  192.     tile->prev->next = tile->next;
  193.       else
  194.     list->first = tile->next;
  195.  
  196.       tile->listhead = NULL;
  197.     }
  198. }
  199.  
  200.  
  201. /* untested -- ADM */
  202. void
  203. tile_cache_set_size (unsigned long cache_size)
  204. {
  205.   if (initialize)
  206.     tile_cache_init ();
  207.  
  208.   max_cache_size = cache_size;
  209.   CACHE_LOCK;
  210.   while (cur_cache_size > max_cache_size)
  211.     {
  212.       if (!tile_cache_zorch_next ())
  213.     break;
  214.     }
  215.   CACHE_UNLOCK;
  216. }
  217.  
  218.  
  219. static void
  220. tile_cache_init ()
  221. {
  222.   if (initialize)
  223.     {
  224.       initialize = FALSE;
  225.  
  226.       clean_list.first = clean_list.last = NULL;
  227.       dirty_list.first = dirty_list.last = NULL;
  228.  
  229.       max_cache_size = tile_cache_size;
  230.  
  231. #ifdef USE_PTHREADS
  232.       pthread_create (&preswap_thread, NULL, &tile_idle_thread, NULL);
  233. #else
  234.       idle_swapper = gtk_timeout_add (250,
  235.                       (GtkFunction) tile_idle_preswap, 
  236.                       (gpointer) 0);
  237. #endif
  238.     }
  239. }
  240.  
  241. static gint
  242. tile_cache_zorch_next ()
  243. {
  244.   Tile *tile;
  245.  
  246.   /*  fprintf(stderr, "cache zorch: %u/%u\n", cur_cache_size, cur_cache_dirty);*/
  247.  
  248.   if      (clean_list.first) tile = clean_list.first;
  249.   else if (dirty_list.first) tile = dirty_list.first;
  250.   else return FALSE;
  251.  
  252.   CACHE_UNLOCK;
  253.   TILE_MUTEX_LOCK (tile);
  254.   CACHE_LOCK;
  255.   tile_cache_flush_internal (tile);
  256.   if (tile->dirty || tile->swap_offset == -1)
  257.     {
  258.       tile_swap_out (tile);
  259.     }
  260.   if (! tile->dirty) {
  261.     g_free (tile->data);
  262.     tile->data = NULL;
  263.     TILE_MUTEX_UNLOCK (tile);
  264.     return TRUE;
  265.   }
  266.   /* unable to swap out tile for some reason */
  267.   TILE_MUTEX_UNLOCK (tile);
  268.   return FALSE;
  269. }
  270.  
  271. #if USE_PTHREADS
  272. static void *
  273. tile_idle_thread (void *data)
  274. {
  275.   Tile *tile;
  276.   TileList *list;
  277.   int count;
  278.  
  279.   fprintf (stderr, "starting tile preswapper\n");
  280.  
  281.   count = 0;
  282.   while (1)
  283.     {
  284.       CACHE_LOCK;
  285.       if (count > 5 || dirty_list.first == NULL)
  286.     {
  287.       CACHE_UNLOCK;
  288.       count = 0;
  289.       pthread_mutex_lock (&dirty_mutex);
  290.       pthread_cond_wait (&dirty_signal, &dirty_mutex);
  291.       pthread_mutex_unlock (&dirty_mutex);
  292.       CACHE_LOCK;
  293.     }
  294.  
  295.       if ((tile = dirty_list.first)) 
  296.     {
  297.       CACHE_UNLOCK;
  298.       TILE_MUTEX_LOCK (tile);
  299.       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.           if (tile->next) 
  308.         tile->next->prev = tile->prev;
  309.           else
  310.         list->last = tile->prev;
  311.  
  312.           if (tile->prev)
  313.         tile->prev->next = tile->next;
  314.           else
  315.         list->first = tile->next;
  316.  
  317.           tile->next = NULL;
  318.           tile->prev = clean_list.last;
  319.           tile->listhead = &clean_list;
  320.  
  321.           if (clean_list.last) clean_list.last->next = tile;
  322.           else                 clean_list.first = tile;
  323.           clean_list.last = tile;
  324.  
  325.           CACHE_UNLOCK;
  326.  
  327.           tile_swap_out (tile);
  328.         }
  329.       else 
  330.         {
  331.           CACHE_UNLOCK;
  332.         }
  333.  
  334.       TILE_MUTEX_UNLOCK (tile);
  335.     }
  336.       else 
  337.     {
  338.       CACHE_UNLOCK;
  339.     }
  340.       count++;
  341.     }
  342. }
  343.  
  344. #else
  345. static gint
  346. tile_idle_preswap (gpointer data)
  347. {
  348.   Tile *tile;
  349.   if (cur_cache_dirty*2 < max_cache_size) return TRUE;
  350.   if ((tile = dirty_list.first)) 
  351.     {
  352.       tile_swap_out(tile);
  353.  
  354.       dirty_list.first = tile->next;
  355.       if (tile->next) 
  356.     tile->next->prev = NULL;
  357.       else
  358.     dirty_list.last = NULL;
  359.  
  360.       tile->next = NULL;
  361.       tile->prev = clean_list.last;
  362.       tile->listhead = &clean_list;
  363.       if (clean_list.last) clean_list.last->next = tile;
  364.       else                 clean_list.first = tile;
  365.       clean_list.last = tile;
  366.       cur_cache_dirty -= tile_size (tile);
  367.     }
  368.   
  369.   return TRUE;
  370. }
  371. #endif
  372.  
  373.