m_mem.cc

00001 /* 
00002    EyeDB Object Database Management System
00003    Copyright (C) 1994-2008 SYSRA
00004    
00005    EyeDB is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009    
00010    EyeDB is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014    
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with this library; if not, write to the Free Software
00017    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA 
00018 */
00019 
00020 /*
00021   Author: Eric Viara <viara@sysra.com>
00022 */
00023 
00024 #include <eyedbconfig.h>
00025 
00026 #include <sys/types.h>
00027 #include <sys/time.h>
00028 #include <stdlib.h>
00029 #include <string.h>
00030 #include <errno.h>
00031 #include <unistd.h>
00032 #include <pthread.h>
00033 #include <sys/mman.h>
00034 #include <stdarg.h>
00035 
00036 #include <eyedblib/m_mem.h>
00037 #include <eyedblib/log.h>
00038 #include <eyedblib/iassert.h>
00039 #include <lib/m_mem_p.h>
00040 
00041 struct m_Map {
00042   caddr_t *p;
00043   caddr_t addr;
00044   size_t size;
00045   int prot, flags, fildes;
00046   off_t off;
00047   u_int ref;
00048   char locked;
00049   void (*gtrig)(void *client_data);
00050   void *client_data;
00051   char *file;
00052   int startns, endns;
00053   m_Map *prev, *next;
00054 };
00055 
00056 static pthread_mutex_t m_mp;
00057 static int init_done;
00058 static m_Map *m_head;
00059 static size_t m_tmsize, m_maxsize;
00060 
00061 static const u_int ONE_K = 1024;
00062 static const size_t m_min_maxsize = ONE_K * ONE_K * ONE_K; // 1 Gb
00063 static u_int  m_ref = 0x100;
00064 
00065 static int m_getunlocked()
00066 {
00067   int cnt = 0;
00068   m_Map *m;
00069 
00070   m = m_head;
00071 
00072   while (m) {
00073     if (!m->locked)
00074       cnt++;
00075     m = m->next;
00076   }
00077 
00078   return cnt;
00079 }
00080 
00081 /*#define TRACE*/
00082 
00083 static int m_garbage(m_Map *rm)
00084 {
00085   m_Map *km = rm;
00086 
00087   pthread_mutex_lock(&m_mp);
00088 
00089   if (!rm) {
00090     u_int ref = 0xffffffff;
00091     m_Map *m;
00092     m = m_head;
00093 
00094     while(m) {
00095       if (m->ref < ref && !m->locked) {
00096         ref = m->ref;
00097         rm = m;
00098       }
00099       m = m->next;
00100     }
00101 
00102   }
00103 
00104   if (rm) {
00105     m_tmsize -= rm->size;
00106 
00107     if (rm->prev)
00108       rm->prev->next = rm->next;
00109     if (rm->next)
00110       rm->next->prev = rm->prev;
00111 
00112     if (rm == m_head)
00113       m_head = rm->next;
00114 
00115     pthread_mutex_unlock(&m_mp);
00116 
00117     if (!km && rm->gtrig)
00118       rm->gtrig(rm->client_data);
00119 
00120     IDB_LOG(IDB_LOG_MMAP_DETAIL,
00121             ("m_garbage: unmapping %p for size %u\n", *rm->p, rm->size));
00122 
00123     if (munmap(*rm->p, rm->size)) {
00124       utlog("munmap(%p, %d)\n", *rm->p, rm->size);
00125       //perror("munmap")
00126       abort();
00127     }
00128 
00129     *rm->p = 0;
00130 
00131     // 4/10/05
00132     free(rm->file);
00133     free(rm);
00134 
00135     /*
00136     if (km) { //free only when m_munmap()
00137       free(rm->file);
00138       free(rm);
00139     }
00140     */
00141 
00142     return 0;
00143   }
00144 
00145   pthread_mutex_unlock(&m_mp);
00146   IDB_LOG(IDB_LOG_MMAP_DETAIL, ("m_garbage failed!\n"));
00147 
00148   return 1;
00149 }
00150 
00151 static void m_insert(m_Map *m)
00152 {
00153   pthread_mutex_lock(&m_mp);
00154 
00155   m->next = m_head;
00156   m->prev = 0;
00157 
00158   if (m_head)
00159     m_head->prev = m;
00160   
00161   m->ref = ++m_ref;
00162   m_head = m;
00163   
00164   m_tmsize += m->size;
00165 
00166   pthread_mutex_unlock(&m_mp);
00167 }
00168 
00169 /* guess that this routine is called soon enough in the program
00170    so that the thought stack address (&r) is correct;
00171    in the eyedbsm package, this routine is called from se_init() */
00172 
00173 /*#define USE_MADVISE*/
00174 
00175 #ifdef USE_MADVISE
00176 static int must_madvise;
00177 #endif
00178 
00179 void m_init(void)
00180 {
00181   pthread_mutexattr_t mattr;
00182 
00183   if (init_done)
00184     return;
00185 
00186   pthread_mutexattr_init(&mattr);
00187 
00188 #ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED
00189   pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_PRIVATE);
00190 #endif
00191 
00192   pthread_mutex_init(&m_mp, &mattr);
00193 
00194 #ifdef USE_MADVISE
00195   must_madvise = getenv("EYEDB_MADVISE") ? 1 : 0;
00196 #endif
00197 
00198   init_done = 1;
00199 }
00200 
00201 static void reduce_memory(size_t size)
00202 {
00203   if (!m_maxsize)
00204     return;
00205 
00206   bool must_reduced = false;
00207   while (size + m_tmsize > m_maxsize) {
00208     must_reduced = true;
00209     IDB_LOG(IDB_LOG_MMAP_DETAIL,
00210             ("must reduced total size: %llu\n", m_tmsize));
00211     if (m_garbage(0))
00212       break;
00213   }
00214   if (must_reduced)
00215     IDB_LOG(IDB_LOG_MMAP_DETAIL, ("reduced: %llu\n", m_tmsize));
00216 }
00217 
00218 void m_gtrig_set(m_Map *m, void (*gtrig)(void *client_data), void *client_data)
00219 {
00220   m->gtrig = gtrig;
00221   m->client_data = client_data;
00222 }
00223 
00224 m_Map *m_mmap(caddr_t addr, size_t size, int prot, int flags,
00225               int fildes, off_t off, caddr_t *p, const char *file,
00226               off_t startns, off_t endns)
00227 {
00228   if (!init_done)
00229     m_init();
00230 
00231   IDB_LOG(IDB_LOG_MMAP_DETAIL,
00232           ("mapping attempt file=\"%s\" size=%lu prot=%p "
00233            "flags=%p fd=%d offset=%u",
00234            file, size, prot, flags, fildes, off));
00235 
00236   errno = 0;
00237 
00238   if (endns > 0)
00239     IDB_LOG_X(IDB_LOG_MMAP_DETAIL, (" startns=%d endns=%d\n", startns, endns));
00240   else
00241     IDB_LOG_X(IDB_LOG_MMAP_DETAIL, ("\n"));
00242 
00243   reduce_memory(size);
00244 
00245   /* disconnected the 25/07/01 */
00246 #if 0
00247   /* re-added the 8/12/99 */
00248   while (!is_enough_place(size))
00249     if (m_garbage(0))
00250       break; /* was: return 0; */
00251   /* ... */
00252 #endif
00253 
00254   for (unsigned int ntries = 0;; ntries++) {
00255     if ((*p = (caddr_t)mmap(addr, size, prot, flags, fildes, off)) != MAP_FAILED) {
00256       m_Map *m = (m_Map *)calloc(sizeof(m_Map), 1);
00257           
00258       assert(m);
00259       m->p = p;
00260       m->addr = addr;
00261       m->size = size;
00262       m->prot = prot;
00263       m->flags = flags;
00264       m->fildes = fildes;
00265       m->off = off;
00266       m->file = strdup(file);
00267       m->startns = startns;
00268       m->endns = endns;
00269 
00270       m_insert(m);
00271 
00272       IDB_LOG(IDB_LOG_MMAP,
00273               ("segment mapped file=\"%s\" segment=[%p, %p[ "
00274                "size=%lu prot=%p flags=%p fd=%d offset=%u",
00275                file, *p, (*p)+size, size, prot, flags, fildes, off));
00276 
00277       if (endns > 0)
00278         IDB_LOG_X(IDB_LOG_MMAP,
00279                   (" startns=%d endns=%d\n", startns, endns));
00280       else
00281         IDB_LOG_X(IDB_LOG_MMAP, ("\n"));
00282 
00283 #ifdef USE_MADVISE
00284       if (must_madvise && madvise(*p, size, MADV_RANDOM)) {
00285         IDB_LOG(IDB_LOG_MMAP, 
00286                 ("madvise failed for %p %d\n", *p, size));
00287         perror("madvise");
00288       }
00289 #endif
00290       return m;
00291     }
00292 
00293     IDB_LOG(IDB_LOG_MMAP_DETAIL,
00294             ("mapping failed file=\"%s\" size=%lu prot=%p flags=%p "
00295              "fd=%d offset=%u attempt=#%d\n",
00296              file, size, prot, flags, fildes, off, ntries));
00297 
00298     //perror("m_mem.c: mmap");
00299 
00300     if (endns > 0)
00301       IDB_LOG_X(IDB_LOG_MMAP_DETAIL,
00302                 (" startns=%d endns=%d\n", startns, endns));
00303     else
00304       IDB_LOG_X(IDB_LOG_MMAP, ("\n"));
00305     
00306     if (m_garbage(0))
00307       break;
00308   }
00309 
00310   *p = 0;
00311 
00312   IDB_LOG((eyedblib::LogMask)~0,
00313           ("mapping *definitively* failed file=\"%s\""
00314            "size=%lu prot=%p flags=%p fd=%d offset=%u startns=%d endns=%d\n",
00315            file, *p, (*p)+size, size, prot, flags, fildes, off, startns, endns));
00316 
00317   /*perror("mmap");
00318     printf("m_mmap(size=%lu) failed after %d attemps\n", size, ntries);*/
00319 
00320   /*abort(); */ /* added the 23/08/99 */
00321   return 0;
00322 }
00323 
00324 void m_lock(m_Map *m)
00325 {
00326   m->locked = 1;
00327 }
00328 
00329 void m_unlock(m_Map *m)
00330 {
00331   m->locked = 0;
00332 }
00333 
00334 void m_access(m_Map *m)
00335 {
00336   m->ref = ++m_ref;
00337 }
00338 
00339 int m_munmap(m_Map *map, caddr_t addr, size_t size)
00340 {
00341   IDB_LOG(IDB_LOG_MMAP,
00342           ("segment unmapped file=\"%s\" segment=[%p, %p[ "
00343            "size=%lu prot=%p flags=%p fd=%d offset=%u "
00344            "startns=%d endns=%d\n",
00345            map->file, *map->p, (*map->p)+map->size, map->size, map->prot,
00346            map->flags, map->fildes, map->off,
00347            map->startns, map->endns));
00348 
00349   //  assert(map->size == size);
00350 
00351   if (map->size != size) {
00352     IDB_LOG(IDB_LOG_MMAP, 
00353             ("warning unmap size differ : %lu %lu",
00354              map->size, size));
00355   }
00356 
00357 
00358   return m_garbage(map);
00359 }
00360 
00361 void *m_malloc(size_t size)
00362 {
00363   void *p;
00364 
00365   if (!init_done)
00366     m_init();
00367 
00368   if (!size)
00369     size = 4;
00370 
00371   while (!(p = malloc(size)))
00372     if (errno != ENOMEM || m_garbage(0)) /* really ? */
00373       m_abort_msg("malloc(%lu) failed [errno %d]\n", size, errno);
00374 
00375   return p;
00376 }
00377 
00378 void *m_calloc(size_t nelem, size_t elsize)
00379 {
00380   void *p;
00381 
00382   if (!init_done)
00383     m_init();
00384 
00385   while (!(p = calloc(nelem, elsize)))
00386     if (errno != ENOMEM || m_garbage(0))
00387       m_abort_msg("calloc(%d, %lu) failed [errno %d]\n", nelem, elsize, 
00388                   errno);
00389            
00390   return p;
00391 }
00392 
00393 void *m_realloc(void *ptr, size_t size)
00394 {
00395   void *p;
00396 
00397   if (!init_done)
00398     m_init();
00399 
00400   while (!(p = realloc(ptr, size)))
00401     if (errno != ENOMEM || m_garbage(0))
00402       m_abort_msg("realloc(%p, %lu) failed [errno %d]\n", ptr, size,
00403                   errno);
00404            
00405   return p;
00406 }
00407 
00408 void m_free(void *ptr)
00409 {
00410   free(ptr);
00411 }
00412 
00413 void m_abort_msg(const char *fmt, ...)
00414 {
00415   va_list ap;
00416   va_start(ap, fmt);
00417   m_maptrace(std::cerr);
00418   vfprintf(stderr, fmt, ap);
00419   m_abort();
00420   va_end(ap);
00421 }
00422 
00423 void m_abort()
00424 {
00425   m_mmaps_garbage();
00426   abort();
00427 }
00428 
00429 void m_mmaps_garbage()
00430 {
00431   m_Map *m = m_head, *next;
00432 
00433   while (m) {
00434     next = m->next;
00435     m_garbage(m);
00436     m = next;
00437   }
00438 }
00439  
00440 void m_maptrace(std::ostream &os)
00441 {
00442   m_Map *m = m_head;
00443 
00444   os << "----------------------- eyedb memory map manager ---------------------\n";
00445 
00446   while (m) {
00447     os << " addr " << *m->p << " size " << m->size << "[" <<
00448       (m->size/ONE_K) << " kb\n";
00449     m = m->next;
00450   }
00451 
00452   os << " total memory used: " << (m_tmsize/ONE_K) << " kb\n";
00453   if (m_maxsize)
00454     os << " maximum memory size: " << (m_maxsize/ONE_K) << " kb\n";
00455     
00456 }
00457 
00458 size_t m_get_totalsize()
00459 {
00460   return m_tmsize;
00461 }
00462 
00463 void m_set_maxsize(size_t maxsize)
00464 {
00465   m_maxsize = maxsize;
00466   if (m_maxsize != 0 && m_maxsize < m_min_maxsize)
00467     m_maxsize = m_min_maxsize;
00468 
00469   reduce_memory(0);
00470 }
00471 
00472 size_t m_get_maxsize()
00473 {
00474   return m_maxsize;
00475 }

Generated on Mon Dec 22 18:15:58 2008 for eyedb by  doxygen 1.5.3