Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  sql.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::{in_shutdown, Interrupted, Interruptee};
use rusqlite::{Connection, InterruptHandle};
use std::fmt;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    Arc,
};

/// Interrupt operations that use SQL
///
/// Typical usage of this type:
///   - Components typically create a wrapper class around an `rusqlite::Connection`
///     (`PlacesConnection`, `LoginStore`, etc.)
///   - The wrapper stores an `Arc<SqlInterruptHandle>`
///   - The wrapper has a method that clones and returns that `Arc`.  This allows passing the interrupt
///     handle to a different thread in order to interrupt a particular operation.
///   - The wrapper calls `begin_interrupt_scope()` at the start of each operation.  The code that
///     performs the operation periodically calls `err_if_interrupted()`.
///   - Finally, the wrapper class implements `AsRef<SqlInterruptHandle>` and calls
///     `register_interrupt()`.  This causes all operations to be interrupted when we enter
///     shutdown mode.
pub struct SqlInterruptHandle {
    db_handle: InterruptHandle,
    // Counter that we increment on each interrupt() call.
    // We use Ordering::Relaxed to read/write to this variable.  This is safe because we're
    // basically using it as a flag and don't need stronger synchronization guarantees.
    interrupt_counter: Arc<AtomicUsize>,
}

impl SqlInterruptHandle {
    #[inline]
    pub fn new(conn: &Connection) -> Self {
        Self {
            db_handle: conn.get_interrupt_handle(),
            interrupt_counter: Arc::new(AtomicUsize::new(0)),
        }
    }

    /// Begin an interrupt scope that will be interrupted by this handle
    ///
    /// Returns Err(Interrupted) if we're in shutdown mode
    #[inline]
    pub fn begin_interrupt_scope(&self) -> Result<SqlInterruptScope, Interrupted> {
        if in_shutdown() {
            Err(Interrupted)
        } else {
            Ok(SqlInterruptScope::new(Arc::clone(&self.interrupt_counter)))
        }
    }

    /// Interrupt all interrupt scopes created by this handle
    #[inline]
    pub fn interrupt(&self) {
        self.interrupt_counter.fetch_add(1, Ordering::Relaxed);
        self.db_handle.interrupt();
    }
}

impl fmt::Debug for SqlInterruptHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SqlInterruptHandle")
            .field(
                "interrupt_counter",
                &self.interrupt_counter.load(Ordering::Relaxed),
            )
            .finish()
    }
}

/// Check if an operation has been interrupted
///
/// This is used by the rust code to check if an operation should fail because it was interrupted.
/// It handles the case where we get interrupted outside of an SQL query.
#[derive(Debug)]
pub struct SqlInterruptScope {
    start_value: usize,
    interrupt_counter: Arc<AtomicUsize>,
}

impl SqlInterruptScope {
    fn new(interrupt_counter: Arc<AtomicUsize>) -> Self {
        let start_value = interrupt_counter.load(Ordering::Relaxed);
        Self {
            start_value,
            interrupt_counter,
        }
    }

    // Create an `SqlInterruptScope` that's never interrupted.
    //
    // This should only be used for testing purposes.
    pub fn dummy() -> Self {
        Self::new(Arc::new(AtomicUsize::new(0)))
    }

    /// Check if scope has been interrupted
    #[inline]
    pub fn was_interrupted(&self) -> bool {
        self.interrupt_counter.load(Ordering::Relaxed) != self.start_value
    }

    /// Return Err(Interrupted) if we were interrupted
    #[inline]
    pub fn err_if_interrupted(&self) -> Result<(), Interrupted> {
        if self.was_interrupted() {
            Err(Interrupted)
        } else {
            Ok(())
        }
    }
}

impl Interruptee for SqlInterruptScope {
    #[inline]
    fn was_interrupted(&self) -> bool {
        self.was_interrupted()
    }
}

// Needed to allow Weak<SqlInterruptHandle> to be passed to `interrupt::register_interrupt`
impl AsRef<SqlInterruptHandle> for SqlInterruptHandle {
    fn as_ref(&self) -> &SqlInterruptHandle {
        self
    }
}

[ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge