Acme::Urinal - assign resources using the same algorithm used by men choosing which urinal to use
use Acme::Urinal; my $urinals = Acme::Urinal->new(8); say $urinal->pick_one; # prints 1 say $urinal->pick_one; # prints 3 say $urinal->pick_one; # prints 5 say $urinal->pick_one; # prints 7 say $urinal->pick_one; # prints 2 say $urinal->pick_one; # prints 4 say $urinal->pick_one; # prints 6 say $urinal->pick_one; # prints 0 say $urinal->pick_one; # prints nothing, triggers an uninit warning $urinal->leave(3); $urinal->leave(4); say $urinal->pick_one; # prints 4 $urinal->leave(2); $urinal->leave(1); say $urinal->pick_one; # prints 1
When men use a bathroom with multiple urinals. The way the urinal to use is chosen is nearly deterministic. This module allocates resources in a way that emulates this process.
Basically, a Acme::Urinal object keeps track of a list of resources. You can then request these resources be allocated and used by asking for one using the "pick_one" method. It will return the next resource according to the algorithm. Once finished suing that resource, you may return it using the "leave" method.
Each resource is chosen according to the following rules:
If possible, the lowest index resource that has a free resource on either side is chosen.
Failing that, the lowest index resource with a lesser neighbor free is chosen.
Failing that, the lowest index resource with a greater neighbor free is chosen.
Failing that, the lowest index resource that is not at either end is chosen (because those end ones usually tend to be the less preferable low urinal).
Finally, the lowest index resource that is available is chosen.
my $urinal = Acme::Urinal->new($count); my $urinal = Acme::Urinal->new(\@resources);
Constructs a new Acme::Urinal object. If the argument is a positive integer, it is the same as if an array reference were passed like this:
[ 0 .. $count ]
If an array reference is passed, the object will use that array as the list of resources. The array will be copied, so changes to the original, won't change the one used by Acme::Urinal.
Anything else should cause an error.
my $index = Acme::Urinal->pick_one; my ($index, $resource, $comfort_level) = Acme::Urinal->pick_one;
This will choose an available resource from those available using the algorithm described in the "DESCRIPTION". If no resource is available, the return will be
undef or an empty list.
In scalar context, the index of the resource is returned. In list context, a three-element list is returned where the first element is the index, the second is the resource that was allocated, and the third is the comfort level with which the resource was allocated. The higher the level, the better the allocation was (the earlier the rule from the "DESCRIPTION" that was used to make the allocation). Currently, the comfort level will be between 1 and 5.
my $resource = $self->pick($index); my ($resource, $comfort_level) = $self->pick($index);
Allows you to violate the usual algorithm to pick a urinal explicitly. In scalar context it returns the resource picked. In list context, it returns that and the comfort level your pick has. If the resource picked is already in use, an exception will be thrown.
my $resource = $self->look($index); my ($resource, $comfort_level) = $self->look($index);
In most algorithms, this would be called "peek," but peeking in urinals is, at best, awkward and, at worst, likely to get you beat up.
This is the same as "pick", but does not actually allocate. Also, the
$comfort_level returned will be
0 if the resource is currently in use.
Frees up the resource at the given index. Throws an exception if the resource is not currently in use.
Andrew Sterling Hanenkamp
Copyright 2014 Andrew Sterling Hanenkamp.
This is free software and may be copied and distributed under the same terms as Perl itself.