Subject: Re: Simultaneous builds
To: David Maxwell <david@fundy.net>
From: Alistair Crooks <agc@pkgsrc.org>
List: tech-pkg
Date: 01/15/2002 15:35:00
--5vNYLRcllDrimb99
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Fri, Jan 11, 2002 at 11:31:11AM -0400, David Maxwell wrote:
> 
> Is that 'doesn't do too well on NFS' with multiple clients, or with
> multiple connections from one client? (or both?)
> 
> I guess multiple machines of the same ARCH using the same pkgsrc tree
> wasn't something I was concerned about. Intuitively, I would have
> expected shlock to work okay with NFS from a single client though.

Yes, shlock does work fine from a single client. But it needs a
single coherent PID space to do its job effectively, so that
precludes multiple clients using the same ${WRKDIR}s.

Anyway, help is at hand.

You may have noticed that I made some modifications to bsd.pkg.mk
to append the first component of the hostname to a WRKDIR, if
OBJHOSTNAME is defined in /etc/mk.conf. I've also committed a
package for the shlock utility, which can be used on Operating
Systems which don't have it as standard (and here I've guessed
at Solaris and Linux - unfortunately I can't connect to the
Darwin host right now to check).

Together with the attached diffs, you should be able to use
the PKGSRC_LOCKTYPE= "sleep" or "once" (in /etc/mk.conf), and
also the PKGSRC_SLEEPSECS definition if you're using "sleep"
waits.

With the attached diffs applied, and PKGSRC_LOCKTYPE set to "sleep"
in /etc/mk.conf, I get the following output in one window:

[14:23:45] agc@fs0 ...pkgsrc/devel/cvs 185 > make
=> Checksum OK for cvs-1.11.tar.gz.
=> Checksum OK for cvs-1.11-v6-20020111.diff.gz.
work.fs0 -> /usr/obj/pkgsrc/devel/cvs/work.fs0
=> Lock acquired on behalf of process 22189
===> extract-message [cvs-1.11nb2] ===> Extracting for cvs-1.11nb2
=> Lock released on behalf of process 22189
=> Lock acquired on behalf of process 22189
===> patch-message [cvs-1.11nb2] ===> Patching for cvs-1.11nb2
===> do-patch [cvs-1.11nb2] ===> Applying distribution patches for cvs-1.11nb2
===> do-patch [cvs-1.11nb2] ===> Applying distribution patch cvs-1.11-v6-20020111.diff.gz
Hmm...  Looks like a new-style context diff to me...
The text leading up to this was:
...

and a simultaneous make in another window gives...

[14:09:10] agc@fs0 ...pkgsrc/devel/cvs 130 > make
=> Checksum OK for cvs-1.11.tar.gz.
=> Checksum OK for cvs-1.11-v6-20020111.diff.gz.
=> Lock is held by pid 22189
=> Lock is held by pid 22189
^C[14:23:56] agc@fs0 ...pkgsrc/devel/cvs 131 >

The reason I posted these here is that I've had to move the creation
of the ${WRKDIR} out of the do-extract target, and I would like
others to check that I haven't borked anything.

If I get positive feedback, I'll apply these diffs to the main
tree.

Thanks,
Alistair

--5vNYLRcllDrimb99
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=locking-diffs

Index: bsd.pkg.defaults.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/bsd.pkg.defaults.mk,v
retrieving revision 1.32
diff -u -r1.32 bsd.pkg.defaults.mk
--- bsd.pkg.defaults.mk	2002/01/11 14:41:42	1.32
+++ bsd.pkg.defaults.mk	2002/01/15 14:23:55
@@ -31,6 +31,24 @@
 # Possible: defined, not defined
 # Default: not defined
 
+PKGSRC_LOCKTYPE?= none
+# The type of locking which will be done if competing processes attempt to
+# do work on one package directory simultaneously.
+# + Locking requires that OBJHOSTNAME is set.
+# + Locking may require the pkgsrc/pkgtools/shlock package to be installed
+#   on certain OS types.
+# + Sleep locking means that the process will sleep for ${PKGSRC_SLEEPSECS}
+#   seconds, then try to acquire the lock, and sleeping again if it's not
+#   available.
+# + Once locking will try once to acquire the lock, and then fail.
+# Possible: none, sleep, once
+# Default: none
+
+PKGSRC_SLEEPSECS?= 5
+# The number of seconds to wait when PKGSRC_LOCKTYPE is sleep
+# Possible: a positive integer
+# Default: 5
+
 #USETBL=
 # Run raw manual pages through tbl(1) before piping through troff(1)
 # when formatting manual pages.
Index: bsd.pkg.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/bsd.pkg.mk,v
retrieving revision 1.903
diff -u -r1.903 bsd.pkg.mk
--- bsd.pkg.mk	2002/01/15 09:02:09	1.903
+++ bsd.pkg.mk	2002/01/15 14:24:00
@@ -911,6 +911,20 @@
 	@${FALSE}
 .endif
 
+.if (${PKGSRC_LOCKTYPE} == "sleep" || ${PKGSRC_LOCKTYPE} == "once") && !defined(OBJHOSTNAME) 
+.BEGIN:
+	@${ECHO_MSG} 'PKGSRC_LOCKTYPE needs OBJHOSTNAME defined.'
+	@${FALSE}
+.endif
+
+.if (${PKGSRC_LOCKTYPE} == "sleep" || ${PKGSRC_LOCKTYPE} == "once") && !exists(${SHLOCK}) 
+.BEGIN:
+	@${ECHO_MSG} 'The ${SHLOCK} utility does not exist, and is necessary for locking.'
+	@${ECHO_MSG} 'Please go to the ${.CURDIR}/../../pkgtools/shlock directory, and type'
+	@${ECHO_MSG} 'make install'
+	@${FALSE}
+.endif
+
 PKGREPOSITORYSUBDIR?=	All
 PKGREPOSITORY?=		${PACKAGES}/${PKGREPOSITORYSUBDIR}
 PKGFILE?=		${PKGREPOSITORY}/${PKGNAME}${PKG_SUFX}
@@ -1383,6 +1397,31 @@
 .  endif
 .endif
 
+# pkgsrc coarse-grained locking definitions and targets
+.if ${PKGSRC_LOCKTYPE} == "none"
+_ACQUIRE_LOCK=	${_PKG_SILENT}${_PKG_DEBUG}${DO_NADA}
+_RELEASE_LOCK=	${_PKG_SILENT}${_PKG_DEBUG}${DO_NADA}
+.else
+LOCKFILE=	${WRKDIR}/.lockfile
+
+_ACQUIRE_LOCK=								\
+	${_PKG_SILENT}${_PKG_DEBUG}					\
+	ppid=`${PS} -p $$$$ -o ppid | ${AWK} 'NR == 2 { print $$0 }'`;	\
+	while ${TRUE}; do						\
+		${SHLOCK} -f ${LOCKFILE} -p $$ppid && break;		\
+		${ECHO} "=> Lock is held by pid `cat ${LOCKFILE}`";	\
+		case "${PKGSRC_LOCKTYPE}" in				\
+		once)	exit 1 ;;					\
+		sleep)	sleep ${PKGSRC_SLEEPSECS} ;;			\
+		esac							\
+	done;								\
+	${ECHO_MSG} "=> Lock acquired on behalf of process $$ppid"
+
+_RELEASE_LOCK=								\
+	${_PKG_SILENT}${_PKG_DEBUG}					\
+	${ECHO_MSG} "=> Lock released on behalf of process `${CAT} ${LOCKFILE}`"; \
+	${RM} ${LOCKFILE}
+.endif # PKGSRC_LOCKTYPE
 
 # Extract
 
@@ -1450,19 +1489,30 @@
 .  endif
 .endfor
 
-.if !target(do-extract)
-do-extract:
-.  ifndef KEEP_WRKDIR
+${WRKDIR}:
+.if !defined(KEEP_WRKDIR)
+.  if ${PKGSRC_LOCKTYPE} == "sleep" || ${PKGSRC_LOCKTYPE} == "once"
+.    if !exists(${LOCKFILE})
 	${_PKG_SILENT}${_PKG_DEBUG}${RM} -rf ${WRKDIR}
+.    endif
 .  endif
+.endif
 	${_PKG_SILENT}${_PKG_DEBUG}${MKDIR} ${WRKDIR}
-.  ifdef WRKOBJDIR
+.ifdef WRKOBJDIR
+.  if ${PKGSRC_LOCKTYPE} == "sleep" || ${PKGSRC_LOCKTYPE} == "once"
+.    if !exists(${LOCKFILE})
 	${_PKG_SILENT}${_PKG_DEBUG}					\
-	${RM} -f ${WRKDIR_BASENAME} || ${TRUE};				\
+	${RM} -f ${WRKDIR_BASENAME} || ${TRUE}
+.    endif
+.  endif
+	${_PKG_SILENT}${_PKG_DEBUG}					\
 	if ${LN} -s ${WRKDIR} ${WRKDIR_BASENAME} 2>/dev/null; then	\
 		${ECHO} "${WRKDIR_BASENAME} -> ${WRKDIR}";		\
 	fi
-.  endif # WRKOBJDIR
+.endif # WRKOBJDIR
+
+.if !target(do-extract)
+do-extract: ${WRKDIR}
 .  if defined(EXTRACT_CMD) && !empty(EXTRACT_CMD)
 	${_PKG_SILENT}${_PKG_DEBUG}					\
 	for file in "" ${EXTRACT_ONLY}; do				\
@@ -1820,7 +1870,7 @@
 	if [ X"$$found" != X"" ]; then					\
 		${ECHO} "$$found" >> ${WRKDIR}/.CONFLICTS;		\
 	fi
-.    endfor
+.     endfor
 	${_PKG_SILENT}${_PKG_DEBUG}					\
 	if [ -s ${WRKDIR}/.CONFLICTS ]; then \
 		found=`${SED} -e s'|${PKG_DBDIR}/||g' ${WRKDIR}/.CONFLICTS | tr '\012' ' '`; \
@@ -2140,6 +2190,23 @@
 .  endif # libc.sylib
 .endif
 
+acquire-extract-lock:
+	${_ACQUIRE_LOCK}
+acquire-patch-lock:
+	${_ACQUIRE_LOCK}
+acquire-configure-lock:
+	${_ACQUIRE_LOCK}
+acquire-build-lock:
+	${_ACQUIRE_LOCK}
+
+release-extract-lock:
+	${_RELEASE_LOCK}
+release-patch-lock:
+	${_RELEASE_LOCK}
+release-configure-lock:
+	${_RELEASE_LOCK}
+release-build-lock:
+	${_RELEASE_LOCK}
 
 ################################################################
 # Skeleton targets start here
@@ -2156,19 +2223,19 @@
 .endif
 
 .if !target(extract)
-extract: checksum ${EXTRACT_COOKIE}
+extract: checksum ${WRKDIR} acquire-extract-lock ${EXTRACT_COOKIE} release-extract-lock
 .endif
 
 .if !target(patch)
-patch: extract ${PATCH_COOKIE}
+patch: extract acquire-patch-lock ${PATCH_COOKIE} release-patch-lock
 .endif
 
 .if !target(configure)
-configure: patch ${CONFIGURE_COOKIE}
+configure: patch acquire-configure-lock ${CONFIGURE_COOKIE} release-configure-lock
 .endif
 
 .if !target(build)
-build: configure ${BUILD_COOKIE}
+build: configure acquire-build-lock ${BUILD_COOKIE} release-build-lock
 .endif
 
 .if !target(install)
Index: defs.Darwin.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/defs.Darwin.mk,v
retrieving revision 1.16
diff -u -r1.16 defs.Darwin.mk
--- defs.Darwin.mk	2001/12/27 21:27:07	1.16
+++ defs.Darwin.mk	2002/01/15 14:24:00
@@ -36,11 +36,13 @@
 PATCH?=		/usr/bin/patch
 PAX?=		/bin/pax
 PKGLOCALEDIR?=	share
+PS?=		/bin/ps
 RM?=		/bin/rm
 RMDIR?=		/bin/rmdir
 SED?=		/usr/bin/sed
 SETENV?=	/usr/bin/env
 SH?=		/bin/sh
+SHLOCK=		/usr/bin/shlock
 SORT?=		/usr/bin/sort
 SU?=		/usr/bin/su
 TAIL?=		/usr/bin/tail
Index: defs.Linux.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/defs.Linux.mk,v
retrieving revision 1.21
diff -u -r1.21 defs.Linux.mk
--- defs.Linux.mk	2001/12/19 10:29:11	1.21
+++ defs.Linux.mk	2002/01/15 14:24:01
@@ -36,11 +36,13 @@
 PATCH?=		/usr/bin/patch
 PAX?=		${ZOULARISBASE}/bin/pax
 PKGLOCALEDIR?=	share
+PS?=		/bin/ps
 RM?=		/bin/rm
 RMDIR?=		/bin/rmdir
 SED?=		/bin/sed
 SETENV?=	/usr/bin/env
 SH?=		/bin/sh
+SHLOCK=		${LOCALBASE}/bin/shlock
 .if exists(/bin/sort)
 SORT?=		/bin/sort
 .else
Index: defs.NetBSD.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/defs.NetBSD.mk,v
retrieving revision 1.19
diff -u -r1.19 defs.NetBSD.mk
--- defs.NetBSD.mk	2001/12/19 10:29:11	1.19
+++ defs.NetBSD.mk	2002/01/15 14:24:01
@@ -36,11 +36,13 @@
 PATCH?=		/usr/bin/patch
 PAX?=		/bin/pax
 PKGLOCALEDIR?=	share
+PS?=		/bin/ps
 RM?=		/bin/rm
 RMDIR?=		/bin/rmdir
 SED?=		/usr/bin/sed
 SETENV?=	/usr/bin/env
 SH?=		/bin/sh
+SHLOCK=		/usr/bin/shlock
 SORT?=		/usr/bin/sort
 SU?=		/usr/bin/su
 TAIL?=		/usr/bin/tail
Index: defs.SunOS.mk
===================================================================
RCS file: /cvsroot/pkgsrc/mk/defs.SunOS.mk,v
retrieving revision 1.19
diff -u -r1.19 defs.SunOS.mk
--- defs.SunOS.mk	2001/12/19 10:29:11	1.19
+++ defs.SunOS.mk	2002/01/15 14:24:01
@@ -47,11 +47,13 @@
 .endif
 PAX?=		/bin/pax
 PKGLOCALEDIR?=	lib
+PS?=		/bin/ps
 RM?=		/usr/bin/rm
 RMDIR?=		/usr/bin/rmdir
 SED?=		/usr/xpg4/bin/sed
 SETENV?=	/usr/bin/env
 SH?=		/bin/ksh
+SHLOCK=		${LOCALBASE}/bin/shlock
 SORT?=		/usr/bin/sort
 SU?=		/usr/bin/su
 TAIL?=		/usr/xpg4/bin/tail

--5vNYLRcllDrimb99--