mappedfile.c

Go to the documentation of this file.
00001 /*                    M A P P E D F I L E . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 2004-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** \addtogroup mf */
00023 /*@{*/
00024 
00025 /** @file mappedfile.c
00026  * @brief Routines for sharing large read-only data files.
00027  *
00028  *  Routines for sharing large read-only data files
00029  *  like height fields, bit map solids, texture maps, etc.
00030  *  Uses memory mapped files where available.
00031  *
00032  *  Each instance of the file has the raw data available as element "buf".
00033  *  If a particular application needs to transform the raw data in a
00034  *  manner that is identical across all uses of that application
00035  *  (e.g. height fields, EBMs, etc), then the application should
00036  *  provide a non-null "appl" string, to tag the format of the "apbuf".
00037  *  This will keep different applications from sharing that instance
00038  *  of the file.
00039  *  Thus, if the same filename is opened for interpretation as both
00040  *  an EBM and a height field, they will be assigned different mapped file
00041  *  structures, so that the "apbuf" pointers are distinct.
00042  *
00043  *
00044  *  @author     Michael John Muuss
00045  *
00046  *  @par Source -
00047  *      The U. S. Army Research Laboratory
00048  * @n   Aberdeen Proving Ground, Maryland  21005-5068  USA
00049  *
00050  */
00051 
00052 #ifndef lint
00053 static const char libbu_mappedfile_RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/libbu/mappedfile.c,v 14.16 2006/09/03 15:14:07 lbutler Exp $ (ARL)";
00054 #endif
00055 
00056 #include "common.h"
00057 
00058 #include <stdio.h>
00059 #include <math.h>
00060 #include <fcntl.h>
00061 #ifdef HAVE_UNISTD_H
00062 #  include <unistd.h>
00063 #endif
00064 #ifdef HAVE_STRING_H
00065 #  include <string.h>
00066 #else
00067 #  include <strings.h>
00068 #endif
00069 
00070 #include "machine.h"
00071 
00072 #ifdef HAVE_UNIX_IO
00073 #  include <sys/types.h>
00074 #  include <sys/stat.h>
00075 #endif
00076 
00077 #ifdef HAVE_SYS_MMAN_H
00078 #  include <sys/mman.h>
00079 #  if !defined(MAP_FAILED)
00080 #    define MAP_FAILED  ((void *)-1)    /* Error return from mmap() */
00081 #  endif
00082 #endif
00083 
00084 #include "bu.h"
00085 
00086 
00087 static struct bu_list   bu_mapped_file_list = {
00088         0,
00089         (struct bu_list *)NULL,
00090         (struct bu_list *)NULL
00091 };      /* list of currently open mapped files */
00092 
00093 /**
00094  *                      B U _ O P E N _ M A P P E D _ F I L E
00095  *
00096  *  If the file can not be opened, as descriptive an error message as
00097  *  possible will be printed, to simplify code handling in the caller.
00098  *
00099  *  Mapped files are always opened read-only.
00100  *
00101  *  If the system does not support mapped files, the data is read into memory.
00102  */
00103 struct bu_mapped_file *
00104 bu_open_mapped_file(const char *name, const char *appl)
00105                                 /* file name */
00106                                 /* non-null only when app. will use 'apbuf' */
00107 {
00108         struct bu_mapped_file   *mp = (struct bu_mapped_file *)NULL;
00109 #ifdef HAVE_UNIX_IO
00110         struct stat             sb;
00111         int                     fd;     /* unix file descriptor */
00112 #else
00113         FILE                    *fp = (FILE *)NULL;     /* stdio file pointer */
00114 #endif
00115         int                     ret;
00116 
00117         if( bu_debug&BU_DEBUG_MAPPED_FILE )
00118 #ifdef HAVE_SBRK
00119                 bu_log("bu_open_mapped_file(%s, %s) sbrk=x%lx\n", name, appl?appl:"(NIL)", (long)sbrk(0));
00120 #else
00121                 bu_log("bu_open_mapped_file(%s, %s)\n", name, appl?appl:"(NIL)");
00122 #endif
00123 
00124         /* See if file has already been mapped, and can be shared */
00125         bu_semaphore_acquire(BU_SEM_MAPPEDFILE);
00126         if( BU_LIST_UNINITIALIZED( &bu_mapped_file_list ) )  {
00127                 BU_LIST_INIT( &bu_mapped_file_list );
00128         }
00129         for( BU_LIST_FOR( mp, bu_mapped_file, &bu_mapped_file_list ) )  {
00130                 BU_CK_MAPPED_FILE(mp);
00131                 if( strcmp( name, mp->name ) )  continue;
00132                 if( appl && strcmp( appl, mp->appl ) )
00133                         continue;
00134                 /* File is already mapped -- verify size and modtime */
00135 #ifdef HAVE_UNIX_IO
00136                 if( !mp->dont_restat )  {
00137                         bu_semaphore_acquire(BU_SEM_SYSCALL);
00138                         ret = stat( name, &sb );
00139                         bu_semaphore_release(BU_SEM_SYSCALL);
00140                         if( ret < 0 )  goto do_reuse;   /* File vanished from disk, mapped copy still OK */
00141                         if( sb.st_size != mp->buflen )  {
00142                                 bu_log("bu_open_mapped_file(%s) WARNING: File size changed from %ld to %ld, opening new version.\n",
00143                                         name, (long)mp->buflen, (long)sb.st_size );
00144                                 goto dont_reuse;
00145                         }
00146                         if( (long)sb.st_mtime != mp->modtime )  {
00147                                 bu_log("bu_open_mapped_file(%s) WARNING: File modified since last mapped, opening new version.\n",
00148                                         name);
00149                                 goto dont_reuse;
00150                         }
00151                         /* To be completely safe, should check st_dev and st_inum */
00152                 }
00153 #endif
00154 do_reuse:
00155                 /* It is safe to reuse mp */
00156                 mp->uses++;
00157                 bu_semaphore_release(BU_SEM_MAPPEDFILE);
00158                 if( bu_debug&BU_DEBUG_MAPPED_FILE )
00159                         bu_pr_mapped_file("open_reused", mp);
00160                 return mp;
00161 dont_reuse:
00162                 /* mp doesn't reflect the file any longer.  Invalidate. */
00163                 mp->appl = "__STALE__";
00164                 /* Can't invalidate old copy, it may still be in use. */
00165                 /* Fall through, and open the new version */
00166         }
00167         bu_semaphore_release(BU_SEM_MAPPEDFILE);
00168         mp = (struct bu_mapped_file *)NULL;
00169 
00170         /* File is not yet mapped, open file read only. */
00171 #ifdef HAVE_UNIX_IO
00172         bu_semaphore_acquire(BU_SEM_SYSCALL);
00173         fd = open( name, O_RDONLY );
00174         bu_semaphore_release(BU_SEM_SYSCALL);
00175 
00176         if( fd < 0 )  {
00177                 if (bu_debug&BU_DEBUG_DB)
00178                         perror(name);
00179                 goto fail;
00180         }
00181 
00182         bu_semaphore_acquire(BU_SEM_SYSCALL);
00183         ret = fstat( fd, &sb );
00184         bu_semaphore_release(BU_SEM_SYSCALL);
00185 
00186         if( ret < 0 )  {
00187                 perror(name);
00188                 goto fail;
00189         }
00190 
00191         if( sb.st_size == 0 )  {
00192                 bu_log("bu_open_mapped_file(%s) 0-length file\n", name);
00193                 goto fail;
00194         }
00195 #endif /* HAVE_UNIX_IO */
00196 
00197         /* Optimisticly assume that things will proceed OK */
00198         BU_GETSTRUCT( mp, bu_mapped_file );
00199         mp->name = bu_strdup( name );
00200         if( appl ) mp->appl = bu_strdup( appl );
00201 
00202 #ifdef HAVE_UNIX_IO
00203         mp->buflen = (size_t)sb.st_size;
00204         mp->modtime = (long)sb.st_mtime;
00205 #  ifdef HAVE_SYS_MMAN_H
00206 
00207         /* Attempt to access as memory-mapped file */
00208         bu_semaphore_acquire(BU_SEM_SYSCALL);
00209         mp->buf = mmap((caddr_t)0, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0 );
00210         bu_semaphore_release(BU_SEM_SYSCALL);
00211 
00212         if( mp->buf == MAP_FAILED )  perror(name);
00213         if( mp->buf != MAP_FAILED )  {
00214                 /* OK, it's memory mapped in! */
00215                 mp->is_mapped = 1;
00216                 /* It's safe to close the fd now, the manuals say */
00217         } else
00218 #  endif /* HAVE_SYS_MMAN_H */
00219         {
00220                 /* Allocate a local buffer, and slurp it in */
00221                 mp->buf = bu_malloc( (size_t)sb.st_size, name );
00222 
00223                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00224                 ret = read( fd, mp->buf, (size_t)sb.st_size );
00225                 bu_semaphore_release(BU_SEM_SYSCALL);
00226 
00227                 if( ret != sb.st_size )  {
00228                         perror(name);
00229                         bu_free( mp->buf, name );
00230                         bu_semaphore_acquire(BU_SEM_SYSCALL);
00231                         (void)close(fd);
00232                         bu_semaphore_release(BU_SEM_SYSCALL);
00233                         goto fail;
00234                 }
00235         }
00236 
00237         bu_semaphore_acquire(BU_SEM_SYSCALL);
00238         (void)close(fd);
00239         bu_semaphore_release(BU_SEM_SYSCALL);
00240 
00241 #else /* !HAVE_UNIX_IO */
00242 
00243         /* Read it in with stdio, with no clue how big it is */
00244         bu_semaphore_acquire(BU_SEM_SYSCALL);
00245 #if defined(_WIN32) && !defined(__CYGWIN__)
00246         fp = fopen( name, "rb");
00247 #else
00248         fp = fopen( name, "r");
00249 #endif
00250         bu_semaphore_release(BU_SEM_SYSCALL);
00251 
00252         if( fp == NULL )  {
00253                 perror(name);
00254                 goto fail;
00255         }
00256         /* Read it once to see how large it is */
00257         {
00258                 char    buf[32768] = {0};
00259                 int     got;
00260                 mp->buflen = 0;
00261 
00262                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00263                 while( (got = fread( buf, 1, sizeof(buf), fp )) > 0 )
00264                         mp->buflen += got;
00265                 rewind(fp);
00266                 bu_semaphore_release(BU_SEM_SYSCALL);
00267 
00268         }
00269         /* Malloc the necessary buffer */
00270         mp->buf = bu_malloc( mp->buflen, name );
00271 
00272         /* Read it again into the buffer */
00273         bu_semaphore_acquire(BU_SEM_SYSCALL);
00274         ret = fread( mp->buf, mp->buflen, 1, fp );
00275         bu_semaphore_release(BU_SEM_SYSCALL);
00276 
00277         if( ret != 1 )  {
00278                 bu_semaphore_acquire(BU_SEM_SYSCALL);
00279                 perror("fread");
00280                 fclose(fp);
00281                 bu_semaphore_release(BU_SEM_SYSCALL);
00282 
00283                 bu_log("bu_open_mapped_file() 2nd fread failed? len=%d\n", mp->buflen);
00284                 bu_free( mp->buf, "non-unix fread buf" );
00285                 goto fail;
00286         }
00287 
00288         bu_semaphore_acquire(BU_SEM_SYSCALL);
00289         fclose(fp);
00290         bu_semaphore_release(BU_SEM_SYSCALL);
00291 #endif
00292 
00293         mp->uses = 1;
00294         mp->l.magic = BU_MAPPED_FILE_MAGIC;
00295 
00296         bu_semaphore_acquire(BU_SEM_MAPPEDFILE);
00297         BU_LIST_APPEND( &bu_mapped_file_list, &mp->l );
00298         bu_semaphore_release(BU_SEM_MAPPEDFILE);
00299 
00300         if( bu_debug&BU_DEBUG_MAPPED_FILE )  {
00301                 bu_pr_mapped_file("1st_open", mp);
00302 #ifdef HAVE_SBRK
00303                 bu_log("bu_open_mapped_file() sbrk=x%lx\n", (long)sbrk(0));
00304 #endif
00305         }
00306         return mp;
00307 
00308 fail:
00309         if( mp )  {
00310                 bu_free( mp->name, "mp->name" );
00311                 if( mp->appl ) bu_free( mp->appl, "mp->appl" );
00312                 /* Don't free mp->buf here, it might not be bu_malloced but mmaped */
00313                 bu_free( mp, "mp from bu_open_mapped_file fail");
00314         }
00315 
00316         if (bu_debug&BU_DEBUG_DB)
00317           bu_log("bu_open_mapped_file(%s, %s) can't open file\n",
00318                  name, appl?appl:"(NIL)" );
00319 
00320         return (struct bu_mapped_file *)NULL;
00321 }
00322 
00323 /**
00324  *                      B U _ C L O S E _ M A P P E D _ F I L E
00325  *
00326  *  Release a use of a mapped file.
00327  *  Because it may be re-used shortly, e.g. by the next frame of
00328  *  an animation, don't release the memory even on final close,
00329  *  so that it's available when next needed.
00330  *  Call bu_free_mapped_files() after final close to reclaim space.
00331  *  But only do that if you're SURE that ALL these files will never again
00332  *  need to be mapped by this process.  Such as when running multi-frame
00333  *  animations.
00334  */
00335 void
00336 bu_close_mapped_file(struct bu_mapped_file *mp)
00337 {
00338         BU_CK_MAPPED_FILE(mp);
00339 
00340         if( bu_debug&BU_DEBUG_MAPPED_FILE )
00341                 bu_pr_mapped_file("close:uses--", mp);
00342 
00343         if (! mp) {
00344             bu_log("bu_close_mapped_file() called with null pointer\n");
00345             return;
00346         }
00347 
00348         bu_semaphore_acquire(BU_SEM_MAPPEDFILE);
00349         --mp->uses;
00350         bu_semaphore_release(BU_SEM_MAPPEDFILE);
00351 }
00352 
00353 /**
00354  *                      B U _ P R _ M A P P E D _ F I L E
00355  */
00356 void
00357 bu_pr_mapped_file(const char *title, const struct bu_mapped_file *mp)
00358 {
00359         BU_CK_MAPPED_FILE(mp);
00360 
00361         bu_log("%8lx mapped_file %s %lx len=%ld mapped=%d, uses=%d %s\n",
00362                 (long)mp, mp->name, (long)mp->buf, mp->buflen,
00363                 mp->is_mapped, mp->uses,
00364                 title );
00365 }
00366 
00367 /**
00368  *                      B U _ F R E E _ M A P P E D _ F I L E S
00369  *
00370  *  Release storage being used by mapped files with no remaining users.
00371  *  This entire routine runs inside a critical section, for parallel protection.
00372  *  Only call this routine if you're SURE that ALL these files will never
00373  *  again need to be mapped by this process.  Such as when running multi-frame
00374  *  animations.
00375  */
00376 void
00377 bu_free_mapped_files(int verbose)
00378 {
00379         struct bu_mapped_file   *mp, *next;
00380 
00381         if( bu_debug&BU_DEBUG_MAPPED_FILE )
00382                 bu_log("bu_free_mapped_files(verbose=%d)\n", verbose);
00383 
00384         bu_semaphore_acquire(BU_SEM_MAPPEDFILE);
00385 
00386         next = BU_LIST_FIRST( bu_mapped_file, &bu_mapped_file_list );
00387         while( BU_LIST_NOT_HEAD( next, &bu_mapped_file_list ) )  {
00388                 BU_CK_MAPPED_FILE(next);
00389                 mp = next;
00390                 next = BU_LIST_NEXT( bu_mapped_file, &mp->l );
00391 
00392                 if( mp->uses > 0 )  continue;
00393 
00394                 /* Found one that needs to have storage released */
00395                 if(verbose || (bu_debug&BU_DEBUG_MAPPED_FILE))
00396                         bu_pr_mapped_file( "freeing", mp );
00397 
00398                 BU_LIST_DEQUEUE( &mp->l );
00399 
00400                 /* If application pointed mp->apbuf at mp->buf, break that
00401                  * association so we don't double-free the buffer.
00402                  */
00403                 if( mp->apbuf == mp->buf )  mp->apbuf = (genptr_t)NULL;
00404 
00405 #ifdef HAVE_SYS_MMAN_H
00406                 if( mp->is_mapped )  {
00407                         int     ret;
00408                         bu_semaphore_acquire(BU_SEM_SYSCALL);
00409                         ret = munmap( mp->buf, (size_t)mp->buflen );
00410                         bu_semaphore_release(BU_SEM_SYSCALL);
00411                         if( ret < 0 )  perror("munmap");
00412                         /* XXX How to get this chunk of address space back to malloc()? */
00413                 } else
00414 #endif
00415                 {
00416                         bu_free( mp->buf, "bu_mapped_file.buf[]" );
00417                 }
00418                 mp->buf = (genptr_t)NULL;               /* sanity */
00419                 bu_free( (genptr_t)mp->name, "bu_mapped_file.name" );
00420                 if( mp->appl )  bu_free( (genptr_t)mp->appl, "bu_mapped_file.appl" );
00421                 bu_free( (genptr_t)mp, "struct bu_mapped_file" );
00422         }
00423         bu_semaphore_release(BU_SEM_MAPPEDFILE);
00424 }
00425 
00426 /**
00427  *      B U _ O P E N _ M A P P E D _ F I L E _ W I T H _ P A T H
00428  *
00429  *  A wrapper for bu_open_mapped_file() which uses a search path
00430  *  to locate the file.
00431  *  The search path is specified as a normal C argv array,
00432  *  terminated by a null string pointer.
00433  *  If the file name begins with a slash ('/') the path is not used.
00434  */
00435 struct bu_mapped_file *
00436 bu_open_mapped_file_with_path(char *const *path, const char *name, const char *appl)
00437 
00438                                 /* file name */
00439                                 /* non-null only when app. will use 'apbuf' */
00440 {
00441         char    * const *pathp = path;
00442         struct bu_vls   str;
00443         struct bu_mapped_file   *ret;
00444 
00445         BU_ASSERT_PTR( name, !=, NULL );
00446         BU_ASSERT_PTR( pathp, !=, NULL );
00447 
00448         /* Do not resort to path for a rooted filename */
00449         if( name[0] == '/' )
00450                 return bu_open_mapped_file( name, appl );
00451 
00452         bu_vls_init(&str);
00453 
00454         /* Try each path prefix in sequence */
00455         for( ; *pathp != NULL; pathp++ )  {
00456                 bu_vls_strcpy( &str, *pathp );
00457                 bu_vls_putc( &str, '/' );
00458                 bu_vls_strcat( &str, name );
00459 
00460                 ret = bu_open_mapped_file( bu_vls_addr(&str), appl );
00461                 if( ret )  {
00462                         bu_vls_free( &str );
00463                         return ret;
00464                 }
00465         }
00466 
00467         /* Failure, none of the opens succeeded */
00468         bu_vls_free( &str );
00469         return (struct bu_mapped_file *)NULL;
00470 }
00471 
00472 /*@}*/
00473 /*
00474  * Local Variables:
00475  * mode: C
00476  * tab-width: 8
00477  * c-basic-offset: 4
00478  * indent-tabs-mode: t
00479  * End:
00480  * ex: shiftwidth=4 tabstop=8
00481  */

Generated on Mon Sep 18 01:24:48 2006 for BRL-CAD by  doxygen 1.4.6