/* * AUTHOR * * Rob Mueller * * COPYRIGHT AND LICENSE * * Copyright (C) 2003 by FastMail IP Partners * * This library is free software; you can redistribute it and/or modify * it under the same terms as Perl itself. * * mmap_cache * * Uses an mmap'ed file to act as a shared memory interprocess cache * * The C interface is quite explicit in it's use, in that you have to * call individual functions to hash a key, lock a page, and find a * value. This allows a simpler higher level interface to be written * * #include * * mmap_cache * cache = mmc_new(); * cache->param = val; * mmc_init(cache); * * // Read a key * * // Hash get to find page and slot * mmc_hash(cache, (void *)key_ptr, (int)key_len, &hash_page, &hash_slot); * // Lock page * mmc_lock(cache, hash_page); * // Get pointer to value data * mmc_read(cache, hash_slot, (void *)key_ptr, (int)key_len, (void **)&val_ptr, (int *)val_len, &flags); * // Unlock page * mmc_unlock(cache); * * // Write a key * * // Hash get to find page and slot * mmc_hash(cache, (void *)key_ptr, (int)key_len, &hash_page, &hash_slot); * // Lock page * mmc_lock(cache, hash_page); * // Get pointer to value data * mmc_write(cache, hash_slot, (void *)key_ptr, (int)key_len, (void *)val_ptr, (int)val_len); * // Unlock page * mmc_unlock(cache); * * DESCRIPTION * * This class implements a shared memory cache through an mmap'ed file. It * uses locking to ensure multiple processes can safely access the cache * at the same time. It uses a basic LRU algorithm to keep the most used * entries in the cache. * * It tries to be quite efficient through a number of means: * * It uses multiple pages within a file, and uses Fcntl to only lock * a page at a time to reduce contention when multiple processes access * the cache. * * It uses a dual level hashing system (hash to find page, then hash * within each page to find a slot) to make most I calls O(1) and * fast * * On each I, if there are slots and page space available, only * the slot has to be updated and the data written at the end of the used * data space. If either runs out, a re-organisation of the page is * performed to create new slots/space which is done in an efficient way * * The locking is explicitly done in the C interface, so you can create * a 'read_many' or 'write_many' function that reduces the number of * locks required * * * IMPLEMENTATION * * Each file is made up of a number of 'pages'. The number of * pages and size of each page is specified when the class is * constructed. These values are stored in the cache class * and must be the same for each class connecting to the cache * file. * * NumPages - Number of 'pages' in the cache * PageSize - Size of each 'page' in the cache * * The layout of each page is: * * - Magic (4 bytes) - 0x92f7e3b1 magic page start marker * * - NumSlots (4 bytes) - Number of hash slots in this page * * - FreeSlots (4 bytes) - Number of free slots left in this page. * This includes all slots with a last access time of 0 * (empty and don't search past) or 1 (empty, but keep searching * because deleted slot) * * - OldSlots (4 bytes) - Of all the free slots, how many were in use * and are now deleted. This is slots with a last access time of 1 * * - FreeData (4 bytes) - Offset to free data area to place next item * * - FreeBytes (4 bytes) - Bytes left in free data area * * - Reserved (8 bytes) * * - Slots (4 bytes * NumSlots) - Hash slots * * - Data (to end of page) - Key/value data * * Each slot is made of: * * - Offset (4 bytes) - offset from start of page to actual data. This * is 0 if slot is empty, 1 if was used but now empty. This is needed * so deletes don't require a complete rehash with the linear * searching method we use * * Each data item is made of: * * - LastAccess (4 bytes) - Unix time data was last accessed * * - ExpireTime (4 bytes) - Unix time data should expire. This is 0 if it * should never expire * * - HashValue (4 bytes) - Value key was hashed to, so we don't have to * rehash on a re-organisation of the hash table * * - Flags (4 bytes) - Various flags * * - KeyLen (4 bytes) - Length of key * * - ValueLen (4 bytes) - Length of value * * - Key (KeyLen bytes) - Key data * * - Value (ValueLen bytes) - Value data * * Each set/get/delete operation involves: * * - Find the page for the key * - Lock the page * - Read the page header * - Find the hash slot for the key * * For get's: * * - Use linear probing to find correct key, or empty slot * * For set's: * * - Use linear probing to find empty slot * - If not enough free slots, do an 'expunge' run * - Store key/value at FreeData offset, update, and store in slot * - If not enough space at FreeData offset, do an 'expunge' run * then store data * * For delete's: * * - Use linear probing to find correct key, or empty slot * - Set slot to empty (data cleaned up in expunge run) * * An expunge run consists of: * * - Scan slots to find used key/value parts. Remove older items * - If ratio used/free slots too high, increase slot count * - Compact key/value data into one memory block * - Restore and update offsets in slots * */ /* Main cache structure passed as a pointer to each function */ typedef struct mmap_cache mmap_cache; /* Iterator structure for iterating over items in cache */ typedef struct mmap_cache_it mmap_cache_it; /* Unsigned 32 bit integer */ typedef unsigned int MU32; /* Initialisation/closing/error functions */ mmap_cache * mmc_new(); 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 *); /* Functions for getting/setting/deleting values in current page */ int mmc_read(mmap_cache *, MU32, void *, int, void **, int *, MU32 *); int mmc_write(mmap_cache *, MU32, void *, int, void *, int, 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 **); /* Functions for iterating over items in a cache */ mmap_cache_it * mmc_iterate_new(mmap_cache *); MU32 * mmc_iterate_next(mmap_cache_it *); void mmc_iterate_close(mmap_cache_it *); /* Retrieve details of a cache entry */ void mmc_get_details(mmap_cache *, MU32 *, void **, int *, void **, int *, MU32 *, MU32 *, MU32 *);