CVS update: /ccvs/, /ccvs/src/

[email protected] 2 May 2005 19:16:39 -0000
Newsgroups gmane.comp.version-control.cvs.cvs
Message-ID <[email protected]>
User: dprice  
Date: 05/05/02 12:16:39

Modified:
 /ccvs/
  ChangeLog, NEWS
 /ccvs/src/
  ChangeLog, cvs.h, lock.c, sanity.sh, tag.c

Log:
 Merge changes from 1.11.x.

File Changes:

Directory: /ccvs/
=================

File [changed]: ChangeLog
Url: https://ccvs.cvshome.org/source/browse/ccvs/ChangeLog?r1=1.1199&r2=1.1200
Delta lines:  +4 -0
-------------------
--- ChangeLog	2 May 2005 14:13:08 -0000	1.1199
+++ ChangeLog	2 May 2005 19:16:35 -0000	1.1200
@@ -1,5 +1,9 @@
 2005-05-02  Derek Price  <[email protected]>
 
+	* NEWS: Note new val-tags locks.
+
+2005-05-02  Derek Price  <[email protected]>
+
 	* NEWS: Note that lock compatibility is now enabled on Windows by
 	default.
 

File [changed]: NEWS
Url: https://ccvs.cvshome.org/source/browse/ccvs/NEWS?r1=1.315&r2=1.316
Delta lines:  +8 -4
-------------------
--- NEWS	2 May 2005 14:13:08 -0000	1.315
+++ NEWS	2 May 2005 19:16:35 -0000	1.316
@@ -14,10 +14,14 @@
 * Thanks to an old report and patch from Stewart Brodie <[email protected]>, a
   potential crash in response to a corrupt RCS file has been fixed.
 
-* CVS now locks the history file before writing to it.  Especially with large
-  repositories, users should no longer see new warnings about corrupt history
-  records when using the `cvs history' command.  Existing corrupt history
-  records will still need to be removed.
+* CVS now locks the history and val-tags files before writing to them.
+  Especially with large repositories, users should no longer see new warnings
+  about corrupt history records when using the `cvs history' command.  Existing
+  corrupt history records will still need to be removed manually.  val-tags
+  corruption should have had less obvious effects, but removing the
+  CVSROOT/val-tags file and allowing a 1.11.21 or later version of CVS to
+  regenerate it may eliminate a few odd behaviors and possibly cause a slight
+  speed up of read transactions in large repositories over time.
 
 Changes from 1.12.11 to 1.12.12:
 ********************************

Directory: /ccvs/src/
=====================

File [changed]: ChangeLog
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/ChangeLog?r1=1.3181&r2=1.3182
Delta lines:  +16 -0
--------------------
--- ChangeLog	30 Apr 2005 16:52:13 -0000	1.3181
+++ ChangeLog	2 May 2005 19:16:35 -0000	1.3182
@@ -1,3 +1,19 @@
+2005-05-02  Derek Price  <[email protected]>
+
+	* cvs.h (CVSHISTLCK): Rename macro to...
+	(CVSHISTORYLCK): ...this.
+	(CVSVALTAGSLCK): New macro.
+	(val_tags_lock, clear_val_tags_lock): New functions.
+	* lock.c (global_val_tags_lock): New global.
+	(Lock_Cleanup): Clean up after val-tags lock if necessary.
+	(L_HISTORY_LOCK, L_VAL_TAGS_LOCK): New local macros.
+	(internal_lock, val_tags_lock, clear_val_tags_lock): New functions.
+	(history_lock): Use new internal function.
+	* tag.c (is_in_val_tags, add_to_val_tags): New functions using the
+	write lock for val-tags and factored from...
+	(tag_check_valid): ...this function.
+	* sanity.sh (lockfiles-22): Add val-tags lock test.
+
 2005-04-30  Mark D. Baushke  <[email protected]>
 
 	* lock.c (global_readlock, global_writelock, global_history_lock):

File [changed]: cvs.h
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/cvs.h?r1=1.332&r2=1.333
Delta lines:  +6 -1
-------------------
--- cvs.h	30 Apr 2005 03:55:07 -0000	1.332
+++ cvs.h	2 May 2005 19:16:36 -0000	1.333
@@ -193,7 +193,8 @@
 #define	CVSATTIC	"Attic"
 
 #define	CVSLCK		"#cvs.lock"
-#define	CVSHISTLCK	"#cvs.history.lock"
+#define	CVSHISTORYLCK	"#cvs.history.lock"
+#define	CVSVALTAGSLCK	"#cvs.val-tags.lock"
 #define	CVSRFL		"#cvs.rfl"
 #define	CVSPFL		"#cvs.pfl"
 #define	CVSWFL		"#cvs.wfl"
@@ -561,6 +562,10 @@
 int history_lock (const char *);
 void clear_history_lock (void);
 
+/* Get a write lock for the val-tags file.  */
+int val_tags_lock (const char *);
+void clear_val_tags_lock (void);
+
 void Scratch_Entry (List * list, const char *fname);
 void ParseTag (char **tagp, char **datep, int *nonbranchp);
 void WriteTag (const char *dir, const char *tag, const char *date,

File [changed]: lock.c
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/lock.c?r1=1.114&r2=1.115
Delta lines:  +74 -13
---------------------
--- lock.c	30 Apr 2005 16:52:13 -0000	1.114
+++ lock.c	2 May 2005 19:16:36 -0000	1.115
@@ -133,13 +133,17 @@
 static struct lock global_readlock = {NULL, NULL, NULL, CVSLCK, NULL, false};
 static struct lock global_writelock = {NULL, NULL, NULL, CVSLCK, NULL, false};
 
-static struct lock global_history_lock = {NULL, NULL, NULL, CVSHISTLCK,
+static struct lock global_history_lock = {NULL, NULL, NULL, CVSHISTORYLCK,
+					  NULL, false};
+static struct lock global_val_tags_lock = {NULL, NULL, NULL, CVSVALTAGSLCK,
 					  NULL, false};
 #else
 static struct lock global_readlock = {NULL, NULL, CVSLCK, NULL, false};
 static struct lock global_writelock = {NULL, NULL, CVSLCK, NULL, false};
 
-static struct lock global_history_lock = {NULL, NULL, CVSHISTLCK, NULL,
+static struct lock global_history_lock = {NULL, NULL, CVSHISTORYLCK, NULL,
+					  false};
+static struct lock global_val_tags_lock = {NULL, NULL, CVSVALTAGSLCK, NULL,
 					  false};
 #endif /* LOCK_COMPATIBILITY */
 
@@ -385,6 +389,12 @@
     /* clean up simple write locks (if any) */
     if (global_history_lock.repository)
 	remove_lock_files (&global_history_lock, true);
+    SIG_endCrSect();
+
+    SIG_beginCrSect();
+
+    if (global_val_tags_lock.repository)
+	remove_lock_files (&global_val_tags_lock, true);
     /* See note in Lock_Cleanup() below.  */
     SIG_endCrSect();
 }
@@ -1294,21 +1304,42 @@
 
 
 
-/* Get a write lock for the history file.  Return true on success and false on
- * error.
+#define L_HISTORY_LOCK	1
+#define L_VAL_TAGS_LOCK	2
+
+/* This is the internal implementation behind history_lock & val_tags_lock.  It
+ * gets a write lock for the history or val-tags file.
+ *
+ * RETURNS
+ *   true, on success
+ *   false, on error
  */
-int
-history_lock (xrepository)
-    const char *xrepository;
+static inline int
+internal_lock (const char *xrepository, int type)
 {
+    struct lock *lock;
+
+    switch (type)
+    {
+	case L_HISTORY_LOCK:
+	    lock = &global_history_lock;
+	    break;
+
+	case L_VAL_TAGS_LOCK:
+	    lock = &global_val_tags_lock;
+	    break;
+
+	default:
+	    error (1, 0, "internal error: unknown lock type requested");
+    }
+
     /* remember what we're locking (for Lock_Cleanup) */
-    assert (!global_history_lock.repository);
-    global_history_lock.repository = Xasprintf ("%s/%s", xrepository,
-						CVSROOTADM);
-    global_history_lock.free_repository = true;
+    assert (!lock->repository);
+    lock->repository = Xasprintf ("%s/%s", xrepository, CVSROOTADM);
+    lock->free_repository = true;
 
     /* get the lock dir for our own */
-    if (set_lock (&global_history_lock, 1) != L_OK)
+    if (set_lock (lock, 1) != L_OK)
     {
 	if (!really_quiet)
 	    error (0, 0, "failed to obtain history lock in repository `%s'",
@@ -1322,10 +1353,40 @@
 
 
 
-/* Remove the history lock, if it exists.
+/* Lock the CVSROOT/history file for write.
+ */
+int
+history_lock (const char *xrepository)
+{
+    return internal_lock (xrepository, L_HISTORY_LOCK);
+}
+
+
+
+/* Remove the CVSROOT/history lock, if it exists.
  */
 void
 clear_history_lock ()
 {
     remove_lock_files (&global_history_lock, true);
 }
+
+
+
+/* Lock the CVSROOT/val-tags file for write.
+ */
+int
+val_tags_lock (const char *xrepository)
+{
+    return internal_lock (xrepository, L_VAL_TAGS_LOCK);
+}
+
+
+
+/* Remove the CVSROOT/val-tags lock, if it exists.
+ */
+void
+clear_val_tags_lock ()
+{
+    remove_lock_files (&global_val_tags_lock, true);
+}

File [changed]: sanity.sh
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/sanity.sh?r1=1.1059&r2=1.1060
Delta lines:  +9 -0
-------------------
--- sanity.sh	30 Apr 2005 03:55:07 -0000	1.1059
+++ sanity.sh	2 May 2005 19:16:36 -0000	1.1060
@@ -22559,6 +22559,15 @@
 $SPROG commit: \[[0-9:]*\] waiting for $username's lock in $CVSROOT_DIRNAME/CVSROOT
 $SPROG commit: \[[0-9:]*\] obtained lock in $CVSROOT_DIRNAME/CVSROOT"
 
+	  dotest lockfiles-21 "$testcvs -Q tag newtag first-dir"
+
+	  rm $CVSROOT_DIRNAME/CVSROOT/val-tags
+	  mkdir "$TESTDIR/locks/CVSROOT/#cvs.val-tags.lock"
+	  (sleep 5; rmdir "$TESTDIR/locks/CVSROOT/#cvs.val-tags.lock")&
+	  dotest lockfiles-22 "$testcvs -q up -r newtag first-dir" \
+"$SPROG update: \[[0-9:]*\] waiting for $username's lock in $CVSROOT_DIRNAME/CVSROOT
+$SPROG update: \[[0-9:]*\] obtained lock in $CVSROOT_DIRNAME/CVSROOT"
+
 	  cd CVSROOT
 	  dotest lockfiles-cleanup-1 "$testcvs -q up -pr1.1 config >config" ""
 	  dotest lockfiles-cleanup-2 "$testcvs -q ci -m config-it" \

File [changed]: tag.c
Url: https://ccvs.cvshome.org/source/browse/ccvs/src/tag.c?r1=1.137&r2=1.138
Delta lines:  +154 -75
----------------------
--- tag.c	17 Mar 2005 22:41:06 -0000	1.137
+++ tag.c	2 May 2005 19:16:36 -0000	1.138
@@ -1410,6 +1410,154 @@
 
 
 
+/* This routine determines whether a tag appears in CVSROOT/val-tags.
+ *
+ * The val-tags file will be open read-only when IDB is NULL.  Since writes to
+ * val-tags always append to it, the lack of locking is okay.  The worst case
+ * race condition might misinterpret a partially written "foobar" matched, for
+ * instance,  a request for "f", "foo", of "foob".  Such a mismatch would be
+ * caught harmlessly later.
+ *
+ * Before CVS adds a tag to val-tags, it will lock val-tags for write and
+ * verify that the tag is still not present to avoid adding it twice.
+ *
+ * NOTES
+ *   This function expects its parent to handle any necessary locking of the
+ *   val-tags file.
+ *
+ * INPUTS
+ *   idb	When this value is NULL, the val-tags file is opened in
+ *   		in read-only mode.  When present, the val-tags file is opened
+ *   		in read-write mode and the DBM handle is stored in *IDB.
+ *   name	The tag to search for.
+ *
+ * OUTPUTS
+ *   *idb	The val-tags file opened for read/write, or NULL if it couldn't
+ *   		be opened.
+ *
+ * ERRORS
+ *   Exits with an error message if the val-tags file cannot be opened for
+ *   read (failure to open val-tags read/write is harmless - see below).
+ *
+ * RETURNS
+ *   true	1. If NAME exists in val-tags.
+ *   		2. If IDB is non-NULL and val-tags cannot be opened for write.
+ *   		   This allows callers to ignore the harmless inability to
+ *   		   update the val-tags cache.
+ *   false	If the file could be opened and the tag is not present.
+ */
+static int is_in_val_tags (DBM **idb, const char *name)
+{
+    DBM *db = NULL;
+    char *valtags_filename;
+    datum mytag;
+    int status;
+
+    /* Casting out const should be safe here - input datums are not
+     * written to by the myndbm functions.
+     */
+    mytag.dptr = (char *)name;
+    mytag.dsize = strlen (name);
+
+    valtags_filename = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
+				  CVSROOTADM, CVSROOTADM_VALTAGS);
+
+    if (idb)
+    {
+	db = dbm_open (valtags_filename, O_RDWR, 0666);
+	if (!db)
+	{
+	    mode_t omask;
+
+	    if (!existence_error (errno))
+	    {
+		error (0, errno, "warning: cannot open %s read/write",
+		       valtags_filename);
+		*idb = NULL;
+		return 1;
+	    }
+
+	    omask = umask (cvsumask);
+	    db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
+	    umask (omask);
+	    if (!db)
+	    {
+		error (0, errno, "warning: cannot create %s",
+		       valtags_filename);
+		*idb = NULL;
+		return 1;
+	    }
+
+	    *idb = db;
+	    return 0;
+	}
+
+	*idb = db;
+    }
+    else
+    {
+	db = dbm_open (valtags_filename, O_RDONLY, 0444);
+	if (!db && !existence_error (errno))
+	    error (1, errno, "cannot read %s", valtags_filename);
+    }
+
+    /* If the file merely fails to exist, we just keep going and create
+       it later if need be.  */
+
+    status = 0;
+    if (db)
+    {
+	datum val;
+
+	val = dbm_fetch (db, mytag);
+	if (val.dptr != NULL)
+	    /* Found.  The tag is valid.  */
+	    status = 1;
+
+	/* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
+
+	if (!idb) dbm_close (db);
+    }
+
+    free (valtags_filename);
+    return status;
+}
+
+
+
+/* Add a tag to the CVSROOT/val-tags cache.  Establishes a write lock and
+ * reverifies that the tag does not exist before adding it.
+ */
+static void add_to_val_tags (const char *name)
+{
+    DBM *db;
+    datum mytag;
+    datum value;
+
+    if (noexec) return;
+
+    val_tags_lock (current_parsed_root->directory);
+
+    /* Check for presence again since we have a lock now.  */
+    if (is_in_val_tags (&db, name)) return;
+
+    /* Casting out const should be safe here - input datums are not
+     * written to by the myndbm functions.
+     */
+    mytag.dptr = (char *)name;
+    mytag.dsize = strlen (name);
+    value.dptr = "y";
+    value.dsize = 1;
+
+    if (dbm_store (db, mytag, value, DBM_REPLACE) < 0)
+	error (0, errno, "failed to store %s into val-tags", name);
+    dbm_close (db);
+
+    clear_val_tags_lock ();
+}
+
+
+
 static Dtype
 val_direntproc (void *callerdat, const char *dir, const char *repository,
                 const char *update_dir, List *entries)
@@ -1458,10 +1606,6 @@
 tag_check_valid (const char *name, int argc, char **argv, int local, int aflag,
                  char *repository, bool valid)
 {
-    DBM *db;
-    char *valtags_filename;
-    int nowrite = 0;
-    datum mytag, val;
     struct val_args the_val_args;
     struct saved_cwd cwd;
     int which;
@@ -1507,46 +1651,7 @@
      */
     RCS_check_tag (name);
 
-    /* FIXME: This routine doesn't seem to do any locking whatsoever
-       (and it is called from places which don't have locks in place).
-       If two processes try to write val-tags at the same time, it would
-       seem like we are in trouble.  */
-
-    mytag.dptr = xstrdup (name);
-    mytag.dsize = strlen (name);
-
-    valtags_filename = Xasprintf ("%s/%s/%s", current_parsed_root->directory,
-                                  CVSROOTADM, CVSROOTADM_VALTAGS);
-    db = dbm_open (valtags_filename, O_RDWR, 0666);
-    if (db == NULL)
-    {
-	if (!existence_error (errno))
-	{
-	    error (0, errno, "warning: cannot open %s read/write",
-		   valtags_filename);
-	    db = dbm_open (valtags_filename, O_RDONLY, 0666);
-	    if (db != NULL)
-		nowrite = 1;
-	    else if (!existence_error (errno))
-		error (1, errno, "cannot read %s", valtags_filename);
-	}
-	/* If the file merely fails to exist, we just keep going and create
-	   it later if need be.  */
-    }
-    if (db != NULL)
-    {
-	val = dbm_fetch (db, mytag);
-	if (val.dptr)
-	{
-	    /* The tag is already in val-tags - return valid and don't insert
-	     * it a second time.
-	     */
-	    dbm_close (db);
-	    free (valtags_filename);
-	    return;
-	}
-	/* FIXME: should check errors somehow (add dbm_error to myndbm.c?).  */
-    }
+    if (is_in_val_tags (NULL, name)) return;
 
     if (!valid)
     {
@@ -1586,35 +1691,9 @@
 	    error (1, 0, "no such tag `%s'", name);
     }
 
+    if (!the_val_args.found)
+	error (1, 0, "no such tag %s", name);
+    else
     /* The tags is valid but not mentioned in val-tags.  Add it.  */
-    if (noexec || nowrite)
-    {
-	if (db != NULL)
-	    dbm_close (db);
-	free (valtags_filename);
-	return;
-    }
-
-    if (db == NULL)
-    {
-	mode_t omask;
-	omask = umask (cvsumask);
-	db = dbm_open (valtags_filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
-	(void)umask (omask);
-
-	if (db == NULL)
-	{
-	    error (0, errno, "warning: cannot create %s", valtags_filename);
-	    free (valtags_filename);
-	    return;
-	}
-    }
-    val.dptr = "y";
-    val.dsize = 1;
-    if (dbm_store (db, mytag, val, DBM_REPLACE) < 0)
-	error (0, errno, "cannot store %s into %s", name,
-	       valtags_filename);
-    dbm_close (db);
-    free (mytag.dptr);
-    free (valtags_filename);
+	add_to_val_tags (name);
 }