Hi Cliff,
I've also been working in the memory management area over the last
couple of days, towards adding more memory management capability to the
SHMEM code.. Here's a patch including the changes I've been working on.
Summary of the enhancements to SHMEM :
--------------------------------------
1. Keep track of the memory chunks allocated (a linked list of the allocated
blocks)
2. Provide the "realloc" functionality
3. Maintain a list of the memory chunks that were "freed" so that those
chunks can be reused for future memory allocations.
With this enhanced capability, it'd be easier to enable the SHMHT and the
SHMCB modes of SSL Session Caching. I'm also working on porting the
ssl_scache_shmht.c and ssl_scache_shmcb.c, and should be submitting a patch
soon.
Pl. let me know if it helps in satisfying the memory mgmt. problems that
you're trying to resolve. If not, I'd like to work with you to add any
additional functionality.
Pl. review the changes and commit it accordingly. Any feedback / inputs are
welcome.
Thanks
-Madhu
-----Original Message-----
From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]]
Sent: Friday, September 07, 2001 1:05 PM
To: [EMAIL PROTECTED]
Subject: cvs commit: httpd-2.0 STATUS
jwoolley 01/09/07 13:05:07
Modified: . STATUS
Log:
Bright and sunny in C'ville...
Revision Changes Path
1.287 +10 -7 httpd-2.0/STATUS
Index: STATUS
===================================================================
RCS file: /home/cvs/httpd-2.0/STATUS,v
retrieving revision 1.286
retrieving revision 1.287
diff -u -d -u -r1.286 -r1.287
--- STATUS 2001/09/07 14:01:26 1.286
+++ STATUS 2001/09/07 20:05:06 1.287
@@ -1,5 +1,5 @@
APACHE 2.0 STATUS: -*-text-*-
-Last modified at [$Date: 2001/09/07 14:01:26 $]
+Last modified at [$Date: 2001/09/07 20:05:06 $]
Release:
@@ -142,12 +142,15 @@
longer needed. Enabling simple debugging features like guard
bands, double free detection, etc. would be cool but certainly
not a hard requirement.
- Status: Cliff, David, et al have discussed using the blocks SMS
- for this. First step is to s/malloc/apr_sms_malloc/g, etc.
- We could then have a thread-private SMS that is pointed
- to by the conn_rec's or something so that all calls to
- the bucket create functions can pass in that SMS. No locks
- required. Should be fast...
+
+ Status: Cliff started to implement this using SMS as has
+ been discussed at length for months, but since
+ SMS is not being used anywhere else in the server,
+ several people expressed the opinion that we should
+ get rid of it entirely, meaning that the buckets
+ need their own memory management (free list) functions.
+ Cliff will implement that this weekend so we at least
+ have something to look at/compare with.
* Eliminate unnecessary creation of pipes in mod_cgid
Index: shmem.c
===================================================================
RCS file: /home/cvspublic/apr/shmem/unix/shmem.c,v
retrieving revision 1.33
diff -u -r1.33 shmem.c
--- shmem.c 2001/08/30 17:11:04 1.33
+++ shmem.c 2001/09/08 01:12:18
@@ -89,136 +89,296 @@
#include <kernel/OS.h>
#endif
+#define MIN_BLK_SIZE 128
+
+typedef struct memchunk_t {
+ apr_pool_t *p;
+ void *addr;
+ apr_size_t size;
+ struct memchunk_t *next;
+ struct memchunk_t *prev;
+} memchunk_t;
+
struct shmem_t {
- void *mem;
- void *curmem;
- apr_size_t length;
- apr_lock_t *lock;
- char *filename;
+ apr_pool_t *p;
+ void *mem;
+ void *curmem;
+ memchunk_t *uselist;
+ memchunk_t *freelist;
+ apr_size_t length;
+ apr_lock_t *lock;
+ char *filename;
#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || APR_USE_SHMEM_MMAP_ZERO
- apr_file_t *file;
-#elif APR_USE_SHMEM_MMAP_ANON
- /* Nothing else. */
+ apr_file_t *file;
#elif APR_USE_SHMEM_SHMGET
- apr_os_file_t file;
+ apr_os_file_t file;
#elif APR_USE_SHMEM_BEOS
- area_id areaid;
+ area_id areaid;
#endif
};
+static void add_chunk (memchunk_t **list, memchunk_t *blk)
+{
+ if ((*list) == (memchunk_t *)NULL)
+ (*list) = blk;
+
+ if (blk == (*list)){
+ blk->prev = blk;
+ blk->next = blk;
+ } else {
+ ((memchunk_t*)(*list)->prev)->next = blk;
+ blk->prev = ((memchunk_t *)(*list))->prev;
+ blk->next = (*list);
+ (*list)->prev = blk;
+ }
+}
+
+static void remove_chunk(memchunk_t **list, memchunk_t *blk)
+{
+ if (((*list) == blk) && (blk->next == blk) && (blk == blk->prev)) {
+ *list = NULL;
+ } else {
+ ((memchunk_t*)(blk->next))->prev = blk->prev;
+ ((memchunk_t*)(blk->prev))->next = blk->next;
+ if (*list == blk)
+ *list = blk->next;
+ }
+ blk->next = NULL;
+ blk->prev = NULL;
+}
+
+static void split_chunk(memchunk_t **list, memchunk_t *blk, apr_size_t size)
+{
+ memchunk_t *b;
+
+ b = (memchunk_t *)apr_pcalloc(blk->p, sizeof(memchunk_t));
+ b->p = blk->p;
+ b->size = blk->size - size;
+ b->addr = blk->addr + size;
+ blk->size = size;
+ add_chunk(list, b);
+}
+
+static memchunk_t *find_chunk_by_addr(memchunk_t *list, void *addr)
+{
+ memchunk_t *iter = list;
+
+ if (iter == (memchunk_t *)NULL) return NULL;
+
+ do {
+ if (iter->addr == addr)
+ return iter;
+ } while (iter && ((iter = iter->next) != list));
+
+ return NULL;
+}
+
+static memchunk_t *find_chunk_by_size(memchunk_t *list, apr_size_t size)
+{
+ memchunk_t *iter = list, *found = NULL;
+ apr_size_t diff = -1;
+
+ if (!list) return NULL;
+
+ do {
+ if (iter->size == size)
+ return iter;
+ if (iter->size > size) {
+ if (diff == -1)
+ diff = iter->size;
+ if ((iter->size - size) < diff) {
+ diff = iter->size - size;
+ found = iter;
+ }
+ }
+ } while ((iter = iter->next) != list);
+
+ if (diff > MIN_BLK_SIZE)
+ split_chunk(&list, found, size);
+
+ return found;
+}
+
+static memchunk_t *alloc_chunk(apr_shmem_t *m, apr_size_t size)
+{
+ memchunk_t *b = NULL;
+
+ /* Align size to a word boundary */
+ size = ((1 + ((size - 1) / sizeof (void *))) * sizeof (void *));
+ if (m->length < size)
+ return NULL;
+
+ if ((b = find_chunk_by_size(m->freelist, size)) != NULL){
+ remove_chunk(&(m->freelist), b);
+ } else {
+ b = (memchunk_t *)apr_pcalloc(m->p, sizeof(memchunk_t));
+ b->p = m->p;
+ b->addr = m->curmem;
+ b->size = size;
+ m->curmem += size;
+ }
+ /* actual size may be different if we're reusing a chunk */
+ m->length -= b->size;
+ add_chunk(&(m->uselist), b);
+
+ return b;
+}
+
+static void free_chunk(apr_shmem_t *m, void *entity)
+{
+ memchunk_t *b;
+ if (entity == NULL)
+ return;
+
+ if ((b = find_chunk_by_addr(m->uselist, entity)) != NULL) {
+ remove_chunk(&(m->uselist), b);
+ add_chunk(&(m->freelist), b);
+ m->length += b->size;
+ }
+}
+
+static memchunk_t *realloc_chunk(apr_shmem_t *m, void *entity, apr_size_t size)
+{
+ memchunk_t *b, *new_b;
+
+ /* Align size to a word boundary */
+ size = ((1 + ((size - 1) / sizeof (void *))) * sizeof (void *));
+ if ((b = find_chunk_by_addr(m->uselist, entity)) != NULL) {
+ if (b->size > size)
+ split_chunk(&(m->uselist), b, size);
+
+ else if ((b->size < size) && (size < m->length)) {
+ new_b = alloc_chunk (m, size);
+ memcpy (new_b->addr, b->addr, b->size);
+ free_chunk (m, entity);
+ b = new_b;
+ }
+ }
+ return b;
+}
+
+/*
+ * FIXME:
+ * 1. Is APR_OS_DEFAULT sufficient?
+ * 2. status = apr_file_remove(filename, p);
+ * 3. Handle errors from return values of system calls.
+ */
APR_DECLARE(apr_status_t) apr_shm_init(apr_shmem_t **m, apr_size_t reqsize,
- const char *filename, apr_pool_t *pool)
+ const char *filename, apr_pool_t *p)
{
- apr_shmem_t *new_m;
- void *mem;
+ void *addr;
+
#if APR_USE_SHMEM_SHMGET
struct shmid_ds shmbuf;
apr_uid_t uid;
apr_gid_t gid;
#endif
+
#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || APR_USE_SHMEM_MMAP_ZERO
apr_status_t status;
+ status = APR_SUCCESS;
#endif
+
#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \
APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_SHMGET
int tmpfd;
#endif
- new_m = apr_palloc(pool, sizeof(apr_shmem_t));
- if (!new_m)
+ (*m) = apr_palloc(p, sizeof(apr_shmem_t));
+ if (!(*m))
return APR_ENOMEM;
-/* These implementations are very similar except for opening the file. */
#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || APR_USE_SHMEM_MMAP_ZERO
- /* FIXME: Ignore error for now. *
- * status = apr_file_remove(filename, pool);*/
- status = APR_SUCCESS;
#if APR_USE_SHMEM_MMAP_TMP
- /* FIXME: Is APR_OS_DEFAULT sufficient? */
- status = apr_file_open(&new_m->file, filename,
- APR_READ | APR_WRITE | APR_CREATE, APR_OS_DEFAULT,
- pool);
+ status = apr_file_open(&(*m)->file, filename,
+ APR_READ | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, p);
if (status != APR_SUCCESS)
return APR_EGENERAL;
- status = apr_os_file_get(&tmpfd, new_m->file);
- status = apr_file_trunc(new_m->file, reqsize);
+ status = apr_os_file_get(&tmpfd, (*m)->file);
+ status = apr_file_trunc((*m)->file, reqsize);
if (status != APR_SUCCESS)
return APR_EGENERAL;
#elif APR_USE_SHMEM_MMAP_SHM
- /* FIXME: Is APR_OS_DEFAULT sufficient? */
tmpfd = shm_open(filename, O_RDWR | O_CREAT, APR_OS_DEFAULT);
if (tmpfd == -1)
return errno;
- apr_os_file_put(&new_m->file, &tmpfd, pool);
- status = apr_file_trunc(new_m->file, reqsize);
+ apr_os_file_put(&(*m)->file, &tmpfd, p);
+ status = apr_file_trunc((*m)->file, reqsize);
if (status != APR_SUCCESS)
{
shm_unlink(filename);
return APR_EGENERAL;
}
#elif APR_USE_SHMEM_MMAP_ZERO
- status = apr_file_open(&new_m->file, "/dev/zero", APR_READ | APR_WRITE,
- APR_OS_DEFAULT, pool);
+ status = apr_file_open(&(*m)->file, "/dev/zero", APR_READ | APR_WRITE,
+ APR_OS_DEFAULT, p);
if (status != APR_SUCCESS)
return APR_EGENERAL;
- status = apr_os_file_get(&tmpfd, new_m->file);
+ status = apr_os_file_get(&tmpfd, (*m)->file);
#endif
- mem = mmap(NULL, reqsize, PROT_READ|PROT_WRITE, MAP_SHARED, tmpfd, 0);
+ addr = mmap(NULL, reqsize, PROT_READ|PROT_WRITE, MAP_SHARED, tmpfd, 0);
#elif APR_USE_SHMEM_MMAP_ANON
- mem = mmap(NULL, reqsize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
+ addr = mmap(NULL, reqsize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED,-1, 0);
#elif APR_USE_SHMEM_SHMGET
+
tmpfd = shmget(IPC_PRIVATE, reqsize, (SHM_R|SHM_W|IPC_CREAT));
if (tmpfd == -1)
return errno;
- new_m->file = tmpfd;
+ (*m)->file = tmpfd;
- mem = shmat(new_m->file, NULL, 0);
+ addr = (void *)shmat((*m)->file, NULL, 0);
- /* FIXME: Handle errors. */
- if (shmctl(new_m->file, IPC_STAT, &shmbuf) == -1)
- return errno;
+ if (shmctl((*m)->file, IPC_STAT, &shmbuf) == -1)
+ return errno + APR_OS_START_SYSERR;
- apr_current_userid(&uid, &gid, pool);
+ apr_current_userid(&uid, &gid, p);
shmbuf.shm_perm.uid = uid;
shmbuf.shm_perm.gid = gid;
- if (shmctl(new_m->file, IPC_SET, &shmbuf) == -1)
- return errno;
+ if (shmctl((*m)->file, IPC_SET, &shmbuf) == -1)
+ return errno + APR_OS_START_SYSERR;
- /* remove in future (once use count hits zero) */
- if (shmctl(new_m->file, IPC_RMID, NULL) == -1)
- return errno;
-
#elif APR_USE_SHMEM_BEOS
- new_m->area_id = create_area("mm", (void*)&mem, B_ANY_ADDRESS, reqsize,
- B_LAZY_LOCK, B_READ_AREA|B_WRITE_AREA);
- /* FIXME: error code? */
- if (new_m->area_id < 0)
- return APR_EGENERAL;
+ (*m)->area_id = create_area("apr_shm", (void*)&addr, B_ANY_ADDRESS,
+ reqsize, B_LAZY_LOCK, B_READ_AREA|B_WRITE_AREA);
+ if ((*m)->area_id < 0)
+ return APR_EGENERAL; /* FIXME: error code? */
#endif
- new_m->mem = mem;
- new_m->curmem = mem;
- new_m->length = reqsize;
+ (*m)->p = p;
+ (*m)->mem = addr;
+ (*m)->curmem = addr;
+ (*m)->length = reqsize;
+ (*m)->uselist = NULL;
+ (*m)->freelist = NULL;
+fprintf(stderr,"Initial addr %lx, length %ld ", addr, reqsize);
- apr_lock_create(&new_m->lock, APR_MUTEX, APR_CROSS_PROCESS, NULL, pool);
- if (!new_m->lock)
+ apr_lock_create(&(*m)->lock, APR_MUTEX, APR_CROSS_PROCESS, NULL, p);
+ if (!(*m)->lock)
return APR_EGENERAL;
- *m = new_m;
return APR_SUCCESS;
}
APR_DECLARE(apr_status_t) apr_shm_destroy(apr_shmem_t *m)
{
+#if APR_USE_SHMEM_SHMGET
+ struct shmid_ds shmbuf;
+ apr_uid_t uid;
+ apr_gid_t gid;
+#endif
+
+ if (m == (apr_shmem_t *)NULL)
+ return APR_SUCCESS;
+
#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || APR_USE_SHMEM_MMAP_ZERO
munmap(m->mem, m->length);
apr_file_close(m->file);
@@ -226,45 +386,81 @@
munmap(m->mem, m->length);
#elif APR_USE_SHMEM_SHMGET
shmdt(m->mem);
+ apr_current_userid(&uid, &gid, m->p);
+ shmbuf.shm_perm.uid = uid;
+ shmbuf.shm_perm.gid = gid;
+
+ if (shmctl(m->file, IPC_RMID, &shmbuf) == -1)
+ return errno + APR_OS_START_SYSERR;
#elif APR_USE_SHMEM_BEOS
- delete_area(new_m->area_id);
+ delete_area(m->area_id);
#endif
+ m->length = 0;
+ m->uselist = NULL;
+ m->freelist = NULL;
+ m->mem = NULL;
return APR_SUCCESS;
}
APR_DECLARE(void *) apr_shm_malloc(apr_shmem_t *m, apr_size_t reqsize)
{
- void *new;
- new = NULL;
+ memchunk_t *b = (memchunk_t *)NULL;
- apr_lock_acquire(m->lock);
- /* Do we have enough space? */
- if (((char *)m->curmem - (char *)m->mem + reqsize) <= m->length)
+ if (m != (apr_shmem_t *)NULL)
{
- new = m->curmem;
- m->curmem = (char *)m->curmem + reqsize;
+ apr_lock_acquire(m->lock);
+ b = alloc_chunk(m, reqsize);
+ apr_lock_release(m->lock);
}
- apr_lock_release(m->lock);
- return new;
+
+ return ((b) ? b->addr : NULL);
}
APR_DECLARE(void *) apr_shm_calloc(apr_shmem_t *m, apr_size_t reqsize)
+{
+ memchunk_t *b = (memchunk_t *)NULL;
+
+ if (m != (apr_shmem_t *)NULL)
+ {
+ apr_lock_acquire(m->lock);
+ b = alloc_chunk(m, reqsize);
+ if (b != NULL)
+ memset(b->addr, 0, reqsize);
+ apr_lock_release(m->lock);
+ }
+ return ((b) ? b->addr : NULL);
+}
+
+APR_DECLARE(void *) apr_shm_realloc(apr_shmem_t *m, void *p, apr_size_t reqsize)
{
- void *new = apr_shm_malloc(m, reqsize);
- if (new)
- memset(new, '\0', reqsize);
- return new;
+ memchunk_t *b = (memchunk_t *)NULL;
+
+ if (m != (apr_shmem_t *)NULL)
+ {
+ apr_lock_acquire(m->lock);
+ if (p != NULL)
+ b = realloc_chunk(m, p, reqsize);
+ else
+ b = alloc_chunk(m, reqsize);
+ apr_lock_release(m->lock);
+ }
+
+ return ((b) ? b->addr : NULL);
}
-APR_DECLARE(apr_status_t) apr_shm_free(apr_shmem_t *shared, void *entity)
+APR_DECLARE(apr_status_t) apr_shm_free(apr_shmem_t *m, void *entity)
{
- /* Without a memory management scheme within our shared memory, it
- * is impossible to implement free. */
+ if (m != (apr_shmem_t *)NULL)
+ {
+ apr_lock_acquire(m->lock);
+ free_chunk(m, entity);
+ apr_lock_release(m->lock);
+ }
return APR_SUCCESS;
}
-APR_DECLARE(apr_status_t) apr_shm_name_get(apr_shmem_t *c,
+APR_DECLARE(apr_status_t) apr_shm_name_get(apr_shmem_t *m,
apr_shm_name_t **name)
{
#if APR_USES_ANONYMOUS_SHM
@@ -280,7 +476,7 @@
#endif
}
-APR_DECLARE(apr_status_t) apr_shm_name_set(apr_shmem_t *c,
+APR_DECLARE(apr_status_t) apr_shm_name_set(apr_shmem_t *m,
apr_shm_name_t *name)
{
#if APR_USES_ANONYMOUS_SHM
@@ -295,7 +491,7 @@
#endif
}
-APR_DECLARE(apr_status_t) apr_shm_open(apr_shmem_t *c)
+APR_DECLARE(apr_status_t) apr_shm_open(apr_shmem_t *m)
{
#if APR_USES_ANONYMOUS_SHM
@@ -315,16 +511,16 @@
APR_DECLARE(apr_status_t) apr_shm_avail(apr_shmem_t *m, apr_size_t *size)
{
- apr_status_t status;
-
- status = APR_ENOSHMAVAIL;
+ apr_status_t status = APR_ENOSHMAVAIL;
- apr_lock_acquire(m->lock);
+ if (m != (apr_shmem_t *)NULL)
+ {
+ apr_lock_acquire(m->lock);
+ if ((*size = m->length) > 0)
+ status = APR_SUCCESS;
- *size = m->length - ((char *)m->curmem - (char *)m->mem);
- if (*size)
- status = APR_SUCCESS;
+ apr_lock_release(m->lock);
+ }
- apr_lock_release(m->lock);
return status;
}