Affinity.pm 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. # ex:ts=8 sw=4:
  2. # $OpenBSD: Affinity.pm,v 1.18 2016/05/15 20:08:30 espie Exp $
  3. #
  4. # Copyright (c) 2012-2013 Marc Espie <espie@openbsd.org>
  5. #
  6. # Permission to use, copy, modify, and distribute this software for any
  7. # purpose with or without fee is hereby granted, provided that the above
  8. # copyright notice and this permission notice appear in all copies.
  9. #
  10. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. use strict;
  18. use warnings;
  19. # on multiple hosts setup, it's useful to record which host is building what,
  20. # so that on restart, we try to avoid starting a task on the "wrong" box...
  21. # note that this is only superficially similar to locks
  22. use DPB::User;
  23. package DPB::Affinity;
  24. our @ISA = (qw(DPB::UserProxy));
  25. use File::Path;
  26. use DPB::PkgPath;
  27. sub new
  28. {
  29. my ($class, $state, $dir) = @_;
  30. my $o = bless {dir => $dir, user => $state->{log_user}}, $class;
  31. $o->make_path($dir);
  32. $o->retrieve_existing_markers($state->logger);
  33. return $o;
  34. }
  35. # each path being built creates an affinity marker
  36. sub affinity_marker
  37. {
  38. my ($self, $v) = @_;
  39. my $s = $v->fullpkgpath;
  40. $s =~ tr|/|.|;
  41. return join('/', $self->{dir}, $s);
  42. }
  43. # we create a separate marker for each path being built in a MULTI_PACKAGES
  44. # setting, so that if we finish building one, we lose the affinity for it.
  45. sub start
  46. {
  47. my ($self, $v, $core) = @_;
  48. my $host = $core->hostname;
  49. for my $w ($v->build_path_list) {
  50. next if $w->{info}->is_stub;
  51. my $fh = $self->open('>', $self->affinity_marker($w));
  52. next if !defined $fh;
  53. $w->{affinity} = $host;
  54. print $fh "host=$host\n";
  55. print $fh "path=", $w->fullpkgpath, "\n";
  56. if ($core->{inmem}) {
  57. print $fh "mem=$core->{inmem}\n";
  58. $w->{mem_affinity} = $core->{inmem};
  59. }
  60. close $fh;
  61. }
  62. }
  63. # when we see a package is already done, we have no way of knowing which
  64. # MULTI_PACKAGES led to that, so we just unmark a single file
  65. sub unmark
  66. {
  67. my ($self, $v) = @_;
  68. $self->unlink($self->affinity_marker($v));
  69. delete $v->{affinity};
  70. delete $v->{mem_affinity};
  71. }
  72. # on the other hand, when we finish building a port, we can unmark all paths.
  73. sub finished
  74. {
  75. my ($self, $v) = @_;
  76. for my $w ($v->build_path_list) {
  77. $self->unmark($w);
  78. }
  79. }
  80. sub retrieve_existing_markers
  81. {
  82. my ($self, $logger) = @_;
  83. my $log = $logger->append('affinity');
  84. my $d = $self->opendir($self->{dir});
  85. return if !defined $d;
  86. while (my $e = readdir $d) {
  87. next unless -f "$self->{dir}/$e";
  88. my $fh = $self->open('<', "$self->{dir}/$e");
  89. return if !defined $fh;
  90. my ($hostname, $pkgpath, $memory);
  91. while (<$fh>) {
  92. chomp;
  93. if (m/^host\=(.*)/) {
  94. $hostname = $1;
  95. }
  96. if (m/^path\=(.*)/) {
  97. $pkgpath = $1;
  98. }
  99. if (m/^mem\=(.*)/) {
  100. $memory = $1;
  101. }
  102. }
  103. close $fh;
  104. next unless (defined $pkgpath) && (defined $hostname);
  105. my $v = DPB::PkgPath->new($pkgpath);
  106. $v->{affinity} = $hostname;
  107. if ($memory) {
  108. $v->{mem_affinity} = $memory;
  109. }
  110. print $log "$$:", $v->fullpkgpath, " => ", $hostname, "\n";
  111. }
  112. close $log;
  113. }
  114. sub simplifies_to
  115. {
  116. my ($self, $v, $w) = @_;
  117. for my $tag ("affinity", "mem_affinity") {
  118. if (defined $v->{$tag}) {
  119. $w->{$tag} //= $v->{$tag};
  120. }
  121. if (defined $w->{$tag}) {
  122. $v->{$tag} //= $w->{$tag};
  123. }
  124. }
  125. }
  126. my $queued = {};
  127. sub sorted
  128. {
  129. my ($self, $queue, $core) = @_;
  130. # okay, we know we have affinity stuff in the queue (maybe, so we want to do something special here
  131. # maybe...
  132. my $n = $core->hostname;
  133. if ($queued->{$n}) {
  134. # XXX for now, look directly inside the queue
  135. my @l = grep
  136. { defined($_->{affinity}) && $_->{affinity} eq $n }
  137. values %{$queue->{o}};
  138. if (@l == 0) {
  139. delete $queued->{$n};
  140. } else {
  141. return DPB::AffinityQueue->new(\@l, $queue, $core);
  142. }
  143. }
  144. return $queue->sorted($core);
  145. }
  146. sub has_in_queue
  147. {
  148. my ($self, $v) = @_;
  149. if (defined $v->{affinity}) {
  150. $queued->{$v->{affinity}} = 1;
  151. }
  152. }
  153. package DPB::AffinityQueue;
  154. sub new
  155. {
  156. my ($class, $l, $queue, $core) = @_;
  157. bless { l => $l,
  158. queue => $queue,
  159. core => $core}, $class;
  160. }
  161. sub next
  162. {
  163. my $self = shift;
  164. if (@{$self->{l}} > 0) {
  165. return pop @{$self->{l}};
  166. }
  167. if (!defined $self->{sorted}) {
  168. $self->{sorted} =
  169. $self->{queue}->sorted($self->{core});
  170. }
  171. return $self->{sorted}->next;
  172. }
  173. 1;