Sorry.. I'd sent it to the wrong list again :-(.. -Madhu
-----Original Message----- From: MATHIHALLI,MADHUSUDAN (HP-Cupertino,ex1) [mailto:[EMAIL PROTECTED] Sent: Tuesday, November 06, 2001 9:49 PM To: '[email protected]' Subject: [PATCH]1. SHMEM (repost) Hi, As you might be aware, the current code in apr_shmem.c doesn't do any memory management. I'd posted this patch sometime back, but I didn't get any feedback regarding whether it's accepted / rejected (but for the code structure comments from Justin - thanks).. Here's a repost of the SHMEM patch. It'd be great if somebody could pl. review and let me know you comments / suggestions.. This would be required for the SHMHT / SHMCB patch that follows.. -Madhu
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/25 05:43:51 @@ -52,14 +52,6 @@ * <http://www.apache.org/>. */ -#include "apr_general.h" -#include "apr_shmem.h" -#include "apr_lock.h" -#include "apr_portable.h" -#include "apr_errno.h" -#define APR_WANT_MEMFUNC -#include "apr_want.h" - /* * This is the Unix implementation of shared memory. * @@ -72,45 +64,15 @@ * - shmget (SysV) * - create_area (BeOS) */ - -#if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON -#include <sys/mman.h> -#elif APR_USE_SHMEM_SHMGET -#include <sys/ipc.h> -#include <sys/shm.h> -#if !defined(SHM_R) -#define SHM_R 0400 -#endif -#if !defined(SHM_W) -#define SHM_W 0200 -#endif -#include <sys/file.h> -#elif APR_USE_SHMEM_BEOS -#include <kernel/OS.h> -#endif -struct shmem_t { - void *mem; - void *curmem; - 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. */ -#elif APR_USE_SHMEM_SHMGET - apr_os_file_t file; -#elif APR_USE_SHMEM_BEOS - area_id areaid; -#endif -}; +#include "shmem.h" APR_DECLARE(apr_status_t) apr_shm_init(apr_shmem_t **m, apr_size_t reqsize, const char *filename, apr_pool_t *pool) { apr_shmem_t *new_m; - void *mem; + void *mem, *addr; + int i, listlen, listelem; #if APR_USE_SHMEM_SHMGET struct shmid_ds shmbuf; apr_uid_t uid; @@ -128,6 +90,9 @@ if (!new_m) return APR_ENOMEM; + listelem = (reqsize / MIN_BLK_SIZE) + 1; + listlen = listelem * sizeof(memchunk_t) + sizeof(memoffsets_t); + /* 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. * @@ -168,10 +133,18 @@ status = apr_os_file_get(&tmpfd, new_m->file); #endif - mem = mmap(NULL, reqsize, PROT_READ|PROT_WRITE, MAP_SHARED, tmpfd, 0); + /* Not yet tested */ + addr = mmap(NULL, reqsize + listlen, + PROT_READ|PROT_WRITE, MAP_SHARED, tmpfd, 0); + new_m->listlen = listlen; + mem = addr + listlen; #elif APR_USE_SHMEM_MMAP_ANON - mem = mmap(NULL, reqsize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0); + mem = mmap(NULL, reqsize, + PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0); + addr = mmap(NULL, listlen, + PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_SHARED, -1, 0); + new_m->listlen = listlen; #elif APR_USE_SHMEM_SHMGET tmpfd = shmget(IPC_PRIVATE, reqsize, (SHM_R|SHM_W|IPC_CREAT)); if (tmpfd == -1) @@ -180,7 +153,16 @@ new_m->file = tmpfd; mem = shmat(new_m->file, NULL, 0); + if (!mem) + return errno; + tmpfd = shmget(IPC_PRIVATE, listlen, (SHM_R|SHM_W|IPC_CREAT)); + if (tmpfd == -1) + return errno; + + new_m->listfd = tmpfd; + addr = shmat(new_m->listfd, NULL, 0); + /* FIXME: Handle errors. */ if (shmctl(new_m->file, IPC_STAT, &shmbuf) == -1) return errno; @@ -192,8 +174,7 @@ if (shmctl(new_m->file, IPC_SET, &shmbuf) == -1) return errno; - /* remove in future (once use count hits zero) */ - if (shmctl(new_m->file, IPC_RMID, NULL) == -1) + if (shmctl(new_m->listfd, IPC_SET, &shmbuf) == -1) return errno; #elif APR_USE_SHMEM_BEOS @@ -205,8 +186,36 @@ #endif + if (!addr) + return errno; + + new_m->offsets = addr; + new_m->list = addr + (sizeof(memoffsets_t)); + + memset(new_m->list, 0, listlen); + for (i = 0; i < listelem; i++) { + new_m->list[i].prev = -1; + new_m->list[i].next = -1; + } + + /* + * Initially, there's only one element c_free (=0, l_total = 1). + * The size of this free element is the total size requested. + * The c_used list is set to NULL (c_used = -1). + */ + new_m->list[0].offset = 0; + new_m->list[0].size = reqsize; + new_m->list[0].next = 0; + new_m->list[0].prev = 0; + + new_m->offsets->l_total = 1; + new_m->offsets->c_free = 0; + new_m->offsets->c_used = -1; + new_m->offsets->shm_offset = 0; + new_m->offsets->shm_length = reqsize; + + new_m->p = pool; new_m->mem = mem; - new_m->curmem = mem; new_m->length = reqsize; apr_lock_create(&new_m->lock, APR_MUTEX, APR_CROSS_PROCESS, NULL, pool); @@ -219,48 +228,86 @@ 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) + return APR_SUCCESS; + #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || APR_USE_SHMEM_MMAP_ZERO - munmap(m->mem, m->length); + munmap(m->offsets, (m->length + m->listlen)); apr_file_close(m->file); #elif APR_USE_SHMEM_MMAP_ANON munmap(m->mem, m->length); + munmap(m->offsets,m->listlen); #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; + + shmdt(m->list); + if (shmctl(m->listfd, IPC_RMID, &shmbuf) == -1) + return errno; + #elif APR_USE_SHMEM_BEOS - delete_area(new_m->area_id); + delete_area(m->area_id); #endif + m->offsets = 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 = NULL; apr_lock_acquire(m->lock); - /* Do we have enough space? */ - if (((char *)m->curmem - (char *)m->mem + reqsize) <= m->length) - { - new = m->curmem; - m->curmem = (char *)m->curmem + reqsize; - } + b = apr_shm_alloc_chunk(m, reqsize); apr_lock_release(m->lock); - return new; + + return ((b) ? APR_SHM_ADDR(m, b->offset) : NULL); } APR_DECLARE(void *) apr_shm_calloc(apr_shmem_t *m, apr_size_t reqsize) +{ + memchunk_t *b = NULL; + + apr_lock_acquire(m->lock); + b = apr_shm_alloc_chunk(m, reqsize); + if (b != NULL) + memset(APR_SHM_ADDR(m, b->offset), 0, reqsize); + apr_lock_release(m->lock); + return ((b) ? APR_SHM_ADDR(m, b->offset) : 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 = NULL; + + apr_lock_acquire(m->lock); + if (p != NULL) + b = apr_shm_realloc_chunk(m, p, reqsize); + else + b = apr_shm_alloc_chunk(m, reqsize); + apr_lock_release(m->lock); + + return ((b) ? APR_SHM_ADDR(m, b->offset) : 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. */ + apr_lock_acquire(m->lock); + apr_shm_free_chunk(m, entity); + apr_lock_release(m->lock); return APR_SUCCESS; } @@ -315,16 +362,16 @@ APR_DECLARE(apr_status_t) apr_shm_avail(apr_shmem_t *m, apr_size_t *size) { - apr_status_t status; + apr_status_t status = APR_ENOSHMAVAIL; - status = APR_ENOSHMAVAIL; - - apr_lock_acquire(m->lock); + if (m) + { + apr_lock_acquire(m->lock); + if ((*size = m->offsets->shm_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; } Index: shmem_lib.c =================================================================== file: /home/cvspublic/apr/shmem/unix/shmem_lib.c,v #include "shmem.h" /* * Find the insert position in a sorted list of elements. The sorting is * based on the value of the list[idx].offset. * Input : Begining index of the sorted list (*first), elem to be inserted. * Return : The index before which the elem is to be inserted. */ index_t apr_shm_pos_in_sorted_list(apr_shmem_t *m, index_t first, index_t elem) { index_t idx = first; if (idx < 0) return -1; do { if (m->list[idx].offset > m->list[elem].offset) break; idx = m->list[idx].next; } while ((idx >= 0) && (idx < APR_SHM_MAX_LIST_ELEM(m)) && (idx != first)); return idx; } /* * Adds a list element (elem) to the list pointed by *first. If *first points * to a freelist, it finds the appropirate insert position else appends elem * to the end of the list. * Ex. Adds a listelement to the usedlist / freelist. */ void apr_shm_addlist(apr_shmem_t *m, index_t *first, index_t elem) { index_t prev, idx; if (*first == m->offsets->c_free) idx = apr_shm_pos_in_sorted_list(m, *first, elem); else idx = *first; if (idx == -1) { idx = *first = elem; prev = elem; } else prev = m->list[idx].prev; m->list[idx].prev = elem; m->list[prev].next = elem; m->list[elem].prev = prev; m->list[elem].next = idx; if (idx == m->offsets->c_free) *first = elem; } /* * Removes the elem from the list pointed to by *first. * Ex. Removes a listelement from freelist so that it can be added to usedlist. */ void apr_shm_removelist(apr_shmem_t *m, index_t *first, index_t elem) { index_t prev, next; next = m->list[elem].next; prev = m->list[elem].prev; m->list[prev].next = next; m->list[next].prev = prev; if (next == elem) *first = -1; } /* * Frees a list element. This is useful during garbage collection. If 2 list * elements can be merged (to form a bigger chunk), the 2nd list element has * to be freed. The freed list element is made available at the end of the * list. (No need for maintaining a seperate list of available listelements) */ void apr_shm_freelist(apr_shmem_t *m, index_t elem) { index_t idx; if (elem >= 0) { idx = m->offsets->l_total - 1; memcpy(&m->list[elem], &m->list[idx], sizeof(memchunk_t)); m->list[m->list[idx].prev].next = idx; m->list[m->list[idx].next].prev = idx; m->offsets->l_total--; } } /* * Splits a memory chunk into two. The second mem chunk is allocated a list * element, and filled with updated memory offsets / size. */ index_t apr_shm_split_chunk(apr_shmem_t *m, index_t elem, apr_size_t size) { index_t nelem = m->offsets->l_total; if (nelem > APR_SHM_MAX_LIST_ELEM(m)) return -1; m->list[nelem].size = m->list[elem].size - size; m->list[elem].size = size; m->list[nelem].offset = m->list[elem].offset + size; apr_shm_addlist(m, &m->offsets->c_free, nelem); m->offsets->l_total++; return nelem; } /* * Finds the list element for a givem memory chunk */ index_t apr_shm_find_by_addr(apr_shmem_t *m, index_t first, void *addr) { index_t idx = first; if (idx < 0) return -1; do { if (APR_SHM_ADDR(m, m->list[idx].offset) == addr) return idx; } while ((idx >= 0) && ((idx = m->list[idx].next) != first)); return -1; } /* * Finds a list element that best satisfies the memory requirement. If there's * no best match available, it splits the memchunk into two. */ index_t apr_shm_find_by_size(apr_shmem_t *m, index_t first, apr_size_t size) { apr_size_t diff = -1; index_t idx = first, found = -1; if (idx < 0) return -1; do { if (m->list[idx].size == size) return idx; if (m->list[idx].size > size) { if ((diff == -1) || ((m->list[idx].size - size) < diff)) { diff = m->list[idx].size - size; found = idx; } } } while ((idx >= 0) && (idx = m->list[idx].next) != first); if (diff > MIN_BLK_SIZE) m->offsets->c_free = apr_shm_split_chunk(m, found, size); return found; } /* * Allocates a memory chunk. This also requires a list element to be allocated. * It first checks the freelist to see if any match is available. If none are * available, it allocates a new listelement. It adds the new listelement * to the c_used list. */ memchunk_t *apr_shm_alloc_chunk(apr_shmem_t *m, apr_size_t size) { index_t idx; size = ROUND_UP(size); if (m->offsets->shm_length < size) return NULL; idx = apr_shm_find_by_size(m, m->offsets->c_free, size); if (idx != -1) apr_shm_removelist(m, &m->offsets->c_free, idx); else { idx = m->offsets->l_total; if (idx >= APR_SHM_MAX_LIST_ELEM(m)) return NULL; m->list[idx].offset = m->offsets->shm_offset; m->list[idx].size = size; m->offsets->shm_offset += m->list[idx].size; m->offsets->l_total++; } m->offsets->shm_length -= m->list[idx].size; apr_shm_addlist(m, &m->offsets->c_used, idx); return (&m->list[idx]); } /* * Frees a memory chunk pointed by entity. It removes the corresponding * listelement from the c_used list and appends it to the c_free list */ void apr_shm_free_chunk(apr_shmem_t *m, void *entity) { index_t idx; if (entity == NULL) return; idx = apr_shm_find_by_addr(m, m->offsets->c_used, entity); if (idx != -1) { m->offsets->shm_length += m->list[idx].size; apr_shm_removelist(m, &m->offsets->c_used, idx); apr_shm_addlist(m, &m->offsets->c_free, idx); } } /* * Reallocates (resize) the memory pointed to by entity. It's not a true * replacement of the realloc() - in the sense if the entity is not found * in the used_list, it doesn't allocate a new memory of the requested size */ memchunk_t *apr_shm_realloc_chunk(apr_shmem_t *m, void *entity, apr_size_t size) { index_t idx; memchunk_t *new_b; size = ROUND_UP(size); idx = apr_shm_find_by_addr(m, m->offsets->c_used, entity); if (idx != -1) { if (m->list[idx].size > size) m->offsets->c_free = apr_shm_split_chunk(m, idx, size); else if ((m->list[idx].size < size) && (size < m->offsets->shm_length)) { new_b = apr_shm_alloc_chunk(m, size); memcpy(APR_SHM_ADDR(m, new_b->offset), APR_SHM_ADDR(m, m->list[idx].offset), m->list[idx].size); apr_shm_free_chunk(m, entity); idx = APR_SHM_LIST_INDEX(m,new_b); } } return ((idx >= 0) ? &m->list[idx] : NULL); } Index: shmem.h =================================================================== file: /home/cvspublic/apr/shmem/unix/shmem.h,v #ifndef SHMEM_H #define SHMEM_H #include "apr_general.h" #include "apr_shmem.h" #include "apr_lock.h" #include "apr_portable.h" #include "apr_errno.h" #define APR_WANT_MEMFUNC #include "apr_want.h" #ifdef __cplusplus extern "C" { #endif /* * Currently, this code supports the following shared memory techniques: * * - mmap on a temporary file * - mmap/shm_open on a temporary file (POSIX.1) * - mmap with MAP_ANON (4.4BSD) * - mmap /dev/zero (SVR4) * - shmget (SysV) * - create_area (BeOS) */ #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON #include <sys/mman.h> #elif APR_USE_SHMEM_SHMGET #include <sys/ipc.h> #include <sys/shm.h> #if !defined(SHM_R) #define SHM_R 0400 #endif #if !defined(SHM_W) #define SHM_W 0200 #endif #include <sys/file.h> #elif APR_USE_SHMEM_BEOS #include <kernel/OS.h> #endif #define MIN_BLK_SIZE 256 typedef apr_int32_t index_t; typedef struct memoffsets_t { index_t l_total; /* Index to start of Free list elements */ index_t c_used; /* Index to start of the used chunk list */ index_t c_free; /* Index to start of the freed chunk list */ apr_off_t shm_offset; /* The current offset of the shared memory */ apr_size_t shm_length; /* Total length of shared memory available */ } memoffsets_t; typedef struct memchunk_t { apr_off_t offset; /* Offset of the memory - from m->mem */ apr_size_t size; /* Size of the chunk */ index_t next; /* Index of Next chunk in the list */ index_t prev; /* Index of Previous chunk in the list*/ } memchunk_t; struct shmem_t { apr_pool_t *p; void *mem; /* Starting address of the shared memory */ memoffsets_t *offsets; /* Begining of the set of offsets */ memchunk_t *list; /* Begining of the list elements */ apr_size_t length; apr_size_t listlen; 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_SHMGET apr_os_file_t file; apr_os_file_t listfd; #elif APR_USE_SHMEM_BEOS area_id areaid; #endif }; #define APR_SHM_ADDR(m,offset) ((offset < 0) ? (void *)NULL : \ (void *)((unsigned char *)m->mem + offset)) #define APR_SHM_LIST_INDEX(m,ptr) ((ptr == NULL) ? -1 : \ (((unsigned char *)ptr - (unsigned char *)m->list)/sizeof(memchunk_t))) #define APR_SHM_MAX_LIST_ELEM(m) ((m->length / MIN_BLK_SIZE) + 1) #define ROUND_UP(size) ((size < MIN_BLK_SIZE) ? MIN_BLK_SIZE : \ ((1 + ((size - 1) / sizeof (void *))) * sizeof (void *))) memchunk_t *apr_shm_alloc_chunk(apr_shmem_t *m, apr_size_t size); memchunk_t *apr_shm_realloc_chunk(apr_shmem_t *m, void *entity, apr_size_t size); void apr_shm_free_chunk(apr_shmem_t *m, void *entity); index_t apr_shm_split_chunk(apr_shmem_t *m, index_t elem, apr_size_t size); index_t apr_shm_pos_in_sorted_list(apr_shmem_t *m, index_t first, index_t elem); index_t apr_shm_find_by_addr(apr_shmem_t *m, index_t first, void *addr); index_t apr_shm_find_by_size(apr_shmem_t *m, index_t first, apr_size_t size); void apr_shm_addlist(apr_shmem_t *m, index_t *first, index_t elem); void apr_shm_removelist(apr_shmem_t *m, index_t *first, index_t elem); void apr_shm_freelist(apr_shmem_t *m, index_t elem); #ifdef __cplusplus } #endif #endif /* !SHMEM_H */
