cpu-miner: make the CPU backend a first-class citizen for API/tooling#56
Open
Schnitzel wants to merge 1 commit into
Open
cpu-miner: make the CPU backend a first-class citizen for API/tooling#56Schnitzel wants to merge 1 commit into
Schnitzel wants to merge 1 commit into
Conversation
Three small, related changes that take the CPU mining backend from "runs and hashes" to "shows up correctly in the public API," so it's actually usable for exercising mujina end-to-end without ASIC hardware (developing against the REST API, testing UIs, prototyping integrations like exporters or message-bus bridges, etc.). * board/cpu.rs: keep the board's telemetry sender alive, and publish per-thread telemetry from it. Previously the watch sender was bound to `_telemetry_tx` and dropped immediately on construction. `BoardRegistry::boards()` evicts any registration whose senders have all gone away, so the CPU board silently disappeared from `/api/v0/boards` (and from `MinerTelemetry.boards`) as soon as it was first queried. The board now snapshots each thread's status handle before handing the threads to the scheduler, then runs a small task that owns the sender and refreshes `BoardTelemetry.threads` from those handles every two seconds. Aborting the task on shutdown drops the sender, letting the registry clean up the board normally. * cpu_miner/thread.rs: expose `status_handle()` so the board layer can read each thread's live `HashThreadStatus` (which the hasher already populates every five seconds with measured hashrate, share counts, etc.) without taking ownership of the thread itself. Ownership still goes to the scheduler. * scheduler.rs: in `measured_hashrate()` and `operational_hashrate()`, fall back to `entry.thread.status().hashrate` when the share-based estimator has no settled value. The CPU backend measures hashrate directly from cycle counts, but at typical CPU rates against any reasonable source target it would essentially never produce shares fast enough for the estimator to settle, so the API field stayed at zero forever. ASIC backends that don't self-report keep falling through to the existing `capabilities().hashrate_estimate` constant exactly as before -- this is purely an additional fallback, ordered after the truthful estimator and before the static guess. Net effect on a CPU-only host: `/api/v0/miner` reports an aggregate hashrate within ~5s of startup, `boards[0]` shows up with per-thread `hashrate` and `is_active` populated. ASIC behaviour is unchanged for boards whose threads don't write `HashThreadStatus.hashrate`. Test plan - `cargo test -p mujina-miner --lib` -- existing scheduler and registry tests still pass. - `MUJINA_CPUMINER_THREADS=2 MUJINA_CPUMINER_DUTY=50 MUJINA_USB_DISABLE=1 \ ./mujina-minerd` on an Antminer S19 control board (aarch64 musl, built per 256foundation#55), then: - `curl /api/v0/miner` returns non-zero `hashrate` after the first 5-second hasher cycle, lists the CPU board with both threads, each showing `hashrate ~ 90000` and `is_active: true`. - `curl /api/v0/boards` shows the CPU board (previously empty array).
There was a problem hiding this comment.
Pull request overview
This PR aims to make the CPU mining backend show up correctly in Mujina’s public API so CPU-only setups can be used for end-to-end API/UI/tooling workflows without ASIC hardware.
Changes:
- Keeps the CPU board registered in API board telemetry by retaining its watch sender in a background publisher task.
- Exposes a live CPU thread status handle so board-level telemetry can read per-thread state.
- Adds scheduler fallback logic to use self-reported thread hashrate when share-based estimation has not settled.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
mujina-miner/src/scheduler.rs |
Adds self-reported hashrate fallback for aggregate and operational scheduler telemetry. |
mujina-miner/src/cpu_miner/thread.rs |
Exposes a cloneable live status handle from CPU hash threads. |
mujina-miner/src/board/cpu.rs |
Publishes CPU board/thread telemetry and keeps the board registration alive. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
97
to
+101
| Ok(BackplaneConnector { | ||
| info, | ||
| threads, | ||
| telemetry_rx, | ||
| shutdown: None, | ||
| shutdown: Some(Box::pin(async move { publisher.abort() })), |
| /// Lets the board layer publish per-thread telemetry without taking | ||
| /// ownership of the thread itself (which gets handed to the | ||
| /// scheduler). | ||
| pub fn status_handle(&self) -> Arc<RwLock<HashThreadStatus>> { |
Comment on lines
+237
to
+241
| let reported = entry.thread.status().hashrate; | ||
| if reported.is_zero() { | ||
| entry.thread.capabilities().hashrate_estimate | ||
| } else { | ||
| reported |
Comment on lines
+213
to
+217
| .map(|entry| { | ||
| let estimated = entry.hashrate.hashrate(); | ||
| if estimated.is_zero() { | ||
| entry.thread.status().hashrate | ||
| } else { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three small, related changes that take the CPU mining backend from "runs and hashes" to "shows up correctly in the public API," so it's actually usable for exercising mujina end-to-end without ASIC hardware — developing against the REST API, testing UIs, prototyping integrations like exporters or message-bus bridges, etc.
board/cpu.rs— keep the board's telemetry sender alive and publish per-thread telemetry from it.Previously the watch sender was bound to
_telemetry_txand dropped immediately on construction.BoardRegistry::boards()evicts any registration whose senders have all gone away, so the CPU board silently disappeared from/api/v0/boards(and fromMinerTelemetry.boards) as soon as the registry was first queried. The board now snapshots each thread's status handle before handing the threads to the scheduler, then runs a small task that owns the sender and refreshesBoardTelemetry.threadsfrom those handles every two seconds. Aborting the task on shutdown drops the sender, letting the registry clean up the board normally.cpu_miner/thread.rs— exposestatus_handle()so the board layer can read each thread's liveHashThreadStatus(which the hasher already populates every five seconds with measured hashrate, share counts, etc.) without taking ownership of the thread itself. Ownership still goes to the scheduler.scheduler.rs— inmeasured_hashrate()andoperational_hashrate(), fall back toentry.thread.status().hashratewhen the share-based estimator has no settled value. The CPU backend measures hashrate directly from cycle counts, but at typical CPU rates against any reasonable source target it would essentially never produce shares fast enough for the estimator to settle, so the API field stayed at zero forever. ASIC backends that don't self-report keep falling through to the existingcapabilities().hashrate_estimateconstant exactly as before — this is purely an additional fallback, ordered after the truthful estimator and before the static guess.Why
The CPU backend exists in the tree for exactly the kind of work where you don't want to plug in (or own) real hashing hardware: developing against the API, testing the daemon end-to-end, and building tools that consume mujina's output. Today, even though the threads do hash, an API consumer sees `{"hashrate": 0, "boards": []}` indefinitely — which is indistinguishable from "nothing is running." That makes the CPU backend a poor base for any of those use cases. These changes close the gap so the API reflects what the backend is actually doing.
Net effect
Test plan
```json
{"uptime_secs":10,"hashrate":174652,"shares_submitted":0,"paused":false,
"boards":[{"name":"cpu-2x50%","model":"CPU Miner","serial":"cpu-2x50%",
"fans":[],"temperatures":[],"powers":[],
"threads":[
{"name":"CPU Core 0","hashrate":89974,"is_active":true},
{"name":"CPU Core 1","hashrate":89964,"is_active":true}
]}],
"sources":[{"name":"dummy","difficulty":2328}]}
```
Notes