Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

syswrite and partial writes of data

30 views
Skip to first unread message

Russ Allbery

unread,
Nov 9, 1996, 3:00:00 AM11/9/96
to

One of the annoyances about the standard write(2) system call is that
under some circumstances it is possible for a successful write to write
fewer than the total number of requested bytes. It's therefore necessary
in C to check the return value of write(2) and repeat as necessary until
all of the data has been written.

Is this also the case for syswrite in Perl, or does Perl take care of that
for us? In other words, do I actually need code like the following:

# It's possible for write(2) to return a fewer number of written
# bytes than the size of the buffer being written. To allow for
# that, we need to keep writing until either the entire buffer has
# been written or we get an error of some sort. Because the socket
# is non-blocking, we also need to select on it to make sure that
# it's ready for data.
do {
my $win = '';
my $wout;
vec ($win, $self->{fh}->fileno, 1) = 1;
my $nbits = select (undef, $wout = $win, undef, $TIMEOUT);
if ($nbits < 1) { return undef }

# Actually write out the data.
$written = syswrite ($self->{fh}, $$buf, (length $$buf) - $count,
$count);
unless ($written) {
$self->shutdown;
return undef;
}
$count += $written;
} until ($count == length $$buf);

or am I going to far too much work? (Note that the above code is writing
to a *non-blocking* socket, which changes the situation somewhat.)

Peering at the LWP::* modules, all of those routines seem to push this
problem off on the user by returning the number of bytes that syswrite
returned.

(And yes, I'm reimplementing low-level socket I/O rather than using
LWP::*. Yes, I'd rather use LWP::*, all things being equal, but I
specifically don't want this module to depend on any modules which aren't
in Perl core and I don't need the entire baggage of LWP. Besides, it only
took a couple of hours to write, and it was fun.)

--
Russ Allbery (r...@cs.stanford.edu) <URL:http://www.eyrie.org/~eagle/>

Tom Christiansen

unread,
Nov 9, 1996, 3:00:00 AM11/9/96
to

[courtesy cc of this posting sent to cited author via email]

In comp.lang.perl.misc,
Russ Allbery <r...@cs.stanford.edu> writes:
:One of the annoyances about the standard write(2) system call is that


:under some circumstances it is possible for a successful write to write
:fewer than the total number of requested bytes. It's therefore necessary
:in C to check the return value of write(2) and repeat as necessary until
:all of the data has been written.
:
:Is this also the case for syswrite in Perl, or does Perl take care of that
:for us? In other words, do I actually need code like the following:

[elision for the sake of wit's soul]
:or am I going to far too much work? (Note that the above code is writing


:to a *non-blocking* socket, which changes the situation somewhat.)
:
:Peering at the LWP::* modules, all of those routines seem to push this
:problem off on the user by returning the number of bytes that syswrite
:returned.

Use the Source, Russ:-)

PP(pp_syswrite)
{
return pp_send(ARGS);
}

PP(pp_send)
{
dSP; dMARK; dORIGMARK; dTARGET;
GV *gv;
IO *io;
int offset;
SV *bufsv;
char *buffer;
int length;
STRLEN blen;

gv = (GV*)*++MARK;
if (!gv)
goto say_undef;
bufsv = *++MARK;
buffer = SvPV(bufsv, blen);
length = SvIVx(*++MARK);
if (length < 0)
DIE("Negative length");
SETERRNO(0,0);
io = GvIO(gv);
if (!io || !IoIFP(io)) {
length = -1;
if (dowarn) {
if (op->op_type == OP_SYSWRITE)
warn("Syswrite on closed filehandle");
else
warn("Send on closed socket");
}
}
else if (op->op_type == OP_SYSWRITE) {
if (MARK < SP)
offset = SvIVx(*++MARK);
else
offset = 0;
if (length > blen - offset)
length = blen - offset;
length = write(PerlIO_fileno(IoIFP(io)), buffer+offset, length);
}
#ifdef HAS_SOCKET
else if (SP > MARK) {
char *sockbuf;
STRLEN mlen;
sockbuf = SvPVx(*++MARK, mlen);
length = sendto(PerlIO_fileno(IoIFP(io)), buffer, blen, length,
(struct sockaddr *)sockbuf, mlen);
}
else
length = send(PerlIO_fileno(IoIFP(io)), buffer, blen, length);
#else
else
DIE(no_sock_func, "send");
#endif
if (length < 0)
goto say_undef;
SP = ORIGMARK;
PUSHi(length);
RETURN;

say_undef:
SP = ORIGMARK;
RETPUSHUNDEF;
}

So, yes, you have to worry about it. Here's one example:

#!/usr/bin/perl
# cpfiles -- copy files paranoically
# tch...@perl.com

$DEFSIZE = 16 * 2**10;

($src, $dst) = @ARGV;

die "usage: $0 oldfile newfile\n" unless @ARGV == 2;
die "no source file $src: $!" unless -e $src;
die "dest file $dst exists" if -e $dst || -l $dst;

open(SRC, "< $src") || die "can't open $src: $!";
open(DST, "> $dst") || die "can't open $dst: $!";

$bsize = (stat(SRC))[11];
$bsize = $DEFSIZE unless $bsize > $DEFSIZE;

while ($readcount = sysread(SRC, $buffer, $bsize)) {
for ($offset = 0;
$offset < length($buffer);
$offset += $writecount
)
{
$writecount = syswrite(DST, $buffer, length($buffer), $offset);
die "syswrite on $dst failed: $!" unless defined $writecount;
}

}
die "sysread on $src failed: $!" unless defined $readcount;
close(SRC) || die "can't close $src: $!";
close(DST) || die "can't close $dst: $!";

Does anyone else see the bug that I see? Hint: The code was
written back before I could address it, but now I could.

--tom
--
Tom Christiansen Perl Consultant, Gamer, Hiker tch...@mox.perl.com
: I've heard that there is a shell (bourne or csh) to perl filter, does
: anyone know of this or where I can get it?
Yeah, you filter it through Tom Christiansen. :-) --Larry Wall

0 new messages