=head1 NAME

  CFX5::Parallel::Reqs::impi_memlock - Check for Intel MPI lockable memory

This prerequisite performs the checks required for Intel MPI
parallel on Linux, to ensure enough lockable memory is available.

=cut

package CFX5::Parallel::Reqs::impi_memlock;

use CFX5::Parallel::Reqs::Base;
push @ISA, qw(CFX5::Parallel::Reqs::Base);

use CFX5::Config qw(%Config);
use CFX5::Output qw(:all);
use CFX5::Commands qw(do_output_cmd);

use strict;

sub memlock_limit_hosts {
  my $self = shift;
  my @hosts = @_;

  # Minimum lockable memory required by Intel MPI (kB)
  my $limit_min = 32768;

  my @cmd = ('/bin/sh','-c','ulimit -l');

  my @warnings = ();
  for my $h (@hosts) {
    my $rv = CFX5::Commands::Result->new_not_run();

    # Run command to find limit
    if ($h->[1]->is_local()) {
      $rv = do_output_cmd(@cmd);
    } else {
      @cmd = map { $_ =~ m/\s/ ? qq('$_') : $_ } @cmd;
      $rv = $h->[1]->do_cmd(@cmd) unless ($h->[1]->disable_remote_checks());
    }

    # Interrogate output to find limit (kB)
    my $limit = undef;
    if ($rv->is_success()) {
      my @out = $rv->cmd_output();
      $limit = pop @out;
      chomp $limit;
    }

    # Check if returned limit is numeric and less than the required minimum
    if (defined $limit and $limit =~ m/\d+/ and $limit < $limit_min) {
      push @warnings, sprintf("%12d kB on host %s",$limit,$h->[1]->hostname());
    }
  }

  # Print warning message for all affected hosts
  if (@warnings) {
    my $sm_name = $self->ppe()->start_method()->name();
    my $warning_list = join "\n ", @warnings;
    _warn("The system resource limit 'max locked memory' is reported as:\n $warning_list\n\nbut '$sm_name' requires a minimum of $limit_min kB on all hosts. This limit should be increased. More information is provided in the section on Intel MPI in the CFX Solver documentation. The run will proceed, but may fail.");
  }

  return 1;
}

sub setup {
  my $self=shift;
  my $workdir=shift;

  if ($Config{CFX5OS} =~ m/linux/) {
    # Skip the check if interconnect is limited to TCP
    unless (exists $ENV{I_MPI_FABRICS} and $ENV{I_MPI_FABRICS} eq 'shm:tcp') {
      my @hosts = $self->ppe()->partitions_master_first();
      # Only run the check if there are multiple hosts
      if (scalar(@hosts) > 1) {
        $self->memlock_limit_hosts(@hosts);
      }
    }
  } else {
    internal_error("Intel MPI lockable memory check is only available for Linux.");
  }

  return 1;
}

1;
