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 )