UNPKG

shm-typed-lru

Version:

LRU shared memory list build on top of shm-type-array.

505 lines (452 loc) 13.7 kB
'use strict'; const shm = require('./build/Release/shm-typed-lru.node'); const uint32Max = Math.pow(2,32) - 1; const keyMin = 1; const keyMax = uint32Max - keyMin; const perm = Number.parseInt('660', 8); const lengthMin = 1; /** * Max length of shared memory segment (count of elements, not bytes) */ const lengthMax = shm.NODE_BUFFER_MAX_LENGTH; const cleanup = () => { try { var cnt = shm.detachAll(); if (cnt > 0) console.info('shm segments destroyed:', cnt); } catch(exc) { console.error(exc); } }; process.on('exit', cleanup); let sigint_proc_stop = false process.on('SIGINT',() => { cleanup() if ( sigint_proc_stop === true ) process.exit(0) if ( typeof sigint_proc_stop === "function" ) sigint_proc_stop() }) function set_sigint_proc_stop(func) { sigint_proc_stop = func } /** * Types of shared memory object */ const BufferType = { 'Buffer': shm.SHMBT_BUFFER, 'Int8Array': shm.SHMBT_INT8, 'Uint8Array': shm.SHMBT_UINT8, 'Uint8ClampedArray': shm.SHMBT_UINT8CLAMPED, 'Int16Array': shm.SHMBT_INT16, 'Uint16Array': shm.SHMBT_UINT16, 'Int32Array': shm.SHMBT_INT32, 'Uint32Array': shm.SHMBT_UINT32, 'Float32Array': shm.SHMBT_FLOAT32, 'Float64Array': shm.SHMBT_FLOAT64, }; const BufferTypeSizeof = { 'Buffer': 1, 'Int8Array': 1, 'Uint8Array': 1, 'Uint8ClampedArray': 1, 'Int16Array': 2, 'Uint16Array': 2, 'Int32Array': 4, 'Uint32Array': 4, 'Float32Array': 4, 'Float64Array': 8, }; /** * Create shared memory segment * @param {int} count - number of elements * @param {string} typeKey - see keys of BufferType * @param {int/null} key - integer key of shared memory segment, or null to autogenerate * @return {mixed/null} shared memory buffer/array object, or null on error * Class depends on param typeKey: Buffer or descendant of TypedArray * Return object has property 'key' - integer key of created shared memory segment */ function create(count, typeKey /*= 'Buffer'*/, key /*= null*/) { if (typeKey === undefined) typeKey = 'Buffer'; if (key === undefined) key = null; if (BufferType[typeKey] === undefined) throw new Error("Unknown type key " + typeKey); if (key !== null) { if (!(Number.isSafeInteger(key) && key >= keyMin && key <= keyMax)) throw new RangeError('Shm key should be ' + keyMin + ' .. ' + keyMax); } var type = BufferType[typeKey]; //var size1 = BufferTypeSizeof[typeKey]; //var size = size1 * count; if (!(Number.isSafeInteger(count) && count >= lengthMin && count <= lengthMax)) throw new RangeError('Count should be ' + lengthMin + ' .. ' + lengthMax); let res; if (key) { res = shm.get(key, count, shm.IPC_CREAT|shm.IPC_EXCL|perm, 0, type); } else { do { key = _keyGen(); // generate random numbers until a new one comes up. res = shm.get(key, count, shm.IPC_CREAT|shm.IPC_EXCL|perm, 0, type); } while(!res); } if (res) { res.key = key; } return res; } /** * Get shared memory segment * @param {int} key - integer key of shared memory segment * @param {string} typeKey - see keys of BufferType * @return {mixed/null} shared memory buffer/array object, see create(), or null on error */ function get(key, typeKey /*= 'Buffer'*/) { if (typeKey === undefined) typeKey = 'Buffer'; if (BufferType[typeKey] === undefined) throw new Error("Unknown type key " + typeKey); var type = BufferType[typeKey]; if (!(Number.isSafeInteger(key) && key >= keyMin && key <= keyMax)) throw new RangeError('Shm key should be ' + keyMin + ' .. ' + keyMax); let res = shm.get(key, 0, 0, 0, type); if (res) { res.key = key; } return res; } /** * Detach shared memory segment * If there are no other attaches for this segment, it will be destroyed * @param {int} key - integer key of shared memory segment * @param {bool} forceDestroy - true to destroy even there are other attaches * @return {int} count of left attaches or -1 on error */ function detach(key, forceDestroy /*= false*/) { if (forceDestroy === undefined) forceDestroy = false; return shm.detach(key, forceDestroy); } /** * Detach all created and getted shared memory segments * Will be automatically called on process exit/termination * @return {int} count of destroyed segments */ function detachAll() { return shm.detachAll(); } function _keyGen() { return keyMin + Math.floor(Math.random() * keyMax); } /** * Calls the module initLRU * * * @param {Number} key * @param {Number} record_size * @param {Number} region_size * @param {boolean} i_am_initializer * @returns {Number} */ function initLRU(key,record_size,region_size,i_am_initializer) { if ( i_am_initializer === undefined ) { i_am_initializer = true } return shm.initLRU(key,record_size,region_size,i_am_initializer) } /** * * @param {Number} key * @returns {Number} */ function getSegmentSize(key) { return shm.getSegSize(key) } /** * * @param {Number} key * @returns {Number} */ function lru_max_count(key) { return shm.max_count(key) } /** * * @param {Number} key * @returns {Number} */ function current_count(key) { return shm.current_count(key) } /** * * @param {Number} key * @returns {Number} */ function free_count(key) { return shm.free_count(key) } /** * * @returns {Number} */ function epoch_time() { return shm.epoch_time() } /** * Internally, this will create a key out of the hash and the index. * If the hash is in the table, `set` will attempt to update the value and the offset to the data in the memory section will be returned. * If the hash is not in the table, `set` will attempt to put the value in the table and return the offset to th data in the memory section. * * @param {Number} key * @param {string} value * @param {Number} hh_hash - a 32 bit hash of the value * @param {Number} index - a 32 number equal to the hash modulus the number of possible buckets in the table or just a number between zero and count depending on the application * @returns {Number|boolean} - retuns the offset of the element into the share memory region, or false if the element cannot be inserted */ function set(key,value,hh_hash,index) { //console.log("Set: ",hh_hash,index) if ( index == undefined ) index = 0 return shm.set_el(key,hh_hash,index,value) } /** * * @param {Number} key * @param {Array} value_hash_array * @returns {Array} */ function set_many(key,value_hash_array) { if ( !(Array.isArray(value_hash_array)) ) return false return shm.set_many(key,value_hash_array) } /** * Finds the memory segment and returns the item stored as a string at the offset which is index. * * @param {Number} key - a key identifying the shared memory segment * @param {Number} index - a byte offset into the memory segment * @returns {string|Number} - returns a number on error otherwise it is the stored string, the value at the hash index. */ function get_el(key,index) { return shm.get_el(key,index) } /** * `get_el_hash` transates the hash string to the storage offset in the shared memory table. * Then, it obtains the memory segment and returns the string stored at that asset. * * @param {Number} key * @param {Number} hh_hash - the 32 bit hash of the stored value. * @param {Number} index - the hopscotch offset returned when the value was stored * @returns {string|Number} - returns a number on error otherwise it is the stored string, the value at the hash index. */ function get_el_hash(key,hh_hash,index) { if ( index == undefined ) index = 0 return shm.get_el_hash(key,hh_hash,index) } /** * Deletes an item from the shared memory table without managing the hash scheme. * * @param {Number} key - a key identifying the shared memory segment * @param {Number} index - a byte offset into the memory segment * @returns {string|Number} - returns a number on error otherwise it is the stored string, the value at the hash index. */ function del_el(key,index) { return shm.del_el(key,index) } /** * * @param {Number} key * @param {Number} hh_hash - the 32 bit hash of the stored value. * @param {Number} index - the hopscotch offset returned when the value was stored * @returns {boolean} */ function del_key(key,hh_hash,index) { if ( index == undefined ) index = 0 return shm.del_key(key,hh_hash,index) } /** * * @param {Number} key * @param {Number} hh_hash - the 32 bit hash of the stored value. * @param {Number} index - the hopscotch offset returned when the value was stored * @returns {boolean} */ function remove_key(key,hh_hash,index) { if ( index == undefined ) index = 0 return shm.remove_key(key,hh_hash,index) } /** * * @param {Number} key * @returns {string} */ function get_last_reason(key) { return shm.get_last_reason(key) } /** * * @param {Number} key * @returns {boolean} */ function reload_hash_map(key) { return shm.reload_hash_map(key) } /** * * @param {Number} key * @param {Number} share_key * @returns */ function reload_hash_map_update(key,share_key) { return shm.reload_hash_map_update(key,share_key) } /** * * @param {Number} key * @param {Number} cutoff_time * @param {Number} max_evictions * @returns {object} */ function run_lru_eviction(key,cutoff_time,max_evictions) { return shm.run_lru_eviction(key,cutoff_time,max_evictions) } /** * * @param {Number} key * @param {Number} cutoff_time * @param {Number} max_evictions * @returns {object} */ function run_lru_eviction_get_values(key,cutoff_time,max_evictions) { return shm.run_lru_eviction_get_values(key,cutoff_time,max_evictions) } /** * * @param {Number} key * @param {Number} cutoff_time * @param {Number} max_evictions * @returns {object} * @param {Number} hh_hash * @param {Number} index * @returns */ function run_lru_targeted_eviction_get_values(key,cutoff_time,max_evictions,hh_hash,index) { return shm.run_lru_targeted_eviction_get_values(key,cutoff_time,max_evictions,hh_hash,index) } /** * * @param {Number} key * @param {Number} index * @param {Number} share_key * @returns {boolean} */ function set_share_key(key,index,share_key) { return shm.set_share_key(key,index,share_key) } function debug_dump_list(key,backwards) { if ( backwards == undefined ) { backwards = false; } return shm.debug_dump_list(key,backwards) } // // // // // // // /** * * @param {Number} key * @param {Number} lru_key * @param {boolean} am_initializer * @param {Number} max_element_count * @returns {boolean|Number} */ function initHopScotch(key,lru_key,am_initializer,max_element_count) { return shm.initHopScotch(key,lru_key,am_initializer,max_element_count) } // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- /** * * @param {Number} key * @param {boolean} initializer - true if the calling process creates and initializes the mutex, false otherwise * @returns {boolean|Number} */ function init_mutex(key,initializer) { return shm.init_mutex(key,initializer) } /** * * Calls the posix `pthread_mutex_trylock` method. * If the call is able to aquire the lock, thie method returns true. * If the method fails to acquire the lock without abnormal error, this method returns false. * Otherwise, it returns a negative number indicating an error state. * * -3 -> the key matches a region in the OS, but the library has lost reference to it * * -1 -> the key does not match a region and no such region is known or the posix call failed for a reason other than EBUSY * * @param {Number} key - the key or `token` identifying the shared memory segment where the locks are stored * @returns {Number|boolean} */ function try_lock(key) { return shm.try_lock(key) } /** * * @param {Number} key * @returns {boolean|Number} */ function lock(key) { return shm.lock(key) } /** * * @param {Number} key * @returns {boolean|Number} */ function unlock(key) { return shm.unlock(key) } /** * * @param {Number} key * @returns {string} */ function get_last_mutex_reason(key) { return shm.get_last_mutex_reason(key) } // ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- //Exports module.exports.create = create; module.exports.get = get; module.exports.detach = detach; module.exports.detachAll = detachAll; module.exports.getTotalSize = shm.getTotalSize; module.exports.BufferType = BufferType; module.exports.LengthMax = lengthMax; // // The following is for the LRU implementation... // module.exports.initLRU = initLRU; module.exports.getSegmentSize = getSegmentSize; module.exports.lru_max_count = lru_max_count module.exports.current_count = current_count module.exports.free_count = free_count module.exports.epoch_time = epoch_time module.exports.set = set; module.exports.set_many = set_many; module.exports.get_el = get_el; module.exports.del_el = del_el; module.exports.del_key = del_key; module.exports.remove_key = remove_key; module.exports.get_el_hash = get_el_hash; // module.exports.get_last_reason = get_last_reason; module.exports.reload_hash_map = reload_hash_map; module.exports.reload_hash_map_update = reload_hash_map_update; module.exports.run_lru_eviction = run_lru_eviction; module.exports.run_lru_eviction_get_values = run_lru_eviction_get_values; module.exports.run_lru_targeted_eviction_get_values = run_lru_targeted_eviction_get_values module.exports.set_share_key = set_share_key; // module.exports.debug_dump_list = debug_dump_list; // module.exports.initHopScotch = initHopScotch // module.exports.init_mutex = init_mutex module.exports.try_lock = try_lock module.exports.lock = lock module.exports.unlock = unlock module.exports.get_last_mutex_reason = get_last_mutex_reason module.exports.set_sigint_proc_stop = set_sigint_proc_stop