#!/usr/bin/perl -w
use strict;
use English     qw( -no_match_vars );
use POSIX       qw( :sys_wait_h strftime );
use Time::HiRes qw( time );
use IO::File;

our %kids   = ();
our @deaths = ();
our $log_fh = undef;

$SIG{ 'CHLD' } = \&REAPER;

log_info( 'starting' );

for my $i ( 1 .. 15 ) {
    start_new_worker( $i, 'read' );
    start_new_worker( $i, 'write' );
    sleep 20;
}

log_info( 'all workers started' );

while ( scalar keys %kids ) {
    sleep 10;
    while ( my $d = shift @deaths ) {
        my $k = delete $kids{ $d->{ 'pid' } };
        log_info( 'Worker %u finished after %f seconds with status: %u', $d->{ 'pid' }, $d->{ 'died' } - $k->{ 'started' }, $d->{ 'status' } );
    }
}

log_info( 'Test finished.' );

exit;

sub start_new_worker {
    my ( $num, $type ) = @_;
    my $pid = fork;
    die 'horrible death?!' unless defined $pid;
    if ( $pid ) {
        $kids{ $pid }->{ 'started' } = time();
        log_info( 'Worker %u started - type: %s, num: %u.', $pid, $type, $num );
        return;
    }
    log_info( 'Starting "%s" work', $type );
    work( $num, $type );
    exit;
}

sub REAPER {
    my $child;
    while ( ( $child = waitpid( -1, WNOHANG ) ) > 0 ) {
        push @deaths,
            {
            'pid'    => $child,
            'status' => $CHILD_ERROR,
            'died'   => time(),
            };
    }
    $SIG{ 'CHLD' } = \&REAPER;
}

sub log_info {
    my ( $format, @args ) = @_;
    unless ( defined $log_fh ) {
        open $log_fh, '>>', '/tmp/tester.log' or die "Cannot write to log: $OS_ERROR\n";
    }
    my $message = sprintf $format, @args;

    my $time         = time();
    my $date_time    = strftime( '%Y-%m-%d %H:%M:%S', localtime $time );
    my $microseconds = ( $time * 1_000_000 ) % 1_000_000;
    my $time_zone    = strftime( '%z', localtime $time );

    my $time_stamp = sprintf "%s.%06u %s", $date_time, $microseconds, $time_zone;

    print $log_fh "$time_stamp : $PROCESS_ID : $message\n";
    $log_fh->flush();
    $log_fh->sync();
    return;
}

sub work {
    my ( $num, $type ) = @_;

    my $buffer = 'test' x 2048;    # 8kb of text

    my $test_file_name = sprintf 'operations/%02u', $num;
    open my $test_fh, '+<', $test_file_name or die "Cannot open $test_file_name : $OS_ERROR\n";
    binmode $test_fh;

    my $starting  = time();
    my $finish_at = $starting + 20 * 60;    # 20 minutes of runtime

    while ( time() < $finish_at ) {
        my $random_block = int( rand() * 131072 );    # Each file is 1073741824 bytes long, which is 131072 8k blocks
        sysseek( $test_fh, $random_block * 8192, 0 ) or die 'Cannot seek?!';
        my $res;
        if ( $type eq 'write' ) {
            $res = syswrite( $test_fh, $buffer, 8192 );
        }
        else {
            $res = sysread( $test_fh, $buffer, 8192 );
        }
        die "Couldn't $type 8192 bytes to output : $res ?!" unless $res == 8192;
        log_info( '%s++', $type );
    }

    close $test_fh;
    return;
}

