Page MenuHomeFreeBSD

D58183.diff
No OneTemporary

D58183.diff

diff --git a/usr.sbin/bsdconfig/include/messages.subr b/usr.sbin/bsdconfig/include/messages.subr
--- a/usr.sbin/bsdconfig/include/messages.subr
+++ b/usr.sbin/bsdconfig/include/messages.subr
@@ -174,6 +174,7 @@
msg_installed="Installed"
msg_installed_desc="Leave package as-is, installed"
msg_installed_lc="installed"
+msg_installing_n_packages="Installing %u package%s (up to %u at a time)"
msg_invalid_gateway_ipv4_address_specified="Invalid gateway IPv4 address specified"
msg_invalid_hostname_value="Invalid hostname value"
msg_invalid_ipv4_address="Invalid IPv4 address"
@@ -264,6 +265,7 @@
msg_other="other"
msg_package_is_needed_by_other_installed_packages="Warning: Package %s is needed by\n %d other installed package%s."
msg_package_not_installed_cannot_delete="Warning: package %s not installed\n No package can be deleted."
+msg_packages_failed_to_install="The following package%s failed to install:\n%s\nLogs have been saved to %s"
msg_package_temp="Package Temp"
msg_package_was_added_successfully="Package %s was added successfully"
msg_packages="packages"
diff --git a/usr.sbin/bsdconfig/share/packages/packages.subr b/usr.sbin/bsdconfig/share/packages/packages.subr
--- a/usr.sbin/bsdconfig/share/packages/packages.subr
+++ b/usr.sbin/bsdconfig/share/packages/packages.subr
@@ -48,6 +48,12 @@
#
: ${PACKAGE_MENU_PAGESIZE:=2000}
+#
+# How many packages to install simultaneously (dependencies permitting).
+# Default is the number of CPUs reported by hw.ncpu.
+#
+: ${PACKAGE_INSTALL_JOBS:=$( sysctl -n hw.ncpu 2> /dev/null || echo 1 )}
+
############################################################ GLOBALS
#
@@ -569,6 +575,256 @@
return $retval
}
+# f_package_parallel_install $package ...
+#
+# Install each $package argument concurrently, scaling to at-most
+# $PACKAGE_INSTALL_JOBS simultaneous pkg-install(8) jobs (default `hw.ncpu')
+# while honoring inter-package dependencies. A package becomes eligible for
+# installation the moment its last dependency is installed, allowing packages
+# with no outstanding dependencies to `skip the line' rather than wait on
+# unrelated installations. Dependency information comes from the INDEX (see
+# f_index_read() of packages/index.subr) while dependency solving within each
+# job is deferred to the pkg(8) SAT solver. Overall progress is displayed
+# with bsdpv(1). Packages whose dependencies fail to install are skipped.
+# Returns success if every package installed successfully.
+#
+# Job scheduling is achieved through Kahn-style topological ordering: each
+# package knows how many of its dependencies remain uninstalled and each
+# dependency knows which dependents to unblock upon completion. Workers
+# announce their exit status over a FIFO, serialized by the kernel guarantee
+# that writes of PIPE_BUF bytes or less are atomic.
+#
+f_package_parallel_install()
+{
+ local funcname=f_package_parallel_install
+ local package varpkg dep vardep rundeps rdeps state visit n
+ local maxjobs="$PACKAGE_INSTALL_JOBS"
+ local njobs=0 ndone=0 nfail=0 nskip=0 total=0
+ local ready= failed= logdir fifo pvfifo bsdpv_pid= status
+ local tmpdir
+
+ [ $# -gt 0 ] || return $SUCCESS
+
+ f_musthavepkg_init # Make sure we have a usable pkg(8) with $PKG_ABI
+
+ f_isinteger "$maxjobs" || maxjobs=1
+ [ $maxjobs -ge 1 ] || maxjobs=1
+
+ #
+ # Create a scratch area to hold the notification FIFOs and a
+ # pkg-install(8) log file for each package
+ #
+ f_getvar $VAR_PKG_TMPDIR:-/var/tmp tmpdir
+ logdir=$( mktemp -d "$tmpdir/packages.XXXXXX" ) || return $FAILURE
+ fifo="$logdir/job.fifo"
+ pvfifo="$logdir/bsdpv.fifo"
+ if ! mkfifo "$fifo" "$pvfifo"; then
+ rm -rf "$logdir"
+ return $FAILURE
+ fi
+
+ #
+ # Build the dependency graph for the requested set. Packages depended
+ # upon that are not part of the set are either already installed or
+ # will be satisfied by the pkg(8) solver within a single job.
+ #
+ for package in "$@"; do
+ f_str2varname $package varpkg
+ setvar _pjob_state_$varpkg wait
+ setvar _pjob_ndeps_$varpkg 0
+ setvar _pjob_rdeps_$varpkg ""
+ total=$(( $total + 1 ))
+ done
+ for package in "$@"; do
+ f_str2varname $package varpkg
+ rundeps=
+ debug= f_getvar _rundeps_$varpkg rundeps
+ for dep in $rundeps; do
+ f_str2varname $dep vardep
+ # Only count dependencies belonging to this job set
+ debug= f_getvar _pjob_state_$vardep state || continue
+ [ "$state" ] || continue
+ debug= f_getvar _pjob_ndeps_$varpkg n
+ setvar _pjob_ndeps_$varpkg $(( $n + 1 ))
+ debug= f_getvar _pjob_rdeps_$vardep visit
+ setvar _pjob_rdeps_$vardep "$visit $package"
+ done
+ done
+
+ #
+ # Seed the ready-queue with packages lacking pending dependencies
+ #
+ for package in "$@"; do
+ f_str2varname $package varpkg
+ debug= f_getvar _pjob_ndeps_$varpkg n
+ if [ $n -eq 0 ]; then
+ setvar _pjob_state_$varpkg ready
+ ready="$ready${ready:+ }$package"
+ fi
+ done
+ f_dprintf "%s: total=%u maxjobs=%u ready=[%s]" $funcname \
+ $total $maxjobs "$ready"
+
+ #
+ # Start the progress viewer, fed one line per package completion
+ #
+ local grammatical_s= pprompt
+ [ $total -ne 1 ] && grammatical_s=s
+ f_sprintf pprompt "$msg_installing_n_packages" \
+ $total "$grammatical_s" $maxjobs
+ if [ ! "$USE_XDIALOG" ] && f_have bsdpv; then
+ bsdpv -DklN -t "$DIALOG_TITLE" -b "$DIALOG_BACKTITLE" \
+ -p "$pprompt" "$total:$msg_packages" \
+ < "$pvfifo" &
+ bsdpv_pid=$!
+ else
+ f_show_info "%s" "$pprompt"
+ # Prevent deadlock on FIFO lacking a reader
+ cat < "$pvfifo" > /dev/null &
+ fi
+ exec 9> "$pvfifo"
+
+ # Hold the notification FIFO open so job completions never block and
+ # so read(1) below blocks (rather than EOF) while jobs are running
+ exec 8<> "$fifo"
+
+ #
+ # Dispatch jobs as dependencies allow, blocking on the notification
+ # FIFO whenever the ready-queue empties or all job slots are taken
+ #
+ while [ $(( $ndone + $nfail + $nskip )) -lt $total ]; do
+ #
+ # Fill available job slots from the ready-queue
+ #
+ while [ $njobs -lt $maxjobs -a "$ready" ]; do
+ package="${ready%% *}"
+ if [ "$package" = "$ready" ]; then
+ ready=
+ else
+ ready="${ready#* }"
+ fi
+ f_str2varname $package varpkg
+ debug= f_getvar _pjob_state_$varpkg state
+ [ "$state" = "ready" ] || continue # e.g., skipped
+ setvar _pjob_state_$varpkg busy
+ njobs=$(( $njobs + 1 ))
+ f_dprintf "%s: starting %s (%u/%u jobs)" $funcname \
+ "$package" $njobs $maxjobs
+ (
+ exec > "$logdir/$varpkg.log" 2>&1
+ export ASSUME_ALWAYS_YES=YES
+ export LOCK_WAIT=1 LOCK_RETRIES=7200
+ # Tolerate sibling pkg(8) locks
+ pkg fetch -U "$package"
+ # Parallel-friendly; failure deferred
+ # to pkg-install(8) below
+ pkg install -U "$package"
+ echo "$? $package" > "$fifo"
+ ) &
+ done
+
+ #
+ # Await a job completion notification
+ #
+ status= package=
+ read status package <&8
+ njobs=$(( $njobs - 1 ))
+ f_str2varname $package varpkg
+ if [ "$status" = "0" ]; then
+ ndone=$(( $ndone + 1 ))
+ setvar _pjob_state_$varpkg done
+ setvar _mark_$varpkg X
+ f_dprintf "%s: %s installed successfully" $funcname \
+ "$package"
+ echo "$package" >&9 # Advance bsdpv(1)
+
+ # Unblock dependents; enqueue those becoming ready
+ rdeps=
+ debug= f_getvar _pjob_rdeps_$varpkg rdeps
+ for dep in $rdeps; do
+ f_str2varname $dep vardep
+ debug= f_getvar _pjob_state_$vardep state
+ [ "$state" = "wait" ] || continue
+ debug= f_getvar _pjob_ndeps_$vardep n
+ setvar _pjob_ndeps_$vardep $(( $n - 1 ))
+ if [ $(( $n - 1 )) -eq 0 ]; then
+ setvar _pjob_state_$vardep ready
+ ready="$ready${ready:+ }$dep"
+ fi
+ done
+ else
+ nfail=$(( $nfail + 1 ))
+ setvar _pjob_state_$varpkg fail
+ failed="$failed${failed:+ }$package"
+ f_dprintf "%s: %s failed to install (see %s)" \
+ $funcname "$package" "$logdir/$varpkg.log"
+ echo "$package" >&9 # Advance bsdpv(1)
+
+ #
+ # Propagate failure breadth-first: dependents (and
+ # theirs) can never run, so mark each skipped and
+ # emit a progress line to keep accounting whole
+ #
+ visit="$package"
+ while [ "$visit" ]; do
+ package="${visit%% *}"
+ if [ "$package" = "$visit" ]; then
+ visit=
+ else
+ visit="${visit#* }"
+ fi
+ f_str2varname $package varpkg
+ rdeps=
+ debug= f_getvar _pjob_rdeps_$varpkg rdeps
+ for dep in $rdeps; do
+ f_str2varname $dep vardep
+ debug= f_getvar \
+ _pjob_state_$vardep state
+ case "$state" in
+ wait|ready) : ok ;;
+ *) continue
+ esac
+ setvar _pjob_state_$vardep skip
+ nskip=$(( $nskip + 1 ))
+ failed="$failed $dep"
+ f_dprintf "%s: %s skipped (%s)" \
+ $funcname "$dep" \
+ "dependency failed"
+ echo "$dep" >&9 # Advance bsdpv(1)
+ visit="$visit${visit:+ }$dep"
+ done
+ done
+ fi
+ done
+
+ # Dismiss the progress viewer and notification plumbing
+ exec 9>&-
+ exec 8<&-
+ [ "$bsdpv_pid" ] && wait $bsdpv_pid
+
+ # Discard per-package job state
+ for package in "$@"; do
+ f_str2varname $package varpkg
+ unset _pjob_state_$varpkg
+ unset _pjob_ndeps_$varpkg
+ unset _pjob_rdeps_$varpkg
+ done
+
+ f_dprintf "%s: ndone=%u nfail=%u nskip=%u" $funcname \
+ $ndone $nfail $nskip
+
+ if [ "$failed" ]; then
+ grammatical_s=
+ [ $(( $nfail + $nskip )) -ne 1 ] && grammatical_s=s
+ f_show_msg "$msg_packages_failed_to_install" \
+ "$grammatical_s" "$failed" "$logdir"
+ return $FAILURE
+ fi
+
+ rm -rf "$logdir"
+ return $SUCCESS
+}
+
# f_package_review
#
# Display a review screen, showing selected packages and what they are marked
@@ -626,18 +882,34 @@
#
# Process each of the selected packages:
- # + First, process packages marked for Install.
+ # + First, process packages marked for Install (parallel).
# + Second, process packages marked for Re-install.
# + Finally, process packages marked for Uninstall.
#
+ # Packages marked for Install -- and their to-be-installed
+ # dependencies (marked `D') -- are handed as one job set to
+ # f_package_parallel_install(), installing independent packages
+ # simultaneously while dependents await their dependencies.
+ #
+ local install_list=""
for package in $SELECTED_PACKAGES; do
mark=
f_str2varname "$package" varpkg
debug= f_getvar _mark_$varpkg mark
- [ "$mark" = "I" ] || continue
- f_dprintf "$funcname: Installing %s package" "$package"
- f_package_add "$package"
+ [ "$mark" = "I" -o "$mark" = "D" ] || continue
+ install_list="$install_list${install_list:+ }$package"
done
+ case "$install_list" in
+ "") : nothing to install ;;
+ *" "*)
+ f_dprintf "$funcname: Installing packages in parallel: %s" \
+ "$install_list"
+ f_package_parallel_install $install_list
+ ;;
+ *)
+ f_dprintf "$funcname: Installing %s package" "$install_list"
+ f_package_add "$install_list"
+ esac
for package in $SELECTED_PACKAGES; do
mark=
f_str2varname "$package" varpkg

File Metadata

Mime Type
text/plain
Expires
Fri, Aug 7, 6:40 AM (7 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36150515
Default Alt Text
D58183.diff (10 KB)

Event Timeline