UNPKG

key_mutex

Version:

key-mapped read-write mutex lock that supports cluster and distributed network

340 lines (287 loc) 10.1 kB
var $ = {}; module.exports = $; var cluster = require('cluster'); var net = require('net'); var util = require('./util'); var msg_process = require('./msg_process'); var handlers = new Map(); var clients = new Map(); function Client(host, timeout_ms){ var thiz = this; thiz.mutexes = new Map(); thiz.add = function(name, mutex){ thiz.mutexes.set(name, mutex); } thiz.del = function(name){ thiz.mutexes.delete(name); if(thiz.mutexes.size === 0){ clients.delete(host); disconnect_server(host); } } thiz.on_message = function(msg){ var mutex = thiz.mutexes.get(msg.m_name); if(mutex === undefined) return; if(msg.m_cmd === 'wlock') mutex.on_wlock(msg.m_key); else if(msg.m_cmd === 'rlock') mutex.on_rlock(msg.m_key); } thiz.send = function(waiter, msg){ var str = JSON.stringify(msg); return connect_server(thiz, host, timeout_ms).then(function(connection){ if(waiter !== undefined){ waiter.connection = connection; connection.add_waiter(waiter); } return connection.send(timeout_ms, str); }); } } $.get = function(host, timeout_ms){ var client = clients.get(host); if(client === undefined){ client = new Client(host, timeout_ms); clients.set(host, client); } else{ if(timeout_ms === undefined) client.timeout_ms = undefined; else if(client.timeout_ms !== undefined && client.timeout_ms < timeout_ms) client.timeout_ms = timeout_ms; } return client; } /* 接收数据包格式,18字节 uint8_t 启始标志 0xAA uint8_t 校验值(亦或) uint8_t 版本号/包长度(包含启始标志) uint8_t[] 其他数据 */ function check_sum(buf, start, end){ var checksum = 0; for(var i = start; i < end; ++i) checksum ^= buf[i]; return checksum; } function send_socket_data(timeout_ms, socket, str){ var buf = Buffer.from(str, 'utf8'); var header = Buffer.allocUnsafe(3); header[0] = 0xAA; header[2] = 3 + buf.length; buf = Buffer.concat([header, buf]); buf[1] = check_sum(buf, 2, buf.length) socket.write(buf, 0, buf.length); } function parse_socket_data(handler, buf, on_message){ while(1){ //寻找启始标志0xAA var i = 0; for(; i < buf.length; ++i) if(buf[i] == 0xAA) break; if(i != 0) buf = Buffer.from(buf.slice(i)); //console.log(buf.length) //校验值 if(buf.length < 3) break; //包长度 var packet_len = buf[2]; if(buf.length < packet_len) break; var checksum = check_sum(buf, 2, packet_len); if(checksum != buf[1]){ //校验值失败,移除0xAA,重新寻找启始标志 buf = Buffer.from(buf.slice(1)); continue; } //校验值检查成功 var str = buf.slice(3, packet_len).toString('utf8'); buf = Buffer.from(buf.slice(packet_len)); var msg = JSON.parse(str); on_message(msg); //on_recv_socket_data(str); //console.log(str); //send_socket_data(handler, str); } return buf; } function server_on_create_sock(socket, timeout_ms){ var Null = {}; var handler = {}; handler.closed = false; handler.pendings = new Map(); function close_sock(){ if(handler.closed) return; handler.closed = true; handler.force_unlock(); } socket.on('error', close_sock); socket.on('end', close_sock); socket.on('close', close_sock); var buf = Buffer.allocUnsafe(0); socket.on('data', function (data){ if(handler.closed) return; buf = Buffer.concat([buf, data]); buf = parse_socket_data(handler, buf, function(msg){ if(msg.m_cmd === 'unlock') handler.del(msg.m_name, msg.m_key); msg_process.on_message(msg, handler); }); }); handler.lock = function(msg){ if(handler.closed) { msg_process.force_unlock(handler, msg.m_name, msg.m_key); throw new Error('closed'); } //console.log(timeout_ms, typeof msg, msg); handler.add(msg.m_name, msg.m_key); var str = JSON.stringify(msg); send_socket_data(timeout_ms, socket, str); } handler.force_unlock = function(){ var pendings = handler.pendings; handler.pendings = new Map(); //console.log(pendings); pendings.forEach(function(keys, name){ var name1 = (name === Null ? undefined : name); keys.forEach(function(value, key){ var key1 = (key === Null ? undefined : key); for(var i = 0; i < value.ref_count; ++i){ msg_process.force_unlock(handler, name1, key1); } }); }); } handler.add = function(name, key){ if(name === undefined) name = Null; if(key === undefined) key = Null; //console.log('add', name, key); var keys = handler.pendings.get(name); if(keys === undefined){ keys = new Map(); handler.pendings.set(name, keys); } var value = keys.get(key); if(value === undefined){ value = {ref_count : 1}; keys.set(key, value); } else value.ref_count++; } handler.del = function(name, key){ if(name === undefined) name = Null; if(key === undefined) key = Null; //console.log('del', name, key) var keys = handler.pendings.get(name); if(keys === undefined) return; var value = keys.get(key); if(value === undefined) return; if(value.ref_count > 0){ value.ref_count--; if(value.ref_count === 0){ keys.delete(key); if(keys.size === 0) handler.pendings.delete(name); } } } } function connect_server_once(client, host, timeout_ms){ return new Promise(function(resolve, reject){ var handler = handlers.get(host); if(handler !== undefined){ if(handler.connected) resolve(handler); else handler.defer_list.add(resolve, reject); return; } var handler = {}; handler.defer_list = new util.defer_list(); handler.defer_list.add(resolve, reject); handler.waiters = new Set(); handlers.set(host, handler); var connect_args = {}; var a = host.split(':'); if(a.length !== 2){ handler.defer_list.reject('invalid host address'); return; } connect_args.host = a[0]; connect_args.port = parseInt(a[1], 10); if(isNaN(connect_args.port)){ handler.defer_list.reject('invalid host port'); return; } var socket = net.connect(connect_args, function(){ handler.connected = true; handler.defer_list.resolve(handler); //console.log('server connected'); //send_socket_data(handler, "12345678"); }); handlers.socket = socket; function close_sock(){ if(handler.closed) return; handler.closed = true; handlers.delete(host); handler.connected = false; handler.defer_list.reject(); var waiters = handler.waiters; handler.waiters = new Set(); waiters.forEach(function(waiter){ waiter.done = true; waiter.reject(new Error('closed')); }); } var buf = Buffer.allocUnsafe(0); socket.on('data', function(data){ buf = Buffer.concat([buf, data]); buf = parse_socket_data(handler, buf, function(msg){ client.on_message(msg); }); }); handler.send = function(timeout_ms, str){ //console.log(typeof str, str); send_socket_data(timeout_ms, socket, str); } handler.add_waiter = function(waiter){ handler.waiters.add(waiter); } handler.del_waiter = function(waiter){ handler.waiters.delete(waiter); } socket.on('end', close_sock); socket.on('error', close_sock); socket.on('close', close_sock); }); } function connect_server(client, host, timeout_ms){ var start = process.hrtime(); return new Promise(function(resolve, reject){ (function connect(){ connect_server_once(client, host, timeout_ms).then(function(handler){ resolve(handler); }, function(){ var diff_ms = util.hrtime(start); if(timeout_ms >= 0 && diff_ms >= timeout_ms) reject(new Error('timeout')); else{ setTimeout(connect, 500); } }); })(); }); } function disconnect_server(host){ var handler = handlers.get(host); handler.socket.close(); } $.listen_server = function(port, timeout_ms){ return new Promise(function(resolve, reject){ var server = net.createServer(function(socket){ server_on_create_sock(socket, timeout_ms); }); server.on('error', function(err){ reject(err); }); server.listen(port, function(){ //console.log('listening'); resolve(); }); }); }