<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Client</title> <style type="text/css"> .page { visibility: hidden; position: absolute; top: 40px; left: 10px; border: 1px solid black; padding: 5px; width: 550px; } td, th { border: 1px solid black; margin: 0px; } tr { padding: 0px; margin: 0px; } table { padding: 0px; border: 1px solid black; } </style> <script src="ajax.js" ></script> <script> var localNodeAddr = ''; var originURL = 'Unknown Source'; var connected = false; var numberOfNodes = 0; var connections = new Array(); var tests = new Array(); var testLocks = new Array(); var lockTrys = new Array(); var myLocks = new Array(); var runningTests = new Array(); var rangeLocks = new Array(); var finalResults = new Array(); var doneStage = 0; var runTry = null; var dlc = 0; var msgQ = new Array(); function error(msg) { log("ERROR: " + msg); alert("ERROR: " + msg); clearInterval(cronID); } /* basic ping function for local node * if successful, * if we haven't greeted the server, we do, * otherwise nothing (just a heartbeat) * if it fails, lets the user know we have lost * connection to the node server */ function ping() { var http = ajaxConnect(); var retfn = returnfn(http, function(resp) { if (!connected) { connected = true; greet(); } }, function(resp) { connected = false; var html = "Connection to local Node failed. Try reloading from <a href=\"" + originURL + "\" target=\"window\">" + originURL + "</a>"; document.getElementById('statusLabel').innerHTML = html; } ); ajaxSend(http, "cmd=ping\n\n", retfn); } /* Greets the server * so far just gets the server's IP and origin URL */ function greet() { var http = ajaxConnect(); var retfn = returnfn(http, function(resp) { var arr = resp.split(" "); localNodeAddr = arr[0]; originURL = arr[1]; document.getElementById('statusLabel').innerHTML = "I am <b>" + localNodeAddr + "</b>"; init(); } ); ajaxSend(http, "cmd=greet\n\n", retfn); } function killServer() { announceDead(localNodeAddr); var http = ajaxConnect(); var retfn = returnfn(http, function(resp) { // got a resp, server not dead killServer(); }); ajaxSend(http, "cmd=kill\n\n", retfn); } function getLog() { var http = ajaxConnect(); var retfn = returnfn(http, function(resp) { var log = document.getElementById('log'); //alert("getLog() resp: " + resp); log.value = resp + log.value; // Since getLog returns (a call back is called later) // this is tail recursive friendly getLog(); }); ajaxSend(http, "cmd=getLog\n\n", retfn); } // two digits function d2(str) { if (str <10) return "0"+str; else return str; } function getTime() { var date = new Date(); return d2(date.getHours()) + ":" + d2(date.getMinutes()) + ":" + d2(date.getSeconds()) } var logCheckElem = null; function log(str) { if (logCheckElem == null) logCheckElem = document.getElementById("logCheck"); if (logCheckElem.checked == true) { str = getTime() + ": " + str+"\n"; var log = document.getElementById('log'); log.value = str + log.value; } } function log2(str) { str = getTime() + ": " + str+"\n"; var log = document.getElementById('log'); log.value = str + log.value; } function getMsgs() { var http = ajaxConnect(); var retfn = returnfn(http, function(resp) { //log("getMsgs: len > 0"); log("getMsgs: " + resp); queueMsgs(resp); // Since getMsgs returns (a call back is called later) // this is tail recursive friendly getMsgs(); }); ajaxSend(http, "cmd=getMsgs\n\n", retfn); } function connectedTo(addr) { if (addr == localNodeAddr) return true; for(i = 0; i < connections.length; i++) { if (connections[i] == addr) return true; } return false; } function addConnection(addr) { if(!connectedTo(addr)) { connections[connections.length] = addr; numberOfNodes++; return true; } else return false; } function genNodeList() { var str = "";//localNodeAddr+","; for (var i =0; i < connections.length; i++) { str += connections[i] + ","; } if (connections.length > 0) str = str.substring(0,str.length-1); return str; } function advertiseNewNode(addr) { var m = new Object(); m['query'] = "newNode"; m['addr'] = addr; bcastMsg(m); } function announceDead(addr) { var m = new Object(); m['query'] = "deadNode"; m['addr'] = addr; bcastMsg(m); } function handleNewNode(resp, welcome) { if (welcome == null) welcome = false; var addr = resp.addr; //log2("handle new node " + addr + " from " + resp.origin); // handles re adding if reloaded html //if (!connectedTo(addr)) //{ var ret = genNodeList(); var n = addConnection(addr); if (welcome == true) { sendDLC(addr); sendWelcome(ret, addr); sendLocks(addr); sendTests(addr); } if (n == true) { sendReqs(addr); advertiseNewNode(addr); } } function sendDLC(addr) { var m = new Object(); m.query = "DLC"; sendMsg(m, addr); } function sendReqs(addr) { for(i in lockTrys) { var lock = lockTrys[i]; if (lock == null) continue; var m = new Object(); m["query"] = "getLock"; m["type"] = i; m["name"] = lock["name"]; m["time"] = lock.time; m["addr"] = localNodeAddr; if (i == "range") { m["start"] = lock.start; m["end"] = lock.end; } sendMsg(m, addr); } } function sendWelcome(addrList, addr) { var m = new Object(); m["query"] = "welcome"; m["addrList"] = addrList; sendMsg(m, addr); } function sendLocks(addr) { for (var i in testLocks) { log ("testLog[" + i + "]"); var l = testLocks[i]; var m = new Object(); m["query"] = "addLock"; m["type"] = "test"; m["name"] = l["name"]; m["addr"] = l["addr"]; m["locked"] = l["locked"]; m["perjob"] = m["time"] = l["time"]; if (l.running) { m["running"] = true; m["min"] = l["min"]; m["max"] = l["max"]; m["perjob"] = l["perjob"]; } sendMsg(m, addr); } for (var i in rangeLocks) { var r = rangeLocks[i]; while(r) { log("rangeLock[" + i +" [" + r.start + "-" + r.end +"]]"); var m = new Object(); m["query"] = "addLock"; m["type"] = "range"; m["name"] = i; m["addr"] = r["addr"] m["locked"] = r["locked"]; m["start"] = r["start"]; m["end"] = r["end"]; m["done"] = r["done"]; if (r['results']) m["results"] = r["results"]; sendMsg(m, addr); r = r.next; } } } function sendTests(addr) { for(var i in tests) { var m = makeTestMsg(name); sendMsg(m, addr); } } function handleAddLock(resp) { if(resp['locked'] == null) resp['locked'] = false; // dont add/overwrite if exists? var lock = retrieveLock(resp); if (resp['type'] == "test" ) {//&& (lock == null || lock.addr == null)) { var locks = getLockType(resp['type']); locks[resp['name']] = resp if(resp['running'] == true) { runningTests.push(resp['name']); } } else if(resp['type'] == "range" && lock.addr == null) { log("Add range lock: " + resp['name'] + " (" + resp['start'] + " to " + resp['end'] + ") : "); var lock = getRangeLock(resp['name'], resp['start'], resp['end']); lock.addr = resp['addr']; lock.locked = resp['locked']; lock.done = resp['done']; lock.results = resp['results']; lock.name = resp['name']; } } function packArray(arr) { if (arr instanceof Object) { var str = "{"; for(var i in arr) { str += i +":" + packArray(arr[i]) + ","; } str += "}"; return str; } else { if (arr == null) return ""; return arr.toString(); } } function unpackArray(str) { if (str[0] == "{") { var arr = new Object(); //log2(str); str = str.substring(1,str.length-1); //log2(str); var parts = str.split(","); for (var i=0; i < parts.length;i++) { if (parts[i] == "") continue; var iparts = parts[i].split(":"); arr[iparts[0]] = iparts[1]; } return arr; } else if (str == "true") return true; else if (str == "false") return false; else return Number(str); } function unpackResults(str) { log('UNPACK: "' + str + '"'); var arr = new Array(); var parts = str.split(", "); for(var i =0; i<parts.length; i++) { log(parts[i]); if (parts[i] == "") continue; var iparts = parts[i].split("=", 2); log(iparts[0] + " AND " + iparts[1]); var item = new Object(); item.key= Number(iparts[0]); item.value =unpackArray(iparts[1]); arr.push(item); } return arr; } function packResults(res) { str = ''; if (res == null) return ""; for (var i=0; i<res.length; i++) { str += res[i].key + "=" + packArray(res[i].value); if (i < res.length-1) str+= ", "; } return str; } function packObject(m, defaultValue) { var str = ""; for (var i in m) { if (m[i] == '') { if (defaultValue == null) { continue; } else { m[i] = defaultValue; } } if (i == "results") { str += i + "=" + packResults(m[i]) + "\n"; } else { str += i + "=" + m[i] + "\n"; } } return str; } function removeNodeRangeLocks(addr) { for(var i in rangeLocks) { log("freeing " + i + " locks"); var rlock = rangeLocks[i]; while (rlock != null) { log("looking at " + i + " (" + rlock.start + " to " + rlock.end + ") : " + rlock.addr); if (rlock.addr == addr) { log("unlocking"); rlock.locked = false; } rlock = rlock.next; } } } function disconnect(addr) { log("--------------DISCONNECT " + addr + "----------"); // remove from connections list for (var i=0; i < connections.length; i++) { if (connections[i] == addr) { connections.splice(i,1); numberOfNodes--; break; } } // remove locks // test locks for(var i in testLocks) { var lock = testLocks[i]; if (!lock) continue; if (lock.addr == addr) { lock.locked = false; } } // range locks removeNodeRangeLocks(addr); log("disconnect: freeing range locks"); } function sendMsg(m, addr, time, mtype) { if(time == null) time = dlc; if(mtype == null) mtype = "single"; var str = "cmd=sendMsg\n"; str += "origin=" + localNodeAddr + "\n"; str += "dlc=" + time + "\n"; str += "mtype=" + mtype + "\n"; str += packObject(m); str += "\n"; log ("SEND: " + str); var http = ajaxConnect(); var retfn = returnfn(http, null, function() { disconnect(addr); announceDead(addr); log("ERROR> message to " + addr + " not delivered: '" + str + "'"); }); ajaxSend(http, str, retfn, addr); } function bcastMsg(m) { if (m.query != "heartBeat") dlc ++; //log("BCAST conLen: " + connections.length); for(var i=0; i < connections.length; i++) { log("bcast "+ i +" " + m.query + " to " + connections[i] + " dlc:" + dlc); sendMsg(m, connections[i], dlc, "bcast"); //log("back from sendMsg, i:" + i + " conlen:" + connections.length); } } function encodeStr(str) { var res=""; for(i=0; i<str.length;i++) { if(str[i] == "$") res += "$$"; else if(str[i] == "\n") res += "$n"; else if(str[i] == "\r") res += "$r"; else if(str[i] == "=") res += "$e" else res += str[i]; } return res; } function decodeStr(str) { var res = ""; if (!str) return ""; for(i=0; i<str.length; i++) { if(str[i] == "$") { i++; if(str[i] == "$") res += "$"; else if(str[i] == "n") res += "\n"; else if(str[i] == "r") res += "\r"; else if(str[i] == "e") res += "="; } else res += str[i]; } return res; } function addMsg(resp) { //log("addMsg mdlc: " + resp.dlc + " mqlen: " + msgQ.length + " type:" + resp.mtype + " query: " + resp.query); for(var i=msgQ.length-1; i>=0; i--) { //log("resp.dlc:" + resp.dlc + " msgQ[i].dlc:" + msgQ[i].dlc); if (resp.dlc > msgQ[i].dlc) { //log("moving " + i + " to " + (i+1)); msgQ[i+1] = msgQ[i]; } else { //log("first, moving " + i + " to " + (i+1)); //msgQ[i+1] = msgQ[i]; //log("putting msg in " + i + " + 1, RETURN"); msgQ[i+1] = resp; return; } } //log("END: putting msg in 0"); msgQ[0] = resp; } function queueMsgs(mstr) { log("GOT messages: " + mstr); var resps = mstr.split("\n\n"); for(var rcount=0; rcount < resps.length; rcount++) { var resp = resps[rcount]; if (resp == "") continue; resp = parseResp(resp); if (resp.query == "DLC") { dlc = resp.dlc; //var m = new Object(); //m.query = "DLC"; //bcastMsg(m); } else { addMsg(resp); } } var s = "QUEUE: " for (var i =0; i< msgQ.length; i++) { s += msgQ[i].dlc + " "; } log(s); log("mqlen:" + msgQ.length); while (msgQ.length>0 && ((msgQ[msgQ.length-1].dlc - 1) <= dlc)) { var r = msgQ.pop(); log("mdlc: " + r.dlc + " dlc: " + dlc + " mqlen: " + msgQ.length + " type:" + r.mtype + " query: " + r.query); if (r.mtype == "bcast") dlc = r.dlc processMsg(r); } } function processMsg(resp) { if (resp["query"] == "join_network") { log("Message join_network received"); handleNewNode(resp, true); } else if (resp["query"] == "welcome") { handleWelcome(resp); } else if (resp["query"] == "newNode") { //addConnection(resp["addr"]); handleNewNode(resp, false); } else if (resp["query"] == "deadNode") { disconnect(resp["addr"]); } else if(resp["query"] == "addLock") { handleAddLock(resp); } else if (resp["query"] == "getLock") { handleLockReq(resp); } else if (resp["query"] == "lockResp") { handleLockResp(resp); } else if (resp["query"] == "releaseLock") { handleLockRelease(resp); } else if(resp["query"] == "testUpdate") { handleTestUpdate(resp); } else if(resp["query"] == "activateJob") { handleActivateJob(resp); } else if(resp["query"] == "results") { handleResults(resp); } else if(resp["query"] == "new_network") { join_network(resp["addr"], false); } else if(resp["query"] == "heartBeat") { // do nothing; return; } else { log("Error: Unkown message '" + resp["query"] + "' received from " + resp["origin"]); } } function handleWelcome(resp) { log("Welcome message received"); //var myNodes = genNodeList(); addConnection(resp['origin']); if (resp['addrList']){ var nodes = resp['addrList'].split(","); for(var i=0; i < nodes.length; i++){ addConnection(nodes[i]); } } for(var i =0; i< connections.length; i++) { var addr = connections[i]; if (addr == localNodeAddr) continue; advertiseNewNode(addr); } } function extractHost(url) { // http: or file: var host = url.substr(5); while (host.charAt(0) == '/') host = host.substr(1); var end = host.indexOf('/'); if (end > 0 ) host = host.substr(0, end); return host; } function setStatus() { document.getElementById('numberOfNodes').innerHTML = "Number of nodes: " + numberOfNodes; document.getElementById('connections').innerHTML = "Connected to: " + genNodeList(); document.getElementById('dlc').innerHTML = "DLC: " + dlc; } function init() { if (connected == false) return; numberOfNodes = 1; if (originURL.substr(0, 4) == "file") { // we are the first //numberOfNodes = 1; // ourself log("Loaded from file"); // now we sit and wait } else { // it was http, so we were loaded from another node log("Loaded from " + originURL); originAddress = extractHost(originURL); join_network(originAddress); } } function parseResp(resp) { var lines = resp.split("\n"); var arr = []; for(var i=0; i < lines.length; i++) { //var kv = lines[i].split("="); var mid = lines[i].indexOf("="); var kv = new Array(); kv[0] = lines[i].substring(0, mid); kv[1] = lines[i].substring(mid+1); if (kv[1] == "true") kv[1] = true; else if(kv[0] == "false") kv[1] = false; else if (kv[0] == "dlc" || kv[0] == "start" || kv[0] == "end" || kv[0] == "min" || kv[0] == "max") kv[1] = Number(kv[1]); else if (kv[0] == "results") kv[1] = unpackResults(kv[1]); arr[kv[0]] = kv[1]; } return arr; } function join_network(address, bcast) { if(bcast == null) { bcast = true; } log("join_network -> " + address); var m = new Object(); m["query"] = "join_network"; m["addr"] = localNodeAddr; //m["nodes"] = genNodeList(); sendMsg(m, address); if (bcast == true) { m = new Object(); m["query"] = "new_network"; m["addr"] = address; bcastMsg(m); } } var runCount = 0; var workMod = 1; var maxWorkChange = 10; // every 100 milisseconds function cron() { if (connected == false) return; if(runCount % workMod == 0) { var dateObj = new Date(); var workStart = dateObj.getTime(); doWork(); dateObj = new Date(); var workEnd = dateObj.getTime(); if (isWorking == true) { var newWorkMod = Math.abs(workEnd-workStart+1); //log2("newWorkMod: " + newWorkMod); if (newWorkMod - workMod > maxWorkChange) { workMod = workMod + maxWorkChange; } else if(workMod - newWorkMod > maxWorkChange){ workMod = Math.abs(workMod - maxWorkChange); } else { workMod = newWorkMod; } } else { workMod = 10; } //log("workMod: " + workMod + " start: " + workStart + " end: " + workEnd + " delta: " + (workEnd-workStart)); //log("workPos: " + workMapPos + " workMod: " + workMod + " delta: " + (workEnd-workStart)); //log("workMod: " + workMod); // map or reduce //var lock = myLocks["range"]; //if (workMapPos <= lock.end) } /*if (runCount % 5 == 0) { getMsgs(); }*/ // once a second if (runCount % 10 == 0) { ping(); //getLog(); setStatus(); checkLocks(); updateTestsStatus(); if ( doneStage == 1 ) { updateFinalResults(); } } if (runCount % 20 == 0) { lookForWork(); } if (runCount % 40 == 0) { updateJobsList(); } if (runCount % 20 == 0) updateResults(); if (runCount % 300 == 0) heartBeat(); // 180 seconds, or 3 minutes if (runCount == 1800) runCount = 0; runCount++; } function heartBeat() { var m = new Object(); m.query = "heartBeat"; bcastMsg(m); } function reloadHTML() { var http = ajaxConnect(); var retfn = returnfn(http); ajaxSend(http, "cmd=reloadHTML\n\n",retfn); } function poke() { var http = ajaxConnect(); document.getElementById("pingNodeResp").innerHTML = ""; var dest = document.getElementById("pingNode").value; var retfn = returnfn(http, function(resp) { document.getElementById("pingNodeResp").innerHTML += resp + "<br>"; var retfn = returnfn(http, function(resp) { document.getElementById("pingNodeResp").innerHTML += resp + "<br>"; }); ajaxSend(http, "cmd=greet\n\n", retfn, dest); }); ajaxSend(http, "cmd=ping\n\n", retfn, dest); } function setTestStatus(str, col) { var e = document.getElementById('testStatus'); e.innerHTML = str; if (col == null) col = "white"; e.style.backgroundColor = col; setJobStatus(str, col); } function getLockType(type) { if (type == "test") return testLocks; else if(type == "range") return rangeLocks; else return null; } function isLocked(lockType, lockName, start, end) { if (lockType == "range") { var rlock = getRangeLock(lockname, start, end); if (rlock) return rlock.locked; else return false; } else { var locks = getLockType(lockType); if (locks[lockName] == null) return false; else if(locks[lockName].locked != true) return false; else return true; } } function loadTest(tname) { if(tname == '') { setTestStatus("Invalid name '" + tname + "'", "red"); return false; } if(tname.indexOf(' ') >= 0) { setTestStatus("Invalid name '" + tname + "', Cannot contain spaces", "red"); return false; } if(isLocked("test", tname)) { setTestStatus("Test '" + tname + "' locked by " + testLocks[tname].addr, "red"); return false; } getLock("test", tname); } function retrieveLock(struct) { if (struct.type == "test") { return testLocks[struct.name]; } else if(struct.type == "range") { var l = getRangeLock(struct.name, struct.start, struct.end); if (l == null) error("retrieveLock for range got null"); return l; } } function getLock(type, name, start, end) { var locks = getLockType(type); var lockTry; if (type == "test") { if(locks[name] == null) { locks[name] = new Object(); } lockTry = locks[name]; } else if (type == "range") { lockTry = getRangeLock(name, start, end); if (!lockTry) { error("Cannot get range lock in getLock(" + name +", "+ start + ", " + end + ")"); return; } } lockTry.time = getTime(); lockTry.dlc = dlc+1; lockTry.name = name; lockTry.addr = localNodeAddr; lockTry.type = type; lockTry.okay = 1; lockTry.locked = false; var d = new Date(); lockTry.localTime = d.getTime(); if (type == "test") { lockTrys[type] = lockTry; setTestStatus("Acquiring lock for test '" + name + "' " + lockTry.okay + "/" + numberOfNodes + "...", "yellow"); } else if (type == "range") { lockTrys[type] = lockTry; setWorkStatus("Acquiring lock for work '" + name + "' (" + lockTry.start + " to " + lockTry.end + ")" + lockTry.okay + "/" + numberOfNodes + "...", "yellow"); } var m = new Object(); m["query"] = "getLock"; m["type"] = type; m["name"] = name; m["time"] = lockTry.time; m["addr"] = localNodeAddr; if (type == "range") { m["start"] = start; m["end"] = end; } bcastMsg(m); } function lessTime(t1, t2) { var t1a = t1.split(":"); var t2a = t2.split(":"); if(t1a[0] < t2a[0]) return true; else if (t1a[0] == t2a[0] && t1a[1] < t2a[1]) return true; else if (t1a[0] == t2a[0] && t1a[1] == t2a[1] && t1a[2] < t2a[2]) return true; return false; } function cameFirst(a, b) { log("cameFirst? " + a.dlc + ":" + a.addr + " and " + b.dlc + ":" + b.addr); if (b.dlc == null) return true; if (a.dlc < b.dlc) return true; else if (a.dlc > b.dlc) return false; else { var aip = a.addr.split(":")[0].split("."); aip[4] = a.addr.split(":")[1]; var bip = b.addr.split(":")[0].split("."); bip[4] = b.addr.split(":")[1]; for(var i = 0; i < 5; i++) { if (aip[i] < bip[i]) return true; else if (aip[i] > bip[i]) return false; } alert("cameFirst: SAME NODE?"); log("cameFirst: SAME NODE?"); return false; } } function locksEqual(a, b) { if(a == null || b == null) return false; if(a['type'] != b['type']) return false; if(a['type'] == 'test') { if (a.name == b.name) return true; else return false; } else if (a['type'] == "range") { log("locksEqual? " + a.name + " (" + a.start + " to " + a.end + ") and " + b.name + " (" + b.start + " to " + b.end + ")"); if (a.name == b.name && a.start == b.start && a.end == b.end) { log("true"); return true; } else { log("false"); return false; } } } function grantLock(resp, lock) { removeNodeRangeLocks(resp.origin); lock.name = resp['name']; lock.addr = resp['origin']; lock.time = resp['time']; lock.dlc = resp['dlc']; lock.locked = true; //locks[lock.name] = lock; respLockReq(resp, "okay"); } function denyLock(resp, lock) { respLockReq(resp, lock.addr); } function handleLockReq(resp) { var locks = getLockType(resp["type"]); var lock; if(resp['type'] == "test") { lock = locks[resp["name"]]; } else if(resp['type'] == "range") { lock = getRangeLock(resp['name'], resp['start'], resp['end']); if (lock == null) { // error? respLockReq(resp, "no"); return; } } var tryLock = lockTrys[resp['type']]; if (lock == null) { // can only be for test, null for range is error testLocks[resp['name']] = resp; grantLock(resp, testLocks[resp['name']]); } else { // we have it // is it? // locked, or we are trying for it // was it JUST locked and not confirmed, did this req COME FIRST? if (lock.locked == true) { if (lock.addr == resp.origin) { grantLock(resp, lock); } else if (cameFirst(resp, lock) || resp.origin == lock.addr) { log("true"); grantLock(resp, lock); } else { log("false"); denyLock(resp, lock); } } else if (locksEqual(tryLock, lock)) { if (cameFirst(resp, tryLock)) { // we lost log("true"); lockTrys[resp['type']] = null; grantLock(resp, lock); } else { //we win log("false"); denyLock(resp, lock); } } else { grantLock(resp, lock); } } } /* 1) if we dont have a record of the lock, GRANT * 2) if we have a record of it * 2.1) is it locked or tring to be locked (us or other)? * 2.1.1) yes? was our request BEFORE? * 2.1.1.1) yes? GRANT * 2.1.1.2) no? NO? * 2.1.2) no? GRANT */ /*if (lock == null || (lockTrys[resp['type']] && lockTrys[resp['type']].name == resp['name'] && cameFirst(resp, lock)) || ((!lockTrys[resp['type']] || lockTrys[resp['type']].name != resp['name']) && lock.locked == false)) {*/ /* if (rlock == null || (rlock.locked == false && locksEqual(rlock, tlock) != true) || (locksEqual(rlock, tlock) == true && cameFirst, resp, rlock)) { //lock = new Object(); rlock.name = resp['name']; rlock.addr = resp['origin']; rlock.time = resp['time']; rlock.dlc = resp['dlc']; rlock.locked = true; //locks[lock.name] = lock; respLockReq(resp, "okay"); } else { respLockReq(resp, rlock.addr); } }*/ function handleLockResp(resp) { if (!lockTrys[resp['type']]) return; var tryLock = lockTrys[resp['type']]; var lock = retrieveLock(resp); if( !locksEqual(tryLock, lock)) return; else if (tryLock.time != lock.time) return; if (resp['resp'] == "okay") { lock.okay++; if (resp['type'] == "test") setTestStatus("Acquiring lock for test '" + lock["name"] + "' " + lock.okay + "/" + numberOfNodes + "...", "yellow"); else if(resp['type'] == "range") setWorkStatus("Acquiring lock for work '" + lock.name + "' (" + lock.start + " to " + lock.end + ")" + lock.okay + "/" + numberOfNodes + "...", "yellow"); } else { if (resp['resp'] != 'no'){ lock.locked = true; lock.addr = resp.resp; } if (resp['type'] == "test") setTestStatus("Test '" + lock['name'] + "' locked by " + resp['resp'], "red"); else setWorkStatus("Work '" + lock.name + "' (" + lock.start + " to " + lock.end + ") locked by " + resp['resp'], "red"); lockTrys[resp['type']] = null; runTry = null; } } function openTest(name) { if (!tests[name]) tests[name] = new Array(); var map = ""; if (tests[name]['mapfn']) map = tests[name]['mapfn']; var reduce = ""; if (tests[name]['reducefn']) reduce = tests[name]['reducefn']; document.getElementById('mapEditor').value = map; document.getElementById('reduceEditor').value = reduce; } function lockGranted(type, lock) { if (type == "test") { setTestStatus("Lock for '" + lock['name'] + "' Granted!", "lime"); } else if (type == "range") { setWorkStatus("Lock for work " + lock.name + " (" + lock.start + " to " + lock.end + ") Granted!", "lime"); } myLocks[lock['type']] = lock; if (type == "test") { if (runTry != null && lock['name'] == runTry) { runTry = null; startJobRun(lock); } else { openTest(lock['name']); document.getElementById('mapEditor').readOnly = false; document.getElementById('reduceEditor').readOnly = false; document.getElementById('testLoad').disabled = true; document.getElementById('testRelease').disabled = false; } } else if (type == "range") { compileTest(lock.name); } } function checkLocks() { var timeOut = Math.max(5000, Math.min(15000, numberOfNodes*1500)); var d = new Date(); if (lockTrys["test"]) { var lock = lockTrys["test"]; if (d.getTime() - lock.localTime > timeOut) { // reclaime //log2("RECLAIMING LOCK!!") myLocks["test"] = lockTrys["test"]; lockTrys["test"] = null; setTestStatus("Lock acquire timed out", "red"); releaseLock("test"); } else if (lock["okay"] >= numberOfNodes) { lock["locked"] = true; lockGranted("test", lock); lockTrys["test"] = null; } } if (lockTrys["range"]) { var lock = lockTrys["range"]; //log2("rangeLocks: " + d.getTime() + " - " + lock.localTime + " = " + (d.getTime() - lock.localTime)); if (d.getTime() - lock.localTime > timeOut) { // reclaime //log2("RECLAIMING LOCK!!") myLocks["range"] = lockTrys["range"]; lockTrys["range"] = null; setJobStatus("Lock acquire timed out", "red"); releaseLock("range"); } else if (lock["okay"] >= numberOfNodes) { lock["locked"] = true; lockGranted("range", lock); lockTrys["range"] = null; } } } function respLockReq(resp, ret) { var m = new Object(); m.query = "lockResp"; m.resp = ret; m.name = resp["name"]; m.type = resp["type"]; if (resp.type == "range") { m.start = resp.start; m.end = resp.end; } sendMsg(m, resp["origin"]); } function updateTestsStatus() { var e = document.getElementById("testsList"); var str = ""; for(name in testLocks) { var lock = testLocks[name]; var d = '<div style="background-color: '; if (lock.locked == true) if (lock.running) d += "orange"; else d += "red"; else d += "lime"; d += '">' + lock.name; if (lock.locked) { if (lock.running) d += " running... "; else d += " locked by " + lock.addr; } d += "</div>"; str += d; } e.innerHTML = str; } function updateJobsList() { var e = document.getElementById("jobsList"); var str = '<table>'; str += '<tr><th rowspan="2">Test</th><th colspan="2">Range</th><th rowspan="2">Items/Job</th><th rowspan="2">Go</th></tr>'; str += "<tr><th>Min</th><th>Max</th></tr>"; for(name in tests) { var lock = testLocks[name]; var d = '<tr style="background-color: '; if(lock.locked == true) if (lock.running) d += "orange"; else d += "red"; else d += "lime"; d += '"><td>' + lock.name+"</td>"; var val = 1; var elem = document.getElementById(name+'_min'); if (elem) val = elem.value; d += '<td><input size="8" id="' + lock.name + '_min" value="'+val+'"'; if (lock.locked) d+= ' readonly="true" '; d += '/ ></td>'; val = 101; elem = document.getElementById(name+'_max'); if (elem) val = elem.value; d += '<td><input size="14" id="' + lock.name + '_max" value="'+val+'"'; if (lock.locked) d += ' readonly="true" '; d += '/ ></td>'; val = 10; elem = document.getElementById(name+'_perjob'); if (elem) val = elem.value; d += '<td><input size="6" id="' + lock.name + '_perjob" value="'+val+'"'; if (lock.locked) d += ' readonly="true" '; d += '/ ></td>'; d += '<td><input type="button" value="Start!" onClick="startJob(' + "'" + lock.name + "'" + ');"/ ></td>'; d += "</tr>"; str += d; } str += "</table>"; e.innerHTML = str; } function setJobStatus(str, col) { var e = document.getElementById('jobStatus'); e.innerHTML = str; if (col == null) col = "white"; e.style.backgroundColor = col; } function startJob(name) { var min = Number(document.getElementById(name+'_min').value); var max = Number(document.getElementById(name+'_max').value); var perjob = Number(document.getElementById(name + '_perjob').value); if(min == NaN) { setJobStatus("No Min Value", "red"); return; } if(max == '') { setJobStatus("No Max Value", "red"); return; } if(max <= min) { setJobStatus("Max <= Min", "red"); return; } if ((max-min) % perjob != 0) { setJobStatus("Max not divisible by items/job", "red"); return; } var lock = testLocks[name]; if(lock.locked == true) { setJobStatus("Test is locked by " + lock.addr); return; } //setJobStatus("Running ...", ""); runTry = name; getLock("test", name); } function startJobRun(lock) { var min = Number(document.getElementById(lock.name+'_min').value); var max = Number(document.getElementById(lock.name+'_max').value); var perjob = Number(document.getElementById(name + '_perjob').value); var m = new Object(); m.min = min; m.max = max; m.perjob = perjob; m.name = lock.name; m.query = "activateJob"; bcastMsg(m); handleActivateJob(m); setJobStatus(" Running '" + lock.name + "'...", "orange"); } function handleActivateJob(resp) { var locks = getLockType("test"); var lock = locks[resp['name']]; lock.running = true; lock.min = resp.min; lock.max = resp.max; lock.perjob = resp.perjob; runningTests.push(resp.name); doneStage = 0; setResultsStatus("Proccessing...", "orange"); lookForWork(); } function makeTestMsg(name) { var m = new Object(); m.query = "testUpdate"; m.name = name; m.mapfn = encodeStr(tests[name]["mapfn"]); m.reducefn = encodeStr(tests[name]["reducefn"]); return m; } function releaseTestLock() { // sync data var name = myLocks["test"].name; tests[name]["mapfn"] = document.getElementById('mapEditor').value; document.getElementById('mapEditor').readOnly = true; document.getElementById('mapEditor').value = ""; tests[name]["reducefn"] = document.getElementById('reduceEditor').value; document.getElementById('reduceEditor').readOnly = true; document.getElementById('reduceEditor').value = ""; m = makeTestMsg(name); bcastMsg(m); document.getElementById('testLoad').disabled = false; document.getElementById('testRelease').disabled = true; setTestStatus(""); releaseLock("test"); } function handleTestUpdate(resp) { if (!tests[resp['name']]) tests[resp['name']] = new Array(); tests[resp['name']]['mapfn'] = decodeStr(resp["mapfn"]); tests[resp['name']]['reducefn'] = decodeStr(resp["reducefn"]); } function releaseLock(type) { var lock = myLocks[type]; myLocks[type] = null; lock.locked = false; var m = new Object(); m["query"] = "releaseLock"; m["type"] = type; m["name"] = lock.name; if (type == "range") { m["start"] = lock.start; m["end"] = lock.end; } bcastMsg(m); } function handleLockRelease(resp) { //var locks = getLockType(resp['type']); //locks[resp['name']].locked = false; var lock = retrieveLock(resp); if (lock) lock.locked = false; } function setWorkStatus(str, col) { var e = document.getElementById('workStatus'); e.innerHTML = str; if (col == null) col = "white"; e.style.backgroundColor = col; } var workSize = 10; function lookForWork() { //log("Look For Work!"); if (myLocks["range"] != null || lockTrys["range"] != null) { // have work return; } if (runningTests.length > 0) { for (var i =0; i < runningTests.length; i++) { var lock = testLocks[runningTests[i]]; //log("Looking for work in test " + lock.name); // hilarious test //workSize = Math.round(Math.random()*25); var rlock = makeRangeLock(lock, lock['perjob']) if (rlock) { getLock("range", lock.name, rlock.start, rlock.end); return; } // else no work left in this task } setWorkStatus("No work available (All tasks' work is locked and/or done)"); doneStage = 1; } else { setWorkStatus("No work available (no running tests)"); } } function getRangeLock(name, start, end) { log("getRangeLock for (" + name + " from " + start + " to " + end + ")"); if (!rangeLocks[name]) { log("no locks for " + name + ", so MAKING"); rangeLocks[name] = genRangeCell(name, start, end); return rangeLocks[name]; } log("locks exist for " + name + " so SEARCHING forward"); var rlock = rangeLocks[name]; if(end < rlock.start) { log("Space before first lock, MAKING HERE"); rangeLocks[name] = genRangeCell(name, start, end); rangeLocks[name].next = rlock; return rangeLocks[name]; } while(rlock.next && rlock.start < rlock.end+1) { log("looking at (" + rlock.start + " to " + rlock.end + ")"); if (rlock.start == start && rlock.end == end) { log("FOUND IT"); return rlock; } if (rlock.end < start && rlock.next.start > end) { log("GAP between (" + rlock.start + " to " + rlock.end + ") and (" + rlock.next.start + " to " + rlock.next.end + ") where we should be so MAKING THERE"); var nlock = rlock.next; rlock.next = genRangeCell(name, start, end); rlock.next.next = nlock; return rlock.next; } rlock = rlock.next; } log("SEARCH ended"); if (rlock.start == start && rlock.end == end) { log("Found it!"); return rlock; } else { if (rlock.end < start) { log("There is space at the end to make what we want"); rlock.next = genRangeCell(name, start, end); return rlock.next; } else { log("search ended after what we wanted, FAIL"); return null; } } } function makeRangeLock(tlock, suggestedSize) { suggestedSize--; //log("makeRangeLock or size " + suggestedSize + " for (" + tlock.name + " from " + tlock.min + " to " + tlock.max + ")"); if (!rangeLocks[tlock.name]) { //log("no locks for " + tlock.name + ", so MAKING"); rangeLocks[tlock.name] = genRangeCell( tlock.name, tlock.min, Math.min(tlock.max, tlock.min + suggestedSize)); return rangeLocks[tlock.name]; } else { //log("some locks for " + tlock.name + " so FINDING"); var rlock = rangeLocks[tlock.name]; if (rlock.start > tlock.min) { //log("First lock is above min, so making NEW lock before it to use"); rangeLocks[tlock.name] = genRangeCell(tlock.name, Math.max(tlock.min, rlock.start-1-suggestedSize), rlock.start-1); rangeLocks[tlock.name].next = rlock; return rangeLocks[tlock.name]; } //log("Searching linked list forward"); while (rlock.next && (rlock.locked || rlock.done)) { //log("looking at (" + rlock.start + " to " + rlock.end + ")"); if (rlock.next.start != rlock.end +1) { //log("GAP between (" + rlock.start + " to " + rlock.end + ") and (" + rlock.next.start + " to " + rlock.next.end + ") so MAKING THERE"); var nlock = rlock.next; rlock.next = genRangeCell(tlock.name, rlock.end+1, Math.min(rlock.end+1+suggestedSize, nlock.start-1)); rlock.next.next = nlock; return rlock.next; } rlock = rlock.next; } //log("DONE searching"); if (rlock.locked || rlock.done) { //log("this one not acceptable"); if (rlock.end == tlock.max) { //log ("this one also at end, so FAIL"); return null } else { //log("still more room at end, so MAKING at end"); rlock.next = genRangeCell(tlock.name, rlock.end+1, Math.min(tlock.max, rlock.end+1+ suggestedSize)); return rlock.next; } } else { //log("this one acceptable (" + rlock.start + " to " + rlock.end + ")"); return rlock; } } } function genRangeCell(name, start, end, next) { var c = new Object(); c.name = name; c.start=start; c.end = end; c.next = next; c.locked = false; c.done = false; return c; } var map_test = null; var reduce = null; function compileTest(name) { mapCode = tests[name].mapfn; mapCode = "map_test = function(value) {\n" + mapCode + "\n}"; //log("evaling: " + mapCode); try { eval(mapCode); } catch (exception) { error("compiling map_test: " + exception); } reduceCode = tests[name].reducefn; reduceCode = "reduce = function(list) {\n" + reduceCode + "}\n"; //log("evaling: " + reduceCode); try { eval(reduceCode); } catch (exception) { error("compiling reduce: " + exception); } } var workMapPos = null; var workReduce = false; var workList = new Array(); var isWorking = false; function doWork() { if (myLocks["range"]) { isWorking = true; var lock = myLocks["range"]; //log("Acquired work..."); setWorkStatus("Working on " + lock.name + " set (" + lock.start + " to " + lock.end + ")...", "lime"); if (workMapPos == null) workMapPos = lock.start; if (workMapPos <= lock.end) { //log("Mapping on " + workMapPos); var item = new Object(); item.key = workMapPos; item.value = map_test(workMapPos); //log("result = " + item.value); workList.push(item); workMapPos++; } else if (workReduce == false) { //log("Reducing"); if(reduce == null) error ("no reduce"); lock.results = reduce(workList); //log("FINAL RESULTS: " + lock.results); workMapPos = null; workReduce = false; workList = new Array(); lock.done = true; sendResults(lock); releaseLock("range"); lookForWork(); } } else { isWorking = false; } } function sendResults(lock) { m = new Object(); m.query = "results"; m.name = lock.name; m.start = lock.start; m.end = lock.end; m.results = lock.results; bcastMsg(m); } function handleResults(resp) { lock = getRangeLock(resp.name, resp.start, resp.end); if(lock == null) { error("handleResults: lock == null"); return; } log("Setting results for " + resp.name + " (" + resp.start + " to " + resp.end + ") to " + resp.results); lock.done = true; lock.results = resp.results; } function updateResults() { var e = document.getElementById("workDiv"); var str = ""; //for (var i =0; i < runningTests.length; i++) { for (var i in rangeLocks) { //var lock = testLocks[runningTests[i]]; var lock = testLocks[i]; str += "<i>" + lock.name + " (" + lock.min + " to " + lock.max + "):</i><br>"; var rlock = rangeLocks[lock.name]; while(rlock != null) { str += '<div style="background-color:'; if (rlock.locked == false && rlock.done == false) str += "red"; else if(rlock.locked == true && rlock.done != true) str += "orange"; else if(rlock.done == true) str += "lime"; str += ';">' + rlock.start + "-" + rlock.end + ": "; if(rlock.locked == false && rlock.done == false) str += "AVAILABLE"; else if(rlock.locked == true && rlock.done != true) str += "being PROCESSED at " + rlock.addr; else if(rlock.done == true) { //str += "DONE"; str += packResults(rlock.results); } str += "</div>"; rlock = rlock.next; } str += "<br\/ ><br \/ >"; } e.innerHTML = str; } function setResultsStatus(str, col) { var e = document.getElementById('resultsStatus'); e.innerHTML = str; if (col == null) col = "white"; e.style.backgroundColor = col; } function addArray(a, newRes) { if (!newRes) return; for(var i = 0; i < newRes.length; i++) { a.push(newRes[i]); } } function updateFinalResults() { if (doneStage == 1) { // all locked, make sure all done for (var i =0; i < runningTests.length; i++) { var lock = testLocks[runningTests[i]]; var rlock = rangeLocks[runningTests[i]]; while(rlock) { if (rlock.done != true) return; rlock = rlock.next; } } doneStage = 2; } // pricess new results if (doneStage == 2) { setResultsStatus("Final Reducing...", "yellow"); for(var i=0; i < runningTests.length; i++ ) { var lock = testLocks[runningTests[i]]; var rlock = rangeLocks[runningTests[i]]; var fr = finalResults[runningTests[i]]; if (fr) continue; var results = new Array(); while (rlock) { addArray(results, rlock.results); rlock = rlock.next; } results = reduce(results); finalResults[runningTests[i]] = results; } doneStage++; } if(doneStage == 3) { // all work done! setResultsStatus("Done", "lime"); var e = document.getElementById("resultsDiv"); var str = ""; //for(var i=0; i < runningTests.length; i++ ) { for (i in finalResults) { var lock = testLocks[i]; //var rlock = rangeLocks[i]; str += "<i>" + lock.name + " (" + lock.min + " to " + lock.max + "):</i><br>"; var results = finalResults[i]; str += packResults(results); str += "<br><br>"; } e.innerHTML = str; } } var cronID = setInterval("cron()", 100); getMsgs(); getLog(); function page(p) { // turn off all pages document.getElementById("pageHome").style.visibility="hidden"; document.getElementById("pageTests").style.visibility="hidden"; document.getElementById("pageJobs").style.visibility="hidden"; document.getElementById("pageWork").style.visibility="hidden"; document.getElementById("pageResults").style.visibility="hidden"; // turn on page document.getElementById("page"+p).style.visibility="visible"; } function install_defaults() { var m = new Object(); m.name = "primes"; m.type = "test"; m.locked = false; handleAddLock(m); m = new Object(); m.name = "primes"; m.mapfn = 'var max = Math.round(Math.sqrt(value));\nfor(var i=2; i <= max; i ++) {\n if (value % i == 0) \n return false;\n}\nreturn true;\n'; m.reducefn = 'var r = new Array();\nfor (var i = 0; i < list.length; i++) {\n if (list[i].value == true)\n r.push(list[i]);\n}\nreturn r;\n'; handleTestUpdate(m); } install_defaults(); </script> </head> <body> <input type="button" value="Home" onClick="page('Home');"> <input type="button" value="Tests" onClick="page('Tests');"> <input type="button" value="Jobs" onClick="page('Jobs');"> <input type="button" value="Work" onClick="page('Work');"> <input type="button" value="Results" onClick="page('Results');"> <!-- HOME --> <div id="pageHome" class="page"> <div id="statusLabel"></div> <div id="numberOfNodes">Number of nodes: <script> document.write(numberOfNodes); </script></div> <div id="connections">Connected to:</div> <div id="dlc">DLC:</div> <input id="networkAddr"><input type="button" value="Join Network" onClick="join_network(document.getElementById('networkAddr').value);"><br/> <br /> <b>Log:</b> <input type="checkbox" value="log" id="logCheck"/>Log?<br/> <textarea rows="7" cols="60" id="log" readonly="true"></textarea> <br/> <input type="button" value="Reload HTML" onclick="reloadHTML()" /> <br/><br/> <input type="button" value="Kill Server" onClick="killServer()" /> <script>ping();</script> </div> <!-- TESTS --> <div id="pageTests" class="page"> <b>Tests</b><br/> Tests: <div id="testsList"></div> <br/> <input id="testName" value=""> <input id="testLoad" type="button" value="Load Test" onClick="loadTest(document.getElementById('testName').value);"> <div id="testStatus"></div> <br/>map_test (value) { <textarea rows="10" cols="60" id="mapEditor" readonly="true"></textarea> <br/>}<br/> <br/> <i>// list :: array of hashes with 'key' and 'value' as elements</i><br/> reduce (list) { <textarea rows="10" cols="60" id="reduceEditor" readonly="true"></textarea> <br/>}<br/> <br/> <input id="testRelease" type="button" disabled="true" value="Save/Release" onClick="releaseTestLock()"> </div> <!-- JOBS --> <div id="pageJobs" class="page"> <b>Jobs</b><br/><br/> <div id="jobStatus"></div><br/> <div id="jobsList"> </div> </div> <!-- WORK --> <div id="pageWork" class="page"> <b>Work</b><br/><br/> Currently:<br/> <div id="workStatus"></div> <br/><br/> Results:<br/> <div id="workDiv"></div> </div> <div id="pageResults" class="page"> <b>Results</b><br/><br/> <div id="resultsStatus">Not Proccessing</div> <div id="resultsDiv"></div> </div> <!--- CODE --> <div id="pageCode" class="page"> </div> <script> page("Home"); </script> </body> </html>