From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

/*
* This software is copyright (c) 2008, 2009 by Leon Timmermans <leont@cpan.org>.
*
* This is free software; you can redistribute it and/or modify it under
* the same terms as perl itself.
*
*/
#if defined(linux) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
#endif
#include <assert.h>
#include "mmap-compat.c"
#ifndef MIN
# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#define PERL_NO_GET_CONTEXT
#define PERL_REENTR_API 1
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "perliol.h"
#define NEED_mg_findext
#define NEED_sv_unmagicext
#include "ppport.h"
#ifndef SvPV_free
# define SvPV_free(arg) sv_setpvn_mg(arg, NULL, 0);
#endif
#ifndef SV_CHECK_THINKFIRST_COW_DROP
#define SV_CHECK_THINKFIRST_COW_DROP(sv) SV_CHECK_THINKFIRST(sv)
#endif
struct mmap_info {
void* real_address;
void* fake_address;
size_t real_length;
size_t fake_length;
int flags;
#ifdef USE_ITHREADS
perl_mutex count_mutex;
perl_mutex data_mutex;
PerlInterpreter* owner;
perl_cond cond;
int count;
#endif
};
#define die_sys(format) Perl_croak(aTHX_ format, strerror(errno))
static void reset_var(SV* var, struct mmap_info* info) {
SvPVX(var) = info->fake_address;
SvLEN(var) = 0;
SvCUR(var) = info->fake_length;
SvPOK_only_UTF8(var);
}
static void S_mmap_fixup(pTHX_ SV* var, struct mmap_info* info, const char* string, STRLEN len) {
if (ckWARN(WARN_SUBSTR)) {
Perl_warn(aTHX_ "Writing directly to a memory mapped file is not recommended");
if (SvCUR(var) > info->fake_length)
Perl_warn(aTHX_ "Truncating new value to size of the memory map");
}
if (string && len)
Copy(string, info->fake_address, MIN(len, info->fake_length), char);
SV_CHECK_THINKFIRST_COW_DROP(var);
if (SvROK(var))
sv_unref_flags(var, SV_IMMEDIATE_UNREF);
if (SvPOK(var))
SvPV_free(var);
reset_var(var, info);
}
#define mmap_fixup(var, info, string, len) S_mmap_fixup(aTHX_ var, info, string, len)
static int mmap_write(pTHX_ SV* var, MAGIC* magic) {
struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
if (info->real_length) {
if (!SvOK(var))
mmap_fixup(var, info, NULL, 0);
else if (!SvPOK(var)) {
STRLEN len;
const char* string = SvPV(var, len);
mmap_fixup(var, info, string, len);
}
else if (SvPVX(var) != info->fake_address)
mmap_fixup(var, info, SvPVX(var), SvCUR(var));
else {
if (ckWARN(WARN_SUBSTR) && SvCUR(var) != info->fake_length) {
Perl_warn(aTHX_ "Writing directly to a memory mapped file is not recommended");
SvCUR(var) = info->fake_length;
}
SvPOK_only_UTF8(var);
}
}
else {
if (!SvPOK(var) || sv_len(var) != 0) {
sv_setpvn(var, "", 0);
if (ckWARN(WARN_SUBSTR))
Perl_warn(aTHX_ "Can't overwrite an empty map");
}
SvPOK_only_UTF8(var);
}
return 0;
}
static int mmap_clear(pTHX_ SV* var, MAGIC* magic) {
Perl_die(aTHX_ "Can't clear a mapped variable");
return 0;
}
static int mmap_free(pTHX_ SV* var, MAGIC* magic) {
struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
#ifdef USE_ITHREADS
MUTEX_LOCK(&info->count_mutex);
if (--info->count == 0) {
if (info->real_length && munmap(info->real_address, info->real_length) == -1)
die_sys("Could not unmap: %s");
COND_DESTROY(&info->cond);
MUTEX_DESTROY(&info->data_mutex);
MUTEX_UNLOCK(&info->count_mutex);
MUTEX_DESTROY(&info->count_mutex);
PerlMemShared_free(info);
}
else {
if (info->real_length && msync(info->real_address, info->real_length, MS_ASYNC) == -1)
die_sys("Could not sync: %s");
MUTEX_UNLOCK(&info->count_mutex);
}
#else
if (info->real_length && munmap(info->real_address, info->real_length) == -1)
die_sys("Could not unmap: %s");
PerlMemShared_free(info);
#endif
SvREADONLY_off(var);
SvPVX(var) = NULL;
SvCUR(var) = 0;
return 0;
}
#ifdef USE_ITHREADS
static int mmap_dup(pTHX_ MAGIC* magic, CLONE_PARAMS* param) {
struct mmap_info* info = (struct mmap_info*) magic->mg_ptr;
MUTEX_LOCK(&info->count_mutex);
assert(info->count);
++info->count;
MUTEX_UNLOCK(&info->count_mutex);
return 0;
}
#else
#define mmap_dup 0
#endif
#ifdef MGf_LOCAL
static int mmap_local(pTHX_ SV* var, MAGIC* magic) {
Perl_croak(aTHX_ "Can't localize file map");
}
#define mmap_local_tail , mmap_local
#else
#define mmap_local_tail
#endif
static const MGVTBL mmap_table = { 0, mmap_write, 0, mmap_clear, mmap_free, 0, mmap_dup mmap_local_tail };
static Off_t S_sv_to_offset(pTHX_ SV* var) {
#if IV_SIZE >= 8
return (Off_t)SvUV(var);
#else
return (Off_t)floor(SvNV(var) + 0.5); /* hic sunt dracones */
#endif
}
#define sv_to_offset(var) S_sv_to_offset(aTHX_ var)
static void check_new_variable(pTHX_ SV* var) {
if (SvTYPE(var) > SVt_PVMG && SvTYPE(var) != SVt_PVLV)
Perl_croak(aTHX_ "Trying to map into a nonscalar!\n");
SV_CHECK_THINKFIRST_COW_DROP(var);
if (SvREADONLY(var))
Perl_croak(aTHX_ "%s", PL_no_modify);
if (SvMAGICAL(var) && mg_findext(var, PERL_MAGIC_ext, &mmap_table))
sv_unmagicext(var, PERL_MAGIC_ext, (MGVTBL*)&mmap_table);
if (SvROK(var))
sv_unref_flags(var, SV_IMMEDIATE_UNREF);
if (SvNIOK(var))
SvNIOK_off(var);
if (SvPOK(var))
SvPV_free(var);
SvUPGRADE(var, SVt_PVMG);
}
static void* do_mapping(pTHX_ size_t length, int prot, int flags, int fd, Off_t offset) {
void* address;
address = mmap(0, length, prot, flags | MAP_VARIABLE, fd, offset);
if (address == MAP_FAILED)
die_sys("Could not map: %s");
return address;
}
static void S_set_mmap_info(pTHX_ struct mmap_info* magical, void* address, size_t length, ptrdiff_t correction) {
magical->real_address = address;
magical->fake_address = (char*)address + correction;
magical->real_length = length + correction;
magical->fake_length = length;
#ifdef USE_ITHREADS
MUTEX_INIT(&magical->count_mutex);
MUTEX_INIT(&magical->data_mutex);
COND_INIT(&magical->cond);
magical->count = 1;
#endif
}
#define set_mmap_info(magical, addres, length, correction) S_set_mmap_info(aTHX_ magical, addres, length, correction)
static struct mmap_info* initialize_mmap_info(pTHX_ void* address, size_t length, ptrdiff_t correction, int flags) {
struct mmap_info* magical = PerlMemShared_malloc(sizeof *magical);
set_mmap_info(magical, address, length, correction);
magical->flags = flags;
return magical;
}
static void add_magic(pTHX_ SV* var, struct mmap_info* magical, int writable, int utf8) {
MAGIC* magic = sv_magicext(var, NULL, PERL_MAGIC_ext, &mmap_table, (const char*) magical, 0);
#ifdef MGf_LOCAL
magic->mg_flags |= MGf_LOCAL;
#endif
#ifdef USE_ITHREADS
magic->mg_flags |= MGf_DUP;
#endif
SvTAINTED_on(var);
if (utf8 && !sv_utf8_decode(var))
Perl_croak(aTHX_ "Invalid utf8 in memory mapping");
if (!writable)
SvREADONLY_on(var);
}
static int _is_mappable(pTHX_ int fd) {
Stat_t info;
return Fstat(fd, &info) == 0 && (S_ISREG(info.st_mode) || S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
}
#define is_mappable(fd) _is_mappable(aTHX_ fd)
static struct mmap_info* S_get_mmap_magic(pTHX_ SV* var, const char* funcname) {
MAGIC* magic;
if (!SvMAGICAL(var) || (magic = mg_findext(var, PERL_MAGIC_ext, &mmap_table)) == NULL)
Perl_croak(aTHX_ "Could not %s: this variable is not memory mapped", funcname);
return (struct mmap_info*) magic->mg_ptr;
}
#define get_mmap_magic(var, funcname) S_get_mmap_magic(aTHX_ var, funcname)
#ifdef USE_ITHREADS
static void magic_end(pTHX_ void* pre_info) {
struct mmap_info* info = (struct mmap_info*) pre_info;
info->owner = NULL;
MUTEX_UNLOCK(&info->data_mutex);
}
#endif
typedef struct { const char* key; size_t length; int value; } map[];
static map prots = {
{ STR_WITH_LEN("<"), PROT_READ },
{ STR_WITH_LEN("+<"), PROT_READ | PROT_WRITE },
{ STR_WITH_LEN(">"), PROT_WRITE },
{ STR_WITH_LEN("+>"), PROT_READ | PROT_WRITE },
};
static int S_protection_pvn(pTHX_ const char* mode, size_t mode_len) {
int i;
for (i = 0; i < sizeof prots / sizeof *prots; ++i) {
if (prots[i].length == mode_len && strnEQ(mode, prots[i].key, mode_len))
return prots[i].value;
}
Perl_croak(aTHX_ "No such mode '%s' known", mode);
}
#define protection_pvn(mode, mode_len) S_protection_pvn(aTHX_ mode, mode_len)
static int S_protection_sv(pTHX_ SV* mode_sv) {
STRLEN mode_len;
const char* mode = SvPV(mode_sv, mode_len);
const char* end = memchr(mode, ':', mode_len);
return protection_pvn(mode, end ? end - mode : mode_len);
}
#define protection_sv(mode) S_protection_sv(aTHX_ mode)
#define MAP_CONSTANT(cons) newCONSTSUB(stash, #cons, newSVuv(cons))
#define ADVISE_CONSTANT(key, value) hv_store(advise_constants, key, sizeof key - 1, newSVuv(value), 0)
#define EMPTY_MAP(info) ((info)->real_length == 0)
static void S_boot(pTHX) {
HV* stash = get_hv("File::Map::", FALSE);
HV* advise_constants = newHV();
MAP_CONSTANT(PROT_NONE);
MAP_CONSTANT(PROT_READ);
MAP_CONSTANT(PROT_WRITE);
MAP_CONSTANT(PROT_EXEC);
MAP_CONSTANT(MAP_ANONYMOUS);
MAP_CONSTANT(MAP_SHARED);
MAP_CONSTANT(MAP_PRIVATE);
MAP_CONSTANT(MAP_ANON);
MAP_CONSTANT(MAP_FILE);
/**/
hv_store(PL_modglobal, "File::Map::ADVISE_CONSTANTS", 27, (SV*)advise_constants, 0);
ADVISE_CONSTANT("normal", MADV_NORMAL);
ADVISE_CONSTANT("random", MADV_RANDOM);
ADVISE_CONSTANT("sequential", MADV_SEQUENTIAL);
ADVISE_CONSTANT("willneed", MADV_WILLNEED);
ADVISE_CONSTANT("dontneed", MADV_DONTNEED);
/* Linux specific advices */
#ifdef MADV_REMOVE
ADVISE_CONSTANT("remove", MADV_REMOVE);
#endif
#ifdef MADV_DONTFORK
ADVISE_CONSTANT("dontfork", MADV_DONTFORK);
#endif
#ifdef MADV_DOFORK
ADVISE_CONSTANT("dofork", MADV_DOFORK);
#endif
#ifdef MADV_MERGEABLE
ADVISE_CONSTANT("mergeable", MADV_MERGEABLE);
#endif
#ifdef MADV_UNMERGEABLE
ADVISE_CONSTANT("unmergeable", MADV_UNMERGEABLE);
#endif
/* BSD, Mac OS X & Solaris specific advice */
#ifdef MADV_FREE
ADVISE_CONSTANT("free", MADV_FREE);
#endif
/* FreeBSD specific advices */
#ifdef MADV_NOSYNC
ADVISE_CONSTANT("nosync", MADV_NOSYNC);
#endif
#ifdef MADV_AUTOSYNC
ADVISE_CONSTANT("autosync", MADV_AUTOSYNC);
#endif
#ifdef MADV_NOCORE
ADVISE_CONSTANT("nocore", MADV_NOCORE);
#endif
#ifdef MADV_CORE
ADVISE_CONSTANT("core", MADV_CORE);
#endif
#ifdef MADV_PROTECT
ADVISE_CONSTANT("protect", MADV_PROTECT);
#endif
#ifdef MADV_SPACEAVAIL
ADVISE_CONSTANT("spaceavail", MADV_SPACEAVAIL);
#endif
}
#define boot() S_boot(aTHX)
#if PTRSIZE == 8 && (defined(WIN32) || defined(__CYGWIN__))
#ifndef ULLONG_MAX
#define PTR_MAX _UI64_MAX /* MS Platform SDK crt */
#else
#define PTR_MAX ULLONG_MAX
#endif
#else
#define PTR_MAX ULONG_MAX
#endif
void S_mmap_impl(pTHX_ SV* var, size_t length, int prot, int flags, int fd, Off_t offset, int utf8) {
check_new_variable(aTHX_ var);
ptrdiff_t correction = offset % page_size();
void* address;
struct mmap_info* magical;
if (length > PTR_MAX - correction)
Perl_croak(aTHX_ "can't map: length + offset overflows");
if (length)
address = do_mapping(aTHX_ length + correction, prot, flags, fd, offset - correction);
else {
if (!is_mappable(fd)) {
errno = EACCES;
die_sys("Could not map: %s");
}
address = "";
correction = 0;
}
magical = initialize_mmap_info(aTHX_ address, length, correction, flags);
reset_var(var, magical);
SvSETMAGIC(var);
add_magic(aTHX_ var, magical, prot & PROT_WRITE, utf8);
}
#define mmap_impl(var, length, prot, flags, fd, offset, utf8) S_mmap_impl(aTHX_ var, length, prot, flags, fd, offset, utf8)
static const map mappable = {
{ STR_WITH_LEN("unix"), 1 },
{ STR_WITH_LEN("perlio"), 1 },
{ STR_WITH_LEN("crlf"), 1 },
{ STR_WITH_LEN("stdio"), 1 },
{ STR_WITH_LEN("flock"), 1 },
{ STR_WITH_LEN("creat"), 1 },
{ STR_WITH_LEN("mmap"), 1 },
};
static int S_map_get(pTHX_ const map table, size_t table_size, const char* name, int fallback) {
int i;
for (i = 0; i < table_size; ++i) {
if (strEQ(name, table[i].key))
return table[i].value;
}
return fallback;
}
#define map_get(table, name, default) S_map_get(aTHX_ table, sizeof table / sizeof *table, name, default)
int S_check_layers(pTHX_ PerlIO* fh) {
PerlIO* current;
if (PerlIO_fileno(fh) < 0)
Perl_croak(aTHX_ "Can't map fake filehandle");
for (current = fh; *current; current = PerlIONext(current)) {
if (!map_get(mappable, (*current)->tab->name, 0) || (*current)->flags & PERLIO_F_CRLF)
Perl_croak(aTHX_ "Shouldn't map non-binary filehandle");
}
return (*fh)->flags & PERLIO_F_UTF8;
}
#define check_layers(fh) S_check_layers(aTHX_ fh)
size_t S_get_length(pTHX_ PerlIO* fh, Off_t offset, SV* length_sv) {
Stat_t info;
Fstat(PerlIO_fileno(fh), &info);
size_t length = SvOK(length_sv) ? SvIV(length_sv) : info.st_size - offset;
size_t end = offset + length;
if (offset < 0 || end > info.st_size && !S_ISCHR(info.st_mode))
Perl_croak(aTHX_ "Window (%ld,%lu) is outside the file", offset, length);
return length;
}
#define get_length(fh, offset, length) S_get_length(aTHX_ fh, offset, length)
#define READONLY sv_2mortal(newSVpvs("<"))
#define undef &PL_sv_undef
void S_map_handle(pTHX_ SV* var, PerlIO* fh, SV* mode, Off_t offset, SV* length_sv) {
int utf8 = check_layers(fh);
size_t length = get_length(fh, offset, length_sv);
mmap_impl(var, length, protection_sv(mode), MAP_SHARED | MAP_FILE, PerlIO_fileno(fh), offset, utf8);
}
#define map_handle(var, fh, mode, offset, length) S_map_handle(aTHX_ var, fh, mode, offset, length)
void S_map_file(pTHX_ SV* var, SV* filename, SV* mode, Off_t offset, SV* length_sv) {
STRLEN mode_len;
const char* mode_raw = SvPV(mode, mode_len);
if (memchr(mode_raw, ':', mode_len) == NULL) {
SV* newmode = sv_2mortal(newSVsv(mode));
sv_catpvs(newmode, ":raw");
mode_raw = SvPV(newmode, mode_len);
}
GV* gv = MUTABLE_GV(sv_2mortal(newSV_type(SVt_NULL)));
gv_init_pvn(gv, CopSTASH(PL_curcop), "__ANONIO__", 10, GV_ADDMULTI);
if (!do_openn(gv, mode_raw, mode_len, 0, 0, 0, NULL, &filename, 1))
Perl_croak(aTHX_ "Couldn't open file %s: %s", SvPV_nolen(filename), strerror(errno));
map_handle(var, IoIFP(GvIO(gv)), mode, offset, length_sv);
}
#define map_file(var, filename, mode, offset, length) S_map_file(aTHX_ var, filename, mode, offset, length)
static const map flags = {
{ STR_WITH_LEN("shared") , MAP_SHARED },
{ STR_WITH_LEN("private"), MAP_PRIVATE },
};
void S_map_anonymous(pTHX_ SV* var, size_t length, const char* flag_name) {
int flag = map_get(flags, flag_name, -1);
if (flag == -1)
Perl_croak(aTHX_ "No such flag '%s'", flag_name);
if (length == 0)
Perl_croak(aTHX_ "Zero length specified for anonymous map");
mmap_impl(var, length, PROT_READ | PROT_WRITE, flag | MAP_ANONYMOUS, -1, 0, 0);
}
#define map_anonymous(var, length, flag_name) S_map_anonymous(aTHX_ var, length, flag_name)
void S_sys_map(pTHX_ SV* var, size_t length, int protection, int flags, SV* fh, Off_t offset) {
if (flags & MAP_ANONYMOUS)
mmap_impl(var, length, protection, flags, -1, offset, 0);
else {
PerlIO* pio = IoIFP(sv_2io(fh)); // XXX error check
int utf8 = check_layers(pio);
int fd = PerlIO_fileno(pio);
mmap_impl(var, length, protection, flags, fd, offset, utf8);
}
}
#define sys_map(var, length, protection, flags, fh, offset) S_sys_map(aTHX_ var, length, protection, flags, fh, offset)
void S_sync(pTHX_ SV* var, bool sync) {
struct mmap_info* info = get_mmap_magic(var, "sync");
if (EMPTY_MAP(info))
return;
if (SvREADONLY(var) && ckWARN(WARN_IO))
Perl_warn(aTHX_ "Syncing a readonly map makes no sense");
if (msync(info->real_address, info->real_length, sync ? MS_SYNC : MS_ASYNC ) == -1)
die_sys("Could not sync: %s");
}
#define sync(var, sync) S_sync(aTHX_ var, sync)
#ifdef __linux__
void S_remap(pTHX_ SV* var, size_t new_size) {
struct mmap_info* info = get_mmap_magic(var, "remap");
ptrdiff_t correction = info->real_length - info->fake_length;
void* new_address;
CODE:
#ifdef USE_ITHREADS
if (info->count != 1)
Perl_croak(aTHX_ "Can't remap a shared mapping");
#endif
if (EMPTY_MAP(info))
Perl_croak(aTHX_ "Can't remap empty map"); /* XXX */
if (new_size == 0)
Perl_croak(aTHX_ "Can't remap to zero");
if ((info->flags & (MAP_ANONYMOUS|MAP_SHARED)) == (MAP_ANONYMOUS|MAP_SHARED))
Perl_croak(aTHX_ "Can't remap a shared anonymous mapping");
if ((new_address = mremap(info->real_address, info->real_length, new_size + correction, MREMAP_MAYMOVE)) == MAP_FAILED)
die_sys("Could not remap: %s");
set_mmap_info(info, new_address, new_size, correction);
reset_var(var, info);
}
#define remap(var, new_size) S_remap(aTHX_ var, new_size)
#endif
void S_unmap(pTHX_ SV* var) {
get_mmap_magic(var, "unmap");
sv_unmagicext(var, PERL_MAGIC_ext, (MGVTBL*)&mmap_table);
}
#define unmap(var) S_unmap(aTHX_ var)
void S_pin(pTHX_ struct mmap_info* info) {
#ifndef VMS
if (EMPTY_MAP(info))
return;
if (mlock(info->real_address, info->real_length) == -1)
die_sys("Could not pin: %s");
#else
Perl_croak(aTHX_ "pin not implemented on VMS");
#endif
}
#define pin(var) S_pin(aTHX_ var)
void S_unpin(pTHX_ struct mmap_info* info) {
#ifndef VMS
if (EMPTY_MAP(info))
return;
if (munlock(info->real_address, info->real_length) == -1)
die_sys("Could not unpin: %s");
#else
Perl_croak(aTHX_ "unpin not implemented on VMS");
#endif
}
#define unpin(var) S_unpin(aTHX_ var)
void S_advise(pTHX_ struct mmap_info* info, SV* name) {
HV* constants = (HV*) *hv_fetch(PL_modglobal, "File::Map::ADVISE_CONSTANTS", 27, 0);
HE* value = hv_fetch_ent(constants, name, 0, 0);
if (EMPTY_MAP(info))
return;
if (!value) {
if (ckWARN(WARN_PORTABLE))
Perl_warn(aTHX_ "Unknown advice '%s'", SvPV_nolen(name));
}
else if (madvise(info->real_address, info->real_length, SvUV(HeVAL(value))) == -1)
die_sys("Could not advice: %s");
}
#define advise(var, name) S_advise(aTHX_ var, name)
void S_protect(pTHX_ SV* var, SV* prot) {
struct mmap_info* info = get_mmap_magic(var, "protect");
int prot_val = SvIOK(prot) ? SvIV(prot) : protection_sv(prot);
if (!EMPTY_MAP(info))
mprotect(info->real_address, info->real_length, prot_val);
if (prot_val & PROT_WRITE)
SvREADONLY_off(var);
else
SvREADONLY_on(var);
}
#define protect(var, prot) S_protect(aTHX_ var, prot)
void S_lock_map(pTHX_ struct mmap_info* info) {
#ifdef USE_ITHREADS
LEAVE;
SAVEDESTRUCTOR_X(magic_end, info);
MUTEX_LOCK(&info->data_mutex);
info->owner = aTHX;
ENTER;
#endif
}
#define lock_map(var) S_lock_map(aTHX_ var)
#ifdef USE_ITHREADS
SV* S_wait_until(pTHX_ SV* block, SV* var) {
struct mmap_info* info = get_mmap_magic(var, "wait_until");
if (info->owner != aTHX)
Perl_croak(aTHX_ "Trying to wait on an unlocked map");
SAVESPTR(DEFSV);
DEFSV = var;
dSP;
while (1) {
PUSHMARK(SP);
call_sv(block, G_SCALAR | G_NOARGS);
SPAGAIN;
SV* result = POPs;
if (SvTRUE(result))
return SvREFCNT_inc(result);
COND_WAIT(&info->cond, &info->data_mutex);
}
}
#define wait_until(block, var) S_wait_until(aTHX_ block, var)
void S_notify(pTHX_ struct mmap_info* info) {
if (info->owner != aTHX)
Perl_croak(aTHX_ "Trying to notify on an unlocked map");
COND_SIGNAL(&info->cond);
}
#define notify(var) S_notify(aTHX_ var)
void S_broadcast(pTHX_ struct mmap_info* info) {
if (info->owner != aTHX)
Perl_croak(aTHX_ "Trying to broadcast on an unlocked map");
COND_BROADCAST(&info->cond);
}
#define broadcast(var) S_broadcast(aTHX_ var)
#endif
MODULE = File::Map PACKAGE = File::Map
PROTOTYPES: DISABLED
BOOT:
boot();
void map_file(SV* var, SV* filename, SV* mode = READONLY, Off_t offset = 0, SV* length = undef)
void map_handle(SV* var, PerlIO* fh, SV* mode = READONLY, Off_t offset = 0, SV* length = undef)
void map_anonymous(SV* var, size_t length, const char* flag_name = "shared")
void sys_map(SV* var, size_t length, int protection, int flags, SV* fh = undef, Off_t offset = 0)
void sync(SV* var, bool sync = TRUE)
#ifdef __linux__
void remap(SV* var, size_t new_size)
#endif
void unmap(SV* var)
void pin(struct mmap_info* var)
void unpin(struct mmap_info* var)
void advise(struct mmap_info* var, SV* name)
void protect(SV* var, SV* prot)
void lock_map(struct mmap_info* var)
#ifdef USE_ITHREADS
SV* wait_until(SV* block, SV* var)
PROTOTYPE: &@
void notify(struct mmap_info* var)
void broadcast(struct mmap_info* var)
#endif /* USE ITHREADS */