apr-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "William A. Rowe, Jr." <wr...@rowe-clan.net>
Subject sdbm locking revisited [or, I surrender :-)
Date Tue, 08 May 2001 19:58:47 GMT
Ok, considering the interest by Greg and Ryan [from an offline conversation]
in offering refcounted locks, and the fact that exclusive locks hurt with
a large amount of concurency, I'm putting together Greg's and my requirements.

I've documented the shared-lock promotion problem in the apr_sdbm_lock() docs
below.

An interesting solution to the 'expect failure' in an APR_FLOCK_SHARED sdbm
when they try writing, should we return True from apr_sdbm_isreadonly() when
there is a shared lock in place (that can't be upgraded to exclusive)?

Anyway, I'd be happy to commit later today, once greg offers his 2c.

Bill


Index: dbm/sdbm/sdbm.c
===================================================================
RCS file: /home/cvs/apr-util/dbm/sdbm/sdbm.c,v
retrieving revision 1.20
diff -u -r1.20 sdbm.c
--- dbm/sdbm/sdbm.c 2001/05/06 13:21:17 1.20
+++ dbm/sdbm/sdbm.c 2001/05/08 19:46:06
@@ -112,8 +112,13 @@
 {
     apr_sdbm_t *db = data;
 
+    /*
+     * Can't rely on apr_sdbm_unlock, since it will merely
+     * decrement the refcnt if several locks are held.
+     */
+    if (db->flags & (SDBM_SHARED_LOCK | SDBM_EXCLUSIVE_LOCK))
+        (void) apr_file_unlock(db->dirf);
     (void) apr_file_close(db->dirf);
-    (void) sdbm_unlock(db);
     (void) apr_file_close(db->pagf);
     free(db);
 
@@ -143,6 +148,17 @@
         db->flags |= SDBM_RDONLY;
     }
 
+    /*
+     * adjust the file open flags so that we handle locking
+     * on our own (don't rely on any locking behavior within
+     * an apr_file_t, in case it's ever introduced, and set
+     * our own flag.
+     */
+    if (flags & APR_SHARELOCK) {
+        db->flags |= SDBM_SHARED;
+        flags &= ~APR_SHARELOCK;
+    }
+
     flags |= APR_BINARY | APR_READ;
 
     /*
@@ -150,15 +166,17 @@
      * If we fail anywhere, undo everything, return NULL.
      */
 
-    if ((status = apr_file_open(&db->pagf, pagname, flags, perms, p))
+    if ((status = apr_file_open(&db->dirf, dirname, flags, perms, p))
                 != APR_SUCCESS)
         goto error;
 
-    if ((status = sdbm_lock(db, !(db->flags & SDBM_RDONLY)))
+    if ((status = apr_file_open(&db->pagf, pagname, flags, perms, p))
                 != APR_SUCCESS)
         goto error;
 
-    if ((status = apr_file_open(&db->dirf, dirname, flags, perms, p))
+    if ((status = apr_sdbm_lock(db, (db->flags & SDBM_RDONLY) 
+                                        ? APR_FLOCK_SHARED
+                                        : APR_FLOCK_EXCLUSIVE))
                 != APR_SUCCESS)
         goto error;
 
@@ -173,12 +191,17 @@
      * zero size: either a fresh database, or one with a single,
      * unsplit data page: dirpage is all zeros.
      */
-    db->dirbno = (!finfo.size) ? 0 : -1;
-    db->pagbno = -1;
-    db->maxbno = finfo.size * BYTESIZ;
+    SDBM_INVALIDATE_CACHE(db, finfo);
 
     /* (apr_pcalloc zeroed the buffers) */
 
+    /*
+     * if we are opened in SHARED mode, unlock ourself 
+     */
+    if (db->flags & SDBM_SHARED)
+        if ((status = apr_sdbm_unlock(db)) != APR_SUCCESS)
+            goto error;
+
     /* make sure that we close the database at some point */
     apr_pool_cleanup_register(p, db, database_cleanup, apr_pool_cleanup_null);
 
@@ -187,19 +210,20 @@
     return APR_SUCCESS;
 
 error:
+    if (db->dirf && db->pagf)
+        (void) apr_sdbm_unlock(db);
     if (db->dirf != NULL)
         (void) apr_file_close(db->dirf);
     if (db->pagf != NULL) {
-        (void) sdbm_unlock(db);
         (void) apr_file_close(db->pagf);
     }
     free(db);
     return status;
 }
 
-apr_status_t apr_sdbm_open(apr_sdbm_t **db, const char *file, 
-                           apr_int32_t flags, apr_fileperms_t perms,
-                           apr_pool_t *p)
+APU_DECLARE(apr_status_t) apr_sdbm_open(apr_sdbm_t **db, const char *file, 
+                                        apr_int32_t flags, 
+                                        apr_fileperms_t perms, apr_pool_t *p)
 {
     char *dirname = apr_pstrcat(p, file, APR_SDBM_DIRFEXT, NULL);
     char *pagname = apr_pstrcat(p, file, APR_SDBM_PAGFEXT, NULL);
@@ -207,24 +231,29 @@
     return prep(db, dirname, pagname, flags, perms, p);
 }
 
-apr_status_t apr_sdbm_close(apr_sdbm_t *db)
+APU_DECLARE(apr_status_t) apr_sdbm_close(apr_sdbm_t *db)
 {
     return apr_pool_cleanup_run(db->pool, db, database_cleanup);
 }
 
-apr_status_t apr_sdbm_fetch(apr_sdbm_t *db, apr_sdbm_datum_t *val, 
-                            apr_sdbm_datum_t key)
+APU_DECLARE(apr_status_t) apr_sdbm_fetch(apr_sdbm_t *db, apr_sdbm_datum_t *val,
+                                         apr_sdbm_datum_t key)
 {
     apr_status_t status;
+    
     if (db == NULL || bad(key))
         return APR_EINVAL;
 
+    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
+        return status;
+
     if ((status = getpage(db, exhash(key))) == APR_SUCCESS) {
         *val = getpair(db->pagbuf, key);
         /* ### do we want a not-found result? */
-        return APR_SUCCESS;
     }
 
+    (void) apr_sdbm_unlock(db);
+
     return status;
 }
 
@@ -239,42 +268,43 @@
     return status;
 }
 
-apr_status_t apr_sdbm_delete(apr_sdbm_t *db, const apr_sdbm_datum_t key)
+APU_DECLARE(apr_status_t) apr_sdbm_delete(apr_sdbm_t *db, 
+                                          const apr_sdbm_datum_t key)
 {
     apr_status_t status;
-
+    
     if (db == NULL || bad(key))
         return APR_EINVAL;
     if (apr_sdbm_rdonly(db))
         return APR_EINVAL;
+    
+    if ((status = apr_sdbm_lock(db, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS)
+        return status;
 
     if ((status = getpage(db, exhash(key))) == APR_SUCCESS) {
         if (!delpair(db->pagbuf, key))
             /* ### should we define some APRUTIL codes? */
-            return APR_EGENERAL;
-
-        /*
-         * update the page file
-         */
-        status = write_page(db, db->pagbuf, db->pagbno);
-        return status;
+            status = APR_EGENERAL;
+        else
+            status = write_page(db, db->pagbuf, db->pagbno);
     }
+
+    (void) apr_sdbm_unlock(db);
 
-    return APR_EACCES;
+    return status;
 }
 
-apr_status_t apr_sdbm_store(apr_sdbm_t *db, apr_sdbm_datum_t key, 
-                            apr_sdbm_datum_t val, int flags)
+APU_DECLARE(apr_status_t) apr_sdbm_store(apr_sdbm_t *db, apr_sdbm_datum_t key,
+                                         apr_sdbm_datum_t val, int flags)
 {
     int need;
     register long hash;
     apr_status_t status;
-
+    
     if (db == NULL || bad(key))
         return APR_EINVAL;
     if (apr_sdbm_rdonly(db))
         return APR_EINVAL;
-
     need = key.dsize + val.dsize;
     /*
      * is the pair too big (or too small) for this database ??
@@ -282,6 +312,9 @@
     if (need < 0 || need > PAIRMAX)
         return APR_EINVAL;
 
+    if ((status = apr_sdbm_lock(db, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS)
+        return status;
+
     if ((status = getpage(db, (hash = exhash(key)))) == APR_SUCCESS) {
 
         /*
@@ -290,21 +323,28 @@
          */
         if (flags == APR_SDBM_REPLACE)
             (void) delpair(db->pagbuf, key);
-        else if (!(flags & APR_SDBM_INSERTDUP) && duppair(db->pagbuf, key))
-            return APR_EEXIST;
+        else if (!(flags & APR_SDBM_INSERTDUP) && duppair(db->pagbuf, key))
{
+            status = APR_EEXIST;
+            goto error;
+        }
         /*
          * if we do not have enough room, we have to split.
          */
         if (!fitpair(db->pagbuf, need))
             if ((status = makroom(db, hash, need)) != APR_SUCCESS)
-                return status;
+                goto error;
         /*
          * we have enough room or split is successful. insert the key,
          * and update the page file.
          */
         (void) putpair(db->pagbuf, key, val);
+
         status = write_page(db, db->pagbuf, db->pagbno);
-    }    
+    }
+
+error:
+    (void) apr_sdbm_unlock(db);    
+
     return status;
 }
 
@@ -405,6 +445,7 @@
             status = APR_SUCCESS;
         }
     }
+
     return status;
 }
 
@@ -412,26 +453,37 @@
  * the following two routines will break if
  * deletions aren't taken into account. (ndbm bug)
  */
-apr_status_t apr_sdbm_firstkey(apr_sdbm_t *db, apr_sdbm_datum_t *key)
+APU_DECLARE(apr_status_t) apr_sdbm_firstkey(apr_sdbm_t *db, 
+                                            apr_sdbm_datum_t *key)
 {
+    apr_status_t status;
+    
+    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
+        return status;
+
     /*
      * start at page 0
      */
-    apr_status_t status;
-    if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(0), PBLKSIZ))
-                != APR_SUCCESS)
-        return status;
+    status = read_from(db->pagf, db->pagbuf, OFF_PAG(0), PBLKSIZ);
 
-    db->pagbno = 0;
-    db->blkptr = 0;
-    db->keyptr = 0;
+    (void) apr_sdbm_unlock(db);
 
     return getnext(key, db);
 }
 
-apr_status_t apr_sdbm_nextkey(apr_sdbm_t *db, apr_sdbm_datum_t *key)
+APU_DECLARE(apr_status_t) apr_sdbm_nextkey(apr_sdbm_t *db, 
+                                           apr_sdbm_datum_t *key)
 {
-    return getnext(key, db);
+    apr_status_t status;
+    
+    if ((status = apr_sdbm_lock(db, APR_FLOCK_SHARED)) != APR_SUCCESS)
+        return status;
+
+    status = getnext(key, db);
+
+    (void) apr_sdbm_unlock(db);
+
+    return status;
 }
 
 /*
@@ -467,9 +519,8 @@
          * ### we make it so in read_from anyway.
          */
         if ((status = read_from(db->pagf, db->pagbuf, OFF_PAG(pagb), PBLKSIZ)) 
-                    != APR_SUCCESS) {
+                    != APR_SUCCESS)
             return status;
-        }
 
         if (!chkpage(db->pagbuf))
             return APR_ENOSPC; /* ### better error? */
@@ -571,8 +622,11 @@
 }
 
 
-int apr_sdbm_rdonly(apr_sdbm_t *db)
+APU_DECLARE(int) apr_sdbm_rdonly(apr_sdbm_t *db)
 {
+    /* ### Should we return true if the first lock is a share lock,
+     *     to reflect that apr_sdbm_store and apr_sdbm_delete will fail?
+     */
     return (db->flags & SDBM_RDONLY) != 0;
 }
 
Index: dbm/sdbm/sdbm_lock.c
===================================================================
RCS file: /home/cvs/apr-util/dbm/sdbm/sdbm_lock.c,v
retrieving revision 1.6
diff -u -r1.6 sdbm_lock.c
--- dbm/sdbm/sdbm_lock.c 2001/04/30 17:16:19 1.6
+++ dbm/sdbm/sdbm_lock.c 2001/05/08 19:46:07
@@ -52,25 +52,65 @@
  * <http://www.apache.org/>.
  */
 
+#include "apr_file_info.h"
 #include "apr_file_io.h"
 #include "apr_sdbm.h"
 
 #include "sdbm_private.h"
+#include "sdbm_tune.h"
 
 /* NOTE: this function blocks until it acquires the lock */
-apr_status_t sdbm_lock(apr_sdbm_t *db, int exclusive)
+APU_DECLARE(apr_status_t) apr_sdbm_lock(apr_sdbm_t *db, int type)
 {
-    int type;
+    apr_status_t status;
 
-    if (exclusive)
-        type = APR_FLOCK_EXCLUSIVE;
-    else
-        type = APR_FLOCK_SHARED;
+    if (!(type == APR_FLOCK_SHARED || type == APR_FLOCK_EXCLUSIVE))
+        return APR_EINVAL;
 
-    return apr_file_lock(db->pagf, type);
+    if (db->flags & SDBM_EXCLUSIVE_LOCK) {
+        ++db->lckcnt;
+        return APR_SUCCESS;
+    }
+    else if (db->flags & SDBM_SHARED_LOCK) {
+        /*
+         * Cannot promote a shared lock to an exlusive lock
+         * in a cross-platform compatibile manner.
+         */
+        if (type == APR_FLOCK_EXCLUSIVE)
+            return APR_EINVAL;
+        ++db->lckcnt;
+        return APR_SUCCESS;
+    }
+    /*
+     * zero size: either a fresh database, or one with a single,
+     * unsplit data page: dirpage is all zeros.
+     */
+    if ((status = apr_file_lock(db->dirf, type)) == APR_SUCCESS) 
+    {
+        apr_finfo_t finfo;
+        if ((status = apr_file_info_get(&finfo, APR_FINFO_SIZE, db->dirf))
+                != APR_SUCCESS) {
+            (void) apr_file_unlock(db->dirf);
+            return status;
+        }
+
+        SDBM_INVALIDATE_CACHE(db, finfo);
+
+        ++db->lckcnt;
+        if (type == APR_FLOCK_SHARED)
+            db->flags |= SDBM_SHARED_LOCK;
+        else if (type == APR_FLOCK_EXCLUSIVE)
+            db->flags |= SDBM_EXCLUSIVE_LOCK;
+    }
+    return status;
 }
 
-apr_status_t sdbm_unlock(apr_sdbm_t *db)
+APU_DECLARE(apr_status_t) apr_sdbm_unlock(apr_sdbm_t *db)
 {
-    return apr_file_unlock(db->pagf);
+    if (!(db->flags & (SDBM_SHARED_LOCK | SDBM_EXCLUSIVE_LOCK)))
+        return APR_EINVAL;
+    if (--db->lckcnt > 0)
+        return APR_SUCCESS;
+    db->flags &= ~(SDBM_SHARED_LOCK | SDBM_EXCLUSIVE_LOCK);
+    return apr_file_unlock(db->dirf);
 }
Index: dbm/sdbm/sdbm_private.h
===================================================================
RCS file: /home/cvs/apr-util/dbm/sdbm/sdbm_private.h,v
retrieving revision 1.6
diff -u -r1.6 sdbm_private.h
--- dbm/sdbm/sdbm_private.h 2001/05/06 12:58:05 1.6
+++ dbm/sdbm/sdbm_private.h 2001/05/08 19:46:07
@@ -79,30 +79,40 @@
 #define SPLTMAX 10   /* maximum allowed splits */
 
 /* for apr_sdbm_t.flags */
-#define SDBM_RDONLY 0x1        /* data base open read-only */
+#define SDBM_RDONLY         0x1    /* data base open read-only */
+#define SDBM_SHARED         0x2    /* data base open for sharing */
+#define SDBM_SHARED_LOCK 0x4    /* data base locked for shared read */
+#define SDBM_EXCLUSIVE_LOCK 0x8    /* data base locked for write */
 
 struct apr_sdbm_t {
     apr_pool_t *pool;
     apr_file_t *dirf;         /* directory file descriptor */
     apr_file_t *pagf;         /* page file descriptor */
-    apr_int32_t flags;         /* status/error flags, see above */
+    apr_int32_t flags;         /* status/error flags, see below */
     long maxbno;         /* size of dirfile in bits */
     long curbit;         /* current bit number */
     long hmask;          /* current hash mask */
     long blkptr;         /* current block for nextkey */
-    int keyptr;          /* current key for nextkey */
+    int  keyptr;         /* current key for nextkey */
     long blkno;          /* current page to read/write */
     long pagbno;         /* current page in pagbuf */
     char pagbuf[PBLKSIZ];        /* page file block buffer */
     long dirbno;         /* current block in dirbuf */
     char dirbuf[DBLKSIZ];        /* directory file block buffer */
+    int  lckcnt;                       /* number of calls to sdbm_lock */
 };
 
-apr_status_t sdbm_lock(apr_sdbm_t *db, int exclusive);
-apr_status_t sdbm_unlock(apr_sdbm_t *db);
-
 extern const apr_sdbm_datum_t sdbm_nullitem;
 
 long sdbm_hash(const char *str, int len);
+
+/*
+ * zero the cache
+ */
+#define SDBM_INVALIDATE_CACHE(db, finfo) \
+    do { db->dirbno = (!finfo.size) ? 0 : -1; \
+         db->pagbno = -1; \
+         db->maxbno = finfo.size * BYTESIZ; \
+    } while (0);
 
 #endif /* SDBM_PRIVATE_H */
Index: include/apr_sdbm.h
===================================================================
RCS file: /home/cvs/apr-util/include/apr_sdbm.h,v
retrieving revision 1.7
diff -u -r1.7 apr_sdbm.h
--- include/apr_sdbm.h 2001/05/06 12:58:05 1.7
+++ include/apr_sdbm.h 2001/05/08 19:46:10
@@ -62,14 +62,24 @@
 #ifndef APR_SDBM_H
 #define APR_SDBM_H
 
+#include "apu.h"
 #include "apr_errno.h"
 #include "apr_file_io.h"   /* for apr_fileperms_t */
 
+/**
+ * @package apr-util SDBM library
+ */
+
+/**
+ * Structure for referencing an sdbm
+ * @defvar apr_sdbm_t
+ */
 typedef struct apr_sdbm_t apr_sdbm_t;
 
-/* utility functions */
-int apr_sdbm_rdonly(apr_sdbm_t *db);
-
+/**
+ * Structure for referencing the datum record within an sdbm
+ * @defvar apr_sdbm_datum_t
+ */
 typedef struct {
     char *dptr;
     int dsize;
@@ -78,25 +88,128 @@
 /* The extensions used for the database files */
 #define APR_SDBM_DIRFEXT ".dir"
 #define APR_SDBM_PAGFEXT ".pag"
-
-/* Standard dbm interface */
-
-apr_status_t apr_sdbm_open(apr_sdbm_t **db, const char *filename, 
-                           apr_int32_t flags, apr_fileperms_t perms, 
-                           apr_pool_t *p);
 
-apr_status_t apr_sdbm_close(apr_sdbm_t *db);
-
-apr_status_t apr_sdbm_fetch(apr_sdbm_t *db, apr_sdbm_datum_t *val, apr_sdbm_datum_t key);
-apr_status_t apr_sdbm_delete(apr_sdbm_t *db, const apr_sdbm_datum_t key);
-
-/* * flags to sdbm_store */
+/* flags to sdbm_store */
 #define APR_SDBM_INSERT     0
 #define APR_SDBM_REPLACE    1
 #define APR_SDBM_INSERTDUP  2
-apr_status_t apr_sdbm_store(apr_sdbm_t *db, apr_sdbm_datum_t key, 
-                            apr_sdbm_datum_t value, int flags);
-apr_status_t apr_sdbm_firstkey(apr_sdbm_t *db, apr_sdbm_datum_t *key);
-apr_status_t apr_sdbm_nextkey(apr_sdbm_t *db, apr_sdbm_datum_t *key);
+
+/**
+ * Open an sdbm database by file name
+ * @param db The newly opened database
+ * @param name The sdbm file to open
+ * @param mode The flag values (APR_READ and APR_BINARY flags are implicit)
+ * <PRE>
+ *           APR_WRITE          open for read-write access
+ *           APR_CREATE         create the sdbm if it does not exist
+ *           APR_TRUNCATE       empty the contents of the sdbm
+ *           APR_EXCL           fail for APR_CREATE if the file exists
+ *           APR_DELONCLOSE     delete the sdbm when closed
+ *           APR_SHARELOCK      support locking across process/machines
+ * </PRE>
+ * @param perm Permissions to apply to if created
+ * @param pool The pool to use when creating the sdbm
+ * @deffunc apr_status_t apr_sdbm_open(apr_sdbm_t **db, const char *name, apr_int32_t mode,
apr_fileperms_t perms, apr_pool_t *p)
+ * @tip The sdbm name is not a true file name, as sdbm appends suffixes 
+ * for seperate data and index files.
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_open(apr_sdbm_t **db, const char *name, 
+                                        apr_int32_t mode, 
+                                        apr_fileperms_t perms, apr_pool_t *p);
+
+/**
+ * Close an sdbm file previously opened by apr_sdbm_open
+ * @param db The database to close
+ * @deffunc apr_status_t apr_sdbm_close(apr_sdbm_t *db)
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_close(apr_sdbm_t *db);
+
+/**
+ * Lock an sdbm database for concurency of multiple operations
+ * @param db The database to lock
+ * @param type The lock type
+ * <PRE>
+ *           APR_FLOCK_SHARED
+ *           APR_FLOCK_EXCLUSIVE
+ * </PRE>
+ * @deffunc apr_status_t apr_sdbm_lock(apr_sdbm_t *db, int type)
+ * @tip Calls to apr_sdbm_lock may be nested.  All apr_sdbm functions
+ * perform implicit locking.  Since an APR_FLOCK_SHARED lock cannot be 
+ * portably promoted to an APR_FLOCK_EXCLUSIVE lock, apr_sdbm_store and 
+ * apr_sdbm_delete calls will fail if an APR_FLOCK_SHARED lock is held.
+ * The apr_sdbm_lock call requires the database to be opened with the
+ * APR_SHARELOCK mode value.
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_lock(apr_sdbm_t *db, int type);
+
+/**
+ * Release an sdbm lock previously aquired by apr_sdbm_lock
+ * @param db The database to unlock
+ * @deffunc apr_status_t apr_sdbm_unlock(apr_sdbm_t *db)
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_unlock(apr_sdbm_t *db);
+
+/**
+ * Fetch an sdbm record value by key
+ * @param db The database 
+ * @param value The value datum retrieved for this record
+ * @param key The key datum to find this record
+ * @deffunc apr_status_t apr_status_t apr_sdbm_fetch(apr_sdbm_t *db, apr_sdbm_datum_t *value,
apr_sdbm_datum_t key)
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_fetch(apr_sdbm_t *db, 
+                                         apr_sdbm_datum_t *value, 
+                                         apr_sdbm_datum_t key);
+
+/**
+ * Store an sdbm record value by key
+ * @param db The database 
+ * @param key The key datum to store this record by
+ * @param value The value datum to store in this record
+ * @param opt The method used to store the record
+ * <PRE>
+ *           APR_SDBM_INSERT     return an error if the record exists
+ *           APR_SDBM_REPLACE    overwrite any existing record for key
+ * </PRE>
+ * @deffunc apr_status_t apr_sdbm_store(apr_sdbm_t *db, apr_sdbm_datum_t key, apr_sdbm_datum_t
value, int opt)
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_store(apr_sdbm_t *db, apr_sdbm_datum_t key,
+                                         apr_sdbm_datum_t value, int opt);
+
+/**
+ * Delete an sdbm record value by key
+ * @param db The database 
+ * @param key The key datum of the record to delete
+ * @deffunc apr_status_t apr_sdbm_delete(apr_sdbm_t *db, const apr_sdbm_datum_t key)
+ * @tip It is not an error to delete a non-existent record.
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_delete(apr_sdbm_t *db, 
+                                          const apr_sdbm_datum_t key);
+
+/**
+ * Retrieve the first record key from a dbm
+ * @param dbm The database 
+ * @param key The key datum of the first record
+ * @deffunc apr_status_t apr_sdbm_nextkey(apr_sdbm_t *db, apr_sdbm_datum_t *key)
+ * @tip The keys returned are not ordered.  To traverse the list of keys
+ * for an sdbm opened with APR_SHARELOCK, the caller must use apr_sdbm_lock
+ * prior to retrieving the first record, and hold the lock until after the
+ * last call to apr_sdbm_nextkey.
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_firstkey(apr_sdbm_t *db, apr_sdbm_datum_t *key);
+
+/**
+ * Retrieve the next record key from an sdbm
+ * @param db The database 
+ * @param key The key datum of the next record
+ * @deffunc apr_status_t apr_sdbm_nextkey(apr_sdbm_t *db, apr_sdbm_datum_t *key)
+ */
+APU_DECLARE(apr_status_t) apr_sdbm_nextkey(apr_sdbm_t *db, apr_sdbm_datum_t *key);
+
+/**
+ * Returns true if the sdbm database opened for read-only access
+ * @param db The database to test
+ * @deffunc int apr_sdbm_rdonly(apr_sdbm_t *db)
+ */
+APU_DECLARE(int) apr_sdbm_rdonly(apr_sdbm_t *db);
 
 #endif /* APR_SDBM_H */



Mime
View raw message