package TestPerl::ithreads_cloning;

# a few basic tests on how mp2 objects deal with cloning (used
# APR::Table and APR::Pool for the tests)
#

use strict;
use warnings FATAL => 'all';

use APR::Table ();
use APR::Pool ();

use Apache::Test;
use Apache::TestUtil;

use TestCommon::Utils;

use Devel::Peek;

use Apache2::Const -compile => 'OK';

my $pool_ext = APR::Pool->new;
my $table_ext1 = APR::Table::make($pool_ext, 10);
my $table_ext2 = APR::Table::make($pool_ext, 10);

my $threads = 2;

sub handler {
    my $r = shift;

    # this buffers the ok's and will flush them out on sub's end
    my $x = Apache::TestToStringRequest->new($r);

    my $tests = 10 * (2 + $threads);
    plan tests => $tests, need
        need_threads,
        {"perl >= 5.8.1 is required (this is $])" => ($] >= 5.008001)};

    require threads;
    threads->import();

    read_test();
    threads->new(\&read_test)->join() for 1..$threads;
    read_test();

    Apache2::Const::OK;
}

# 10 subtests
sub read_test {

    my $tid = threads->self()->tid();
    t_debug "tid: $tid";

    {
        # use of invalidated cloned object
        my $error_msg = q[Can't call method "set" on unblessed reference];
        eval { $table_ext1->set(1 => 2); };
        if ($tid > 0) { # child thread
            # set must fail, since $table_ext1 must have been invalidated
            ok t_cmp $@, qr/\Q$error_msg/,
                '$table_ext1 must have been invalidated';
        }
        else {
            # should work just fine for the parent "thread", which
            # created this variable
            ok !$@;
        }
    }

    {
        # use of invalidated cloned object as an argument
        my $error_msg = 'argument is not a blessed reference ' .
            '(expecting an APR::Pool derived object)';
        eval { my $table = APR::Table::make($pool_ext, 10) };
        if ($tid > 0) { # child thread
            # make() must fail, since $pool_ext must have been invalidated
            ok t_cmp $@, qr/\Q$error_msg/,
                '$pool_ext must have been invalidated';
        }
        else {
            # should work just fine for the parent "thread", which
            # created this variable
            ok !$@;
        }
    }

    {
        # this is an important test, since the thread assigns a new
        # value to the cloned $table_ext1 (since it existed before the
        # thread was started)

        my $save = $table_ext1;

        $table_ext1 = APR::Table::make(APR::Pool->new, 10);

        validate($table_ext1);

        $table_ext1 = $save;
    }

    {
        # here $table_ext2 is a private variable, so the cloned
        # variable $table_ext2 is not touched
        my $table_ext2 = APR::Table::make(APR::Pool->new, 10);

        validate($table_ext2);
    }

    return undef;
}

# 4 subtests
sub validate {
    my $t = shift;
    my $tid = threads->self()->tid();

    $t->set($_ => $_) for 1..20;
    for my $count (1..2) {
        my $expected = 20;
        my $received = $t->get(20);
        is $received, $expected, "tid: $tid: pass 1:";
        $t->set(20 => 40);
        $received = $t->get(20);
        $expected = 40;
        is $received, $expected, "tid: $tid: pass 2:";
        # reset
        $t->set(20 => 20);
    }
}

1;

__END__

