From 5e8f0fbaf6df0cb919e4b02401cc21d5280bf09c Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 9 Jul 2019 10:44:45 +0000 Subject: Add force_take function to ignore poisoned cache The take and take_blocking functions return a PoisonError if the cache is poisoned, i. e. if a thread panicked while holding the manager. This is a sensible default behaviour, but for example during testing, one might want to ignore the poisoned cache. This patch adds the force_take function that unwraps the PoisonError and returns the cached Manager even if the cache was poisoned. --- CHANGELOG.md | 2 ++ src/lib.rs | 32 +++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06769bd..b779929 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ SPDX-License-Identifier: MIT - Add the `Manager` struct that manages connections to Nitrokey devices. - Remove `connect`, `connect_model`, `Pro::connect` and `Storage::connect`. - Add the `into_manager` function to the `Device` trait. + - Add the `force_take` function that ignores a `PoisonError` when accessing + the manager instance. # v0.3.4 (2019-01-20) - Fix authentication methods that assumed that `char` is signed. diff --git a/src/lib.rs b/src/lib.rs index 4e45877..a4402c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -377,7 +377,8 @@ pub fn take_blocking() -> Result, Error> { /// /// There may only be one [`Manager`][] instance at the same time. If there already is an /// instance, a [`ConcurrentAccessError`][] is returned. If you want a blocking version, use -/// [`take_blocking`][]. +/// [`take_blocking`][]. If you want to access the manager instance even if the cache is poisoned, +/// use [`force_take`][]. /// /// # Errors /// @@ -385,6 +386,7 @@ pub fn take_blocking() -> Result, Error> { /// - [`PoisonError`][] if the lock is poisoned /// /// [`take_blocking`]: fn.take_blocking.html +/// [`force_take`]: fn.force_take.html /// [`ConcurrentAccessError`]: struct.Error.html#variant.ConcurrentAccessError /// [`PoisonError`]: struct.Error.html#variant.PoisonError /// [`Manager`]: struct.Manager.html @@ -392,6 +394,34 @@ pub fn take() -> Result, Error> { MANAGER.try_lock().map_err(Into::into) } +/// Try to take an instance of the connection manager, ignoring a poisoned cache. +/// +/// There may only be one [`Manager`][] instance at the same time. If there already is an +/// instance, a [`ConcurrentAccessError`][] is returned. If you want a blocking version, use +/// [`take_blocking`][]. +/// +/// If a thread has previously panicked while accessing the manager instance, the cache is +/// poisoned. The default implementation, [`take`][], returns a [`PoisonError`][] on subsequent +/// calls. This implementation ignores the poisoned cache and returns the manager instance. +/// +/// # Errors +/// +/// - [`ConcurrentAccessError`][] if the token for the `Manager` instance cannot be locked +/// +/// [`take`]: fn.take.html +/// [`take_blocking`]: fn.take_blocking.html +/// [`ConcurrentAccessError`]: struct.Error.html#variant.ConcurrentAccessError +/// [`Manager`]: struct.Manager.html +pub fn force_take() -> Result, Error> { + match take() { + Ok(guard) => Ok(guard), + Err(err) => match err { + Error::PoisonError(err) => Ok(err.into_inner()), + err => Err(err), + }, + } +} + /// Enables or disables debug output. Calling this method with `true` is equivalent to setting the /// log level to `Debug`; calling it with `false` is equivalent to the log level `Error` (see /// [`set_log_level`][]). -- cgit v1.2.1