Skip to content

Commit c75d701

Browse files
authored
Merge pull request #48 from hnez/error-handling
watched_tasks: improve error handling in spawned tasks and threads
2 parents c06a7b4 + 79f241b commit c75d701

File tree

4 files changed

+50
-80
lines changed

4 files changed

+50
-80
lines changed

src/adc/iio/hardware.rs

Lines changed: 15 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::path::Path;
2222
use std::sync::atomic::{AtomicU16, AtomicU64, Ordering};
2323
use std::time::{Duration, Instant};
2424

25-
use anyhow::{anyhow, Context, Error, Result};
25+
use anyhow::{anyhow, Context, Result};
2626
use async_std::channel::bounded;
2727
use async_std::sync::Arc;
2828

@@ -275,40 +275,27 @@ impl IioThread {
275275
// setup was sucessful.
276276
// This is why we create Self inside the thread and send it back
277277
// to the calling thread via a queue.
278-
let (thread_res_tx, thread_res_rx) = bounded(1);
278+
let (thread_tx, thread_rx) = bounded(1);
279279

280280
// Spawn a high priority thread that updates the atomic values in `thread`.
281281
wtb.spawn_thread(thread_name, move || {
282-
let adc_setup_res = Self::adc_setup(
282+
let (channels, mut buf) = Self::adc_setup(
283283
adc_name,
284284
trigger_name,
285285
sample_rate,
286286
channel_descs,
287287
buffer_len,
288-
);
289-
let (thread, channels, mut buf) = match adc_setup_res {
290-
Ok((channels, buf)) => {
291-
let thread = Arc::new(Self {
292-
ref_instant: Instant::now(),
293-
timestamp: AtomicU64::new(TIMESTAMP_ERROR),
294-
values: channels.iter().map(|_| AtomicU16::new(0)).collect(),
295-
channel_descs,
296-
});
297-
298-
(thread, channels, buf)
299-
}
300-
Err(e) => {
301-
// Can not fail in practice as the queue is known to be empty
302-
// at this point.
303-
thread_res_tx
304-
.try_send(Err(e))
305-
.expect("Failed to signal ADC setup error due to full queue");
306-
return Ok(());
307-
}
308-
};
288+
)?;
289+
290+
let thread = Arc::new(Self {
291+
ref_instant: Instant::now(),
292+
timestamp: AtomicU64::new(TIMESTAMP_ERROR),
293+
values: channels.iter().map(|_| AtomicU16::new(0)).collect(),
294+
channel_descs,
295+
});
309296

310297
let thread_weak = Arc::downgrade(&thread);
311-
let mut signal_ready = Some((thread, thread_res_tx));
298+
let mut signal_ready = Some((thread, thread_tx));
312299

313300
// Stop running as soon as the last reference to this Arc<IioThread>
314301
// is dropped (e.g. the weak reference can no longer be upgraded).
@@ -318,18 +305,7 @@ impl IioThread {
318305

319306
error!("Failed to refill {} ADC buffer: {}", adc_name, e);
320307

321-
// If the ADC has not yet produced any values we still have the
322-
// queue at hand that signals readiness to the main thread.
323-
// This gives us a chance to return an Err from new().
324-
// If the queue was already used just print an error instead.
325-
if let Some((_, tx)) = signal_ready.take() {
326-
// Can not fail in practice as the queue is only .take()n
327-
// once and thus known to be empty.
328-
tx.try_send(Err(Error::new(e)))
329-
.expect("Failed to signal ADC setup error due to full queue");
330-
}
331-
332-
break;
308+
Err(e)?;
333309
}
334310

335311
let values = channels.iter().map(|ch| {
@@ -356,17 +332,14 @@ impl IioThread {
356332
if let Some((content, tx)) = signal_ready.take() {
357333
// Can not fail in practice as the queue is only .take()n
358334
// once and thus known to be empty.
359-
tx.try_send(Ok(content))
360-
.expect("Failed to signal ADC setup completion due to full queue");
335+
tx.try_send(content)?;
361336
}
362337
}
363338

364339
Ok(())
365340
})?;
366341

367-
let thread = thread_res_rx.recv().await??;
368-
369-
Ok(thread)
342+
Ok(thread_rx.recv().await?)
370343
}
371344

372345
pub async fn new_stm32(

src/digital_io/gpio/demo_mode.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct LineHandle {
2727
}
2828

2929
impl LineHandle {
30-
pub fn set_value(&self, val: u8) -> Result<(), ()> {
30+
pub fn set_value(&self, val: u8) -> Result<()> {
3131
// This does not actually set up any IIO things.
3232
// It is just a hack to let adc/iio/demo_mode.rs
3333
// communicate with this function so that toggling an output

src/digital_io/gpio/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub struct LineHandle {
3232
}
3333

3434
impl LineHandle {
35-
pub fn set_value(&self, val: u8) -> Result<(), ()> {
35+
pub fn set_value(&self, val: u8) -> Result<()> {
3636
println!("GPIO simulation set {} to {}", self.name, val);
3737
self.val.store(val, Ordering::Relaxed);
3838
Ok(())

src/dut_power.rs

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ mod prio {
5050
use thread_priority::*;
5151

5252
pub fn realtime_priority() -> Result<()> {
53+
let prio = ThreadPriorityValue::try_from(10)
54+
.map_err(|e| anyhow!("Failed to choose realtime priority level 10: {e:?}"))?;
55+
5356
set_thread_priority_and_policy(
5457
thread_native_id(),
55-
ThreadPriority::Crossplatform(ThreadPriorityValue::try_from(10).unwrap()),
58+
ThreadPriority::Crossplatform(prio),
5659
ThreadSchedulePolicy::Realtime(RealtimeThreadSchedulePolicy::Fifo),
5760
)
5861
.map_err(|e| anyhow!("Failed to set up realtime priority {e:?}"))
@@ -260,10 +263,12 @@ fn turn_off_with_reason(
260263
pwr_line: &LineHandle,
261264
discharge_line: &LineHandle,
262265
fail_state: &AtomicU8,
263-
) {
264-
pwr_line.set_value(1 - PWR_LINE_ASSERTED).unwrap();
265-
discharge_line.set_value(DISCHARGE_LINE_ASSERTED).unwrap();
266+
) -> Result<()> {
267+
pwr_line.set_value(1 - PWR_LINE_ASSERTED)?;
268+
discharge_line.set_value(DISCHARGE_LINE_ASSERTED)?;
266269
fail_state.store(reason as u8, Ordering::Relaxed);
270+
271+
Ok(())
267272
}
268273

269274
/// Labgrid has a fixed assumption of how a REST based power port should work.
@@ -333,7 +338,7 @@ impl DutPwrThread {
333338
// as well.
334339
// Use a queue to notify the calling thread if the priority setup
335340
// succeeded.
336-
let (thread_res_tx, mut thread_res_rx) = bounded(1);
341+
let (thread_tx, thread_rx) = bounded(1);
337342

338343
// Spawn a high priority thread that handles the power status
339344
// in a realtimey fashion.
@@ -348,24 +353,20 @@ impl DutPwrThread {
348353
let mut volt_filter = MedianFilter::<4>::new();
349354
let mut curr_filter = MedianFilter::<4>::new();
350355

351-
let (tick_weak, request, state) = match realtime_priority() {
352-
Ok(_) => {
353-
let tick = Arc::new(AtomicU32::new(0));
354-
let tick_weak = Arc::downgrade(&tick);
356+
realtime_priority()?;
355357

356-
let request = Arc::new(AtomicU8::new(OutputRequest::Idle as u8));
357-
let state = Arc::new(AtomicU8::new(OutputState::Off as u8));
358+
let (tick_weak, request, state) = {
359+
let tick = Arc::new(AtomicU32::new(0));
360+
let tick_weak = Arc::downgrade(&tick);
358361

359-
thread_res_tx
360-
.try_send(Ok((tick, request.clone(), state.clone())))
361-
.unwrap();
362+
let request = Arc::new(AtomicU8::new(OutputRequest::Idle as u8));
363+
let state = Arc::new(AtomicU8::new(OutputState::Off as u8));
362364

363-
(tick_weak, request, state)
364-
}
365-
Err(e) => {
366-
thread_res_tx.try_send(Err(e)).unwrap();
367-
panic!()
368-
}
365+
thread_tx
366+
.try_send((tick, request.clone(), state.clone()))
367+
.expect("Queue that should be empty wasn't");
368+
369+
(tick_weak, request, state)
369370
};
370371

371372
// The grace period contains the number of loop iterations until
@@ -406,7 +407,7 @@ impl DutPwrThread {
406407
&pwr_line,
407408
&discharge_line,
408409
&state,
409-
);
410+
)?;
410411
} else {
411412
// We have a fresh ADC value. Signal "everything is well"
412413
// to the watchdog task.
@@ -463,7 +464,7 @@ impl DutPwrThread {
463464
&pwr_line,
464465
&discharge_line,
465466
&state,
466-
);
467+
)?;
467468

468469
continue;
469470
}
@@ -474,7 +475,7 @@ impl DutPwrThread {
474475
&pwr_line,
475476
&discharge_line,
476477
&state,
477-
);
478+
)?;
478479

479480
continue;
480481
}
@@ -485,7 +486,7 @@ impl DutPwrThread {
485486
&pwr_line,
486487
&discharge_line,
487488
&state,
488-
);
489+
)?;
489490

490491
continue;
491492
}
@@ -496,34 +497,30 @@ impl DutPwrThread {
496497
match req {
497498
OutputRequest::Idle => {}
498499
OutputRequest::On => {
499-
discharge_line
500-
.set_value(1 - DISCHARGE_LINE_ASSERTED)
501-
.unwrap();
502-
pwr_line.set_value(PWR_LINE_ASSERTED).unwrap();
500+
discharge_line.set_value(1 - DISCHARGE_LINE_ASSERTED)?;
501+
pwr_line.set_value(PWR_LINE_ASSERTED)?;
503502
state.store(OutputState::On as u8, Ordering::Relaxed);
504503
}
505504
OutputRequest::Off => {
506-
discharge_line.set_value(DISCHARGE_LINE_ASSERTED).unwrap();
507-
pwr_line.set_value(1 - PWR_LINE_ASSERTED).unwrap();
505+
discharge_line.set_value(DISCHARGE_LINE_ASSERTED)?;
506+
pwr_line.set_value(1 - PWR_LINE_ASSERTED)?;
508507
state.store(OutputState::Off as u8, Ordering::Relaxed);
509508
}
510509
OutputRequest::OffFloating => {
511-
discharge_line
512-
.set_value(1 - DISCHARGE_LINE_ASSERTED)
513-
.unwrap();
514-
pwr_line.set_value(1 - PWR_LINE_ASSERTED).unwrap();
510+
discharge_line.set_value(1 - DISCHARGE_LINE_ASSERTED)?;
511+
pwr_line.set_value(1 - PWR_LINE_ASSERTED)?;
515512
state.store(OutputState::OffFloating as u8, Ordering::Relaxed);
516513
}
517514
}
518515
}
519516

520517
// Make sure to enter fail safe mode before leaving the thread
521-
turn_off_with_reason(OutputState::Off, &pwr_line, &discharge_line, &state);
518+
turn_off_with_reason(OutputState::Off, &pwr_line, &discharge_line, &state)?;
522519

523520
Ok(())
524521
})?;
525522

526-
let (tick, request, state) = thread_res_rx.next().await.unwrap()?;
523+
let (tick, request, state) = thread_rx.recv().await?;
527524

528525
// The request and state topic use the same external path, this way one
529526
// can e.g. publish "On" to the topic and be sure that the output is

0 commit comments

Comments
 (0)