On Thu, Feb 05, 2004 at 09:27:51PM -0800, jw schultz wrote:
> As those of you who watch CVS will be aware Wayne has been
> making progress in reducing memory requirements of rsync.
> Much of what he has done has been the product of discussions
> between he and myself that started a month ago with John Van
> Essen.
> 
> Most recently Wayne has changed how the file_struct and its
> associated data are allocated, eliminating the string areas.
> Most of these changes have been small and relatively low
> impact although combining the allocation of the file_struct
> with the strings does impact the memory management of
> file_struct.
> 
> Attached is a patch that implements the next step.  It
> alters flist memory management and introduces a MM pool
> layer that reduces malloc overhead and allows destructors to
> actually release memory to the OS.
> 
> The patch adds a couple of new files so use patch -p1
> and rerun ./configure after patching.

Here is an update of the patch to cope with patch conflicts
re CVS HEAD.

diff -rupNP --exclude-from cvs/.ignore cvs/Makefile.in pool2/Makefile.in
--- cvs/Makefile.in     Tue Feb  3 16:34:24 2004
+++ pool2/Makefile.in   Tue Feb  3 16:33:00 2004
@@ -27,7 +27,7 @@ [EMAIL PROTECTED]@
 
 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h
 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \
-       lib/permstring.o @LIBOBJS@
+       lib/permstring.o lib/pool_alloc.o @LIBOBJS@
 ZLIBOBJ=zlib/deflate.o zlib/infblock.o zlib/infcodes.o zlib/inffast.o \
        zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \
        zlib/zutil.o zlib/adler32.o
diff -rupNP --exclude-from cvs/.ignore cvs/backup.c pool2/backup.c
--- cvs/backup.c        Wed Feb  4 03:49:36 2004
+++ pool2/backup.c      Wed Feb  4 22:11:26 2004
@@ -189,6 +189,7 @@ static int keep_backup(char *fname)
                        backup_dir[--backup_dir_len] = '\0';
                if (verbose > 0)
                        rprintf(FINFO, "backup_dir is %s\n", backup_dir);
+
                initialised = 1;
        }
 
@@ -199,7 +200,7 @@ static int keep_backup(char *fname)
        if (do_stat(fname, &st)) return 1;
 #endif
 
-       file = make_file(fname, NO_EXCLUDES);
+       file = make_file(fname, NULL, NO_EXCLUDES);
 
        /* the file could have disappeared */
        if (!file) return 1;
@@ -282,7 +283,7 @@ static int keep_backup(char *fname)
                }
        }
        set_perms(keep_name, file, NULL, 0);
-       free_file(file, FREE_STRUCT);
+       free(file);
 
        if (verbose > 1)
                rprintf(FINFO, "keep_backup %s -> %s\n", fname, keep_name);
diff -rupNP --exclude-from cvs/.ignore cvs/batch.c pool2/batch.c
--- cvs/batch.c Fri Feb  6 01:54:56 2004
+++ pool2/batch.c       Fri Feb  6 02:00:58 2004
@@ -136,9 +136,7 @@ struct file_list *create_flist_from_batc
                exit_cleanup(1);
        }
 
-       batch_flist = new(struct file_list);
-       if (!batch_flist)
-               out_of_memory("create_flist_from_batch");
+       batch_flist = flist_new(WITH_HLINK, "create_flist_from_batch");
 
        save_read = stats.total_read;
        save_pv = protocol_version;
@@ -150,9 +148,9 @@ struct file_list *create_flist_from_batc
        for (i = 0; (flags = read_byte(f)) != 0; i++) {
                if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
                        flags |= read_byte(f) << 8;
-               receive_file_entry(&batch_flist->files[i], flags, f);
+               receive_file_entry(&batch_flist->files[i], flags, batch_flist, f);
        }
-       receive_file_entry(NULL, 0, 0); /* Signal that we're done. */
+       receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */
 
        protocol_version = save_pv;
        stats.total_read = save_read;
diff -rupNP --exclude-from cvs/.ignore cvs/flist.c pool2/flist.c
--- cvs/flist.c Fri Feb  6 01:55:41 2004
+++ pool2/flist.c       Fri Feb  6 02:00:59 2004
@@ -76,7 +76,6 @@ static unsigned int min_file_struct_len;
 static void clean_flist(struct file_list *flist, int strip_root, int no_dups);
 static void output_flist(struct file_list *flist);
 
-
 void init_flist(void)
 {
        struct file_struct f;
@@ -507,7 +506,8 @@ void send_file_entry(struct file_struct 
 
 
 
-void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f)
+void receive_file_entry(struct file_struct **fptr, unsigned short flags,
+    struct file_list *flist, int f)
 {
        static time_t modtime;
        static mode_t mode;
@@ -624,12 +624,12 @@ void receive_file_entry(struct file_stru
                idev_len = 0;
 
        sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0;
-       file_struct_len = idev_len? sizeof file[0] : min_file_struct_len;
+       file_struct_len = min_file_struct_len;
 
        alloc_len = file_struct_len + dirname_len + basename_len
-                 + linkname_len + sum_len + idev_len;
-       if (!(bp = new_array(char, alloc_len)))
-               out_of_memory("receive_file_entry");
+                 + linkname_len + sum_len;
+       bp = pool_alloc(flist->file_pool, alloc_len, "receive_file_entry");
+
        file = *fptr = (struct file_struct *)bp;
        memset(bp, 0, min_file_struct_len);
        bp += file_struct_len;
@@ -642,9 +642,9 @@ void receive_file_entry(struct file_stru
        file->gid = gid;
 
 #if SUPPORT_HARD_LINKS
-       if (idev_len) {
-               file->link_u.idev = (struct idev *)bp;
-               bp += idev_len;
+       if (idev_len && flist->hlink_pool) {
+               file->link_u.idev = pool_talloc(flist->hlink_pool,
+                   struct idev, 1, "inode_table");
        }
 #endif
 
@@ -676,15 +676,19 @@ void receive_file_entry(struct file_stru
 
 #if SUPPORT_HARD_LINKS
        if (idev_len) {
+               INO64_T inode;
                if (protocol_version < 26) {
                        dev = read_int(f);
-                       file->F_INODE = read_int(f);
+                       inode = read_int(f);
                } else {
                        if (!(flags & XMIT_SAME_DEV))
                                dev = read_longint(f);
-                       file->F_INODE = read_longint(f);
+                       inode = read_longint(f);
+               }
+               if (flist->hlink_pool) {
+                       file->F_INODE = inode;
+                       file->F_DEV = dev;
                }
-               file->F_DEV = dev;
        }
 #endif
 
@@ -728,7 +732,8 @@ void receive_file_entry(struct file_stru
  * statting directories if we're not recursing, but this is not a very
  * important case.  Some systems may not have d_type.
  **/
-struct file_struct *make_file(char *fname, int exclude_level)
+struct file_struct *make_file(char *fname,
+    struct file_list *flist, int exclude_level)
 {
        static char *lastdir;
        static int lastdir_len = -1;
@@ -742,6 +747,7 @@ struct file_struct *make_file(char *fnam
        char *basename, *dirname, *bp;
        unsigned short flags = 0;
 
+
        if (strlcpy(thisname, fname, sizeof thisname)
            >= sizeof thisname - flist_dir_len) {
                rprintf(FINFO, "skipping overly long name: %s\n", fname);
@@ -838,12 +844,18 @@ struct file_struct *make_file(char *fnam
                idev_len = 0;
 
        sum_len = always_checksum && S_ISREG(st.st_mode) ? MD4_SUM_LENGTH : 0;
-       file_struct_len = idev_len? sizeof file[0] : min_file_struct_len;
+       file_struct_len = min_file_struct_len;
 
        alloc_len = file_struct_len + dirname_len + basename_len
-                 + linkname_len + sum_len + idev_len;
-       if (!(bp = new_array(char, alloc_len)))
-               out_of_memory("receive_file_entry");
+           + linkname_len + sum_len;
+       if (flist) {
+               bp = pool_alloc(flist->file_pool, alloc_len,
+                   "receive_file_entry");
+       } else {
+               if (!(bp = new_array(char, alloc_len)))
+                       out_of_memory("receive_file_entry");
+       }
+
        file = (struct file_struct *)bp;
        memset(bp, 0, min_file_struct_len);
        bp += file_struct_len;
@@ -856,9 +868,9 @@ struct file_struct *make_file(char *fnam
        file->gid = st.st_gid;
 
 #if SUPPORT_HARD_LINKS
-       if (idev_len) {
-               file->link_u.idev = (struct idev *)bp;
-               bp += idev_len;
+       if (idev_len && flist && flist->hlink_pool) {
+               file->link_u.idev = pool_talloc(flist->hlink_pool,
+                   struct idev, 1, "inode_table");
                file->F_DEV = st.st_dev;
                file->F_INODE = st.st_ino;
        }
@@ -913,9 +925,8 @@ void send_file_name(int f, struct file_l
        extern int delete_excluded;
 
        /* f is set to -1 when calculating deletion file list */
-       file = make_file(fname,
-                        f == -1 && delete_excluded? SERVER_EXCLUDES
-                                                  : ALL_EXCLUDES);
+       file = make_file(fname, flist,
+           f == -1 && delete_excluded? SERVER_EXCLUDES : ALL_EXCLUDES);
 
        if (!file)
                return;
@@ -1034,9 +1045,12 @@ struct file_list *send_file_list(int f, 
 
        start_write = stats.total_written;
 
-       flist = flist_new();
+       flist = flist_new(f == -1 ? WITHOUT_HLINK : WITH_HLINK,
+           "send_file_list");
 
        if (f != -1) {
+               flist->hlink_pool = pool_create(128 * 1024,
+                   sizeof (struct idev), out_of_memory, POOL_INTERN);
                io_start_buffering_out(f);
                if (filesfrom_fd >= 0) {
                        if (argv[0] && !push_dir(argv[0])) {
@@ -1185,6 +1199,12 @@ struct file_list *send_file_list(int f, 
                        finish_filelist_progress(flist);
        }
 
+       if (flist->hlink_pool)
+       {
+               pool_destroy(flist->hlink_pool);
+               flist->hlink_pool = NULL;
+       }
+
        clean_flist(flist, 0, 0);
 
        if (f != -1) {
@@ -1224,10 +1244,10 @@ struct file_list *recv_file_list(int f)
 
        start_read = stats.total_read;
 
-       flist = new(struct file_list);
-       if (!flist)
-               goto oom;
+       flist = flist_new(WITH_HLINK, "recv_file_list");
 
+       flist->hlink_pool = pool_create(128 * 1024, sizeof (struct idev),
+           out_of_memory, POOL_INTERN);
        flist->count = 0;
        flist->malloced = 1000;
        flist->files = new_array(struct file_struct *, flist->malloced);
@@ -1242,7 +1262,7 @@ struct file_list *recv_file_list(int f)
 
                if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
                        flags |= read_byte(f) << 8;
-               receive_file_entry(&flist->files[i], flags, f);
+               receive_file_entry(&flist->files[i], flags, flist, f);
 
                if (S_ISREG(flist->files[i]->mode))
                        stats.total_size += flist->files[i]->length;
@@ -1256,7 +1276,7 @@ struct file_list *recv_file_list(int f)
                                f_name(flist->files[i]));
                }
        }
-       receive_file_entry(NULL, 0, 0); /* Signal that we're done. */
+       receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */
 
        if (verbose > 2)
                rprintf(FINFO, "received %d names\n", flist->count);
@@ -1345,34 +1365,42 @@ int flist_find(struct file_list *flist, 
        return -1;
 }
 
-
 /*
- * Free up any resources a file_struct has allocated, and optionally free
- * it up as well.
+ * Free up any resources a file_struct has allocated
+ * and clear the file.
  */
-void free_file(struct file_struct *file, int free_the_struct)
+void clear_file(int i, struct file_list *flist)
 {
-       if (free_the_struct)
-               free(file);
-       else
-               memset(file, 0, min_file_struct_len);
+       if (flist->hlink_pool && flist->files[i]->link_u.idev)
+               pool_free(flist->hlink_pool, 0, flist->files[i]->link_u.idev);
+       memset(flist->files[i], 0, min_file_struct_len);
 }
 
 
 /*
  * allocate a new file list
  */
-struct file_list *flist_new(void)
+struct file_list *flist_new(int with_hlink, char *msg)
 {
        struct file_list *flist;
 
        flist = new(struct file_list);
        if (!flist)
-               out_of_memory("send_file_list");
+               out_of_memory(msg);
 
-       flist->count = 0;
-       flist->malloced = 0;
-       flist->files = NULL;
+       memset(flist, 0, sizeof (struct file_list));
+
+       if (!(flist->file_pool = pool_create(FILE_EXTENT, 0,
+           out_of_memory, POOL_INTERN)))
+               out_of_memory(msg);
+
+#if SUPPORT_HARD_LINKS
+       if (with_hlink && preserve_hard_links) {
+               if (!(flist->hlink_pool = pool_create(HLINK_EXTENT, 0,
+                   out_of_memory, POOL_INTERN)))
+                       out_of_memory(msg);
+       }
+#endif
 
        return flist;
 }
@@ -1382,9 +1410,8 @@ struct file_list *flist_new(void)
  */
 void flist_free(struct file_list *flist)
 {
-       int i;
-       for (i = 1; i < flist->count; i++)
-               free_file(flist->files[i], FREE_STRUCT);
+       pool_destroy(flist->file_pool);
+       pool_destroy(flist->hlink_pool);
        free(flist->files);
        free(flist);
 }
@@ -1424,7 +1451,8 @@ static void clean_flist(struct file_list
                         * else deletions will mysteriously fail with -R). */
                        if (flist->files[i]->flags & FLAG_TOP_DIR)
                                flist->files[prev_i]->flags |= FLAG_TOP_DIR;
-                       free_file(flist->files[i], CLEAR_STRUCT);
+
+                       clear_file(i, flist);
                } else
                        prev_i = i;
        }
Binary files cvs/getgroups and pool2/getgroups differ
diff -rupNP --exclude-from cvs/.ignore cvs/hlink.c pool2/hlink.c
--- cvs/hlink.c Mon Feb  2 22:21:19 2004
+++ pool2/hlink.c       Wed Feb  4 01:35:49 2004
@@ -46,26 +46,41 @@ int hlink_count;
 
 /* Analyze the data in the hlink_list[], remove items that aren't multiply
  * linked, and replace the dev+inode data with the hlindex+next linked list. */
-static void link_idev_data(void)
+static void link_idev_data(struct file_list *flist)
 {
        struct file_struct *head;
        int from, to, start;
 
+       alloc_pool_t hlink_pool;
+       alloc_pool_t idev_pool = flist->hlink_pool;
+
+       hlink_pool = pool_create(128 * 1024, sizeof (struct hlink),
+           out_of_memory, POOL_INTERN);
+
        for (from = to = 0; from < hlink_count; from++) {
                start = from;
                head = hlink_list[start];
                while (from < hlink_count-1
                    && LINKED(hlink_list[from], hlink_list[from+1])) {
+                       pool_free(idev_pool, 0, hlink_list[from]->link_u.idev);
+                       hlink_list[from]->link_u.links = pool_talloc(hlink_pool,
+                           struct hlink, 1, "hlink_list");
+
                        hlink_list[from]->F_HLINDEX = to;
                        hlink_list[from]->F_NEXT = hlink_list[from+1];
                        from++;
                }
                if (from > start) {
+                       pool_free(idev_pool, 0, hlink_list[from]->link_u.idev);
+                       hlink_list[from]->link_u.links = pool_talloc(hlink_pool,
+                           struct hlink, 1, "hlink_list");
+
                        hlink_list[from]->F_HLINDEX = to;
                        hlink_list[from]->F_NEXT = head;
                        hlink_list[from]->flags |= FLAG_HLINK_EOL;
                        hlink_list[to++] = head;
                } else {
+                       pool_free(idev_pool, 0, head->link_u.idev);
                        head->link_u.idev = NULL;
                }
        }
@@ -73,12 +88,16 @@ static void link_idev_data(void)
        if (!to) {
                free(hlink_list);
                hlink_list = NULL;
+               pool_destroy(hlink_pool);
+               hlink_pool = NULL;
        } else {
                hlink_count = to;
                if (!(hlink_list = realloc_array(hlink_list,
                    struct file_struct *, hlink_count)))
                        out_of_memory("init_hard_links");
        }
+       flist->hlink_pool = hlink_pool;
+       pool_destroy(idev_pool);
 }
 #endif
 
@@ -109,7 +128,7 @@ void init_hard_links(struct file_list *f
                free(hlink_list);
                hlink_list = NULL;
        } else
-               link_idev_data();
+               link_idev_data(flist);
 #endif
 }
 
diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.3 pool2/lib/pool_alloc.3
--- cvs/lib/pool_alloc.3        Wed Dec 31 16:00:00 1969
+++ pool2/lib/pool_alloc.3      Tue Feb  3 16:30:24 2004
@@ -0,0 +1,199 @@
+.ds d \-\^\-
+.ds o \fR[\fP
+.ds c \fR]\fP
+.ds | \fR|\fP
+.de D
+\\.B \*d\\$1
+..
+.de DI
+\\.BI \*d\\$1 \\$2
+..
+.de DR
+\\.BR \*d\\$1 \\$2
+..
+.de Di
+\\.BI \*d\\$1 " \\$2"
+..
+.de Db
+\\.B \*d\\$1 " \\$2"
+..
+.de Df
+\\.B \*d\*ono\*c\\$1
+..
+.de See
+See \fB\\$1\fP for details.
+..
+.de SeeIn
+See \fB\\$1\fP in \fB\\$2\fP for details.
+..
+.TH POOL_ALLOC 3
+.SH NAME
+pool_alloc, pool_free, pool_talloc, pool_tfree, pool_create, pool_destroy
+\- Allocate and free memory in managed allocation pools.
+.SH SYNOPSIS
+.B #include "pool_alloc.h"
+
+\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void 
(*\fIbomb\fB)(char *), int \fIflags\fB);
+
+\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);
+
+\fBvoid *pool_alloc(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, char 
*\fImsg\fB);
+
+\fBvoid pool_free(struct alloc_pool *\fIpool\fB, sise_t \fIsize\fB, void *\fIaddr\fB);
+
+\fBvoid *pool_talloc(struct alloc_pool *\fIpool\fB, \fItype\fB), int \fIcount\fB, 
char *\fImsg\fB);
+
+\fBvoid pool_tfree(struct alloc_pool *\fIpool\fB, \fItype\fB, int \fIcount\fB, void 
*\fIaddr\fB);
+.SH DESCRIPTION
+.P
+The pool allocation routines use
+.B malloc()
+for underlying memory management.
+What allocation pools do is cause
+memory within a given pool to be in large contigious blocks
+(called extents) that when freed will be reusable.  Unlike
+.B malloc()
+the allocations are not managed individually.
+Instead each extent tracks the total free memory within the
+extent.  Each extent can either be used to allocate memory
+or to manage the freeing of memory within that extent.
+When an extent has less free memory than a given
+allocation request or when the first request to free
+memory within that extent is received the extent ceases to
+be used for allocation.
+.P
+This form of memory management is suited to large numbers of small
+related allocations that are held for a while
+and then freed as a group.
+Because the
+underlying allocations are done in large contigious extents
+when an extent is freed it releases a large enough
+contigious block of memory to be useful to subsequent
+.B malloc()
+and
+.B pool_alloc()
+calls even if allocations from other pools or from
+.B malloc()
+are made between allocations from a given pool.
+.P
+.B pool_create()
+Creates an allocation pool for subsequent calls to the pool
+allocation functions.
+When an extent is created for allocations it will be
+.I size 
+bytes.
+Allocations from the pool have their sizes rounded up to a
+multiple of
+.I quantum
+bytes in length.
+Specifying
+.B 0
+for
+.I quantum
+Will produce a quantum that should meet maximal allignment
+on most platforms.
+If the
+.B POOL_QALIGN
+.I flag
+is set allocations will be aligned to addresses that are a
+multiple of
+.IR quantum .
+If the
+.B POOL_CLEAR
+.I flag
+is set all allocations from the pool will be zero filled.
+.P
+.B pool_destroy()
+destroys an allocation pool and frees all memory allocated
+in that pool.
+.P
+.B pool_alloc()
+allocates
+.I size
+bytes from the specified
+.IR pool .
+If
+.I size
+is
+.B 0
+.I quantum
+bytes will be freed.
+If the requested memory cannot be allocated
+.B pool_alloc()
+will call
+.I bomb()
+function, if defined, with
+.I msg
+as it's sole argument and
+.B NULL
+will be returned.
+.P
+.B pool_free()
+frees
+.I size
+bytes pointed to by
+.I addr
+previously allocated in the specified
+.IR pool .
+The memory freed within an extent will not be reusable until
+all of the memory in that extent has been freed but 
+depending on the order in which the
+allocations are freed some extents may be released for reuse
+while others are still in use.
+If
+.I size
+is
+.B 0
+.I quantum
+bytes will be freed.
+If
+.I addr
+is
+.B 0
+no memory will be freed but subsequent allocations will come
+from a new extent.
+.P
+.B pool_talloc()
+is a macro that take a
+.I type
+and
+.I count
+instead of
+.I size
+and will cast the return value to the correct type.
+.P
+.B pool_tfree
+is a macro to free memory previously allocated in the
+specified
+.IR pool .
+.SH RETURN VALUE
+.B pool_create()
+returns a pointer to
+.BR "struct alloc_pool" .
+.P
+.B pool_alloc()
+and
+.B pool_talloc()
+return pointers to the allocated memory,
+or NULL if the request fails.
+For each extent so long as no allocations are smaller than varaible
+allignment requirements this pointer will be suitably
+alligned for any kind of variable.
+The return type of
+.B pool_alloc()
+will normally require casting to the desired type but
+.B pool_talloc()
+will returns a pointer of the requested
+.IR type .
+.P
+.BR pool_free() ,
+.B pool_tfree()
+and
+.B pool_destroy()
+return no value.
+.SH SEE ALSO
+.nf
+malloc(3)
+.SH AUTHOR
+pool_alloc was created by J.W. Schultz of Pegasystems Technologies.
+.SH BUGS AND ISSUES
diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.c pool2/lib/pool_alloc.c
--- cvs/lib/pool_alloc.c        Wed Dec 31 16:00:00 1969
+++ pool2/lib/pool_alloc.c      Thu Feb  5 19:28:58 2004
@@ -0,0 +1,311 @@
+#include "rsync.h"
+
+#define POOL_DEF_EXTENT        (32 * 1024)
+
+struct alloc_pool
+{
+       size_t                  size;           /* extent size          */
+       size_t                  quantum;        /* allocation quantum   */
+       struct pool_extent      *live;          /* current extent for
+                                                * allocations          */
+       struct pool_extent      *free;          /* unfreed extent list  */
+       void                    (*bomb)();
+                                               /* function to call if
+                                                * malloc fails         */
+       int                     flags;
+
+       /* statistical data */
+       unsigned long           e_created;      /* extents created      */
+       unsigned long           e_freed;        /* extents detroyed     */
+       uint64                  n_allocated;    /* calls to alloc       */
+       uint64                  n_freed;        /* calls to free        */
+       uint64                  b_allocated;    /* cum. bytes allocated */
+       uint64                  b_freed;        /* cum. bytes freed     */
+};
+
+struct pool_extent
+{
+       void                    *start;         /* starting address     */
+       size_t                  free;           /* free bytecount       */
+       size_t                  bound;          /* bytes bound by padding,
+                                                * overhead and freed   */
+       struct pool_extent      *next;
+};
+
+#define MINALIGN       (sizeof (void *))
+
+alloc_pool_t
+pool_create(size_t size, size_t quantum,
+    void (*bomb)(char *), int flags)
+{
+       struct alloc_pool       *pool;
+
+       if (!(pool = (struct alloc_pool*) malloc(sizeof (struct alloc_pool))))
+               return pool;
+       memset(pool, 0, sizeof (struct alloc_pool));
+
+       pool->size = size       /* round extent size to min alignment reqs */
+           ? (size + MINALIGN - 1) & ~(MINALIGN - 1)
+           : POOL_DEF_EXTENT;
+       pool->quantum = quantum ? quantum : MINALIGN;
+       pool->live = NULL;
+       pool->free = NULL;
+       pool->bomb = bomb;
+       pool->flags = flags;
+
+       return pool;
+}
+
+void
+pool_destroy(alloc_pool_t p)
+{
+       struct alloc_pool *pool = (struct alloc_pool *) p;
+       struct pool_extent      *cur, *next;
+
+       if (!pool)
+               return;
+
+       if(pool->live)
+       {
+               cur = pool->live;
+               free(cur->start);
+               if (!(pool->flags & (POOL_INTERN | POOL_APPEND)))
+                       free(cur);
+       }
+       cur = pool->free;
+       while(cur)
+       {
+               next = cur->next;
+               free(cur->start);
+               if (!(pool->flags & (POOL_INTERN | POOL_APPEND)))
+                       free(cur);
+               cur = next;
+       }
+       free(pool);
+}
+
+void *pool_alloc(alloc_pool_t p, size_t len, char *bomb)
+{
+       struct alloc_pool *pool = (struct alloc_pool *) p;
+       if (!pool)
+               return NULL;
+
+       if(!len)
+               len = pool->quantum;
+       else if (pool->quantum > 1 && len % pool->quantum)
+               len += pool->quantum - len % pool->quantum;
+
+       if (len > pool->size)
+               goto bomb;
+
+       if (!pool->live || len > pool->live->free)
+       {
+               void    *start; 
+               size_t  free;
+               size_t  bound;
+               size_t  sqew;
+               size_t  asize;
+
+               if (pool->live)
+               {
+                       pool->live->next = pool->free;
+                       pool->free = pool->live;
+               }
+
+               free = pool->size;
+               bound = 0;
+
+               asize = pool->size;
+               if (pool->flags & POOL_APPEND)
+                       asize += sizeof (struct pool_extent);
+                       
+               if(!(start = (void *) malloc(asize)))
+                       goto bomb;
+
+               if (pool->flags & POOL_CLEAR)
+                       memset(start, 0, asize);
+
+               if (pool->flags & POOL_INTERN)
+               {
+                       bound = sizeof (struct pool_extent);
+                       free -= sizeof (struct pool_extent);
+                       pool->live = start + free;
+               } 
+               else if (pool->flags & POOL_APPEND)
+               {
+                       pool->live = start + free;
+               }
+               else if(!(pool->live = (struct pool_extent *) malloc(sizeof (struct 
pool_extent))))
+               {
+                       goto bomb;
+               }
+               if(pool->flags & POOL_QALIGN && pool->quantum > 1
+                   && (sqew = (size_t)(start + free) % pool->quantum))
+               {
+                       bound  += sqew;
+                       free -= sqew;
+               }
+               pool->live->start = start;
+               pool->live->free = free;
+               pool->live->bound = bound;
+               pool->live->next = NULL;
+
+               pool->e_created++;
+       }
+
+       pool->n_allocated++;
+       pool->b_allocated += len;
+
+       pool->live->free -= len;
+
+       return pool->live->start + pool->live->free;
+
+bomb:
+       if (pool->bomb)
+               (*pool->bomb)(bomb);
+       return NULL;
+}
+
+void
+pool_free(alloc_pool_t p, size_t len, void *addr)
+{
+       struct alloc_pool *pool = (struct alloc_pool *) p;
+       struct pool_extent      *cur, *next;
+       struct pool_extent      *prev = NULL;
+
+       if (!pool)
+               return;
+
+       if(!len)
+               len = pool->quantum;
+       else if (pool->quantum > 1 && len % pool->quantum)
+               len += pool->quantum - len % pool->quantum;
+
+       if (!addr && pool->live)
+       {
+               pool->live->next = pool->free;
+               pool->free = pool->live;
+               pool->live = NULL;
+               return;
+       }
+       pool->n_freed++;
+       pool->b_freed += len;
+
+       cur = pool->live;
+       if (cur
+           && addr >= cur->start
+           && addr < cur->start + pool->size)
+       {
+               if (addr == cur->start + cur->free)
+               {
+                       if (pool->flags & POOL_CLEAR)
+                               memset(addr, 0, len);
+                       pool->b_freed += len;
+               } else {
+                       cur->bound += len;
+               }
+               if (cur->free + cur->bound >= pool->size)
+               {
+                       size_t sqew;
+
+                       cur->free = pool->size;
+                       cur->bound = 0;
+                       if (pool->flags & POOL_INTERN)
+                       {
+                               cur->bound = sizeof (struct pool_extent);
+                               cur->free -= sizeof (struct pool_extent);
+                       }
+                       if(pool->flags & POOL_QALIGN && pool->quantum > 1
+                           && (sqew = (size_t)(cur->start + cur->free) % 
pool->quantum))
+                       {
+                               cur->bound += sqew;
+                               cur->free -= sqew;
+                       }
+               }
+               return;
+       }
+       cur = pool->free;
+       while(cur)
+       {
+               next = cur->next;
+               if (addr >= cur->start
+                   && addr < cur->start + pool->size) 
+                       break;
+               prev = cur;
+               cur = next;
+       }
+       if (!cur)
+               return;
+
+       if (cur != pool->free)
+       {
+               prev->next = cur->next;
+               cur->next = pool->free;
+               pool->free = cur;
+       }
+       cur->bound += len;
+
+       if (cur->free + cur->bound >= pool->size)
+       {
+               pool->free = cur->next;
+
+               free(cur->start);
+               if (!(pool->flags & (POOL_INTERN | POOL_APPEND)))
+                       free(cur);
+               pool->e_freed++;
+       }
+       return;
+}
+
+#define FDPRINT(label, value) \
+       snprintf(buf, BUFSIZ, label, value); \
+       write(fd, buf, strlen(buf));
+
+#define FDEXTSTAT(ext) \
+       snprintf(buf, BUFSIZ, "  %12ld  %5ld\n", \
+               (long) ext->free, \
+               (long) ext->bound); \
+       write(fd, buf, strlen(buf));
+
+void
+pool_stats(alloc_pool_t p, int fd, int summarize)
+{
+       struct alloc_pool *pool = (struct alloc_pool *) p;
+       struct pool_extent      *cur;
+       char buf[BUFSIZ];
+
+       if (!pool)
+               return;
+
+       FDPRINT("  Extent size:       %12ld\n", (long)  pool->size)
+       FDPRINT("  Alloc quantum:     %12ld\n", (long)  pool->quantum)
+       FDPRINT("  Extents created:   %12ld\n",         pool->e_created)
+       FDPRINT("  Extents freed:     %12ld\n",         pool->e_freed)
+       FDPRINT("  Alloc count:       %12.0f\n", (double) pool->n_allocated)
+       FDPRINT("  Free Count:        %12.0f\n", (double) pool->n_freed)
+       FDPRINT("  Alloc bytes:       %12.0f\n", (double) pool->b_allocated)
+       FDPRINT("  Free bytes:        %12.0f\n", (double) pool->b_freed)
+
+       if (summarize)
+               return;
+
+       if (!pool->live && !pool->free)
+               return;
+
+       write(fd, "\n", 1);
+
+       if (pool->live)
+       {
+               FDEXTSTAT(pool->live) 
+       }
+       strcpy(buf, "   FREE    BOUND\n");
+       write(fd, buf, strlen(buf));
+
+       cur = pool->free;
+       while(cur)
+       {
+               FDEXTSTAT(cur) 
+               cur = cur->next;
+       }
+}
+
diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.h pool2/lib/pool_alloc.h
--- cvs/lib/pool_alloc.h        Wed Dec 31 16:00:00 1969
+++ pool2/lib/pool_alloc.h      Tue Feb  3 16:30:24 2004
@@ -0,0 +1,20 @@
+#include <stddef.h>
+
+#define POOL_CLEAR     (1<<0)          /* zero fill allocations        */
+#define POOL_QALIGN    (1<<1)          /* align data to quanta         */
+#define POOL_INTERN    (1<<2)          /* Allocate extent structures   */
+#define POOL_APPEND    (1<<3)          /*   or appended to extent data */
+
+typedef void *alloc_pool_t;
+
+alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(char *), int 
flags);
+void pool_destroy(alloc_pool_t pool);
+void *pool_alloc(alloc_pool_t pool, size_t size, char *bomb);
+void pool_free(alloc_pool_t pool, size_t size, void *addr);
+
+#define pool_talloc(pool, type, count, bomb) \
+       ((type *)pool_alloc(pool, sizeof(type) * count, bomb))
+
+#define pool_tfree(pool, type, count, addr) \
+       (pool_free(pool, sizeof(type) * count, addr))
+
diff -rupNP --exclude-from cvs/.ignore cvs/proto.h pool2/proto.h
--- cvs/proto.h Fri Feb  6 01:55:48 2004
+++ pool2/proto.h       Fri Feb  6 02:01:00 2004
@@ -74,16 +74,18 @@ int readlink_stat(const char *path, STRU
 int link_stat(const char *path, STRUCT_STAT * buffer);
 void flist_expand(struct file_list *flist);
 void send_file_entry(struct file_struct *file, int f, unsigned short base_flags);
-void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f);
-struct file_struct *make_file(char *fname, int exclude_level);
+void receive_file_entry(struct file_struct **fptr, unsigned short flags,
+    struct file_list *flist, int f);
+struct file_struct *make_file(char *fname,
+    struct file_list *flist, int exclude_level);
 void send_file_name(int f, struct file_list *flist, char *fname,
                    int recursive, unsigned short base_flags);
 struct file_list *send_file_list(int f, int argc, char *argv[]);
 struct file_list *recv_file_list(int f);
 int file_compare(struct file_struct **file1, struct file_struct **file2);
 int flist_find(struct file_list *flist, struct file_struct *f);
-void free_file(struct file_struct *file, int free_the_struct);
-struct file_list *flist_new(void);
+void clear_file(int i, struct file_list *flist);
+struct file_list *flist_new(int with_hlink, char *msg);
 void flist_free(struct file_list *flist);
 int f_name_cmp(struct file_struct *f1, struct file_struct *f2);
 char *f_name_to(struct file_struct *f, char *fbuf);
diff -rupNP --exclude-from cvs/.ignore cvs/receiver.c pool2/receiver.c
--- cvs/receiver.c      Wed Feb  4 03:49:36 2004
+++ pool2/receiver.c    Wed Feb  4 03:08:45 2004
@@ -305,6 +305,12 @@ int recv_files(int f_in,struct file_list
                rprintf(FINFO,"recv_files(%d) starting\n",flist->count);
        }
 
+       if (flist->hlink_pool)
+       {
+               pool_destroy(flist->hlink_pool);
+               flist->hlink_pool = NULL;
+       }
+
        while (1) {
                cleanup_disable();
 
diff -rupNP --exclude-from cvs/.ignore cvs/rsync.h pool2/rsync.h
--- cvs/rsync.h Fri Feb  6 01:13:58 2004
+++ pool2/rsync.h       Fri Feb  6 02:02:23 2004
@@ -112,8 +112,12 @@
 #define FULL_FLUSH     1
 #define NORMAL_FLUSH   0
 
-#define CLEAR_STRUCT   0
-#define FREE_STRUCT    1
+#if HAVE_LINK
+#define WITH_HLINK     1
+#else
+#define WITH_HLINK     0
+#endif
+#define WITHOUT_HLINK  0
 
 /* Log-message categories.  FLOG is only used on the daemon side to
  * output messages to the log file. */
@@ -254,6 +258,7 @@ enum msgcode {
 
 #include <assert.h>
 
+#include "lib/pool_alloc.h"
 
 #define BOOL int
 
@@ -434,19 +439,25 @@ struct file_struct {
  */
 #define FLIST_START    (32 * 1024)
 #define FLIST_LINEAR   (FLIST_START * 512)
+/*
+ * Extent size for allocation pools A minimum size of 128KB
+ * is needed to mmap them so that freeing will release the
+ * space to the OS.
+ *
+ * Larger sizes reduce leftover fragments and speed free calls
+ * (when they happen) Smaller sizes increase the chance of
+ * freed allocations freeing whole extents.
+ */
 
-struct string_area {
-       char *base;
-       char *end;
-       char *current;
-       struct string_area *next;
-};
+#define FILE_EXTENT    (256 * 1024)
+#define HLINK_EXTENT   (128 * 1024)
 
 struct file_list {
        int count;
        int malloced;
+       alloc_pool_t file_pool;
+       alloc_pool_t hlink_pool;
        struct file_struct **files;
-       struct string_area *string_area;
 };
 
 struct sum_buf {
Binary files cvs/t_unsafe and pool2/t_unsafe differ
Binary files cvs/tls and pool2/tls differ
Binary files cvs/trimslash and pool2/trimslash differ
Binary files cvs/wildtest and pool2/wildtest differ
-- 
To unsubscribe or change options: http://lists.samba.org/mailman/listinfo/rsync
Before posting, read: http://www.catb.org/~esr/faqs/smart-questions.html

Reply via email to