Skip to content

Commit c223708

Browse files
committed
Use atomics instead of channel to return results
1 parent 9ea4690 commit c223708

File tree

2 files changed

+42
-55
lines changed

2 files changed

+42
-55
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ pie
192192
| 1 | [Not Quite Lisp](https://adventofcode.com/2015/day/1) | [Source](src/year2015/day01.rs) | 2 |
193193
| 2 | [I Was Told There Would Be No Math](https://adventofcode.com/2015/day/2) | [Source](src/year2015/day02.rs) | 8 |
194194
| 3 | [Perfectly Spherical Houses in a Vacuum](https://adventofcode.com/2015/day/3) | [Source](src/year2015/day03.rs) | 100 |
195-
| 4 | [The Ideal Stocking Stuffer](https://adventofcode.com/2015/day/4) | [Source](src/year2015/day04.rs) | 82000 |
195+
| 4 | [The Ideal Stocking Stuffer](https://adventofcode.com/2015/day/4) | [Source](src/year2015/day04.rs) | 84000 |
196196
| 5 | [Doesn't He Have Intern-Elves For This?](https://adventofcode.com/2015/day/5) | [Source](src/year2015/day05.rs) | 39 |
197197
| 6 | [Probably a Fire Hazard](https://adventofcode.com/2015/day/6) | [Source](src/year2015/day06.rs) | 5780 |
198198
| 7 | [Some Assembly Required](https://adventofcode.com/2015/day/7) | [Source](src/year2015/day07.rs) | 27 |

src/year2015/day04.rs

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -20,85 +20,72 @@
2020
use crate::util::md5::hash;
2121
use std::sync::atomic::Ordering::Relaxed;
2222
use std::sync::atomic::{AtomicBool, AtomicU32};
23-
use std::sync::mpsc::{channel, Sender};
2423
use std::sync::Arc;
2524
use std::thread;
2625

27-
type Input = (u32, u32);
28-
29-
enum Found {
30-
First(u32),
31-
Second(u32),
26+
#[derive(Clone)]
27+
pub struct Shared {
28+
prefix: String,
29+
done: Arc<AtomicBool>,
30+
counter: Arc<AtomicU32>,
31+
first: Arc<AtomicU32>,
32+
second: Arc<AtomicU32>,
3233
}
3334

34-
pub fn parse(input: &str) -> Input {
35-
let prefix = input.trim().to_string();
36-
let done = Arc::new(AtomicBool::new(false));
37-
let counter = Arc::new(AtomicU32::new(1000));
38-
let (tx, rx) = channel::<Found>();
35+
pub fn parse(input: &str) -> Shared {
36+
let shared = Shared {
37+
prefix: input.trim().to_string(),
38+
done: Arc::new(AtomicBool::new(false)),
39+
counter: Arc::new(AtomicU32::new(1000)),
40+
first: Arc::new(AtomicU32::new(u32::MAX)),
41+
second: Arc::new(AtomicU32::new(u32::MAX)),
42+
};
3943

4044
// Handle the first 999 numbers specially as the number of digits varies.
4145
for n in 1..1000 {
42-
let string = format!("{prefix}{n}");
43-
check_hash(string.as_bytes(), n, &tx);
44-
}
45-
46-
// Use as many cores as possible to parallelize the search.
47-
for _ in 0..thread::available_parallelism().unwrap().get() {
48-
let prefix = prefix.clone();
49-
let done = done.clone();
50-
let counter = counter.clone();
51-
let tx = tx.clone();
52-
thread::spawn(move || worker(&prefix, &done, &counter, &tx));
46+
let string = format!("{}{}", shared.prefix, n);
47+
check_hash(string.as_bytes(), n, &shared);
5348
}
5449

55-
// Explicitly drop the reference to the sender object so that when all search threads finish,
56-
// there will be no remaining references. When this happens `rx.recv` will return
57-
// `Error` and exit the loop below. This ensures we wait to receive results from all threads,
58-
// to handle the edge case where two values could be close together and found out of order.
59-
drop(tx);
50+
// Use as many cores as possible to parallelize the remaining search.
51+
let handles: Vec<_> = (0..thread::available_parallelism().unwrap().get())
52+
.map(|_| {
53+
let shared_clone = shared.clone();
54+
thread::spawn(move || worker(&shared_clone))
55+
})
56+
.collect();
6057

61-
// We could potentially find multiple values, keep only the first occurence of each one.
62-
let mut first = u32::MAX;
63-
let mut second = u32::MAX;
64-
65-
while let Ok(message) = rx.recv() {
66-
match message {
67-
Found::First(value) => {
68-
first = first.min(value);
69-
}
70-
Found::Second(value) => {
71-
second = second.min(value);
72-
done.store(true, Relaxed);
73-
}
74-
}
58+
// Wait for threads to finish
59+
for handle in handles {
60+
let _ = handle.join();
7561
}
7662

77-
(first, second)
63+
shared
7864
}
7965

80-
pub fn part1(input: &Input) -> u32 {
81-
input.0
66+
pub fn part1(input: &Shared) -> u32 {
67+
input.first.load(Relaxed)
8268
}
8369

84-
pub fn part2(input: &Input) -> u32 {
85-
input.1
70+
pub fn part2(input: &Shared) -> u32 {
71+
input.second.load(Relaxed)
8672
}
8773

88-
fn check_hash(buffer: &[u8], n: u32, tx: &Sender<Found>) {
74+
fn check_hash(buffer: &[u8], n: u32, shared: &Shared) {
8975
let (result, ..) = hash(buffer);
9076

9177
if result & 0xffffff00 == 0 {
92-
let _ = tx.send(Found::Second(n));
78+
shared.done.store(true, Relaxed);
79+
shared.second.fetch_min(n, Relaxed);
9380
} else if result & 0xfffff000 == 0 {
94-
let _ = tx.send(Found::First(n));
81+
shared.first.fetch_min(n, Relaxed);
9582
}
9683
}
9784

98-
fn worker(prefix: &str, done: &Arc<AtomicBool>, counter: &Arc<AtomicU32>, tx: &Sender<Found>) {
99-
while !done.load(Relaxed) {
100-
let start = counter.fetch_add(1000, Relaxed);
101-
let string = format!("{prefix}{start}");
85+
fn worker(shared: &Shared) {
86+
while !shared.done.load(Relaxed) {
87+
let offset = shared.counter.fetch_add(1000, Relaxed);
88+
let string = format!("{}{}", shared.prefix, offset);
10289
let size = string.len() - 3;
10390
let mut buffer = string.as_bytes().to_vec();
10491

@@ -107,7 +94,7 @@ fn worker(prefix: &str, done: &Arc<AtomicBool>, counter: &Arc<AtomicU32>, tx: &S
10794
buffer[size] = b'0' + (n / 100) as u8;
10895
buffer[size + 1] = b'0' + ((n / 10) % 10) as u8;
10996
buffer[size + 2] = b'0' + (n % 10) as u8;
110-
check_hash(&buffer, start + n, tx);
97+
check_hash(&buffer, offset + n, shared);
11198
}
11299
}
113100
}

0 commit comments

Comments
 (0)