diff --git a/web/source/nollamas/index.js b/web/source/nollamas/index.js index e8ae0a75c..b04a97c23 100644 --- a/web/source/nollamas/index.js +++ b/web/source/nollamas/index.js @@ -45,51 +45,85 @@ document.addEventListener("DOMContentLoaded", function() { // data attributes on the nollamas section. const seed = nollamas.dataset.nollamasSeed; const challenge = nollamas.dataset.nollamasChallenge; + const threads = navigator.hardwareConcurrency; + if (typeof(threads) != "number" || threads < 1) { threads = 1; } console.log("seed:", seed); // eslint-disable-line no-console console.log("challenge:", challenge); // eslint-disable-line no-console + console.log("threads:", threads); // eslint-disable-line no-console - // Prepare the worker with task function. - const worker = new Worker("/assets/dist/nollamasworker.js"); + // Create an array to track + // all workers such that we + // can terminate them all. + const workers = []; + const terminateAll = () => { workers.forEach((worker) => worker.terminate() ); }; + + // Get time before task completion. const startTime = performance.now(); - worker.postMessage({ - challenge: challenge, - seed: seed, - }); - // Set the main worker function. - worker.onmessage = function (e) { - if (e.data.done) { - const endTime = performance.now(); - const duration = endTime - startTime; + // Prepare and start each worker, + // adding them to the workers array. + for (let i = 0; i < threads; i++) { + const worker = new Worker("/assets/dist/nollamasworker.js"); + workers.push(worker); - // Remove spinner and replace it with a tick - // and info about how long it took to solve. - nollamas.removeChild(spinner); - const solutionWrapper = document.createElement("div"); - solutionWrapper.className = "nollamas-status"; + // On any error terminate. + worker.onerror = (ev) => { + console.error("worker error:", ev); // eslint-disable-line no-console + terminateAll(); + }; - const tick = document.createElement("i"); - tick.className = "fa fa-check fa-2x fa-fw"; - tick.setAttribute("title","Solved!"); - tick.setAttribute("aria-label", "Solved!"); - solutionWrapper.appendChild(tick); + // Post worker message, where each + // worker will compute nonces in range: + // $threadNumber + $totalThreads * n + worker.postMessage({ + challenge: challenge, + threads: threads, + thread: i, + seed: seed, + }); - const took = document.createElement("span"); - const solvedText = `Solved after ${e.data.nonce} iterations, in ${duration.toString()}ms!`; - took.appendChild(document.createTextNode(solvedText)); - solutionWrapper.appendChild(took); + // Set main on-success function. + worker.onmessage = function (e) { + if (e.data.done) { + // Stop workers. + terminateAll(); - nollamas.appendChild(solutionWrapper); + // Log which thread found the solution. + console.log("solution from:", e.data.thread); // eslint-disable-line no-console - // Wait for 500ms before redirecting, to give - // visitors a shot at reading the message, but - // not so long that they have to wait unduly. - setTimeout(() => { - let url = new URL(window.location.href); - url.searchParams.set("nollamas_solution", e.data.nonce); - window.location.replace(url.toString()); - }, 500); - } - }; + // Get total computation duration. + const endTime = performance.now(); + const duration = endTime - startTime; + + // Remove spinner and replace it with a tick + // and info about how long it took to solve. + nollamas.removeChild(spinner); + const solutionWrapper = document.createElement("div"); + solutionWrapper.className = "nollamas-status"; + + const tick = document.createElement("i"); + tick.className = "fa fa-check fa-2x fa-fw"; + tick.setAttribute("title","Solved!"); + tick.setAttribute("aria-label", "Solved!"); + solutionWrapper.appendChild(tick); + + const took = document.createElement("span"); + const solvedText = `Solved after ${e.data.nonce} iterations by worker ${e.data.thread} of ${threads}, in ${duration.toString()}ms!`; + took.appendChild(document.createTextNode(solvedText)); + solutionWrapper.appendChild(took); + + nollamas.appendChild(solutionWrapper); + + // Wait for 500ms before redirecting, to give + // visitors a shot at reading the message, but + // not so long that they have to wait unduly. + setTimeout(() => { + let url = new URL(window.location.href); + url.searchParams.set("nollamas_solution", e.data.nonce); + window.location.replace(url.toString()); + }, 500); + } + }; + } }); diff --git a/web/source/nollamasworker/index.js b/web/source/nollamasworker/index.js index 3c9b043c2..0a7711ce1 100644 --- a/web/source/nollamasworker/index.js +++ b/web/source/nollamasworker/index.js @@ -19,10 +19,10 @@ import sha256 from "./sha256"; -let compute = async function(seedStr, challengeStr) { +let compute = async function(seedStr, challengeStr, start, iter) { const textEncoder = new TextEncoder(); - let nonce = 0; + let nonce = start; while (true) { // eslint-disable-line no-constant-condition // Create possible solution string from challenge string + nonce. @@ -38,17 +38,19 @@ let compute = async function(seedStr, challengeStr) { return nonce; } - // Iter. - nonce++; + // Iter nonce. + nonce += iter; } }; onmessage = async function(e) { - console.log('worker started'); // eslint-disable-line no-console + const thread = e.data.thread; + const threads = e.data.threads; + console.log("worker started:", thread); // eslint-disable-line no-console - // Compute nonce value that produces 'challenge' for seed. - let nonce = await compute(e.data.seed, e.data.challenge); + // Compute nonce value that produces 'challenge', for our allotted thread. + let nonce = await compute(e.data.seed, e.data.challenge, thread, threads); - // Post the solution nonce back to caller. - postMessage({ nonce: nonce, done: true }); + // Post the solution nonce back to caller with thread no. + postMessage({ nonce: nonce, done: true, thread: thread }); };