summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2021-08-22 20:39:25 -0700
committerKaz Kylheku <kaz@kylheku.com>2021-08-22 20:39:25 -0700
commita37665e615c504415d5425f71ce5af7b7175b3f2 (patch)
treea78c05024c377578245e9b7d5b7d2f399091f45f
parentf88ab97c627291952ca39a6cdada6c923caed0a4 (diff)
downloadtxr-a37665e615c504415d5425f71ce5af7b7175b3f2.tar.gz
txr-a37665e615c504415d5425f71ce5af7b7175b3f2.tar.bz2
txr-a37665e615c504415d5425f71ce5af7b7175b3f2.zip
ffi: provide mmap through carray.
* configure: configure test for mmap depositing HAVE_MMAP into config.h. * ffi.c (struct carray): Subject to HAVE_MMAP, new mm_len member which keeps track of the size of an underlying mapping so that we can unmap it, as well as peform operations like msync on it. (make_carray): Initialize mm_len to 0. (MAP_GROWSDOWN, MAP_LOCKED, MAP_NORESERVE, MAP_POPULATE, MAP_NONBLOCK, MAP_STACK, MAP_HUGETLB, MAP_SHARED, MAP_PRIVATE, MAP_FIXED, MAP_ANON, MAP_HUGE_SHIFT, MAP_HUGE_MASK, PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE, PROT_GROWSDOWN, PROT_GROWSUP, MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, MADV_WILLNEED, MADV_DONTNEED, MADV_FREE, MADV_REMOVE, MADV_DONTFORK, MADV_DOFORK, MADV_MERGEABLE, MADV_UNMERGEABLE, MADV_HUGEPAGE, MADV_NOHUGEPAGE, MADV_DONTDUMP, MADV_DODUMP, MADV_WIPEONFORK, MADV_KEEPONFORK, MADV_HWPOISON, MS_ASYNC, MS_SYNC, MS_INVALIDATE): #define as 0 if missing. (carray_munmap_op): New static function. (carray_mmap_ops): New static structure. (mmap_wrap, munmap_wrap): New functions. (mmap_op): New static function. (mprotect_wrap, madvise_wrap, msync_wrap): New functions. (ffi_init): Register mmap, munmap, mprotect, madvise and msync as well as numerous integer variables: map-growsdown, map-locked, map-noreserve, map-populate, map-nonblock, map-stack, map-hugetlb, map-shared, map-private, map-fixed, map-anon, map-huge-shift, map-huge-mask, prot-read, prot-write, prot-exec, prot-none, prot-growsdown, prot-growsup, madv-normal, madv-random, madv-sequential, madv-willneed, madv-dontneed, madv-free, madv-remove, madv-dontfork, madv-dofork, madv-mergeable, madv-unmergeable, madv-hugepage, madv-nohugepage, madv-dontdump, madv-dodump, madv-wipeonfork, madv-keeponfork, madv-hwpoison, ms-async, ms-sync, ms-invalidate, page-size. * ffi.h (mmap_wrap, munmap_wrap, mprotect_wrap madvise_wrap, msync_wrap): Declared. * tests/017/mmap.tl: New file. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
-rwxr-xr-xconfigure29
-rw-r--r--ffi.c345
-rw-r--r--ffi.h8
-rw-r--r--stdlib/doc-syms.tl21
-rw-r--r--tests/017/mmap.tl52
-rw-r--r--txr.1498
6 files changed, 953 insertions, 0 deletions
diff --git a/configure b/configure
index c710b778..0008f764 100755
--- a/configure
+++ b/configure
@@ -3781,6 +3781,35 @@ else
printf "no\n"
fi
+printf "Checking for mmap ... "
+cat > conftest.c <<!
+#include <sys/mman.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(void)
+{
+ size_t pgsz = sysconf(_SC_PAGE_SIZE);
+ void *addr = mmap(0, pgsz, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_SHARED, -1, 0);
+ if (addr == MAP_FAILED)
+ return EXIT_FAILURE;
+ mprotect(addr, pgsz, PROT_WRITE);
+ madvise(addr, pgsz, MADV_SEQUENTIAL);
+ msync(addr, pgsz, MS_SYNC);
+ munmap(addr, pgsz);
+ return 0;
+}
+!
+
+if conftest ; then
+ printf "yes\n"
+ printf "#define HAVE_MMAP 1\n" >> config.h
+else
+ printf "no\n"
+fi
+
+
#
# Dependent variables
#
diff --git a/ffi.c b/ffi.c
index 6e23c7d0..708dbbcb 100644
--- a/ffi.c
+++ b/ffi.c
@@ -44,6 +44,11 @@
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
+#if HAVE_MMAP
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+#endif
#include "alloca.h"
#include "lib.h"
#include "stream.h"
@@ -59,6 +64,9 @@
#include "args.h"
#include "utf8.h"
#include "hash.h"
+#if HAVE_MMAP
+#include "sysif.h"
+#endif
#include "ffi.h"
#include "txr.h"
@@ -5262,6 +5270,9 @@ struct carray {
val ref;
cnum offs;
val artype[2];
+#if HAVE_MMAP
+ size_t mm_len;
+#endif
};
static struct carray *carray_struct(val carray)
@@ -5330,6 +5341,9 @@ val make_carray(val type, mem_t *data, cnum nelem, val ref, cnum offs)
scry->eltype = type;
scry->ref = ref;
scry->offs = offs;
+#if HAVE_MMAP
+ scry->mm_len = 0;
+#endif
return obj;
}
@@ -6045,6 +6059,289 @@ val fill_carray(val carray, val offs, val stream)
return ret;
}
+#if HAVE_MMAP
+
+#ifndef MAP_GROWSDOWN
+#define MAP_GROWSDOWN 0
+#endif
+#ifndef MAP_LOCKED
+#define MAP_LOCKED 0
+#endif
+#ifndef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+#ifndef MAP_POPULATE
+#define MAP_POPULATE 0
+#endif
+#ifndef MAP_NONBLOCK
+#define MAP_NONBLOCK 0
+#endif
+#ifndef MAP_STACK
+#define MAP_STACK 0
+#endif
+#ifndef MAP_HUGETLB
+#define MAP_HUGETLB 0
+#endif
+#ifndef MAP_SHARED
+#define MAP_SHARED 0
+#endif
+#ifndef MAP_PRIVATE
+#define MAP_PRIVATE 0
+#endif
+#ifndef MAP_FIXED
+#define MAP_FIXED 0
+#endif
+#if !defined MAP_ANON && defined MAP_ANONYMOUS
+#define MAP_ANON MAP_ANONYMOUS
+#elif !defined MAP_ANON
+#define MAP_ANON 0
+#endif
+#ifndef MAP_HUGE_SHIFT
+#define MAP_HUGE_SHIFT 0
+#endif
+#ifndef MAP_HUGE_MASK
+#define MAP_HUGE_MASK 0
+#endif
+
+#ifndef PROT_READ
+#define PROT_READ 0
+#endif
+#ifndef PROT_WRITE
+#define PROT_WRITE 0
+#endif
+#ifndef PROT_EXEC
+#define PROT_EXEC 0
+#endif
+#ifndef PROT_NONE
+#define PROT_NONE 0
+#endif
+#ifndef PROT_GROWSDOWN
+#define PROT_GROWSDOWN 0
+#endif
+#ifndef PROT_GROWSUP
+#define PROT_GROWSUP 0
+#endif
+
+#ifndef MADV_NORMAL
+#define MADV_NORMAL 0
+#endif
+#ifndef MADV_RANDOM
+#define MADV_RANDOM 0
+#endif
+#ifndef MADV_SEQUENTIAL
+#define MADV_SEQUENTIAL 0
+#endif
+#ifndef MADV_WILLNEED
+#define MADV_WILLNEED 0
+#endif
+#ifndef MADV_DONTNEED
+#define MADV_DONTNEED 0
+#endif
+#ifndef MADV_FREE
+#define MADV_FREE 0
+#endif
+#ifndef MADV_REMOVE
+#define MADV_REMOVE 0
+#endif
+#ifndef MADV_DONTFORK
+#define MADV_DONTFORK 0
+#endif
+#ifndef MADV_DOFORK
+#define MADV_DOFORK 0
+#endif
+#ifndef MADV_MERGEABLE
+#define MADV_MERGEABLE 0
+#endif
+#ifndef MADV_UNMERGEABLE
+#define MADV_UNMERGEABLE 0
+#endif
+#ifndef MADV_HUGEPAGE
+#define MADV_HUGEPAGE 0
+#endif
+#ifndef MADV_NOHUGEPAGE
+#define MADV_NOHUGEPAGE 0
+#endif
+#ifndef MADV_DONTDUMP
+#define MADV_DONTDUMP 0
+#endif
+#ifndef MADV_DODUMP
+#define MADV_DODUMP 0
+#endif
+#ifndef MADV_WIPEONFORK
+#define MADV_WIPEONFORK 0
+#endif
+#ifndef MADV_KEEPONFORK
+#define MADV_KEEPONFORK 0
+#endif
+#ifndef MADV_HWPOISON
+#define MADV_HWPOISON 0
+#endif
+
+#ifndef MS_ASYNC
+#define MS_ASYNC 0
+#endif
+#ifndef MS_SYNC
+#define MS_SYNC 0
+#endif
+#ifndef MS_INVALIDATE
+#define MS_INVALIDATE 0
+#endif
+
+static void carray_munmap_op(val obj)
+{
+ struct carray *scry = carray_struct(obj);
+ munmap(scry->data, scry->mm_len);
+ scry->data = 0;
+ free(scry);
+}
+
+static struct cobj_ops carray_mmap_ops =
+ cobj_ops_init(eq,
+ carray_print_op,
+ carray_munmap_op,
+ carray_mark_op,
+ cobj_eq_hash_op);
+
+val mmap_wrap(val type, val len, val prot, val flags,
+ val source_opt, val offset_opt, val addr_opt)
+{
+ val self = lit("mmap");
+ val source = default_null_arg(source_opt);
+ val offset = default_arg_strict(offset_opt, zero);
+ val addr = default_null_arg(addr_opt);
+ void *ad_req = if3(addr, coerce(void *, c_unum(addr, self)), 0);
+ mem_t *ad_out;
+ int fd = -1;
+ ucnum ln = c_unum(len, self);
+ struct txr_ffi_type *tft = ffi_type_struct_checked(self, type);
+ cnum nelem = if3(tft->size, ln / tft->size, 0);
+ int pro = c_int(prot, self);
+ int flg = c_int(flags, self);
+
+ if (ln != 0 && nelem == 0)
+ uw_throwf(error_s, lit("~a: zero-sized element type ~s specified"),
+ self, type, nao);
+
+ if (streamp(source)) {
+ val fileno = stream_fd(source);
+ if (!fileno)
+ uw_throwf(type_error_s, lit("~a: stream ~s has no file descriptor"),
+ self, source, nao);
+ fd = c_int(fileno, self);
+ } else if (integerp(source)) {
+ fd = c_int(source, self);
+ } else if (stringp(source)) {
+ val mode = if3(pro & PROT_WRITE, lit("r+"), lit("r"));
+ val stream = open_file(source, mode);
+ val map = nil;
+ uw_simple_catch_begin;
+ map = mmap_wrap(type, len, prot, flags, stream, offset_opt, addr_opt);
+ uw_unwind {
+ close_stream(stream, nil);
+ }
+ uw_catch_end;
+ return map;
+ } else if (source) {
+ uw_throwf(type_error_s, lit("~a: unsupported map source object ~s"),
+ self, source, nao);
+ }
+
+ ad_out = coerce(mem_t *,
+ mmap(ad_req, ln, pro, flg, fd, c_u64(offset, self)));
+
+ if (ad_out == MAP_FAILED) {
+ int eno = errno;
+ uw_throwf(system_error_s, lit("~a: mmap failed: ~d/~s"),
+ self, num(eno), errno_to_str(eno), nao);
+ } else {
+ val ca = make_carray(type, ad_out, nelem, nil, 0);
+ struct carray *scry = carray_struct(ca);
+ scry->mm_len = ln;
+ ca->co.ops = &carray_mmap_ops;
+ return ca;
+ }
+}
+
+val munmap_wrap(val carray)
+{
+ val self = lit("munmap");
+ struct carray *scry = carray_struct_checked(self, carray);
+
+ if (carray->co.ops != &carray_mmap_ops)
+ uw_throwf(type_error_s, lit("~a: ~s isn't a mmapped carray"),
+ self, carray, nao);
+ if (scry->data != 0) {
+ munmap(scry->data, scry->mm_len);
+ scry->data = 0;
+ return t;
+ }
+
+ return nil;
+}
+
+static val mmap_op(val carray, val offset_in, val size_in,
+ val arg, int (*op_fn)(void *, size_t, int),
+ val self)
+{
+ struct carray *scry = carray_struct_checked(self, carray);
+ size_t off = 0, sz;
+
+ if (carray->co.ops != &carray_mmap_ops)
+ uw_throwf(type_error_s, lit("~a: ~s isn't a mmaped carray"),
+ self, carray, nao);
+
+ if (missingp(offset_in) && missingp(size_in)) {
+ sz = scry->mm_len;
+ } else if (missingp(offset_in)) {
+ sz = c_unum(size_in, self);
+ } else if (missingp(size_in)) {
+ off = c_unum(offset_in, self);
+ sz = scry->mm_len - off;
+ } else {
+ off = c_unum(offset_in, self);
+ sz = c_unum(size_in, self);
+ }
+
+ if (off > scry->mm_len)
+ uw_throwf(error_s, lit("~a: ~s: offset ~s lies beyond ~s byte mapping"),
+ self, carray, unum(off), unum(scry->mm_len), nao);
+
+ if (off + sz < off)
+ uw_throwf(error_s,
+ lit("~a: ~s: size ~s from offset ~s wraps around"),
+ self, carray, unum(sz), unum(off), nao);
+
+ if (off + sz > scry->mm_len)
+ uw_throwf(error_s,
+ lit("~a: ~s: size ~s from offset ~s extends beyond ~s byte mapping"),
+ self, carray, unum(sz), unum(off), unum(scry->mm_len), nao);
+
+ if (op_fn(scry->data + off, sz, c_int(arg, self)) < 0) {
+ int eno = errno;
+ uw_throwf(system_error_s, lit("~a: ~s: ~a failed: ~d/~s"),
+ self, carray, self, num(eno), errno_to_str(eno), nao);
+ }
+
+ return t;
+}
+
+val mprotect_wrap(val carray, val prot, val offset, val size)
+{
+ return mmap_op(carray, offset, size, prot, mprotect, lit("mprotect"));
+}
+
+val madvise_wrap(val carray, val advice, val offset, val size)
+{
+ return mmap_op(carray, offset, size, advice, madvise, lit("madvise"));
+}
+
+val msync_wrap(val carray, val flags, val offset, val size)
+{
+ return mmap_op(carray, offset, size, flags, msync, lit("msync"));
+}
+
+#endif
+
static val cptr_getobj(val cptr, val type_in)
{
val self = lit("cptr-get");
@@ -6469,6 +6766,54 @@ void ffi_init(void)
reg_fun(intern(lit("fill-carray"), user_package), func_n3o(fill_carray, 1));
reg_fun(intern(lit("cptr-get"), user_package), func_n2o(cptr_getobj, 1));
reg_fun(intern(lit("cptr-out"), user_package), func_n3o(cptr_out, 2));
+#if HAVE_MMAP
+ reg_fun(intern(lit("mmap"), user_package), func_n7o(mmap_wrap, 4));
+ reg_fun(intern(lit("munmap"), user_package), func_n1(munmap_wrap));
+ reg_fun(intern(lit("mprotect"), user_package), func_n4o(mprotect_wrap, 2));
+ reg_fun(intern(lit("madvise"), user_package), func_n4o(madvise_wrap, 2));
+ reg_fun(intern(lit("msync"), user_package), func_n4o(msync_wrap, 2));
+ reg_varl(intern(lit("map-growsdown"), user_package), num_fast(MAP_GROWSDOWN));
+ reg_varl(intern(lit("map-locked"), user_package), num_fast(MAP_LOCKED));
+ reg_varl(intern(lit("map-noreserve"), user_package), num_fast(MAP_NORESERVE));
+ reg_varl(intern(lit("map-populate"), user_package), num_fast(MAP_POPULATE));
+ reg_varl(intern(lit("map-nonblock"), user_package), num_fast(MAP_NONBLOCK));
+ reg_varl(intern(lit("map-stack"), user_package), num_fast(MAP_STACK));
+ reg_varl(intern(lit("map-hugetlb"), user_package), num_fast(MAP_HUGETLB));
+ reg_varl(intern(lit("map-shared"), user_package), num_fast(MAP_SHARED));
+ reg_varl(intern(lit("map-private"), user_package), num_fast(MAP_PRIVATE));
+ reg_varl(intern(lit("map-fixed"), user_package), num_fast(MAP_FIXED));
+ reg_varl(intern(lit("map-anon"), user_package), num_fast(MAP_ANON));
+ reg_varl(intern(lit("map-huge-shift"), user_package), num_fast(MAP_HUGE_SHIFT));
+ reg_varl(intern(lit("map-huge-mask"), user_package), num_fast(MAP_HUGE_MASK));
+ reg_varl(intern(lit("prot-read"), user_package), num_fast(PROT_READ));
+ reg_varl(intern(lit("prot-write"), user_package), num_fast(PROT_WRITE));
+ reg_varl(intern(lit("prot-exec"), user_package), num_fast(PROT_EXEC));
+ reg_varl(intern(lit("prot-none"), user_package), num_fast(PROT_NONE));
+ reg_varl(intern(lit("prot-growsdown"), user_package), num_fast(PROT_GROWSDOWN));
+ reg_varl(intern(lit("prot-growsup"), user_package), num_fast(PROT_GROWSUP));
+ reg_varl(intern(lit("madv-normal"), user_package), num_fast(MADV_NORMAL));
+ reg_varl(intern(lit("madv-random"), user_package), num_fast(MADV_RANDOM));
+ reg_varl(intern(lit("madv-sequential"), user_package), num_fast(MADV_SEQUENTIAL));
+ reg_varl(intern(lit("madv-willneed"), user_package), num_fast(MADV_WILLNEED));
+ reg_varl(intern(lit("madv-dontneed"), user_package), num_fast(MADV_DONTNEED));
+ reg_varl(intern(lit("madv-free"), user_package), num_fast(MADV_FREE));
+ reg_varl(intern(lit("madv-remove"), user_package), num_fast(MADV_REMOVE));
+ reg_varl(intern(lit("madv-dontfork"), user_package), num_fast(MADV_DONTFORK));
+ reg_varl(intern(lit("madv-dofork"), user_package), num_fast(MADV_DOFORK));
+ reg_varl(intern(lit("madv-mergeable"), user_package), num_fast(MADV_MERGEABLE));
+ reg_varl(intern(lit("madv-unmergeable"), user_package), num_fast(MADV_UNMERGEABLE));
+ reg_varl(intern(lit("madv-hugepage"), user_package), num_fast(MADV_HUGEPAGE));
+ reg_varl(intern(lit("madv-nohugepage"), user_package), num_fast(MADV_NOHUGEPAGE));
+ reg_varl(intern(lit("madv-dontdump"), user_package), num_fast(MADV_DONTDUMP));
+ reg_varl(intern(lit("madv-dodump"), user_package), num_fast(MADV_DODUMP));
+ reg_varl(intern(lit("madv-wipeonfork"), user_package), num_fast(MADV_WIPEONFORK));
+ reg_varl(intern(lit("madv-keeponfork"), user_package), num_fast(MADV_KEEPONFORK));
+ reg_varl(intern(lit("madv-hwpoison"), user_package), num_fast(MADV_HWPOISON));
+ reg_varl(intern(lit("ms-async"), user_package), num_fast(MS_ASYNC));
+ reg_varl(intern(lit("ms-sync"), user_package), num_fast(MS_SYNC));
+ reg_varl(intern(lit("ms-invalidate"), user_package), num_fast(MS_INVALIDATE));
+ reg_varl(intern(lit("page-size"), user_package), num_fast(sysconf(_SC_PAGESIZE)));
+#endif
reg_fun(intern(lit("make-union"), user_package), func_n3o(make_union, 1));
reg_fun(intern(lit("union-members"), user_package), func_n1(union_members));
reg_fun(intern(lit("union-get"), user_package), func_n2(union_get));
diff --git a/ffi.h b/ffi.h
index a0adb0e0..fbc9faad 100644
--- a/ffi.h
+++ b/ffi.h
@@ -129,6 +129,14 @@ val uint_carray(val carray);
val int_carray(val carray);
val put_carray(val carray, val offs, val stream);
val fill_carray(val carray, val offs, val stream);
+#if HAVE_MMAP
+val mmap_wrap(val type, val len, val prot, val flags,
+ val source_opt, val offset_opt, val addr_opt);
+val munmap_wrap(val carray);
+val mprotect_wrap(val carray, val prot, val offset, val size);
+val madvise_wrap(val carray, val advice, val offset, val size);
+val msync_wrap(val carray, val flags, val offset, val size);
+#endif
mem_t *union_get_ptr(val self, val uni);
val make_union(val type, val init, val memb);
val union_members(val uni);
diff --git a/stdlib/doc-syms.tl b/stdlib/doc-syms.tl
index b9877466..81ec7211 100644
--- a/stdlib/doc-syms.tl
+++ b/stdlib/doc-syms.tl
@@ -1165,6 +1165,12 @@
("macroexpand-1-lisp1" "N-01E62179")
("macroexpand-lisp1" "N-01E62179")
("macrolet" "N-00AC12C0")
+ ("madv-dontneed" "N-027D1E84")
+ ("madv-normal" "N-027D1E84")
+ ("madv-random" "N-027D1E84")
+ ("madv-sequential" "N-027D1E84")
+ ("madv-willneed" "N-027D1E84")
+ ("madvise" "N-02805A83")
("major" "N-02F0F482")
("make-buf" "N-011445E1")
("make-buf-stream" "N-03F5647C")
@@ -1195,6 +1201,10 @@
("make-zstruct" "N-03855D2D")
("makedev" "N-02F0F482")
("makunbound" "N-01FA4070")
+ ("map-anon" "N-029B13AF")
+ ("map-fixed" "N-029B13AF")
+ ("map-private" "N-029B13AF")
+ ("map-shared" "N-029B13AF")
("mapcar" "N-0202F92F")
("mapcar*" "N-0202F92F")
("mapdo" "N-03A943EE")
@@ -1251,10 +1261,17 @@
("mkstring" "N-033DD796")
("mlet" "N-008216E0")
("mmakunbound" "N-02964FC0")
+ ("mmap" "N-03C6CE44")
("mod" "D-003F")
("mode-t" "N-01D716FE")
+ ("mprotect" "N-02805A83")
+ ("ms-async" "N-01F782B2")
+ ("ms-invalidate" "N-01F782B2")
+ ("ms-sync" "N-01F782B2")
+ ("msync" "N-02805A83")
("multi" "N-034946BA")
("multi-sort" "N-0132852F")
+ ("munmap" "N-00E1BF52")
("n-choose-k" "N-02ACFDE6")
("n-perm-k" "N-02ACFDE6")
("name" "N-01557906")
@@ -1438,6 +1455,10 @@
("promisep" "N-00C7553F")
("prop" "N-01C6D406")
("proper-list-p" "N-03F70343")
+ ("prot-exec" "N-0212DB35")
+ ("prot-none" "N-0212DB35")
+ ("prot-read" "N-0212DB35")
+ ("prot-write" "N-0212DB35")
("pset" "N-008211EC")
("ptr" "N-027B04D0")
("ptr-in" "N-00A494BF")
diff --git a/tests/017/mmap.tl b/tests/017/mmap.tl
new file mode 100644
index 00000000..aab86a0e
--- /dev/null
+++ b/tests/017/mmap.tl
@@ -0,0 +1,52 @@
+(load "../common")
+
+(defun parent (wp mm)
+ (with-stream (s (open-fileno wp "w"))
+ (each ((i 0..1024))
+ (set [mm i] i))
+ (put-char #\X s)))
+
+(defun child (rp mm)
+ (let ((s (open-fileno rp "r")))
+ (assert (eq (get-char s) #\X))
+ (each ((i 0..1024))
+ (assert (eql [mm i] i)))))
+
+(let ((mm (mmap (ffi uint32) 4096
+ (logior prot-read prot-write)
+ (logior map-anon map-shared))))
+ (tree-bind (rp . wp) (pipe)
+ (match-ecase (fork)
+ (0 (child rp mm)
+ (exit t))
+ (-1 (error "fork failed"))
+ (@pid (parent wp mm)
+ (tree-bind (p . s) (wait pid)
+ (unless (zerop s)
+ (error "child failed")))))))
+
+(assert (plusp page-size))
+
+(let* ((mk-rnd-buf (opip (expt 256 page-size) rand buf-uint))
+ (rndbuf0 [mk-rnd-buf])
+ (rndbuf1 [mk-rnd-buf])
+ (fname "rand.bin"))
+ (unwind-protect
+ (progn
+ (file-put-buf fname rndbuf0)
+ (let* ((mm (mmap (ffi uchar) page-size
+ (logior prot-read prot-write)
+ (logior map-shared)
+ fname)))
+ (each ((i 0..page-size))
+ (assert (eq [rndbuf0 i] [mm i]))
+ (set [mm i] [rndbuf1 i]))
+ (msync mm ms-sync)
+ (assert (equal (file-get-buf fname) rndbuf1))
+ (each ((i 0..page-size))
+ (set [mm i] [rndbuf0 i]))
+ (munmap mm))
+ (assert (equal (file-get-buf fname) rndbuf0)))
+ (remove-path fname)))
+
+(assert (null (ignerr (mmap (ffi char) 4096 prot-read map-anon))))
diff --git a/txr.1 b/txr.1
index 54688569..3ca2f9f3 100644
--- a/txr.1
+++ b/txr.1
@@ -72101,6 +72101,497 @@ returns
Further information about resource limits is available in the POSIX standard
and platform documentation.
+.SS Unix Memory Mapping
+
+The \*(TL interface to the POSIX
+.code mmap
+family of functions is based around the
+.code carray
+type. The
+.code mmap
+function returns a special variant of a
+.code carray
+object which keeps track of the memory mapping. When such an object
+becomes unreachable and is reclaimed by garbage collection, the mapping
+is automatically unmapped.
+
+In addition to
+.codn mmap ,
+the functions
+.codn munmap ,
+.codn mprotect ,
+.code madvise
+and
+.code msync
+are provided, all taking a
+.code carray
+as their leftmost argument.
+
+The \*(TL functions do not strictly follow the argument conventions of the
+same-named, corresponding POSIX functions. Adjustments which are likely to
+be defaulted are moved to the right.
+For instance, the
+.code msync
+operation is often applied to the entire memory mapping. Therefore,
+the first argument is the
+.code carray
+object which keeps track of the mapping. The second argument specifies
+the flags to be applied, which constitute the last argument of the
+underlying POSIX function.
+The remaining two arguments are the size and offset. If these are omitted,
+then
+.code msync
+applies to the entire region, whose address and size are known to the
+.code carray
+object.
+
+Cautionary note: misuse of
+.code mmap
+and related functions can easily cause the \*(TX image to receive
+a fatal signal due to a bad memory access. Care must be taken to prevent
+such a situation, or else to catch such signals and recover.
+
+.coNP Function @ mmap
+.synb
+.mets (mmap < ffi-type < length < prot < flags
+.mets \ \ \ \ \ >> [ source >> [ offset <> [ addr ]]])
+.syne
+.desc
+The
+.code mmap
+function provides access to the same-named POSIX platform function
+for creating memory mappings. The POSIX function cab be used for creating
+virtual memory views of files and special devices. Views can be read-only,
+and they can be mutable. They can be in such a way that changes appear
+only in the mapping itself, or in such a way that the changes are actually
+propagated to the mapped object itself. Mappings can be shared among
+processes, providing a shared memory mechanism: for instance, if
+.code fork
+is called, any
+.code map-shared
+mappings created by the parent are shared with the child: the child
+process does not get a copy of a shared mapping, but a reference to it.
+The function can also be used simply for allocating memory: on some
+platforms, the POSIX
+.code mmap
+is used as the basis for the
+.code malloc
+function. It behaves as a pure allocator when asked to create a mapping which
+is private, and anonymous (not backed by an object).
+
+The \*(TL
+.code mmap
+function is integrated with the
+.code carray
+type and the FFI type system. A mapping returned by
+.code mmap
+is represented by a
+.code carray
+object.
+
+The required
+.meta ffi-type
+argument specifies the element type of the array; it must be a compiled
+FFI type. Note: this may be produced by the
+.code ffi
+macro. For instance, the type
+.code int
+may be specified using the expression
+.codn "(ffi int)" .
+The type must be a complete type suitable as the element type of an array;
+a type with a zero size such as
+.code void
+is invalid.
+
+The
+.meta length
+argument specifies the length in bytes. Note that
+.code mmap
+works allocates or configures virtual memory pages, not bytes. Internally
+to the system, the
+.meta length
+argument is converted to a number of pages. If it specifies a fractional
+number of pages, it is rounded up. For instance, if the page size is 4096
+bytes, and
+.meta length
+is specified as 5000, it will be internally rounded up to 8192.
+The returned \*(TL
+.code carray
+object, is oblivious to this padding: it works with the given 5000 byte size.
+Note: the
+.code page-size
+variable holds the system's page size. However, by the use of
+.code mmap
+extensions, it is possible for individual mappings to have their own page size.
+Mixed page size virtual memory systems exist.
+
+The
+.code mmap
+function determines the number of elements in the array by dividing the
+.meta length
+by the size of
+.metn type ,
+using a division that truncates toward zero. The returned
+.code carray
+shall have that many elements. If the division is inexact, it means that
+some bytes from the underlying memory mapping are unused, even if
+.code length
+is a multiple of the page size.
+
+The required
+.meta prot
+argument must some bitwise combination of the portable values
+.codn prot-read ,
+.code prot-write
+and
+.codn prot-exec .
+Additional system-specific
+.code prot-
+values may be available also for specifying additional properties. If
+.meta prot
+is specified as zero, then the mapping, if successfully created, may be
+inaccessible:
+.code prot-read
+must be present to ensure read access, and
+.code prot-write
+to ensure write access.
+
+The
+.meta flags
+argument is a bitwise combination of values given by various
+.code map-
+variables. At the very least, it must contain exactly one of
+.code map-shared
+or
+.codn map-private ,
+to request a shared or private mapping, respectively.
+If a mapping is requested which is neither shared nor private,
+the underlying POSIX function will likely fail.
+If a
+.meta source
+is being specified, indicating a filesystem object to be mapped, the
+.code map-anon
+flag must be omitted. Vice versa, if
+.meta source
+is not being specified, this means that the mapping will be anonymous.
+In this situation, the
+.code map-anon
+flag must be present.
+
+Note: in the context of
+.codn mmap ,
+"anonymous" means "not associated with a filesystem object referenced by a
+descriptor". It does not mean "without a name", but refers to a pure memory
+allocation from virtual memory. Memory maps do not have a name, whether
+anonymous or not. Moreover, the filesystem object associated with a memory map
+itself does not necessarily have a name. An open file that has been deleted
+from the directory structure is anonymous, yet a memory mapping can be created
+using its descriptor, and that mapping is not "anonymous".
+
+The
+.meta offset
+argument is used with a non-anonymous mapping. It specifies that the mapping
+doesn't begin at the start of the file or file-like object, but rather at
+the specified offset. The offset may not be an arbitrary integer; it must be
+multiples of the page size. Unless certain nonportable
+.meta flags
+are used to specify an alternative page size, the value of the
+.code page-size
+variable may be relied upon to indicate the page size. If an
+.meta offset
+is specified for an anonymous mapping, with a nonzero value, the
+underlying POSIX function may indicate failure.
+
+If the
+.meta length
+and
+.meta offset
+values cause one or more pages to be mapped which are beyond the end of the
+file, then accessing those pages may produce a signal which is fatal if
+not handled.
+
+The
+.meta addr
+argument is used for specifying the address in conjunction with the
+.code map-fixed
+flag. Possibly, certain nonportable values in the
+.meta flags
+field may similarly require
+.metn addr .
+If no bit is present to
+.meta flags
+which requires
+.metn addr ,
+then
+.meta addr
+should either not be specified, or specified as zero.
+A non-zero value of
+.meta addr
+must be a multiple of the page size.
+
+The
+.code mmap
+function returns a
+.code carray
+object if successful. Upon failure, an exception derived from
+.code error
+is thrown.
+
+Note: when a
+.code carray
+object returned by
+.code mmap
+is identified by the garbage collector as unreachable, and reclaimed,
+the memory mapping is unmapped. The
+.code munmap
+function can be invoked on the
+.code carray
+to release the mapping before the object becomes garbage. The
+.code carray-free
+function cannot be used on a mapped
+.codn carray .
+
+.coNP Function @ munmap
+.synb
+.mets (munmap << carray )
+.syne
+.desc
+The
+.code munmap
+function releases the memory mapping tracked by
+.metn carray ,
+which must be an object previously returned by
+.codn mmap .
+An exception is thrown if the object is any other kind of
+.codn carray .
+
+Note: the memory mapping is released by means of the same-named POSIX function.
+No provision is made for selectively unmapping the pages of a mapping;
+the entire mapping associated with a
+.meta carray
+is removed.
+
+when the memory mapping is released,
+.code munmap
+returns
+.codn t .
+Thereafter, the
+.meta carray
+contents may no longer be accessed, subject to
+.code error
+exceptions being thrown.
+
+If
+.code munmap
+is called again on a
+.code carray
+on which it had previously been successfully called, the additional calls
+return
+.codn nil .
+
+.coNP Functions @, mprotect @ madvise and @ msync
+.synb
+.mets (mprotect < carray < prot >> [ offset <> [ size ]])
+.mets (madvise < carray < advice >> [ offset <> [ size ]])
+.mets (msync < carray < flags >> [ offset <> [ size ]])
+.syne
+.desc
+The functions
+.codn mprotect ,
+.code madvise
+and
+.code msync
+perform various operations and adjustments on a memory mapping, using the
+same-named, corresponding POSIX functions.
+
+All functions follow the same argument conventions with regard to the
+.meta carray
+argument and the optional
+.meta offset
+and
+.meta size
+arguments. The respective second arguments
+.metn prot ,
+.meta advice
+and
+.meta flags
+are all integers. Of these,
+.meta prot
+and
+.meta flags
+are bitmapped flags, whereas
+.meta advice
+specifies an enumerated command.
+
+The
+.meta prot
+argument is a bitwise combination of
+.code prot-
+values such as
+.codn prot-read ,
+.code prot-write
+and
+.codn prot-exec .
+The
+.code mprotect
+function adjusts the protection bits of the mapping accordingly.
+
+The
+.meta advice
+command of
+.code madvise
+should specify one of the following portable values, or else some
+system-specific nonportable
+.code madv-
+value:
+.codn madv-normal ,
+.codn madv-random ,
+.codn madv-sequential ,
+.code madv-willneed
+or
+.codn madv-dontneed .
+
+The
+.code flags
+argument of
+.code msync
+should specify exactly one of the values
+.code ms-async
+or
+.codn ms-sync .
+Additional
+.code ms-
+values such as
+.code ms-invalidate
+may be combined in.
+
+If
+.meta offset
+and
+.meta size
+are omitted, they default to zero, and the size of the entire mapping, respectively,
+so the operation applies to the entire mapping.
+
+If only a
+.meta size
+is specified, it must not exceed the mapping size, or an error exception is
+thrown. The
+.meta offset
+argument defaults to zero.
+
+If only the
+.meta offset
+is specified, it must not exceed the length of the mapping, or else
+an error exception is thrown. The size is calculated as the difference between
+the offset and the length. It may be zero.
+
+If both
+.meta offset
+and
+.meta size
+are specified, they must not specify a region any portion of which lies outside
+of the mapping. If
+.meta size
+is zero,
+.meta offset
+may be equal to the length of the mapping.
+
+The
+.meta offset
+must be a multiple of the page size, or else the operation will fail,
+since these functions work with virtual memory pages, and not individual
+bytes. The
+.meta length
+is adjusted by the system to a multiple of the applicable page size,
+as noted in the description of
+.codn mmap .
+
+When any of these three functions succeeds, it returns
+.codn t .
+Otherwise, it throws an exception.
+
+.coNP Variables @, map-shared @, map-private @ map-anon and @ map-fixed
+.desc
+The integer values of these variables are bitmasks, intended to be combined with
+.code logior
+to prepare a value for the
+.meta flags
+argument of
+.codn mmap .
+
+Additional nonportable, system-dependent
+.code map-
+variables may be available. Their names are derived by taking the
+.codn MAP_ -prefixed
+symbol from the platform header file, converting it to lower case and
+replacing underscores by hyphen characters.
+Any such variable which exists, but has a value of zero, is
+present only for compatibility with another system. For instance
+.code map-huge-shift
+may be present in non-Linux ports of \*(TX, but with a zero value; it has
+a nonzero value on Linux systems to which it specific. Applications critically
+relying on certain flags should test the corresponding variables for nonzero to
+make sure they are actually available.
+
+.coNP Variables @, prot-none @, prot-read @ prot-write and @ prot-exec
+.desc
+The integer values of these variable are bitmasks, intended to be combined with
+.code logior
+to prepare a value for the
+.meta prot
+argument of
+.code mmap
+and
+.codn mprotect .
+
+Additional nonportable, system-dependent
+.code prot-
+variables may be available. Their names are derived by taking the
+.codn PROT_ -prefixed
+symbol from the platform header file, converting it to lower case and
+replacing underscores by hyphen characters.
+Any such variable which exists, but has a value of zero, is
+present only for compatibility with another system.
+
+.coNP Variables @, madv-normal @, madv-random @, madv-sequential @ madv-willneed and @ madv-dontneed
+.desc
+The integer values of these variable are bitmasks, intended to be combined with
+.code logior
+to prepare a value for the
+.meta advice
+argument of the
+.code madvise
+function.
+
+Additional nonportable, system-dependent
+.code madv-
+variables may be available. Their names are derived by taking the
+.codn MADV_ -prefixed
+symbol from the platform header file, converting it to lower case and
+replacing underscores by hyphen characters.
+Any such variable which exists, but has a value of zero, is
+present only for compatibility with another system.
+
+.coNP Variables @, ms-async @ ms-sync and @ ms-invalidate
+.desc
+The integer values of these variable are bitmasks, intended to be combined with
+.code logior
+to prepare a value for the
+.meta advice
+argument of the
+.code msync
+function.
+
+As described under
+.codn msync ,
+at least one of
+.code ms-async
+and
+.code ms-sync
+should be present;
+.code ms-invalidate
+is optional.
+
.SS* Web Programming Support
.coNP Functions @ url-encode and @ url-decode
@@ -76735,6 +77226,13 @@ It is possible to create a
view over a buffer, using
.codn carray-buf .
+Lastly, the
+.code carray
+type is the basis for the \*(TL
+.code mmap
+function, which is documented in the section
+.BR "Unix Memory Mapping" .
+
.coNP FFI type @ cptr
.synb
.mets (cptr << type-sym )