home *** CD-ROM | disk | FTP | other *** search
- #include "config.h"
-
- #include <glib.h>
-
- #include <errno.h>
- #include <string.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef USE_PTHREADS
- #include <pthread.h>
- #endif
-
- #ifdef G_OS_WIN32
- #include <io.h>
- #endif
-
- #ifndef _O_BINARY
- #define _O_BINARY 0
- #endif
- #ifndef _O_TEMPORARY
- #define _O_TEMPORARY 0
- #endif
-
- #define MAX_OPEN_SWAP_FILES 16
-
- #include "tile.h"
- #include "tile_swap.h"
- #include "tile_pvt.h" /* ick. */
-
- typedef struct _SwapFile SwapFile;
- typedef struct _DefSwapFile DefSwapFile;
- typedef struct _Gap Gap;
- typedef struct _AsyncSwapArgs AsyncSwapArgs;
-
- struct _SwapFile
- {
- char *filename;
- int swap_num;
- SwapFunc swap_func;
- gpointer user_data;
- int fd;
- };
-
- struct _DefSwapFile
- {
- GList *gaps;
- long swap_file_end;
- off_t cur_position;
- };
-
- struct _Gap
- {
- long start;
- long end;
- };
-
- struct _AsyncSwapArgs
- {
- DefSwapFile *def_swap_file;
- int fd;
- Tile *tile;
- };
-
-
- static void tile_swap_init (void);
- static guint tile_swap_hash (int *key);
- static gint tile_swap_compare (int *a,
- int *b);
- static void tile_swap_command (Tile *tile,
- int command);
- static void tile_swap_open (SwapFile *swap_file);
-
- static int tile_swap_default (int fd,
- Tile *tile,
- int cmd,
- gpointer user_data);
- static void tile_swap_default_in (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile);
- static void tile_swap_default_in_async (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile);
- static void tile_swap_default_out (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile);
- static void tile_swap_default_delete (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile);
- static long tile_swap_find_offset (DefSwapFile *def_swap_file,
- int fd,
- int bytes);
- static void tile_swap_resize (DefSwapFile *def_swap_file,
- int fd,
- long new_size);
- static Gap* tile_swap_gap_new (long start,
- long end);
- static void tile_swap_gap_destroy (Gap *gap);
- #ifdef USE_PTHREADS
- static void* tile_swap_in_thread (void *);
- #endif
-
-
- static int initialize = TRUE;
- static GHashTable *swap_files = NULL;
- static GList *open_swap_files = NULL;
- static int nopen_swap_files = 0;
- static int next_swap_num = 1;
- static long swap_file_grow = 16 * TILE_WIDTH * TILE_HEIGHT * 4;
- #ifdef USE_PTHREADS
- static pthread_mutex_t swapfile_mutex = PTHREAD_MUTEX_INITIALIZER;
-
- /* async_swapin_mutex protects only the list, not the tiles therein */
- static pthread_t swapin_thread;
- static pthread_mutex_t async_swapin_mutex = PTHREAD_MUTEX_INITIALIZER;
- static pthread_cond_t async_swapin_signal = PTHREAD_COND_INITIALIZER;
- static GSList *async_swapin_tiles = NULL;
- static GSList *async_swapin_tiles_end = NULL;
- #endif
-
- static gboolean seek_err_msg = TRUE, read_err_msg = TRUE, write_err_msg = TRUE;
-
- static void
- tile_swap_print_gaps (DefSwapFile *def_swap_file)
- {
- GList *gaps;
- Gap *gap;
-
- gaps = def_swap_file->gaps;
- while (gaps)
- {
- gap = gaps->data;
- gaps = gaps->next;
-
- g_print (" %6ld - %6ld\n", gap->start, gap->end);
- }
- }
-
- static void
- tile_swap_exit1 (gpointer key,
- gpointer value,
- gpointer data)
- {
- extern int tile_ref_count;
- SwapFile *swap_file;
- DefSwapFile *def_swap_file;
-
- if (tile_ref_count != 0)
- g_warning ("tile ref count balance: %d\n", tile_ref_count);
-
- swap_file = value;
- if (swap_file->swap_func == tile_swap_default)
- {
- def_swap_file = swap_file->user_data;
- if (def_swap_file->swap_file_end != 0)
- {
- g_warning ("swap file not empty: \"%s\"\n", swap_file->filename);
- tile_swap_print_gaps (def_swap_file);
- }
-
- #if defined (__EMX__) || defined (G_OS_WIN32)
- /* should close before unlink */
- if (swap_file->fd > 0)
- {
- close (swap_file->fd);
- swap_file->fd = -1;
- }
- #endif
- unlink (swap_file->filename);
- }
- }
-
-
- void
- tile_swap_exit ()
- {
- #ifdef HINTS_SANITY
- extern int tile_exist_peak;
-
- fprintf(stderr,"Tile exist peak was %d Tile structs (%d bytes)",
- tile_exist_peak, tile_exist_peak * sizeof(Tile));
- #endif
-
- if (swap_files)
- g_hash_table_foreach (swap_files, tile_swap_exit1, NULL);
- }
-
- int
- tile_swap_add (char *filename,
- SwapFunc swap_func,
- gpointer user_data)
- {
- SwapFile *swap_file;
- DefSwapFile *def_swap_file;
-
- #ifdef USE_PTHREADS
- pthread_mutex_lock(&swapfile_mutex);
- #endif
-
- if (initialize)
- tile_swap_init ();
-
- swap_file = g_new (SwapFile, 1);
- swap_file->filename = g_strdup (filename);
- swap_file->swap_num = next_swap_num++;
-
- if (!swap_func)
- {
- swap_func = tile_swap_default;
-
- def_swap_file = g_new (DefSwapFile, 1);
- def_swap_file->gaps = NULL;
- def_swap_file->swap_file_end = 0;
- def_swap_file->cur_position = 0;
-
- user_data = def_swap_file;
- }
-
- swap_file->swap_func = swap_func;
- swap_file->user_data = user_data;
- swap_file->fd = -1;
-
- g_hash_table_insert (swap_files, &swap_file->swap_num, swap_file);
-
- #ifdef USE_PTHREADS
- pthread_mutex_unlock(&swapfile_mutex);
- #endif
- return swap_file->swap_num;
- }
-
- void
- tile_swap_remove (int swap_num)
- {
- SwapFile *swap_file;
-
- #ifdef USE_PTHREADS
- pthread_mutex_lock(&swapfile_mutex);
- #endif
-
- if (initialize)
- tile_swap_init ();
-
- swap_file = g_hash_table_lookup (swap_files, &swap_num);
- if (!swap_file)
- goto out;
-
- g_hash_table_remove (swap_files, &swap_num);
-
- if (swap_file->fd != -1)
- close (swap_file->fd);
-
- g_free (swap_file);
- out:
- #ifdef USE_PTHREADS
- pthread_mutex_unlock(&swapfile_mutex);
- #endif
- return;
- }
-
- void
- tile_swap_in_async (Tile *tile)
- {
- if (tile->swap_offset == -1)
- return;
-
- tile_swap_command (tile, SWAP_IN_ASYNC);
- }
-
- void
- tile_swap_in (Tile *tile)
- {
- if (tile->swap_offset == -1)
- {
- tile_alloc (tile);
- return;
- }
-
- tile_swap_command (tile, SWAP_IN);
- }
-
- void
- tile_swap_out (Tile *tile)
- {
- tile_swap_command (tile, SWAP_OUT);
- }
-
- void
- tile_swap_delete (Tile *tile)
- {
- tile_swap_command (tile, SWAP_DELETE);
- }
-
- void
- tile_swap_compress (int swap_num)
- {
- tile_swap_command (NULL, SWAP_COMPRESS);
- }
-
- static void
- tile_swap_init ()
- {
-
- if (initialize)
- {
- initialize = FALSE;
-
- swap_files = g_hash_table_new ((GHashFunc) tile_swap_hash,
- (GCompareFunc) tile_swap_compare);
-
- #ifdef NOTDEF /* USE_PTHREADS */
- pthread_create (&swapin_thread, NULL, &tile_swap_in_thread, NULL);
- #endif
- }
- }
-
- static guint
- tile_swap_hash (int *key)
- {
- return ((guint) *key);
- }
-
- static gint
- tile_swap_compare (int *a,
- int *b)
- {
- return (*a == *b);
- }
-
- static void
- tile_swap_command (Tile *tile,
- int command)
- {
- SwapFile *swap_file;
- #ifdef USE_PTHREADS
- pthread_mutex_lock(&swapfile_mutex);
- #endif
-
- if (initialize)
- tile_swap_init ();
-
- do {
- swap_file = g_hash_table_lookup (swap_files, &tile->swap_num);
- if (!swap_file)
- {
- g_warning ("could not find swap file for tile");
- goto out;
- }
-
- if (swap_file->fd == -1)
- {
- tile_swap_open (swap_file);
-
- if (swap_file->fd == -1)
- goto out;
- }
- } while ((* swap_file->swap_func) (swap_file->fd, tile, command, swap_file->user_data));
- out:
- #ifdef USE_PTHREADS
- pthread_mutex_unlock(&swapfile_mutex);
- #endif
- return;
- }
-
- static void
- tile_swap_open (SwapFile *swap_file)
- {
- SwapFile *tmp;
-
- if (swap_file->fd != -1)
- return;
-
- if (nopen_swap_files == MAX_OPEN_SWAP_FILES)
- {
- tmp = open_swap_files->data;
- close (tmp->fd);
- tmp->fd = -1;
-
- open_swap_files = g_list_remove (open_swap_files, open_swap_files->data);
- nopen_swap_files -= 1;
- }
-
- swap_file->fd = open (swap_file->filename,
- O_CREAT | O_RDWR | _O_BINARY | _O_TEMPORARY,
- S_IREAD | S_IWRITE);
-
- if (swap_file->fd == -1)
- {
- g_message ("unable to open swap file...BAD THINGS WILL HAPPEN SOON");
- return;
- }
-
- open_swap_files = g_list_append (open_swap_files, swap_file);
- nopen_swap_files += 1;
- }
-
- /* The actual swap file code. The swap file consists of tiles
- * which have been moved out to disk in order to conserve memory.
- * The swap file format is free form. Any tile in memory may
- * end up anywhere on disk.
- * An actual tile in the swap file consists only of the tile data.
- * The offset of the tile on disk is stored in the tile data structure
- * in memory.
- */
-
- static int
- tile_swap_default (int fd,
- Tile *tile,
- int cmd,
- gpointer user_data)
- {
- DefSwapFile *def_swap_file;
-
- def_swap_file = (DefSwapFile*) user_data;
-
- switch (cmd)
- {
- case SWAP_IN:
- tile_swap_default_in (def_swap_file, fd, tile);
- break;
- case SWAP_IN_ASYNC:
- tile_swap_default_in_async (def_swap_file, fd, tile);
- break;
- case SWAP_OUT:
- tile_swap_default_out (def_swap_file, fd, tile);
- break;
- case SWAP_DELETE:
- tile_swap_default_delete (def_swap_file, fd, tile);
- break;
- case SWAP_COMPRESS:
- g_warning ("tile_swap_default: SWAP_COMPRESS: UNFINISHED");
- break;
- }
-
- return FALSE;
- }
-
- static void
- tile_swap_default_in_async (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile)
- {
- #ifdef NOTDEF /* USE_PTHREADS */
- AsyncSwapArgs *args;
-
- args = g_new(AsyncSwapArgs, 1);
- args->def_swap_file = def_swap_file;
- args->fd = fd;
- args->tile = tile;
-
- /* add this tile to the list of tiles for the async swapin task */
- pthread_mutex_lock (&async_swapin_mutex);
- g_slist_append (async_swapin_tiles_end, args);
-
- if (!async_swapin_tiles)
- async_swapin_tiles = async_swapin_tiles_end;
-
- pthread_cond_signal (&async_swapin_signal);
- pthread_mutex_unlock (&async_swapin_mutex);
-
- #else
- /* ignore; it's only a hint anyway */
- /* this could be changed to call out to another program that
- * tries to make the OS read the data in from disk.
- */
- #endif
-
- return;
- }
-
- /* NOTE: if you change this function, check to see if your changes
- * apply to tile_swap_in_attempt() near the end of the file. The
- * difference is that this version makes guarantees about what it
- * provides, but tile_swap_in_attempt() just tries and gives up if
- * anything goes wrong.
- *
- * I'm not sure that it is worthwhile to try to pull out common
- * bits; I think the two functions are (at least for now) different
- * enough to keep as two functions.
- *
- * N.B. the mutex on the tile must already have been locked on entry
- * to this function. DO NOT LOCK IT HERE.
- */
- static void
- tile_swap_default_in (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile)
- {
- int bytes;
- int err;
- int nleft;
- off_t offset;
-
- err = -1;
-
- if (tile->data)
- {
- return;
- }
-
- if (def_swap_file->cur_position != tile->swap_offset)
- {
- def_swap_file->cur_position = tile->swap_offset;
-
- offset = lseek (fd, tile->swap_offset, SEEK_SET);
- if (offset == -1)
- {
- if (seek_err_msg)
- g_message ("unable to seek to tile location on disk: %d", err);
- seek_err_msg = FALSE;
- return;
- }
- }
-
- bytes = tile_size (tile);
- tile_alloc (tile);
-
- nleft = bytes;
- while (nleft > 0)
- {
- do {
- err = read (fd, tile->data + bytes - nleft, nleft);
- } while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR)));
-
- if (err <= 0)
- {
- if (read_err_msg)
- g_message ("unable to read tile data from disk: %d/%d ( %d ) bytes read", err, errno, nleft);
- read_err_msg = FALSE;
- return;
- }
-
- nleft -= err;
- }
-
- def_swap_file->cur_position += bytes;
-
- /* Do not delete the swap from the file */
- /* tile_swap_default_delete (def_swap_file, fd, tile); */
-
- read_err_msg = seek_err_msg = TRUE;
- }
-
- static void
- tile_swap_default_out (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile)
- {
- int bytes;
- int rbytes;
- int err;
- int nleft;
- off_t offset;
-
- off_t newpos;
-
- bytes = TILE_WIDTH * TILE_HEIGHT * tile->bpp;
- rbytes = tile_size (tile);
-
- /* If there is already a valid swap_offset, use it */
- if (tile->swap_offset == -1)
- newpos = tile_swap_find_offset (def_swap_file, fd, bytes);
- else
- newpos = tile->swap_offset;
-
- if (def_swap_file->cur_position != newpos)
- {
- offset = lseek (fd, newpos, SEEK_SET);
- if (offset == -1)
- {
- if (seek_err_msg)
- g_message ("unable to seek to tile location on disk: %d", errno);
- seek_err_msg = FALSE;
- return;
- }
- def_swap_file->cur_position = newpos;
- }
-
- nleft = rbytes;
- while (nleft > 0)
- {
- err = write (fd, tile->data + rbytes - nleft, nleft);
- if (err <= 0)
- {
- if (write_err_msg)
- g_message ("unable to write tile data to disk: %d ( %d ) bytes written", err, nleft);
- write_err_msg = FALSE;
- return;
- }
-
- nleft -= err;
- }
-
- def_swap_file->cur_position += rbytes;
-
- /* Do NOT free tile->data because we may be pre-swapping.
- * tile->data is freed in tile_cache_zorch_next
- */
- tile->dirty = FALSE;
- tile->swap_offset = newpos;
-
- write_err_msg = seek_err_msg = TRUE;
- }
-
- static void
- tile_swap_default_delete (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile)
- {
- GList *tmp;
- GList *tmp2;
- Gap *gap;
- Gap *gap2;
- long start;
- long end;
-
- if (tile->swap_offset == -1)
- return;
-
- start = tile->swap_offset;
- end = start + TILE_WIDTH * TILE_HEIGHT * tile->bpp;
- tile->swap_offset = -1;
-
- tmp = def_swap_file->gaps;
- while (tmp)
- {
- gap = tmp->data;
-
- if (end == gap->start)
- {
- gap->start = start;
-
- if (tmp->prev)
- {
- gap2 = tmp->prev->data;
- if (gap->start == gap2->end)
- {
- gap2->end = gap->end;
- tile_swap_gap_destroy (gap);
- def_swap_file->gaps = g_list_remove_link (def_swap_file->gaps, tmp);
- g_list_free (tmp);
- }
- }
- break;
- }
- else if (start == gap->end)
- {
- gap->end = end;
-
- if (tmp->next)
- {
- gap2 = tmp->next->data;
- if (gap->end == gap2->start)
- {
- gap2->start = gap->start;
- tile_swap_gap_destroy (gap);
- def_swap_file->gaps = g_list_remove_link (def_swap_file->gaps, tmp);
- g_list_free (tmp);
- }
- }
- break;
- }
- else if (end < gap->start)
- {
- gap = tile_swap_gap_new (start, end);
-
- tmp2 = g_list_alloc ();
- tmp2->data = gap;
- tmp2->next = tmp;
- tmp2->prev = tmp->prev;
- if (tmp->prev)
- tmp->prev->next = tmp2;
- tmp->prev = tmp2;
-
- if (tmp == def_swap_file->gaps)
- def_swap_file->gaps = tmp2;
- break;
- }
- else if (!tmp->next)
- {
- gap = tile_swap_gap_new (start, end);
- tmp->next = g_list_alloc ();
- tmp->next->data = gap;
- tmp->next->prev = tmp;
- break;
- }
-
- tmp = tmp->next;
- }
-
- if (!def_swap_file->gaps)
- {
- gap = tile_swap_gap_new (start, end);
- def_swap_file->gaps = g_list_append (def_swap_file->gaps, gap);
- }
-
- tmp = g_list_last (def_swap_file->gaps);
- gap = tmp->data;
-
- if (gap->end == def_swap_file->swap_file_end)
- {
- tile_swap_resize (def_swap_file, fd, gap->start);
- tile_swap_gap_destroy (gap);
- def_swap_file->gaps = g_list_remove_link (def_swap_file->gaps, tmp);
- g_list_free (tmp);
- }
- }
-
- static void
- tile_swap_resize (DefSwapFile *def_swap_file,
- int fd,
- long new_size)
- {
- if (def_swap_file->swap_file_end > new_size)
- {
- ftruncate (fd, new_size);
- /*fprintf(stderr, "TRUNCATED SWAP from %d to %d bytes.\n",
- (int)def_swap_file->swap_file_end, (int) new_size);*/
- }
- def_swap_file->swap_file_end = new_size;
- }
-
- static long
- tile_swap_find_offset (DefSwapFile *def_swap_file,
- int fd,
- int bytes)
- {
- GList *tmp;
- Gap *gap;
- long offset;
-
- tmp = def_swap_file->gaps;
- while (tmp)
- {
- gap = tmp->data;
-
- if ((gap->end - gap->start) >= bytes)
- {
- offset = gap->start;
- gap->start += bytes;
-
- if (gap->start == gap->end)
- {
- tile_swap_gap_destroy (gap);
- def_swap_file->gaps = g_list_remove_link (def_swap_file->gaps, tmp);
- g_list_free (tmp);
- }
-
- return offset;
- }
-
- tmp = tmp->next;
- }
-
- offset = def_swap_file->swap_file_end;
-
- tile_swap_resize (def_swap_file, fd, def_swap_file->swap_file_end + swap_file_grow);
-
- if ((offset + bytes) < (def_swap_file->swap_file_end))
- {
- gap = tile_swap_gap_new (offset + bytes, def_swap_file->swap_file_end);
- def_swap_file->gaps = g_list_append (def_swap_file->gaps, gap);
- }
-
- return offset;
- }
-
- static Gap*
- tile_swap_gap_new (long start,
- long end)
- {
- Gap *gap;
-
- gap = g_new (Gap, 1);
- gap->start = start;
- gap->end = end;
-
- return gap;
- }
-
- static void
- tile_swap_gap_destroy (Gap *gap)
- {
- g_free (gap);
- }
-
-
- #ifdef NOTDEF /* USE_PTHREADS */
- /* go through the list of tiles that are likely to be used soon and
- * try to swap them in. If any tile is not in a state to be swapped
- * in, ignore it, and the error will get dealt with when the tile
- * is really needed -- assuming that the error still happens.
- *
- * Potential future enhancement: for non-threaded systems, we could
- * fork() a process which merely attempts to bring tiles into the
- * OS's buffer/page cache, where they will be read into the gimp
- * more quickly. This would be pretty trivial, actually.
- */
-
- static void
- tile_swap_in_attempt (DefSwapFile *def_swap_file,
- int fd,
- Tile *tile)
- {
- int bytes;
- int err;
- int nleft;
- off_t offset;
-
- err = -1;
-
- TILE_MUTEX_LOCK (tile);
- if (tile->data)
- goto out;
-
- if (!tile->swap_num || !tile->swap_offset)
- goto out;
-
- if (def_swap_file->cur_position != tile->swap_offset)
- {
- def_swap_file->cur_position = tile->swap_offset;
-
- offset = lseek (fd, tile->swap_offset, SEEK_SET);
- if (offset == -1)
- return;
- }
-
- bytes = tile_size (tile);
- tile_alloc (tile);
-
- nleft = bytes;
- while (nleft > 0)
- {
- do {
- err = read (fd, tile->data + bytes - nleft, nleft);
- } while ((err == -1) && ((errno == EAGAIN) || (errno == EINTR)));
-
- if (err <= 0)
- {
- g_free (tile->data);
- return;
- }
-
- nleft -= err;
- }
-
- def_swap_file->cur_position += bytes;
-
- out:
- TILE_MUTEX_UNLOCK (tile);
- }
-
- static void *
- tile_swap_in_thread (void *data)
- {
- AsyncSwapArgs *args;
- GSList *free_item;
-
- while (1)
- {
- pthread_mutex_lock (&async_swapin_mutex);
-
- if (!async_swapin_tiles)
- {
- pthread_cond_wait (&async_swapin_signal, &async_swapin_mutex);
- }
- args = async_swapin_tiles->data;
-
- free_item = async_swapin_tiles;
- async_swapin_tiles = async_swapin_tiles->next;
- g_slist_free_1(free_item);
- if (!async_swapin_tiles)
- async_swapin_tiles_end = NULL;
-
- pthread_mutex_unlock (&async_swapin_mutex);
-
- tile_swap_in_attempt(args->def_swap_file, args->fd, args->tile);
-
- g_free(args);
- }
- }
- #endif
-