diff --git a/aad_coin_miner_simd.c b/aad_coin_miner_simd.c
index f96fccc..d8af5a1 100644
--- a/aad_coin_miner_simd.c
+++ b/aad_coin_miner_simd.c
@@ -363,6 +363,7 @@ static void mine_coins_avx2_omp(u64_t max_attempts, double max_time)
printf("Press Ctrl+C to stop\n\n");
double start_time = get_wall_time();
+ int should_stop = 0;
#pragma omp parallel
{
@@ -393,15 +394,24 @@ static void mine_coins_avx2_omp(u64_t max_attempts, double max_time)
for(int i = 12; i < 54; i++)
((u08_t *)base_coin)[i ^ 3] = 'A' + ((i - 12 + thread_id * SIMD_WIDTH) % 26);
- while(keep_running)
+ while(keep_running && !should_stop)
{
- // Check stopping conditions
- if(max_attempts > 0 && attempts >= max_attempts)
+ // Check stopping conditions (check shared flag first)
+ if(should_stop)
break;
+ if(max_attempts > 0 && attempts >= max_attempts)
+ {
+ should_stop = 1;
+ break;
+ }
+
double elapsed_time = get_wall_time() - start_time;
if(max_time > 0 && elapsed_time >= max_time)
+ {
+ should_stop = 1;
break;
+ }
prepare_coins(base_coin, interleaved_data, SIMD_WIDTH);
sha1_avx2((v8si *)interleaved_data, (v8si *)interleaved_hash);
@@ -429,23 +439,26 @@ static void mine_coins_avx2_omp(u64_t max_attempts, double max_time)
// Print progress every 1M attempts (only from one thread)
// Periodically update the shared counter and report
- if(thread_attempts % 100000 < SIMD_WIDTH)
+ if(thread_attempts >= 100000)
{
#pragma omp atomic
attempts += thread_attempts;
thread_attempts = 0;
- #pragma omp barrier
-
- #pragma omp master
+ // Only master thread reports progress (no barrier to avoid blocking)
+ if(thread_id == 0)
{
- if(attempts - last_reported_attempts >= 1000000)
+ u64_t current_attempts;
+ #pragma omp atomic read
+ current_attempts = attempts;
+
+ if(current_attempts - last_reported_attempts >= 1000000)
{
double elapsed = get_wall_time() - start_time;
- double rate = attempts / elapsed;
+ double rate = current_attempts / elapsed;
printf("Attempts: %llu, Rate: %.2f MH/s, Coins: %u, Elapsed: %.2fs\n",
- (unsigned long long)attempts, rate / 1e6, coins_found, elapsed);
- last_reported_attempts = attempts;
+ (unsigned long long)current_attempts, rate / 1e6, coins_found, elapsed);
+ last_reported_attempts = current_attempts;
}
}
}
diff --git a/index.html b/index.html
index 48f824f..58c4837 100644
--- a/index.html
+++ b/index.html
@@ -121,6 +121,30 @@
.button-group {
margin: 15px 0;
}
+ .limits-section {
+ background: #e8f4f8;
+ padding: 15px;
+ border-radius: 5px;
+ margin: 15px 0;
+ }
+ .limits-section h3 {
+ margin-top: 0;
+ margin-bottom: 10px;
+ color: #0066cc;
+ }
+ .limit-note {
+ font-size: 12px;
+ color: #666;
+ margin-top: 5px;
+ }
+ .limit-reached {
+ background: #fff3cd;
+ color: #856404;
+ padding: 10px;
+ border-radius: 5px;
+ margin-top: 10px;
+ font-weight: bold;
+ }
@@ -145,6 +169,20 @@
+
+
Mining Limits (Optional)
+
+
+
+ (Leave empty for unlimited)
+
+
+
+
+ (Leave empty for unlimited)
+
+
+
@@ -152,6 +190,8 @@
+
+
Waiting to start...
@@ -170,12 +210,93 @@
let lastDisplayedCoinCount = 0;
let pausedStats = false;
let simdAvailable = false;
+ let miningStartTime = 0;
+ let maxAttempts = null;
+ let maxTime = null;
+ let updateStatsInternal; // Declare in outer scope
+ let needsReset = false; // Flag to track if we need to reset on next start
+
+ function checkLimits(beforeBatch = false) {
+ const attemptsPtr = Module._malloc(8);
+ const coinsPtr = Module._malloc(4);
+ const hashRatePtr = Module._malloc(8);
+ const elapsedPtr = Module._malloc(8);
+
+ Module.setValue(attemptsPtr, 0, 'i32');
+ Module.setValue(attemptsPtr + 4, 0, 'i32');
+
+ Module._get_statistics(attemptsPtr, coinsPtr, hashRatePtr, elapsedPtr);
+
+ const attemptsLowUnsigned = Module.getValue(attemptsPtr, 'i32') >>> 0;
+ const attemptsHighUnsigned = Module.getValue(attemptsPtr + 4, 'i32') >>> 0;
+
+ let attempts;
+ if (attemptsHighUnsigned === 0) {
+ attempts = attemptsLowUnsigned;
+ } else {
+ const low = BigInt(attemptsLowUnsigned);
+ const high = BigInt(attemptsHighUnsigned);
+ attempts = (high * BigInt(4294967296)) + low;
+ }
+
+ const elapsed = Module.getValue(elapsedPtr, 'double');
+
+ Module._free(attemptsPtr);
+ Module._free(coinsPtr);
+ Module._free(hashRatePtr);
+ Module._free(elapsedPtr);
+
+ let limitReached = false;
+ let limitMessage = '';
+
+ // If checking before batch, use a small margin to prevent overshoot
+ const batchSize = parseInt(document.getElementById('batchSize').value);
+
+ if (maxAttempts !== null) {
+ if (beforeBatch && Number(attempts) + batchSize >= maxAttempts) {
+ limitReached = true;
+ limitMessage = `Maximum attempts limit reached (${maxAttempts.toLocaleString()})`;
+ } else if (!beforeBatch && Number(attempts) >= maxAttempts) {
+ limitReached = true;
+ limitMessage = `Maximum attempts limit reached (${maxAttempts.toLocaleString()})`;
+ }
+ }
+
+ if (maxTime !== null && elapsed >= maxTime) {
+ limitReached = true;
+ if (limitMessage) limitMessage += ' and ';
+ limitMessage += `Maximum time limit reached (${maxTime} seconds)`;
+ }
+
+ if (limitReached && mining) {
+ mining = false;
+ Module._stop_mining();
+ clearInterval(miningInterval);
+ clearInterval(updateInterval);
+
+ // Force immediate stats update before showing the message
+ setTimeout(() => {
+ updateStatsInternal();
+
+ const limitDiv = document.getElementById('limitReached');
+ limitDiv.textContent = '⚠️ Mining stopped: ' + limitMessage;
+ limitDiv.style.display = 'block';
+
+ let currentHTML = document.getElementById('stats').innerHTML;
+ document.getElementById('stats').innerHTML =
+ currentHTML.replace('Mining Statistics:', 'Mining Statistics (LIMIT REACHED):');
+ }, 10);
+
+ console.log('Mining stopped due to limit:', limitMessage);
+ }
+
+ return limitReached;
+ }
CoinMinerModule().then(mod => {
Module = mod;
console.log('WebAssembly module loaded');
- // Check if SIMD is available
simdAvailable = Module._is_simd_available();
const simdToggle = document.getElementById('simdToggle');
const simdStatus = document.getElementById('simdStatus');
@@ -185,11 +306,8 @@
simdStatus.className = 'simd-status simd-available';
simdToggle.disabled = false;
- // SYNC LOGIC: Read C state first to match backend
const cState = Module._is_simd_enabled();
simdToggle.checked = (cState === 1);
-
- // Force sync again just to be safe
Module._set_simd_enabled(simdToggle.checked ? 1 : 0);
console.log(`JS: Initialized - SIMD Available, Toggle set to ${simdToggle.checked}`);
} else {
@@ -201,16 +319,12 @@
console.log('JS: Initialized - SIMD Not Available');
}
- // SIMD toggle handler
simdToggle.onchange = () => {
if (simdAvailable) {
const newState = simdToggle.checked ? 1 : 0;
console.log(`JS: User toggled SIMD to ${newState}`);
-
- // Call the C function
Module._set_simd_enabled(newState);
- // Update status display
const currentMode = simdToggle.checked ? 'SIMD Mode' : 'Scalar Mode';
if (!mining) {
document.getElementById('stats').innerHTML =
@@ -221,10 +335,55 @@
document.getElementById('start').onclick = () => {
if (!mining) {
+ // If reset flag is set, reinitialize everything
+ if (needsReset) {
+ // Stop first to ensure everything is halted
+ Module._stop_mining();
+ // Small delay to ensure stop takes effect
+ setTimeout(() => {
+ Module._reset_mining();
+ }, 50);
+ lastDisplayedCoinCount = 0;
+ miningStartTime = 0;
+ needsReset = false;
+
+ // Wait a bit before starting mining
+ setTimeout(() => {
+ const maxAttemptsInput = document.getElementById('maxAttempts').value;
+ const maxTimeInput = document.getElementById('maxTime').value;
+
+ maxAttempts = maxAttemptsInput ? parseInt(maxAttemptsInput) : null;
+ maxTime = maxTimeInput ? parseInt(maxTimeInput) : null;
+
+ document.getElementById('limitReached').style.display = 'none';
+
+ mining = true;
+ Module._resume_mining();
+ pausedStats = false;
+ miningStartTime = Date.now();
+ console.log('Starting mining after reset...');
+ if (maxAttempts) console.log('Max attempts:', maxAttempts);
+ if (maxTime) console.log('Max time:', maxTime, 'seconds');
+ startMining();
+ }, 100);
+ return;
+ }
+
+ const maxAttemptsInput = document.getElementById('maxAttempts').value;
+ const maxTimeInput = document.getElementById('maxTime').value;
+
+ maxAttempts = maxAttemptsInput ? parseInt(maxAttemptsInput) : null;
+ maxTime = maxTimeInput ? parseInt(maxTimeInput) : null;
+
+ document.getElementById('limitReached').style.display = 'none';
+
mining = true;
Module._resume_mining();
pausedStats = false;
+ miningStartTime = Date.now();
console.log('Starting mining...');
+ if (maxAttempts) console.log('Max attempts:', maxAttempts);
+ if (maxTime) console.log('Max time:', maxTime, 'seconds');
startMining();
}
};
@@ -235,7 +394,7 @@
clearInterval(miningInterval);
clearInterval(updateInterval);
- updateStats();
+ updateStatsInternal();
let currentHTML = document.getElementById('stats').innerHTML;
document.getElementById('stats').innerHTML =
@@ -246,19 +405,34 @@
};
document.getElementById('reset').onclick = () => {
- Module._reset_mining();
+ // Stop mining completely - use aggressive stopping
mining = false;
- pausedStats = false;
clearInterval(miningInterval);
clearInterval(updateInterval);
+
+ // Multiple stop calls to ensure it really stops
+ Module._stop_mining();
+ Module._stop_mining();
+ Module._stop_mining();
+
+ // Set the reset flag so next start will reinitialize
+ needsReset = true;
+
+ // Clear JavaScript state
+ pausedStats = false;
lastDisplayedCoinCount = 0;
+ maxAttempts = null;
+ maxTime = null;
+ miningStartTime = 0;
+
+ document.getElementById('limitReached').style.display = 'none';
const mode = simdToggle.checked ? 'SIMD' : 'Scalar';
document.getElementById('stats').innerHTML =
`Reset complete. Using ${mode} mode. Click Start to begin.`;
document.getElementById('coins').innerHTML = '';
document.getElementById('coin-count').textContent = '0';
- console.log('Mining reset');
+ console.log('Mining reset - will reinitialize on next start');
};
document.getElementById('clearCoins').onclick = () => {
@@ -315,7 +489,8 @@
}
}
- function updateStats() {
+ // Assign to outer scope variable
+ updateStatsInternal = function() {
const attemptsPtr = Module._malloc(8);
const coinsPtr = Module._malloc(4);
const hashRatePtr = Module._malloc(8);
@@ -349,15 +524,32 @@
const mode = Module._is_simd_enabled() ? 'SIMD' : 'Scalar';
+ let limitsInfo = '';
+ if (maxAttempts !== null || maxTime !== null) {
+ limitsInfo = '
Limits:
';
+ if (maxAttempts !== null) {
+ const attemptsPercent = Math.min(100, (Number(attempts) / maxAttempts) * 100);
+ limitsInfo += `Max Attempts: ${maxAttempts.toLocaleString()} (${attemptsPercent.toFixed(1)}%)
`;
+ }
+ if (maxTime !== null) {
+ const timePercent = Math.min(100, (elapsed / maxTime) * 100);
+ limitsInfo += `Max Time: ${maxTime}s (${timePercent.toFixed(1)}%)`;
+ }
+ }
+
document.getElementById('stats').innerHTML = `
Mining Statistics (${mode} Mode):
Attempts: ${attempts.toString()}
Coins Found: ${coins}
Hash Rate: ${(hashRate / 1e6).toFixed(2)} MH/s
- Elapsed Time: ${elapsed.toFixed(2)} seconds
+ Elapsed Time: ${elapsed.toFixed(2)} seconds${limitsInfo}
`;
displayNewCoins();
+ };
+
+ function updateStats() {
+ updateStatsInternal();
}
function startMining() {
@@ -369,10 +561,23 @@
clearInterval(miningInterval);
return;
}
+
+ // Check limits BEFORE mining this batch
+ if (checkLimits()) {
+ return;
+ }
+
Module._mine_coins_wasm(batchSize);
+
+ // Check limits immediately AFTER mining this batch
+ if (checkLimits()) {
+ return;
+ }
}, 0);
- updateInterval = setInterval(updateStats, updateMs);
+ updateInterval = setInterval(() => {
+ updateStats();
+ }, updateMs);
}
}).catch(err => {
console.error('Failed to load WebAssembly module:', err);