user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
blob f90c8b568f07e9bd8de700fcd0530c9823ff37a7 2552 bytes (raw)
name: lib/PublicInbox/Limiter.pm 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
 
# Copyright (C) all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>

package PublicInbox::Limiter;
use v5.12;
use PublicInbox::Spawn;
use PublicInbox::OnDestroy;

sub new {
	my ($class, $max) = @_;
	bless {
		# 32 is same as the git-daemon connection limit, but
		# -cgit and -codeblob internal limiters default to 1
		max => $max || 32,
		running => 0,
		run_queue => [],
		# RLIMIT_CPU => undef,
		# RLIMIT_DATA => undef,
		# RLIMIT_CORE => undef,
	}, $class;
}

sub setup_limiter {
	my ($self, $name, $cfg) = @_;
	for my $f (qw(max depth)) {
		my $k = "publicinboxlimiter.$name.$f";
		my $v = $cfg->{$k} // next;
		if ($v =~ /\A[1-9][0-9]*\z/) {
			$self->{$f} = $v + 0;
		} else {
			warn <<EOM
W: `$k=$v' is not a positive integer in $cfg->{-f}
EOM
		}
	}
	for my $rlim (@PublicInbox::Spawn::RLIMITS) {
		my $k = lc($rlim);
		$k =~ tr/_//d;
		$k = "publicinboxlimiter.$name.$k";
		my $v = $cfg->{$k} // next;
		my @rlimit = split(/\s*,\s*/, $v);
		if (scalar(@rlimit) == 1) {
			$rlimit[1] = $rlimit[0];
		} elsif (scalar(@rlimit) != 2) {
			warn <<EOM;
W: could not parse `$k=$v' in $cfg->{-f} (ignored)
EOM
			next;
		}
		my $inf = $v =~ /\binfinity\b/i ?
			$PublicInbox::Spawn::RLIMITS{RLIM_INFINITY} // eval {
				require BSD::Resource;
				BSD::Resource::RLIM_INFINITY();
			} // do {
				warn "BSD::Resource missing for $rlim";
				next;
			} : undef;
		for (@rlimit) {
			$_ = $inf if $_ eq 'INFINITY';
		}
		$self->{$rlim} = \@rlimit;
	}
}

sub _do_start ($$$$) {
	my ($self, $start_cb, $ctx, $fail_cb) = @_;
	$ctx->{"limiter.next.$self"} = on_destroy \&_start_next, $self;
	++$self->{running};
	eval { $start_cb->($ctx, $self) };
	if ($@) {
		print { $ctx->{env}->{'psgi.errors'} } "E: $@\n";
		$fail_cb->($ctx, 500, 'internal error');
	}
}

sub _start_next { # on_destroy cb
	my ($self) = @_;
	--$self->{running};
	my ($rec, $ck, $start_cb, $ctx, $fail_cb);
	while (1) {
		$rec = shift @{$self->{run_queue}} or return;
		($start_cb, $ctx, $fail_cb) = @$rec;
		$ck = $ctx->{env}->{'pi-httpd.ckhup'} or last;
		$ck->($ctx->{env}->{'psgix.io'}->{sock}) or last;
		$fail_cb->($ctx, 499, 'client disconnected');
	}
	_do_start $self, $start_cb, $ctx, $fail_cb;
}

sub may_start {
	my ($self, $start_cb, $ctx, $fail_cb) = @_;
	if ($self->{running} < $self->{max}) {
		_do_start $self, $start_cb, $ctx, $fail_cb;
	} elsif (@{$self->{run_queue}} > ($self->{depth} // 32)) {
		$fail_cb->($ctx, 503, 'too busy');
	} else {
		push @{$self->{run_queue}}, [ $start_cb, $ctx, $fail_cb ];
	}
}

1;

debug log:

solving f90c8b56 ...
found f90c8b56 in https://80x24.org/public-inbox.git

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).