view release on metacpan or search on metacpan
just function calls in Cache::FastMmap
namespace
1.38 Sun Jul 17 18:30 2011
- Fix build process that was completely broken
after moving files around into different
directories
1.37 Fri Jul 15 16:30 2011
- Use a lock object with DESTROY method to avoid
an alarm with a die leaving around a locked
paged
1.36 Wed Sep 29 13:10 2010
- Disable wrapping fcntl() lock call in alarm, hurts
people that use Time::HiRes::alarm() only to try
and catch buggy deadlock code. Enable with
catch_deadlocks option
1.35 Fri Feb 19 12:45 2010
- Fix for returning potential bug that returns old stored
FastMmap.xs view on Meta::CPAN
FC_ENTRY
CODE:
RETVAL = mmc_unlock(cache);
POSTCALL:
if (RETVAL != 0) {
croak("%s", mmc_error(cache));
}
int
fc_is_locked(obj)
SV * obj;
INIT:
FC_ENTRY
CODE:
/* Write value to cache */
RETVAL = mmc_is_locked(cache);
OUTPUT:
RETVAL
void
fc_read(obj, hash_slot, key)
SV * obj;
U32 hash_slot;
SV * key;
lib/Cache/FastMmap.pm view on Meta::CPAN
As with empty_on_exit, this will only unlink the file if the
DESTROY occurs in the same PID that the cache was created in
so that any forked children don't unlink the file.
This value defaults to 1 if the share_file specified does
not already exist. If the share_file specified does already
exist, it defaults to 0.
=item * B<catch_deadlocks>
Sets an alarm(10) before each page is locked via fcntl(F_SETLKW) to catch
any deadlock. This used to be the default behaviour, but it's not really
needed in the default case and could clobber sub-second Time::HiRes
alarms setup by other code. Defaults to 0.
=back
=cut
sub new {
my $Proto = shift;
my $Class = ref($Proto) || $Proto;
lib/Cache/FastMmap.pm view on Meta::CPAN
eval { $write_cb->($Self->{context}, $_[1], $_[2]); };
}
return $DidStore;
}
=item I<get_and_set($Key, $AtomicSub)>
Atomically retrieve and set the value of a Key.
The page is locked while retrieving the $Key and is unlocked only after
the value is set, thus guaranteeing the value does not change between
the get and set operations.
$AtomicSub is a reference to a subroutine that is called to calculate the
new value to store. $AtomicSub gets $Key, the current value from the
cache, and an options hash as paramaters. Currently the only option
passed is the expire_on of the item.
It should return the new value to set in the cache for the given $Key,
and an optional hash of arguments in the same format as would be passed
lib/Cache/FastMmap.pm view on Meta::CPAN
=over 4
=item *
Do not perform any get/set operations from the callback sub, as these
operations lock the page and you may end up with a dead lock!
=item *
If your sub does a die/throws an exception, the page will correctly
be unlocked (1.15 onwards)
=back
=cut
sub get_and_set {
my ($Self, $Cache) = ($_[0], $_[0]->{Cache});
my ($Value, $Unlock, $Opts) = $Self->get($_[1], { skip_unlock => 1 });
# If this throws an error, $Unlock ref will still unlock page
lib/Cache/FastMmap.pm view on Meta::CPAN
eval { $delete_cb->($Self->{context}, $_[1]); };
}
return $DidDel;
}
=item I<get_and_remove($Key)>
Atomically retrieve value of a Key while removing it from the cache.
The page is locked while retrieving the $Key and is unlocked only after
the value is removed, thus guaranteeing the value stored by someone else
isn't removed by us.
=cut
sub get_and_remove {
my ($Self, $Cache) = ($_[0], $_[0]->{Cache});
my ($Value, $Unlock) = $Self->get($_[1], { skip_unlock => 1 });
my $DidDel = $Self->remove($_[1], { skip_lock => \$Unlock });
return wantarray ? ($Value, $DidDel) : $Value;
lib/Cache/FastMmap.pm view on Meta::CPAN
$Unlock = undef;
}
return ($NReads, $NReadHits);
}
=item I<multi_get($PageKey, [ $Key1, $Key2, ... ])>
The two multi_xxx routines act a bit differently to the
other routines. With the multi_get, you pass a separate
PageKey value and then multiple keys. The PageKey value
is hashed, and that page locked. Then that page is
searched for each key. It returns a hash ref of
Key => Value items found in that page in the cache.
The main advantage of this is just a speed one, if you
happen to need to search for a lot of items on each call.
For instance, say you have users and a bunch of pieces
of separate information for each user. On a particular
run, you need to retrieve a sub-set of that information
for a user. You could do lots of get() calls, or you
lib/Cache/FastMmap.pm view on Meta::CPAN
=item I<_lock_page($Page)>
Lock a given page in the cache, and return an object
reference that when DESTROYed, unlocks the page
=cut
sub _lock_page {
my ($Self, $Cache) = ($_[0], $_[0]->{Cache});
my $Unlock = Cache::FastMmap::OnLeave->new(sub {
fc_unlock($Cache) if fc_is_locked($Cache);
});
fc_lock($Cache, $_[1]);
return $Unlock;
}
sub _time {
$time_override ? $time_override : time;
}
sub _set_time_override {
mmap_cache.c view on Meta::CPAN
bad_page = 1;
/* If lock succeeded, test page structure */
} else {
lock_page = 1;
if (!_mmc_test_page(cache)) {
bad_page = 1;
}
}
/* If we locked, unlock */
if (lock_page) {
mmc_unlock(cache);
}
/* A bad page, initialise it */
if (bad_page) {
_mmc_init_page(cache, i);
/* Rerun test on this page, potential infinite
loop if init_page is broken, but then things
are really broken anyway */
mmap_cache.c view on Meta::CPAN
* descriptors.
*
*/
int mmc_close(mmap_cache *cache) {
int res;
/* Shouldn't call if not init'ed */
ASSERT(cache->fh);
ASSERT(cache->mm_var);
/* Shouldn't call if page still locked */
ASSERT(cache->p_cur == NOPAGE);
/* Unlock any locked page */
if (cache->p_cur != NOPAGE) {
mmc_unlock(cache);
}
/* Close file */
if (cache->fh) {
mmc_close_fh(cache);
}
/* Unmap memory */
mmap_cache.c view on Meta::CPAN
*
*/
int mmc_lock(mmap_cache * cache, MU32 p_cur) {
MU64 p_offset;
void * p_ptr;
/* Argument sanity check */
if (p_cur == NOPAGE || p_cur > cache->c_num_pages)
return _mmc_set_error(cache, 0, "page %u is NOPAGE or larger than number of pages", p_cur);
/* Check not already locked */
if (cache->p_cur != NOPAGE)
return _mmc_set_error(cache, 0, "page %u is already locked, can't lock multiple pages", cache->p_cur);
/* Setup page details */
p_offset = (MU64)p_cur * cache->c_page_size;
p_ptr = PTR_ADD(cache->mm_var, p_offset);
if (mmc_lock_page(cache, p_offset) == -1) return -1;
if (!(P_Magic(p_ptr) == 0x92f7e3b1))
return _mmc_set_error(cache, 0, "magic page start marker not found. p_cur is %u, offset is %llu", p_cur, p_offset);
mmap_cache.c view on Meta::CPAN
ASSERT(_mmc_test_page(cache));
return 0;
}
/*
* mmc_unlock(
* cache_mmap * cache
* )
*
* Unlock any currently locked page
*
*/
int mmc_unlock(mmap_cache * cache) {
ASSERT(cache->p_cur != NOPAGE);
/* If changed, save page header changes back */
if (cache->p_changed) {
void * p_ptr = cache->p_base;
mmap_cache.c view on Meta::CPAN
/* Test before unlocking */
ASSERT(_mmc_test_page(cache));
mmc_unlock_page(cache);
return 0;
}
/*
* mmc_is_locked(
* cache_mmap * cache
* )
*
* Return true if page is locked
*
*/
int mmc_is_locked(mmap_cache * cache) {
return cache->p_cur != NOPAGE ? 1 : 0;
}
/*
* int mmc_hash(
* cache_mmap * cache,
* void *key_ptr, int key_len,
* MU32 *hash_page, MU32 *hash_slot
* )
mmap_cache.c view on Meta::CPAN
free(to_expunge);
ASSERT(_mmc_test_page(cache));
return 0;
}
/*
* void mmc_get_page_details(mmap_cache * cache, MU32 * n_reads, MU32 * n_read_hits)
*
* Return details about the current locked page. Currently just
* number of reads and number of reads that hit
*
*/
void mmc_get_page_details(mmap_cache * cache, MU32 * n_reads, MU32 * n_read_hits) {
*n_reads = cache->p_n_reads;
*n_read_hits = cache->p_n_read_hits;
return;
}
/*
mmap_cache.c view on Meta::CPAN
return base_det;
}
/*
* void mmc_iterate_close(mmap_cache_it * it)
*
* Finish and dispose of iterator memory
*
*/
void mmc_iterate_close(mmap_cache_it * it) {
/* Unlock page if locked */
if (it->p_cur != NOPAGE) {
mmc_unlock(it->cache);
}
/* Free memory */
free(it);
}
/*
* void mmc_get_details(
mmap_cache.h view on Meta::CPAN
int mmc_init(mmap_cache *);
int mmc_set_param(mmap_cache *, char *, char *);
int mmc_get_param(mmap_cache *, char *);
int mmc_close(mmap_cache *);
char * mmc_error(mmap_cache *);
/* Functions for find/locking a page */
int mmc_hash(mmap_cache *, void *, int, MU32 *, MU32 *);
int mmc_lock(mmap_cache *, MU32);
int mmc_unlock(mmap_cache *);
int mmc_is_locked(mmap_cache *);
/* Functions for getting/setting/deleting values in current page */
int mmc_read(mmap_cache *, MU32, void *, int, void **, int *, MU32 *, MU32 *);
int mmc_write(mmap_cache *, MU32, void *, int, void *, int, MU32, MU32);
int mmc_delete(mmap_cache *, MU32, void *, int, MU32 *);
/* Functions of expunging values in current page */
int mmc_calc_expunge(mmap_cache *, int, int, MU32 *, MU32 ***);
int mmc_do_expunge(mmap_cache *, int, MU32, MU32 **);
);
return $FC;
}
my $FC = get_fc();
ok( defined $FC );
$FC->set("foo1", "bar");
my $V = eval { $FC->get("foo"); };
ok(!$V, "no return");
like($@, qr/already locked/, "recurse fail");
$FC = undef;
$FC = get_fc(allow_recursive => 1);
ok( defined $FC );
$FC->set("foo1", "bar");
$V = eval { $FC->get("foo"); };
ok(!$@, "recurse success 1");
is($V, "bar", "recurse success 2");
/* Setup fcntl locking structure */
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = cache->p_offset;
lock.l_len = cache->c_page_size;
/* And unlock page */
fcntl(cache->fh, F_SETLKW, &lock);
/* Set to bad value while page not locked */
cache->p_cur = NOPAGE;
return 0;
}
/*
* int _mmc_set_error(mmap_cache *cache, int err, char * error_string, ...)
*
* Set internal error string/state
}
int mmc_unlock_page(mmap_cache* cache) {
OVERLAPPED lock;
memset(&lock, 0, sizeof(lock));
lock.Offset = cache->p_offset;
lock.hEvent = 0;
UnlockFileEx(cache->fh, 0, cache->c_page_size, 0, &lock);
/* Set to bad value while page not locked */
cache->p_cur = NOPAGE;
}
/*
* int _mmc_set_error(mmap_cache *cache, int err, char * error_string, ...)
*
* Set internal error string/state
*
*/
int _mmc_set_error(mmap_cache *cache, int err, char * error_string, ...) {