9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: Anthony Martin <ality@pbrane.org>
To: 9fans@9fans.net
Subject: [9fans] Why does pread(2) modify the channel offset?
Date: Sun, 21 Jun 2015 03:44:44 -0700	[thread overview]
Message-ID: <20150621104444.GA7504@dinah> (raw)

[-- Attachment #1: Type: text/plain, Size: 2160 bytes --]

During the investigation of a failure in the Go test suite,
I was led to the following question:

  Why does pread(2) modify the channel offset?

When pread and pwrite were added to the kernel in Feb 2001,
neither of them modified the underlying channel offset for
a given file descriptor. This was also the behavior when the
4th Edition was released. Fast forward to a year later in
May 2003. The code was reorganized slightly and pread began
modifying the channel offset.

I think this change was inadvertent because pwrite remained
the same (i.e. not modifying the offset) and because it means
that you can't mix calls to read and pread on the same file
descriptor without confusion. Mixing calls is very uncommon,
though, so nobody noticed at the time. A new test was added
to the Go test suite that does this and it instantly caused
the Plan 9 builder to fail.

I've attached a small program that shows the problem. It's
output looks like this:

  % 8c -FTVw test.c && 8l -o test test.c
  % ./test
  0: init       -> offset = 0
  1: write 1    -> offset = 1
  2: pwrite 1 0 -> offset = 1
  3: seek 0     -> offset = 0
  4: read 1     -> offset = 1
  5: pread 1 0  -> offset = 2
  %

The pread in step five clearly affects the file offset.

However, the documentation gives the impression that both
pread and pwrite should update the channel offset. Note the
first sentence in the following passage from pread(2):

  Pread and Pwrite are equivalent to a seek(2) to offset fol-
  lowed by a read or write.  By combining the operations in a
  single atomic call, they more closely match the 9P protocol
  (see intro(5)) and, more important, permit multiprocess pro-
  grams to execute multiple concurrent read and write opera-
  tions on the same file descriptor without interference.

That leaves us with two options:

  1. the docs are correct and pwrite is wrong, or

  2. the docs are incorrect and pread is wrong.

I think the latter is more likely. (Note, also, that all of
the modern Unix variants have APIs that assume the offset
is unchanged after a pread or pwrite).

Thoughts?

  Anthony

[-- Attachment #2: test.c --]
[-- Type: text/plain, Size: 994 bytes --]

#include <u.h>
#include <libc.h>

void
run(int step, int fd)
{
	vlong ret, off;
	char buf[1], *msg;

	msg = nil;
	switch(step){
	case 0:
		ret = 0;
		msg = "init";
		break;
	case 1:
		ret = write(fd, buf, 1);
		msg = "write 1";
		break;
	case 2:
		ret = pwrite(fd, buf, 1, 0);
		msg = "pwrite 1 0";
		break;
	case 3:
		ret = seek(fd, 0, 0);
		msg = "seek 0";
		break;
	case 4:
		ret = read(fd, buf, 1);
		msg = "read 1";
		break;
	case 5:
		ret = pread(fd, buf, 1, 0);
		msg = "pread 1 0";
		break;
	}
	if(ret == -1)
		sysfatal("%s failed: %r", msg);
	off = seek(fd, 0, 1);
	if(off < 0)
		sysfatal("seek failed: %r");
	print("%d: %-10s -> offset = %lld\n", step, msg, off);
}

char *tmpfile = "/tmp/foo.6e907b64";

void
main(void)
{
	int fd, i;

	if((fd = create(tmpfile, ORDWR, 0600)) < 0)
		sysfatal("create failed: %r");
	for(i = 0; i <= 5; i++)
		run(i, fd);
	close(fd);
	if(remove(tmpfile) < 0)
		sysfatal("remove failed: %r");
}

             reply	other threads:[~2015-06-21 10:44 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-21 10:44 Anthony Martin [this message]
2015-06-21 12:35 ` Charles Forsyth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20150621104444.GA7504@dinah \
    --to=ality@pbrane.org \
    --cc=9fans@9fans.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).