2023-08-16 22:26:55 +00:00
|
|
|
# ex:ts=8 sw=4:
|
2023-10-03 01:21:11 +00:00
|
|
|
# $OpenBSD: Affinity.pm,v 1.21 2023/10/02 17:52:58 espie Exp $
|
2023-08-16 22:26:55 +00:00
|
|
|
#
|
|
|
|
# Copyright (c) 2012-2013 Marc Espie <espie@openbsd.org>
|
|
|
|
#
|
|
|
|
# Permission to use, copy, modify, and distribute this software for any
|
|
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
|
|
# copyright notice and this permission notice appear in all copies.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
|
|
use v5.36;
|
|
|
|
|
|
|
|
# on multiple hosts setup, it's useful to record which host is building what,
|
|
|
|
# so that on restart, we try to avoid starting a task on the "wrong" box...
|
|
|
|
|
|
|
|
# note that this is only superficially similar to locks
|
|
|
|
|
|
|
|
use DPB::User;
|
|
|
|
package DPB::Affinity;
|
|
|
|
our @ISA = (qw(DPB::UserProxy));
|
|
|
|
|
|
|
|
use File::Path;
|
|
|
|
use DPB::PkgPath;
|
|
|
|
|
|
|
|
sub new($class, $state, $dir)
|
|
|
|
{
|
|
|
|
my $o = bless {dir => $dir, user => $state->{log_user}}, $class;
|
|
|
|
$o->make_path($dir);
|
|
|
|
$o->retrieve_existing_markers($state->logger);
|
|
|
|
return $o;
|
|
|
|
}
|
|
|
|
|
|
|
|
# each path being built creates an affinity marker
|
|
|
|
sub affinity_marker($self, $v)
|
|
|
|
{
|
|
|
|
my $s = $v->fullpkgpath;
|
|
|
|
$s =~ tr|/|.|;
|
|
|
|
return join('/', $self->{dir}, $s);
|
|
|
|
}
|
|
|
|
|
|
|
|
# we create a separate marker for each path being built in a MULTI_PACKAGES
|
|
|
|
# setting, so that if we finish building one, we lose the affinity for it.
|
|
|
|
sub start($self, $v, $core)
|
|
|
|
{
|
|
|
|
my $host = $core->hostname;
|
|
|
|
for my $w ($v->build_path_list) {
|
2023-10-03 01:21:11 +00:00
|
|
|
next if !defined $w->{info} or $w->{info}->is_stub;
|
2023-08-16 22:26:55 +00:00
|
|
|
my $fh = $self->open('>', $self->affinity_marker($w));
|
|
|
|
next if !defined $fh;
|
|
|
|
$w->{affinity} = $host;
|
|
|
|
print $fh "host=$host\n";
|
|
|
|
print $fh "path=", $w->fullpkgpath, "\n";
|
|
|
|
print $fh "initialpath=", $v->fullpkgpath, "\n";
|
|
|
|
if ($core->{inmem}) {
|
|
|
|
print $fh "mem=$core->{inmem}\n";
|
|
|
|
$w->{mem_affinity} = $core->{inmem};
|
|
|
|
}
|
|
|
|
close $fh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# when we see a package is already done, we have no way of knowing which
|
|
|
|
# MULTI_PACKAGES led to that, so we just unmark a single file
|
|
|
|
sub unmark($self, $v)
|
|
|
|
{
|
|
|
|
$self->unlink($self->affinity_marker($v));
|
|
|
|
delete $v->{affinity};
|
|
|
|
delete $v->{mem_affinity};
|
|
|
|
}
|
|
|
|
|
|
|
|
# on the other hand, when we finish building a port, we can unmark all paths.
|
|
|
|
sub finished($self, $v)
|
|
|
|
{
|
|
|
|
for my $w ($v->build_path_list) {
|
|
|
|
$self->unmark($w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub retrieve_existing_markers($self, $logger)
|
|
|
|
{
|
|
|
|
my $log = $logger->append('affinity');
|
|
|
|
my $d = $self->opendir($self->{dir});
|
|
|
|
return if !defined $d;
|
|
|
|
while (my $e = readdir $d) {
|
|
|
|
next unless -f "$self->{dir}/$e";
|
|
|
|
my $fh = $self->open('<', "$self->{dir}/$e");
|
|
|
|
return if !defined $fh;
|
|
|
|
my ($hostname, $pkgpath, $memory);
|
|
|
|
while (<$fh>) {
|
|
|
|
chomp;
|
|
|
|
if (m/^host\=(.*)/) {
|
|
|
|
$hostname = $1;
|
|
|
|
}
|
|
|
|
if (m/^path\=(.*)/) {
|
|
|
|
$pkgpath = $1;
|
|
|
|
}
|
|
|
|
if (m/^mem\=(.*)/) {
|
|
|
|
$memory = $1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close $fh;
|
|
|
|
next unless (defined $pkgpath) && (defined $hostname);
|
|
|
|
|
|
|
|
my $v = DPB::PkgPath->new($pkgpath);
|
|
|
|
$v->{affinity} = $hostname;
|
|
|
|
if ($memory) {
|
|
|
|
$v->{mem_affinity} = $memory;
|
|
|
|
}
|
|
|
|
print $log "$$:", $v->fullpkgpath, " => ", $hostname, "\n";
|
|
|
|
}
|
|
|
|
close $log;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub simplifies_to($self, $v, $w)
|
|
|
|
{
|
|
|
|
for my $tag ("affinity", "mem_affinity") {
|
|
|
|
if (defined $v->{$tag}) {
|
|
|
|
$w->{$tag} //= $v->{$tag};
|
|
|
|
}
|
|
|
|
if (defined $w->{$tag}) {
|
|
|
|
$v->{$tag} //= $w->{$tag};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
my $queued = {};
|
|
|
|
|
|
|
|
sub sorted($self, $queue, $core)
|
|
|
|
{
|
|
|
|
# okay, we know we have affinity stuff in the queue (maybe, so we want to do something special here
|
|
|
|
# maybe...
|
|
|
|
my $n = $core->hostname;
|
|
|
|
if ($queued->{$n}) {
|
|
|
|
# XXX for now, look directly inside the queue
|
|
|
|
my @l = grep
|
|
|
|
{ defined($_->{affinity}) && $_->{affinity} eq $n }
|
|
|
|
values %{$queue->{o}};
|
|
|
|
if (@l == 0) {
|
|
|
|
delete $queued->{$n};
|
|
|
|
} else {
|
|
|
|
return DPB::AffinityQueue->new(\@l, $queue, $core);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $queue->sorted($core);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub has_in_queue($self, $v)
|
|
|
|
{
|
|
|
|
if (defined $v->{affinity}) {
|
|
|
|
$queued->{$v->{affinity}} = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
package DPB::AffinityQueue;
|
|
|
|
|
|
|
|
sub new($class, $l, $queue, $core)
|
|
|
|
{
|
|
|
|
bless { l => $l,
|
|
|
|
queue => $queue,
|
|
|
|
core => $core}, $class;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub next($self)
|
|
|
|
{
|
|
|
|
if (@{$self->{l}} > 0) {
|
|
|
|
return pop @{$self->{l}};
|
|
|
|
}
|
|
|
|
if (!defined $self->{sorted}) {
|
|
|
|
$self->{sorted} =
|
|
|
|
$self->{queue}->sorted($self->{core});
|
|
|
|
}
|
|
|
|
return $self->{sorted}->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|