OSDN Git Service

* mmap.cc: Restructure. Add, remove and rewrite comments throughout
authorcorinna <corinna>
Thu, 4 Sep 2003 10:27:51 +0000 (10:27 +0000)
committercorinna <corinna>
Thu, 4 Sep 2003 10:27:51 +0000 (10:27 +0000)
for better readability.  Change function names for better
understanding.
(MAP_SET): Accomodate name change from map_map_ to page_map_.
(MAP_CLR): Ditto.
(MAP_ISSET): Ditto.
(mmap_record::page_map_): Rename from page_map_.
(mmap_record::get_map): Remove.
(mmap_record::alloc_page_map): Rename from alloc_map. Return bool
indicating success of cygheap memory allocation.
(mmap_record::free_page_map): Rename from free_map.
(mmap_record::fixup_page_map): Rename from fixup_map.
(mmap_record::find_unused_pages): Rename from find_empty.
(mmap_record::map_pages): Rename from map_map.
(mmap_record::unmap_pages): Rename from unmap_map.
(class list): Make all class members private.
(list::list): Remove.
(list::~list): Remove.
(list::get_fd): New attribute reader.
(list::get_hash): Ditto.
(list::get_record): Ditto.
(list::add_record): Manage all allocation for mmap_records.  Check
for failed memory allocation and return NULL if so.
(list::set): New function.
(list::del_record): Rename from erase. Return true if last mmap_record
has been deleted, false otherwise. Check for legal incoming index
value.
(list::erase): Remove erase/0.
(list::search_record): Rename from match.
(map::map): Remove.
(map::~map): Remove.
(map::add_list): Manage all allocation for lists.  Check for failed
memory allocation and return NULL if so.
(map::get_list): New method.
(map::del_list): Rename from erase. Check for legal incoming index
value.
(mmap64): Check for failed mmap_record memory allocation.  Return
with MAP_FAILED and errno set to ENOMEM if so.
(munmap): Rearrange loop using new list and mmap_record accessor
functions.  Rename loop index variables for better understanding.
Check if list can be deleted after last mmap_record in it has been
deleted.
(msync): Rearrange loop using new list and mmap_record accessor
functions.  Rename loop index variables for better understanding.
(fixup_mmaps_after_fork): Ditto.

winsup/cygwin/ChangeLog
winsup/cygwin/mmap.cc

index 3a39394..332701f 100644 (file)
@@ -1,3 +1,51 @@
+2003-09-04  Corinna Vinschen  <corinna@vinschen.de>
+
+       * mmap.cc: Restructure. Add, remove and rewrite comments throughout
+       for better readability.  Change function names for better
+       understanding.
+       (MAP_SET): Accomodate name change from map_map_ to page_map_.
+       (MAP_CLR): Ditto.
+       (MAP_ISSET): Ditto.
+       (mmap_record::page_map_): Rename from page_map_.
+       (mmap_record::get_map): Remove.
+       (mmap_record::alloc_page_map): Rename from alloc_map. Return bool
+       indicating success of cygheap memory allocation.
+       (mmap_record::free_page_map): Rename from free_map.
+       (mmap_record::fixup_page_map): Rename from fixup_map.
+       (mmap_record::find_unused_pages): Rename from find_empty.
+       (mmap_record::map_pages): Rename from map_map.
+       (mmap_record::unmap_pages): Rename from unmap_map.
+       (class list): Make all class members private.
+       (list::list): Remove.
+       (list::~list): Remove.
+       (list::get_fd): New attribute reader.
+       (list::get_hash): Ditto.
+       (list::get_record): Ditto.
+       (list::add_record): Manage all allocation for mmap_records.  Check
+       for failed memory allocation and return NULL if so.
+       (list::set): New function.
+       (list::del_record): Rename from erase. Return true if last mmap_record
+       has been deleted, false otherwise. Check for legal incoming index
+       value.
+       (list::erase): Remove erase/0.
+       (list::search_record): Rename from match.
+       (map::map): Remove.
+       (map::~map): Remove.
+       (map::add_list): Manage all allocation for lists.  Check for failed
+       memory allocation and return NULL if so.
+       (map::get_list): New method.
+       (map::del_list): Rename from erase. Check for legal incoming index
+       value.
+       (mmap64): Check for failed mmap_record memory allocation.  Return
+       with MAP_FAILED and errno set to ENOMEM if so.
+       (munmap): Rearrange loop using new list and mmap_record accessor
+       functions.  Rename loop index variables for better understanding.
+       Check if list can be deleted after last mmap_record in it has been
+       deleted.
+       (msync): Rearrange loop using new list and mmap_record accessor
+       functions.  Rename loop index variables for better understanding.
+       (fixup_mmaps_after_fork): Ditto.
+
 2003-09-03  Christopher Faylor  <cgf@redhat.com>
 
        * cxx.cc (new): Fix formatting.  Just return result of ccalloc rather
index f38568f..af7d327 100644 (file)
@@ -27,15 +27,29 @@ details. */
 #define PGBITS         (sizeof (DWORD)*8)
 #define MAPSIZE(pages) howmany ((pages), PGBITS)
 
-#define MAP_SET(n)     (map_map_[(n)/PGBITS] |= (1L << ((n) % PGBITS)))
-#define MAP_CLR(n)     (map_map_[(n)/PGBITS] &= ~(1L << ((n) % PGBITS)))
-#define MAP_ISSET(n)   (map_map_[(n)/PGBITS] & (1L << ((n) % PGBITS)))
+#define MAP_SET(n)     (page_map_[(n)/PGBITS] |= (1L << ((n) % PGBITS)))
+#define MAP_CLR(n)     (page_map_[(n)/PGBITS] &= ~(1L << ((n) % PGBITS)))
+#define MAP_ISSET(n)   (page_map_[(n)/PGBITS] & (1L << ((n) % PGBITS)))
 
-/*
- * Simple class used to keep a record of all current
- * mmap areas in a process. Needed so that
- * they can be duplicated after a fork().
- */
+/* Used for accessing the page file (anonymous mmaps). */
+static fhandler_disk_file fh_paging_file;
+
+/* Class structure used to keep a record of all current mmap areas
+   in a process.  Needed for bookkeeping all mmaps in a process and
+   for duplicating all mmaps after fork() since mmaps are not propagated
+   to child processes by Windows.  All information must be duplicated
+   by hand, see fixup_mmaps_after_fork().
+  
+   The class structure:
+  
+   One member of class map per process, global variable mmapped_areas.
+   Contains a dynamic class list array.  Each list entry represents all
+   mapping to a file, keyed by file descriptor and file name hash.
+   Each list entry contains a dynamic class mmap_record array.  Each
+   mmap_record represents exactly one mapping.  For each mapping, there's
+   an additional so called `page_map'.  It's an array of bits, one bit
+   per mapped memory page.  The bit is set if the page is accessible,
+   unset otherwise. */
 
 class mmap_record
 {
@@ -47,7 +61,7 @@ class mmap_record
     _off64_t offset_;
     DWORD size_to_map_;
     caddr_t base_address_;
-    DWORD *map_map_;
+    DWORD *page_map_;
 
   public:
     mmap_record (int fd, HANDLE h, DWORD ac, _off64_t o, DWORD s, caddr_t b) :
@@ -58,15 +72,12 @@ class mmap_record
        offset_ (o),
        size_to_map_ (s),
        base_address_ (b),
-       map_map_ (NULL)
+       page_map_ (NULL)
       {
        if (fd >= 0 && !cygheap->fdtab.not_open (fd))
          devtype_ = cygheap->fdtab[fd]->get_device ();
       }
 
-    /* Default Copy constructor/operator=/destructor are ok */
-
-    /* Simple accessors */
     int get_fd () const { return fdesc_; }
     HANDLE get_handle () const { return mapping_handle_; }
     DWORD get_device () const { return devtype_; }
@@ -74,22 +85,61 @@ class mmap_record
     DWORD get_offset () const { return offset_; }
     DWORD get_size () const { return size_to_map_; }
     caddr_t get_address () const { return base_address_; }
-    DWORD *get_map () const { return map_map_; }
-    void alloc_map (_off64_t off, DWORD len);
-    void free_map () { if (map_map_) cfree (map_map_); }
-
-    DWORD find_empty (DWORD pages);
-    _off64_t map_map (_off64_t off, DWORD len);
-    BOOL unmap_map (caddr_t addr, DWORD len);
-    void fixup_map (void);
+
+    bool alloc_page_map (_off64_t off, DWORD len);
+    void free_page_map () { if (page_map_) cfree (page_map_); }
+    void fixup_page_map (void);
+
+    DWORD find_unused_pages (DWORD pages);
+    _off64_t map_pages (_off64_t off, DWORD len);
+    BOOL unmap_pages (caddr_t addr, DWORD len);
     int access (caddr_t address);
 
     fhandler_base *alloc_fh ();
     void free_fh (fhandler_base *fh);
 };
 
+class list
+{
+  private:
+    mmap_record *recs;
+    int nrecs, maxrecs;
+    int fd;
+    DWORD hash;
+
+  public:
+    int get_fd () const { return fd; }
+    DWORD get_hash () const { return hash; }
+    mmap_record *get_record (int i) { return i >= nrecs ? NULL : recs + i; }
+
+    void set (int nfd);
+    mmap_record *add_record (mmap_record r, _off64_t off, DWORD len);
+    bool del_record (int i);
+    void free_recs () { if (recs) cfree (recs); }
+    mmap_record *search_record (_off64_t off, DWORD len);
+    long search_record (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len,
+               long start);
+};
+
+class map
+{
+  private:
+    list *lists;
+    int nlists, maxlists;
+
+  public:
+    list *get_list (int i) { return i >= nlists ? NULL : lists + i; }
+    list *get_list_by_fd (int fd);
+    list *add_list (int fd);
+    void del_list (int i);
+};
+
+/* This is the global map structure pointer.  It's allocated once on the
+   first call to mmap64(). */
+static map *mmapped_areas;
+
 DWORD
-mmap_record::find_empty (DWORD pages)
+mmap_record::find_unused_pages (DWORD pages)
 {
   DWORD mapped_pages = PAGE_CNT (size_to_map_);
   DWORD start;
@@ -109,12 +159,15 @@ mmap_record::find_empty (DWORD pages)
   return (DWORD)-1;
 }
 
-void
-mmap_record::alloc_map (_off64_t off, DWORD len)
+bool
+mmap_record::alloc_page_map (_off64_t off, DWORD len)
 {
   /* Allocate one bit per page */
-  map_map_ = (DWORD *) ccalloc (HEAP_MMAP, MAPSIZE (PAGE_CNT (size_to_map_)),
-                               sizeof (DWORD));
+  if (!(page_map_ = (DWORD *) ccalloc (HEAP_MMAP,
+                                      MAPSIZE (PAGE_CNT (size_to_map_)),
+                                      sizeof (DWORD))))
+    return false;
+
   if (wincap.virtual_protect_works_on_shared_pages ())
     {
       DWORD old_prot;
@@ -135,15 +188,16 @@ mmap_record::alloc_map (_off64_t off, DWORD len)
       while (len-- > 0)
        MAP_SET (off + len);
     }
+  return true;
 }
 
 _off64_t
-mmap_record::map_map (_off64_t off, DWORD len)
+mmap_record::map_pages (_off64_t off, DWORD len)
 {
   /* Used ONLY if this mapping matches into the chunk of another already
      performed mapping in a special case of MAP_ANON|MAP_PRIVATE.
 
-     Otherwise it's job is now done by alloc_map(). */
+     Otherwise it's job is now done by alloc_page_map(). */
   DWORD prot, old_prot;
   switch (access_mode_)
     {
@@ -158,10 +212,10 @@ mmap_record::map_map (_off64_t off, DWORD len)
       break;
     }
 
-  debug_printf ("map_map (fd=%d, off=%D, len=%u)", fdesc_, off, len);
+  debug_printf ("map_pages (fd=%d, off=%D, len=%u)", fdesc_, off, len);
   len = PAGE_CNT (len);
 
-  if ((off = find_empty (len)) == (DWORD)-1)
+  if ((off = find_unused_pages (len)) == (DWORD)-1)
     return 0L;
   if (wincap.virtual_protect_works_on_shared_pages ()
       && !VirtualProtect (base_address_ + off * getpagesize (),
@@ -177,7 +231,7 @@ mmap_record::map_map (_off64_t off, DWORD len)
 }
 
 BOOL
-mmap_record::unmap_map (caddr_t addr, DWORD len)
+mmap_record::unmap_pages (caddr_t addr, DWORD len)
 {
   DWORD old_prot;
   DWORD off = addr - base_address_;
@@ -186,20 +240,20 @@ mmap_record::unmap_map (caddr_t addr, DWORD len)
   if (wincap.virtual_protect_works_on_shared_pages ()
       && !VirtualProtect (base_address_ + off * getpagesize (),
                          len * getpagesize (), PAGE_NOACCESS, &old_prot))
-    syscall_printf ("-1 = unmap_map (): %E");
+    syscall_printf ("-1 = unmap_pages (): %E");
 
   for (; len-- > 0; ++off)
     MAP_CLR (off);
   /* Return TRUE if all pages are free'd which may result in unmapping
      the whole chunk. */
   for (len = MAPSIZE (PAGE_CNT (size_to_map_)); len > 0; )
-    if (map_map_[--len])
+    if (page_map_[--len])
       return FALSE;
   return TRUE;
 }
 
 void
-mmap_record::fixup_map ()
+mmap_record::fixup_page_map ()
 {
   if (!wincap.virtual_protect_works_on_shared_pages ())
     return;
@@ -232,8 +286,6 @@ mmap_record::access (caddr_t address)
   return MAP_ISSET (off);
 }
 
-static fhandler_disk_file fh_paging_file;
-
 fhandler_base *
 mmap_record::alloc_fh ()
 {
@@ -258,57 +310,38 @@ mmap_record::free_fh (fhandler_base *fh)
     cfree (fh);
 }
 
-class list {
-public:
-  mmap_record *recs;
-  int nrecs, maxrecs;
-  int fd;
-  DWORD hash;
-  list ();
-  ~list ();
-  mmap_record *add_record (mmap_record r, _off64_t off, DWORD len);
-  void erase (int i);
-  void erase ();
-  mmap_record *match (_off64_t off, DWORD len);
-  long match (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len,
-             long start);
-};
-
-list::list ()
-: nrecs (0), maxrecs (10), fd (0), hash (0)
-{
-  recs = (mmap_record *) cmalloc (HEAP_MMAP, 10 * sizeof (mmap_record));
-}
-
-list::~list ()
-{
-  for (mmap_record *rec = recs; nrecs-- > 0; ++rec)
-    rec->free_map ();
-  cfree (recs);
-}
-
 mmap_record *
 list::add_record (mmap_record r, _off64_t off, DWORD len)
 {
   if (nrecs == maxrecs)
     {
+      mmap_record *new_recs;
+      if (maxrecs == 0)
+        new_recs = (mmap_record *)
+                       cmalloc (HEAP_MMAP, 5 * sizeof (mmap_record));
+      else
+       new_recs = (mmap_record *)
+                       crealloc (recs, (maxrecs + 5) * sizeof (mmap_record));
+      if (!new_recs)
+        return NULL;
       maxrecs += 5;
-      recs = (mmap_record *) crealloc (recs, maxrecs * sizeof (mmap_record));
+      recs = new_recs;
     }
   recs[nrecs] = r;
-  recs[nrecs].alloc_map (off, len);
+  if (!recs[nrecs].alloc_page_map (off, len))
+    return NULL;
   return recs + nrecs++;
 }
 
 /* Used in mmap() */
 mmap_record *
-list::match (_off64_t off, DWORD len)
+list::search_record (_off64_t off, DWORD len)
 {
   if (fd == -1 && !off)
     {
       len = PAGE_CNT (len);
       for (int i = 0; i < nrecs; ++i)
-       if (recs[i].find_empty (len) != (DWORD)-1)
+       if (recs[i].find_unused_pages (len) != (DWORD)-1)
          return recs + i;
     }
   else
@@ -324,7 +357,7 @@ list::match (_off64_t off, DWORD len)
 
 /* Used in munmap() */
 long
-list::match (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len,
+list::search_record (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len,
             _off_t start)
 {
   caddr_t low, high;
@@ -346,41 +379,27 @@ list::match (caddr_t addr, DWORD len, caddr_t &m_addr, DWORD &m_len,
 }
 
 void
-list::erase (int i)
-{
-  recs[i].free_map ();
-  for (; i < nrecs-1; i++)
-    recs[i] = recs[i+1];
-  nrecs--;
-}
-
-void
-list::erase ()
-{
-  erase (nrecs-1);
-}
-
-class map {
-public:
-  list **lists;
-  int nlists, maxlists;
-  map ();
-  ~map ();
-  list *get_list_by_fd (int fd);
-  list *add_list (list *l, int fd);
-  void erase (int i);
-};
-
-map::map ()
+list::set (int nfd)
 {
-  lists = (list **) cmalloc (HEAP_MMAP, 10 * sizeof (list *));
-  nlists = 0;
-  maxlists = 10;
+  if ((fd = nfd) != -1)
+    hash = cygheap->fdtab[fd]->get_namehash ();
+  nrecs = maxrecs = 0;
+  recs = NULL;
 }
 
-map::~map ()
+bool
+list::del_record (int i)
 {
-  cfree (lists);
+  if (i < nrecs)
+    {
+      recs[i].free_page_map ();
+      for (; i < nrecs - 1; i++)
+       recs[i] = recs[i + 1];
+      nrecs--;
+    }
+  /* Return true if the list is empty which allows the caller to remove
+     this list from the list array. */
+  return !nrecs;
 }
 
 list *
@@ -388,50 +407,47 @@ map::get_list_by_fd (int fd)
 {
   int i;
   for (i=0; i<nlists; i++)
-#if 0 /* The fd isn't sufficient since it could already be another file. */
-    if (lists[i]->fd == fd
-#else /* so we use the name hash value to identify the file unless
-        it's not an anonymous mapping. */
-    if ((fd == -1 && lists[i]->fd == -1)
-       || (fd != -1 && lists[i]->hash == cygheap->fdtab[fd]->get_namehash ()))
-#endif
-      return lists[i];
+    /* The fd isn't sufficient since it could already be the fd of another
+       file.  So we use the name hash value to identify the file unless
+       it's an anonymous mapping in which case the fd (-1) is sufficient. */
+    if ((fd == -1 && lists[i].get_fd () == -1)
+       || (fd != -1
+           && lists[i].get_hash () == cygheap->fdtab[fd]->get_namehash ()))
+      return lists + i;
   return 0;
 }
 
 list *
-map::add_list (list *l, int fd)
+map::add_list (int fd)
 {
-  l->fd = fd;
-  if (fd != -1)
-    l->hash = cygheap->fdtab[fd]->get_namehash ();
   if (nlists == maxlists)
     {
+      list *new_lists;
+      if (maxlists == 0)
+        new_lists = (list *) cmalloc (HEAP_MMAP, 5 * sizeof (list));
+      else
+       new_lists = (list *) crealloc (lists, (maxlists + 5) * sizeof (list));
+      if (!new_lists)
+        return NULL;
       maxlists += 5;
-      lists = (list **) crealloc (lists, maxlists * sizeof (list *));
+      lists = new_lists;
     }
-  lists[nlists++] = l;
-  return lists[nlists-1];
+  lists[nlists].set (fd);
+  return lists + nlists++;
 }
 
 void
-map::erase (int i)
+map::del_list (int i)
 {
-  for (; i < nlists-1; i++)
-    lists[i] = lists[i+1];
-  nlists--;
+  if (i < nlists)
+    {
+      lists[i].free_recs ();
+      for (; i < nlists - 1; i++)
+        lists[i] = lists[i + 1];
+      nlists--;
+    }
 }
 
-/*
- * Code to keep a record of all mmap'ed areas in a process.
- * Needed to duplicate tham in a child of fork().
- * mmap_record classes are kept in an STL list in an STL map, keyed
- * by file descriptor. This is *NOT* duplicated across a fork(), it
- * needs to be specially handled by the fork code.
- */
-
-static map *mmapped_areas;
-
 extern "C" caddr_t
 mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, _off64_t off)
 {
@@ -463,7 +479,7 @@ mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, _off64_t off)
   if (mmapped_areas == NULL)
     {
       /* First mmap call, create STL map */
-      mmapped_areas = new map;
+      mmapped_areas = (map *) ccalloc (HEAP_MMAP, 1, sizeof (map));
       if (mmapped_areas == NULL)
        {
          set_errno (ENOMEM);
@@ -530,9 +546,9 @@ mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, _off64_t off)
   if (map_list && fd == -1 && off == 0 && !(flags & MAP_FIXED))
     {
       mmap_record *rec;
-      if ((rec = map_list->match (off, len)) != NULL)
+      if ((rec = map_list->search_record (off, len)) != NULL)
        {
-         if ((off = rec->map_map (off, len)) == (_off64_t)-1)
+         if ((off = rec->map_pages (off, len)) == (_off64_t)-1)
            {
              syscall_printf ("-1 = mmap()");
              ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK|WRITE_LOCK, "mmap");
@@ -574,9 +590,8 @@ mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, _off64_t off)
       return MAP_FAILED;
     }
 
-  /* Now we should have a successfully mmapped area.
-     Need to save it so forked children can reproduce it.
-  */
+  /* At this point we should have a successfully mmapped area.
+     Now it's time for bookkeeping stuff. */
   if (fd == -1)
     gran_len = PAGE_CNT (gran_len) * getpagesize ();
   mmap_record mmap_rec (fd, h, access, gran_off, gran_len, base);
@@ -587,7 +602,7 @@ mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, _off64_t off)
   if (!map_list)
     {
       /* Create a new one */
-      map_list = new list;
+      map_list = mmapped_areas->add_list (fd);
       if (!map_list)
        {
          fh->munmap (h, base, gran_len);
@@ -596,11 +611,20 @@ mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, _off64_t off)
          ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
          return MAP_FAILED;
        }
-      map_list = mmapped_areas->add_list (map_list, fd);
   }
 
   /* Insert into the list */
-  mmap_record *rec = map_list->add_record (mmap_rec, off, len > gran_len ? gran_len : len);
+  mmap_record *rec = map_list->add_record (mmap_rec, off,
+                                          len > gran_len ? gran_len : len);
+  if (!rec)
+    {
+      fh->munmap (h, base, gran_len);
+      set_errno (ENOMEM);
+      syscall_printf ("-1 = mmap(): ENOMEM");
+      ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+      return MAP_FAILED;
+    }
+
   caddr_t ret = rec->get_address () + (off - gran_off);
   syscall_printf ("%x = mmap() succeeded", ret);
   ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
@@ -639,27 +663,33 @@ munmap (caddr_t addr, size_t len)
 
   /* Iterate through the map, unmap pages between addr and addr+len
      in all maps. */
-
-  for (int it = 0; it < mmapped_areas->nlists; ++it)
+  list *map_list;
+  for (int list_idx = 0;
+       (map_list = mmapped_areas->get_list (list_idx));
+       ++list_idx)
     {
-      list *map_list = mmapped_areas->lists[it];
-      if (map_list)
-       {
-         long li = -1;
-         caddr_t u_addr;
-         DWORD u_len;
+      long record_idx = -1;
+      caddr_t u_addr;
+      DWORD u_len;
 
-         while ((li = map_list->match(addr, len, u_addr, u_len, li)) >= 0)
+      while ((record_idx = map_list->search_record(addr, len, u_addr,
+                                                  u_len, record_idx)) >= 0)
+       {
+         mmap_record *rec = map_list->get_record (record_idx);
+         if (rec->unmap_pages (u_addr, u_len))
            {
-             mmap_record *rec = map_list->recs + li;
-             if (rec->unmap_map (u_addr, u_len))
-               {
-                 fhandler_base *fh = rec->alloc_fh ();
-                 fh->munmap (rec->get_handle (), addr, len);
-                 rec->free_fh (fh);
+             /* The whole record has been unmapped, so... */
+             fhandler_base *fh = rec->alloc_fh ();
+             fh->munmap (rec->get_handle (), addr, len);
+             rec->free_fh (fh);
 
-                 /* Delete the entry. */
-                 map_list->erase (li);
+             /* ...delete the record. */
+             if (map_list->del_record (record_idx--))
+               {
+                 /* Yay, the last record has been removed from the list,
+                    we can remove the list now, too. */
+                 mmapped_areas->del_list (list_idx--);
+                 break;
                }
            }
        }
@@ -700,33 +730,34 @@ msync (caddr_t addr, size_t len, int flags)
   /* Iterate through the map, looking for the mmapped area.
      Error if not found. */
 
-  for (int it = 0; it < mmapped_areas->nlists; ++it)
+  list *map_list;
+  for (int list_idx = 0;
+       (map_list = mmapped_areas->get_list (list_idx));
+       ++list_idx)
     {
-      list *map_list = mmapped_areas->lists[it];
-      if (map_list != 0)
+      mmap_record *rec;
+      for (int record_idx = 0;
+          (rec = map_list->get_record (record_idx));
+          ++record_idx)
        {
-         for (int li = 0; li < map_list->nrecs; ++li)
+         if (rec->access (addr))
            {
-             mmap_record *rec = map_list->recs + li;
-             if (rec->access (addr))
-               {
-                 /* Check whole area given by len. */
-                 for (DWORD i = getpagesize (); i < len; ++i)
-                   if (!rec->access (addr + i))
-                     goto invalid_address_range;
-                 fhandler_base *fh = rec->alloc_fh ();
-                 int ret = fh->msync (rec->get_handle (), addr, len, flags);
-                 rec->free_fh (fh);
-
-                 if (ret)
-                   syscall_printf ("%d = msync(): %E", ret);
-                 else
-                   syscall_printf ("0 = msync()");
-
-                 ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK,
-                                      "msync");
-                 return 0;
-               }
+             /* Check whole area given by len. */
+             for (DWORD i = getpagesize (); i < len; ++i)
+               if (!rec->access (addr + i))
+                 goto invalid_address_range;
+             fhandler_base *fh = rec->alloc_fh ();
+             int ret = fh->msync (rec->get_handle (), addr, len, flags);
+             rec->free_fh (fh);
+
+             if (ret)
+               syscall_printf ("%d = msync(): %E", ret);
+             else
+               syscall_printf ("0 = msync()");
+
+             ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK,
+                                  "msync");
+             return 0;
            }
        }
     }
@@ -975,94 +1006,94 @@ fixup_mmaps_after_fork (HANDLE parent)
     return 0;
 
   /* Iterate through the map */
-  for (int it = 0; it < mmapped_areas->nlists; ++it)
+  list *map_list;
+  for (int list_idx = 0;
+       (map_list = mmapped_areas->get_list (list_idx));
+       ++list_idx)
     {
-      list *map_list = mmapped_areas->lists[it];
-      if (map_list)
+      mmap_record *rec;
+      for (int record_idx = 0;
+          (rec = map_list->get_record (record_idx));
+          ++record_idx)
        {
-         int li;
-         for (li = 0; li < map_list->nrecs; ++li)
-           {
-             mmap_record *rec = map_list->recs + li;
-
-             debug_printf ("fd %d, h %x, access %x, offset %D, size %u, address %p",
-                 rec->get_fd (), rec->get_handle (), rec->get_access (),
-                 rec->get_offset (), rec->get_size (), rec->get_address ());
-
-             fhandler_base *fh = rec->alloc_fh ();
-             BOOL ret = fh->fixup_mmap_after_fork (rec->get_handle (),
-                                                   rec->get_access (),
-                                                   rec->get_offset (),
-                                                   rec->get_size (),
-                                                   rec->get_address ());
-             rec->free_fh (fh);
 
-             if (!ret)
-               return -1;
-             if (rec->get_access () == FILE_MAP_COPY)
-               {
-                 for (char *address = rec->get_address ();
-                      address < rec->get_address () + rec->get_size ();
-                      address += getpagesize ())
-                   if (rec->access (address)
-                       && !ReadProcessMemory (parent, address, address,
-                                              getpagesize (), NULL))
+         debug_printf ("fd %d, h %x, access %x, offset %D, size %u, address %p",
+             rec->get_fd (), rec->get_handle (), rec->get_access (),
+             rec->get_offset (), rec->get_size (), rec->get_address ());
+
+         fhandler_base *fh = rec->alloc_fh ();
+         BOOL ret = fh->fixup_mmap_after_fork (rec->get_handle (),
+                                               rec->get_access (),
+                                               rec->get_offset (),
+                                               rec->get_size (),
+                                               rec->get_address ());
+         rec->free_fh (fh);
+
+         if (!ret)
+           return -1;
+         if (rec->get_access () == FILE_MAP_COPY)
+           {
+             for (char *address = rec->get_address ();
+                  address < rec->get_address () + rec->get_size ();
+                  address += getpagesize ())
+               if (rec->access (address)
+                   && !ReadProcessMemory (parent, address, address,
+                                          getpagesize (), NULL))
+                 {
+                   DWORD old_prot;
+                   DWORD last_error = GetLastError ();
+
+                   if (last_error != ERROR_PARTIAL_COPY
+                       && last_error != ERROR_NOACCESS
+                       || !wincap.virtual_protect_works_on_shared_pages ())
                      {
-                       DWORD old_prot;
-                       DWORD last_error = GetLastError ();
-
-                       if (last_error != ERROR_PARTIAL_COPY
-                           && last_error != ERROR_NOACCESS
-                           || !wincap.virtual_protect_works_on_shared_pages ())
-                         {
-                           system_printf ("ReadProcessMemory failed for "
-                                          "MAP_PRIVATE address %p, %E",
-                                          rec->get_address ());
-                           return -1;
-                         }
-                       if (!VirtualProtectEx (parent,
-                                              address, getpagesize (),
-                                              PAGE_READONLY, &old_prot))
+                       system_printf ("ReadProcessMemory failed for "
+                                      "MAP_PRIVATE address %p, %E",
+                                      rec->get_address ());
+                       return -1;
+                     }
+                   if (!VirtualProtectEx (parent,
+                                          address, getpagesize (),
+                                          PAGE_READONLY, &old_prot))
+                     {
+                       system_printf ("VirtualProtectEx failed for "
+                                      "MAP_PRIVATE address %p, %E",
+                                      rec->get_address ());
+                       return -1;
+                     }
+                   else
+                     {
+                       BOOL ret;
+                       DWORD dummy_prot;
+
+                       ret = ReadProcessMemory (parent, address, address,
+                                                getpagesize (), NULL);
+                       if (!VirtualProtectEx(parent,
+                                             address, getpagesize (),
+                                             old_prot, &dummy_prot))
+                         system_printf ("WARNING: VirtualProtectEx to "
+                                        "return to previous state "
+                                        "in parent failed for "
+                                        "MAP_PRIVATE address %p, %E",
+                                        rec->get_address ());
+                       if (!VirtualProtect (address, getpagesize (),
+                                            old_prot, &dummy_prot))
+                         system_printf ("WARNING: VirtualProtect to copy "
+                                        "protection to child failed for"
+                                        "MAP_PRIVATE address %p, %E",
+                                        rec->get_address ());
+                       if (!ret)
                          {
-                           system_printf ("VirtualProtectEx failed for "
+                           system_printf ("ReadProcessMemory (2nd try) "
+                                          "failed for "
                                           "MAP_PRIVATE address %p, %E",
                                           rec->get_address ());
                            return -1;
                          }
-                       else
-                         {
-                           BOOL ret;
-                           DWORD dummy_prot;
-
-                           ret = ReadProcessMemory (parent, address, address,
-                                                    getpagesize (), NULL);
-                           if (!VirtualProtectEx(parent,
-                                                 address, getpagesize (),
-                                                 old_prot, &dummy_prot))
-                             system_printf ("WARNING: VirtualProtectEx to "
-                                            "return to previous state "
-                                            "in parent failed for "
-                                            "MAP_PRIVATE address %p, %E",
-                                            rec->get_address ());
-                           if (!VirtualProtect (address, getpagesize (),
-                                                old_prot, &dummy_prot))
-                             system_printf ("WARNING: VirtualProtect to copy "
-                                            "protection to child failed for"
-                                            "MAP_PRIVATE address %p, %E",
-                                            rec->get_address ());
-                           if (!ret)
-                             {
-                               system_printf ("ReadProcessMemory (2nd try) "
-                                              "failed for "
-                                              "MAP_PRIVATE address %p, %E",
-                                              rec->get_address ());
-                               return -1;
-                             }
-                         }
                      }
-               }
-             rec->fixup_map ();
+                 }
            }
+         rec->fixup_page_map ();
        }
     }