# New Ticket Created by # Please include the string: [perl #41874] # in the subject line of all future correspondence about this issue. # <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=41874 >
Attached is an extended implementation of the mmap io layer. It adds the write and seek funcitons to the layer. It can be tested with the attached pir programm. It needs also a little modification in io_unix.c, this comes in a separate bug report.
Index: src/io/io_mmap.c =================================================================== --- src/io/io_mmap.c (Revision 17524) +++ src/io/io_mmap.c (Arbeitskopie) @@ -44,6 +44,36 @@ /* +=item C<static INTVAL +flags_to_mmap(INTVAL flags)> + +Returns a UNIX-specific interpretation of C<flags> suitable for passing +to C<mmap()> in C<PIO_mmap_open()> and C<PIO_mmap_seek()> respectively. + +=cut + +*/ + +static INTVAL +flags_to_mmap(INTVAL flags) +{ + INTVAL oflags; + oflags = 0; + if ((flags & (PIO_F_WRITE | PIO_F_READ)) == (PIO_F_WRITE | PIO_F_READ)) { + oflags |= PROT_READ | PROT_WRITE; + } + else if (flags & PIO_F_WRITE) { + oflags |= PROT_WRITE; + } + else if (flags & PIO_F_READ) { + oflags |= PROT_READ; + } + + return oflags; +} + +/* + =item C<static ParrotIO * PIO_mmap_open(Interp *interp, ParrotIOLayer *layer, const char *path, INTVAL flags)> @@ -60,34 +90,51 @@ { ParrotIO *io; ParrotIOLayer *l = PIO_DOWNLAYER(layer); + INTVAL oflags; if (!l) { l = interp->piodata->default_stack; if (!strcmp(l->name, "buf")) l = PIO_DOWNLAYER(l); } + + if (flags & PIO_F_WRITE) + flags |= PIO_F_READ; /* can't mmap PROT_WRITE and PROT_SHRAED if not */ + io = PIO_open_down(interp, l, path, flags); if (!io) { /* error creating IO stream */ return NULL; } -#ifdef PARROT_HAS_HEADER_SYSMMAN - { - /* XXX assume fstat exists too - */ - struct stat statbuf; - int status; - PIOOFF_T file_size; - status = fstat(io->fd, &statbuf); - file_size = statbuf.st_size; - /* TODO verify flags */ - io->b.startb = mmap(0, file_size, PROT_READ, MAP_SHARED, io->fd, 0); - io->b.size = (size_t)file_size; /* XXX */ - io->b.endb = io->b.startb + io->b.size; - io->b.flags |= PIO_BF_MMAP; + /* XXX assume fstat exists too */ + struct stat statbuf; + int status; + + status = fstat(io->fd, &statbuf); + + io->fpos = io->lpos = 0; + io->fsize = statbuf.st_size; + + oflags = flags_to_mmap(flags); + io->b.startb = mmap(0, io->fsize, oflags & (PROT_WRITE | PROT_READ), MAP_SHARED, io->fd, 0); + + if ( MAP_FAILED == io->b.startb ) { + if (errno == EINVAL) + io->b.size = 0; + else { + PIO_close_down(interp, l, io); + return NULL; + } } -#endif + else + io->b.size = io->fsize; + + + io->b.next = io->b.startb; + io->b.endb = io->b.startb + io->b.size; + io->b.flags |= PIO_BF_MMAP; + return io; } @@ -114,9 +161,9 @@ if (!(io->b.flags & PIO_BF_MMAP)) return PIO_read_down(interp, PIO_DOWNLAYER(layer), io, buf); - if (*buf == NULL) { + if (*buf == NULL) *buf = new_string_header(interp, 0); - } + s = *buf; /* TODO create string_free API for reusing string headers */ if (s->strstart && PObj_sysmem_TEST(s)) @@ -124,6 +171,7 @@ PObj_get_FLAGS(s) |= PObj_external_FLAG; PObj_bufstart(s) = s->strstart = io->b.startb + io->fpos; len = s->bufused ? s->bufused : io->b.size; + io->lpos = io->fpos; io->fpos += len; PObj_buflen(s) = s->strlen = len; return len; @@ -131,7 +179,128 @@ /* +=item C<static PIOOFF_T +PIO_mmap_seek(Interp *interp, ParrotIOLayer *l, ParrotIO *io, + PIOOFF_T offset, INTVAL whence)> + +The mmap layer's C<Seek> function. + +=cut + +*/ + +static PIOOFF_T +PIO_mmap_seek(Interp *interp, ParrotIOLayer *l, ParrotIO *io, + PIOOFF_T offset, INTVAL whence) +{ + PIOOFF_T newpos; + + switch (whence) { + case SEEK_SET: + newpos = offset; + break; + case SEEK_CUR: + newpos = io->b.next - io->b.startb + offset; + break; + case SEEK_END: + newpos = io->b.endb - io->b.startb; + break; + default: + /* XXX: somehow report the illegal whence value */ + return -1; + } + + if (PIO_seek_down(interp, PIO_DOWNLAYER(l), io, newpos, whence) == -1) { + return -1; + } + + if (newpos <= io->b.size) + io->b.next = io->b.startb + newpos; + else { + if ( io->b.size > 0 ) { + if ( -1 == munmap(io->b.startb, io->b.size) ) + return -1; + } + + + if (!write(io->fd, "", 1)) + fprintf(stderr, "write error\n"); + else { + INTVAL oflags; + oflags = flags_to_mmap(io->flags); + io->b.startb = mmap(0, io->fsize, oflags & (PROT_WRITE | PROT_READ), MAP_SHARED, + io->fd, 0); + + if ( MAP_FAILED == io->b.startb) + return -1; + + io->b.endb = io->b.startb + io->fsize; + io->b.next = io->b.startb + newpos; + io->b.size = io->fsize; + } + } + + return io->b.next - io->b.startb; +} + +static size_t +PIO_mmap_write(Interp *interp, ParrotIOLayer *l, ParrotIO *io, STRING *s) +{ + long avail; + void * const buffer = s->strstart; + size_t len = s->bufused; + + if (len <= 0) return 0; + + avail = io->b.endb - io->b.next + len; + + if (avail < 0) { + if (PIO_mmap_seek(interp, l, io, -avail, SEEK_CUR) == -1) + return -1; + else if (!write(io->fd, "", 1)) { + /* is not required that lseek grows the file. Forcing */ + return -1; + } + if ( munmap(io->b.startb, io->b.size) == -1) + return -1; + + if ( (io->b.startb = mmap(0, io->fsize, PROT_WRITE | PROT_READ, MAP_SHARED, io->fd, 0) ) + == MAP_FAILED ) + return -1; + } + + memcpy(io->b.next, buffer, len); + io->b.next += len; + + return (size_t)len; +} + + +/* =item C<static INTVAL +PIO_mmap_flush(Interp *interp, ParrotIOLayer *layer, ParrotIO *io)> + +At lowest layer all we can do for C<flush> is to ask the kernel to +C<sync()>. + +XXX: Is it necessary to C<sync()> here? + +=cut + +*/ + +static INTVAL +PIO_mmap_flush(Interp *interp, ParrotIOLayer *layer, ParrotIO *io) +{ + UNUSED(interp); + UNUSED(layer); + + return msync(io->b.startb + io->fpos, 0, MS_SYNC); /* XXX: MS_ASYNC? */ +} + +/* + +=item C<static INTVAL PIO_mmap_close(Interp *interp, ParrotIOLayer *layer, ParrotIO *io)> Closes C<*io>'s file descriptor. @@ -145,9 +314,9 @@ { INTVAL ret = -1; - if (io->fd >= 0) { + if (io->fd >= 0) ret = PIO_close_down(interp, PIO_DOWNLAYER(layer), io); - } + return ret; } @@ -163,13 +332,13 @@ PIO_null_open_async, PIO_null_fdopen, PIO_mmap_close, - PIO_null_write, + PIO_mmap_write, PIO_null_write_async, PIO_mmap_read, PIO_null_read_async, - PIO_null_flush, + PIO_mmap_flush, PIO_null_peek, - PIO_null_seek, + PIO_mmap_seek, PIO_null_tell, PIO_null_setbuf, PIO_null_setlinebuf, Index: src/io/io.c =================================================================== --- src/io/io.c (Revision 17524) +++ src/io/io.c (Arbeitskopie) @@ -15,7 +15,7 @@ This file implements the generic functionality. Specific layers are in separate files: F<src/io/io_buf.c>, F<src/io/io_stdio.c>, F<src/io/io_unix.c>, -F<src/io/io_win32.c>, and F<src/io/io_layers.c>. +F<src/io/io_win32.c>, F<src/io/io_mmap.c> and F<src/io/io_layers.c>. The C<ParrotIO> PMC provides the class-based interface that is used in Parrot ops. The C<ParrotIO struct> is defined in F<src/io/io_private.h>.
# Copyright (C) 2001-2006, The Perl Foundation. # $Id: io.pir 12835 2006-05-30 13:32:26Z coke $ =head1 NAME examples/pir/io.pir - IO Example =head1 SYNOPSIS % ./parrot examples/pir/io.pir =head1 DESCRIPTION Simple open/seek/write/close on a file. After the file is written it is read in again and printed to STDOUT. You should check where the file is going to be before you run this. =cut .sub 'example' :main .local string test_fn test_fn = "tmp_example_io.tmp" .local pmc pio, cl cl = getclass "ParrotIO" # slurp the file into memory pio = cl."open"(test_fn, ">", "mmap") # P0 = open test_fn, ">", "mmap" seek pio, 300, 0 # 64bit version of seek with high 32bits = 0 #seek IO, P0, 0, 400, 0 print pio, "test3\n" print pio, "test4\n" print pio, "test5\n" seek pio, 0, 0 print pio, "test1\n" print pio, "test2\n" close pio pio = cl."open"(test_fn, "<", "mmap") S0 = read pio, 1024 print S0 # now clean up after ourselves. P1 = new "OS" P1."rm"(test_fn) .end =head1 SEE ALSO F<examples/io>. =cut