The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Sidecar;
# vim: ts=2 sw=2 noexpandtab

# "Sidecar" is what I call a subprocess that handles a particular
# object.  The analogy is to motorcycle sidecars.

use warnings;
use strict;

use Storable qw(nfreeze thaw);

sub BUILD {
	use IPC::Run qw(start);
	use Symbol qw(gensym);

	my ($fh_in, $fh_out, $fh_err) = (gensym(), gensym(), gensym());

	my $ipc_run = start(
		$cmd,
		'<pipe', $fh_in,
		'>pipe', $fh_out,
		'2>pipe', $fh_err,
	) or die "IPC::Run start() failed: $? ($!)";

	return($ipc_run, $fh_in, $fh_out, $fh_err);
}

sub _sidecar_drive {
	my $self = shift;

	my $buffer = "";
	my $read_length;

	binmode(STDIN);
	binmode(STDOUT);
	select STDOUT; $| = 1;

	use bytes;

	while (1) {
		if (defined $read_length) {
			if (length($buffer) >= $read_length) {
				my $request = thaw(substr($buffer, 0, $read_length, ""));
				$read_length = undef;

				my ($request_id, $context, $method, @args) = @$request;

				my $streamable;

				if ($context eq "array") {
					my (@return) = eval { $self->$method(@args); };
					$streamable = nfreeze( [ $request_id, $context, $@, @return ] );
				}
				elsif ($context eq "scalar") {
					my $return = eval { $self->$method(@args); };
					$streamable = nfreeze( [ $request_id, $context, $@, $return ] );
				}
				else {
					eval { $self->$method(@args); undef; };
					$streamable = nfreeze( [ $request_id, $context, $@ ] );
				}

				my $stream = length($streamable) . chr(0) . $streamable;

				my $octets_wrote = syswrite(STDOUT, $stream);
				die $! unless $octets_wrote == length($stream);

				next;
			}
		}
		elsif ($buffer =~ s/^(\d+)\0//) {
			$read_length = $1;
			next;
		}

		my $octets_read = sysread(STDIN, $buffer, 4096, length($buffer));
		last unless $octets_read;
	}

	exit 0;
}

1;