#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);
#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);
#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
#ifdef MADV_FREE
ADVISE_CONSTANT(
"free"
, MADV_FREE);
#endif
#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));
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"
);
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 */