The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#
#	IPC::Mmap test script
#
use Config;
use vars qw($tests $loaded);
BEGIN {
	push @INC, './t';
	$tests = 7;

	$^W= 1;
	$| = 1;
	print "1..$tests\n";
	unless ($Config{usethreads} && (!$ENV{DEVEL_RINGBUF_NOTHREADS})) {
		print "ok $_ # skip your Perl is not configured for threads\n"
			foreach (1..$tests);
		exit;
	}
}

END {print "not ok 1\n" unless $loaded;}

use threads;
use threads::shared;
use Time::HiRes qw(time);
use IPC::Mmap;

use strict;
use warnings;

our $testtype = 'multithread, single process';
my $testno : shared = 1;

sub report_result {
	my ($result, $testmsg, $okmsg, $notokmsg) = @_;

	if ($result) {

		$okmsg = '' unless $okmsg;
		print STDOUT (($result eq 'skip') ?
			"ok $testno # skip $testmsg for $testtype\n" :
			"ok $testno # $testmsg $okmsg for $testtype\n");
	}
	else {
		$notokmsg = '' unless $notokmsg;
		print STDOUT
			"not ok $testno # $testmsg $notokmsg for $testtype\n";
	}
	$testno++;
}

#
#	prelims: use shared test count for eventual
#	threaded tests
#
$loaded = 1;

unless ($Config{useithreads} && ($Config{useithreads} eq 'define')) {
	report_result('skip', "This Perl is not configured to support threads.")
		foreach ($testno..$tests);
	exit 1;
}
#
#	create w/ filename, but wo/ a backing file
#	(works for both Win32 and POSIX)
#
my $mmap = ($^O eq 'MSWin32') ?
	IPC::Mmap->new('test2_mmap.tmp', 10000,
		PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON) :
	IPC::Mmap->new('test2_mmap.tmp', 10000,
		PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FILE);
report_result(defined($mmap), 'create from filename');

unless (defined($mmap)) {
#	skip the rest
	report_result('skip', 'no mmap, skipping')
		while ($testno < $tests);
	exit 1;
}

my $thrdlock : shared = 0;	# to coordinate threads
#
#	create 2 threads; 1st writes, 2nd reads
#	lock the mmap first to control sequencing
#
my $writer;
	$writer = threads->create(\&write_mmap);

my $reader = threads->create(\&read_mmap);

$writer->join();
$reader->join();

sub read_mmap {
	my $value;
	my $result;
#
#	wait forwriter
#
	{
		lock($thrdlock);
		cond_wait($thrdlock)
			while ($thrdlock != 1);

		$result = $mmap->read($value, 100, 2000);
		report_result((defined($result) && ($result == 2000) &&
			defined($value) && (length($value) == $result) &&
			($value eq ('A' x 2000))),
		'read thread', '', "result is $result length of value: " . length($value) .
			' value: ' . substr($value, 0, 20) );
#
#	tell writer to continue
#
		$thrdlock++;
		cond_broadcast($thrdlock);
	}
#
#	wait for writer
#
	{
		lock($thrdlock);
		cond_wait($thrdlock)
			while ($thrdlock != 3);
#
#	unpack something
#
	my @vals = $mmap->unpack(1000, 36, 'l n S d a20');
	report_result((scalar @vals == 5) &&
		($vals[0] == 123456) && ($vals[1] == 2345) && ($vals[2] == 5432) &&
		($vals[3] == 123.456789) && ($vals[4] eq ('Z' x 20)), 'unpack()');
#
#	tell writer to continue
#
		$thrdlock++;
		cond_broadcast($thrdlock);
	}
	return 1;
}

sub write_mmap {
	my $result;
#
#	wait for parent to release
#
	{
		lock($thrdlock);
#
#	lock it
#
		$result = $mmap->lock();
		report_result($result, 'writer lock mmap area');
#
#	write to it: no length
#
		$result = $mmap->write('A' x 2000, 100);
		report_result((defined($result) && ($result == 2000)), 'simple write');

		$result = $mmap->unlock();
		report_result($result, 'writer unlock mmap area');
#
#	and acknowledge
#
		$thrdlock++;
		cond_broadcast($thrdlock);
	}
#
#	wait for reader
#
	{
		lock($thrdlock);
		cond_wait($thrdlock)
			while ($thrdlock != 2);
#
#	pack something
#
		$mmap->lock();
		$result = $mmap->pack(1000, 'l n S d a20', 123456, 2345, 5432, 123.456789, 'Z' x 20);
		report_result(defined($result) && ($result == 36), 'pack()');
		$mmap->unlock();
		$thrdlock++;
		cond_broadcast($thrdlock);
	}
	{
		lock($thrdlock);
		cond_wait($thrdlock)
			while ($thrdlock != 4);
	}
	return 1;
}