Hash-SharedMem
view release on metacpan or search on metacpan
lib/Hash/SharedMem.xs view on Meta::CPAN
PERL_STATIC_INLINE void shash_initiate_rollover(struct shash *sh)
{
word *root_p = &WORD_AT(sh->data_mmap, sh->sizes->dhd_current_root);
while(1) {
word root = word_get(root_p);
if(unlikely(root & PTR_FLAG_ROLLOVER)) break;
if(likely(shash_change_root(sh, root,
root | PTR_FLAG_ROLLOVER)))
break;
}
}
#define shash_try_rollover(sh, act, addsz) \
THX_shash_try_rollover(aTHX_ sh, act, addsz)
PERL_STATIC_INLINE word THX_shash_try_rollover(pTHX_ struct shash *sh,
char const *action, word addsz)
{
char filename[DATA_FILENAME_BUFSIZE];
word *allocfileid_p;
word old_file_id, old_root_word, old_root;
word new_file_id, new_root, new_sz;
struct stat statbuf;
int new_fd;
unlinkfile_ref_t new_ulr;
closefd_ref_t new_fdr;
struct shash new_sh;
SV *old_mmap_sv;
tmps_ix_t old_tmps_floor;
old_root_word = word_get(&WORD_AT(sh->data_mmap,
sh->sizes->dhd_current_root));
old_root = old_root_word & ~PTR_FLAG_ROLLOVER;
new_sz = sh->sizes->dhd_sz + btree_size(sh, old_root);
if(unlikely(new_sz < sh->sizes->dhd_sz || (new_sz & (((word)7) << 61))))
shash_error_toobig(sh, action);
new_sz <<= 3;
new_sz += addsz;
if(unlikely(new_sz < addsz)) shash_error_toobig(sh, action);
new_sz = PAGE_ALIGN(sh->sizes, new_sz);
if(unlikely(!new_sz)) shash_error_toobig(sh, action);
if(unlikely((off_t)new_sz < 0 || (word)(off_t)new_sz != new_sz))
shash_error_errnum(sh, action, EFBIG);
tally_zero(&new_sh.tally);
new_sh.sizes = sh->sizes;
new_sh.parameter = sh->parameter;
new_sh.top_pathname_sv = sh->top_pathname_sv;
allocfileid_p = &WORD_AT(sh->u.live.master_mmap,
sh->sizes->mfl_lastalloc_datafileid);
do {
old_file_id = word_get(allocfileid_p);
new_file_id = old_file_id + 1;
if(unlikely(new_file_id == 0)) new_file_id = 1;
} while(!likely(word_cset(allocfileid_p, old_file_id, new_file_id)));
if(unlikely(dirref_rel_stat(sh->u.live.dir, MASTER_FILENAME, &statbuf)
== -1))
shash_error_errno(sh, action);
dir_make_data_filename(filename, new_file_id);
new_fd = dirref_rel_open_cloexec(sh->u.live.dir, filename,
O_RDWR|O_CREAT|O_EXCL, 0);
if(unlikely(new_fd == -1)) shash_error_errno(sh, action);
new_ulr = unlinkfile_save(sh->u.live.dir, filename);
if(unlikely(fchown(new_fd, -1, statbuf.st_gid) == -1) &&
unlikely(errno != EPERM))
shash_error_errno(sh, action);
if(unlikely(fchmod(new_fd, statbuf.st_mode &
(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))
== -1))
shash_error_errno(sh, action);
if(unlikely(fchown(new_fd, statbuf.st_uid, -1) == -1) &&
unlikely(errno != EPERM))
shash_error_errno(sh, action);
new_fdr = closefd_save(new_fd);
if(unlikely(ftruncate(new_fd, new_sz) == -1)) {
/*
* A file-too-big error may be reported as either
* EFBIG or EINVAL depending on OS. The former is more
* enlightening to the user, so always report it that way.
*/
int e = errno;
shash_error_errnum(sh, action, e == EINVAL ? EFBIG : e);
}
old_tmps_floor = PL_tmps_floor;
SAVETMPS;
new_sh.data_mmap_sv = mmap_as_sv(new_fd, new_sz, 1);
if(!likely(new_sh.data_mmap_sv)) shash_error_errno(sh, action);
new_sh.data_mmap = SvPVX(new_sh.data_mmap_sv);
new_sh.data_size = new_sz;
closefd_early(new_fdr);
WORD_AT(new_sh.data_mmap, DHD_MAGIC) = DATA_FILE_MAGIC;
WORD_AT(new_sh.data_mmap, DHD_PARAM) = sh->parameter;
WORD_AT(new_sh.data_mmap, DHD_LENGTH) = new_sz;
WORD_AT(new_sh.data_mmap, sh->sizes->dhd_nextalloc_space) =
sh->sizes->dhd_sz;
WORD_AT(new_sh.data_mmap, sh->sizes->dhd_current_root) = new_root =
btree_migrate(sh, old_root, &new_sh, action);
tally_add(&sh->tally, &new_sh.tally);
old_file_id = sh->u.live.data_file_id;
if((!(old_root_word & PTR_FLAG_ROLLOVER) &&
!likely(shash_change_root(sh, old_root_word,
old_root_word | PTR_FLAG_ROLLOVER))) ||
!likely(shash_change_file(sh,
old_file_id, new_file_id))) {
FREETMPS;
PL_tmps_floor = old_tmps_floor;
shash_unlinkfile_early(sh, action, new_ulr);
return NULL_PTR;
}
unlinkfile_cancel(new_ulr);
old_mmap_sv = sh->data_mmap_sv;
sh->data_mmap_sv = NULL;
SvREFCNT_dec_NN(old_mmap_sv);
sh->data_mmap_sv = SvREFCNT_inc_simple_NN(new_sh.data_mmap_sv);
sh->data_mmap = new_sh.data_mmap;
sh->data_size = new_sh.data_size;
sh->u.live.data_file_id = new_file_id;
FREETMPS;
PL_tmps_floor = old_tmps_floor;
if(likely(old_file_id != 0)) {
dir_make_data_filename(filename, old_file_id);
if(unlikely(dirref_rel_unlink(sh->u.live.dir, filename)
== -1)) {
int e = errno;
if(!(likely(e == ENOENT) || likely(e == EBUSY)))
shash_error_errnum(sh, action, e);
}
}
return new_root;
}
( run in 3.488 seconds using v1.01-cache-2.11-cpan-71847e10f99 )