X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions crates/stdlib/src/_sqlite3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ mod _sqlite3 {
},
convert::IntoObject,
function::{
ArgCallable, ArgIterable, Either, FsPath, FuncArgs, OptionalArg, PyComparisonValue,
PySetterValue,
ArgCallable, ArgIterable, FsPath, FuncArgs, OptionalArg, PyComparisonValue,
PySetterValue, TimeoutSeconds,
},
object::{Traverse, TraverseFn},
protocol::{
Expand Down Expand Up @@ -333,8 +333,8 @@ mod _sqlite3 {
struct ConnectArgs {
#[pyarg(any)]
database: FsPath,
#[pyarg(any, default = Either::A(5.0))]
timeout: Either<f64, i64>,
#[pyarg(any, default = TimeoutSeconds::new(5.0))]
timeout: TimeoutSeconds,
#[pyarg(any, default = 0)]
detect_types: c_int,
#[pyarg(any, default = Some(vm.ctx.empty_str.to_owned()))]
Expand Down Expand Up @@ -991,10 +991,7 @@ mod _sqlite3 {
fn initialize_db(args: &ConnectArgs, vm: &VirtualMachine) -> PyResult<Sqlite> {
let path = args.database.to_cstring(vm)?;
let db = Sqlite::from(SqliteRaw::open(path.as_ptr(), args.uri, vm)?);
let timeout = (match args.timeout {
Either::A(float) => float,
Either::B(int) => int as f64,
} * 1000.0) as c_int;
let timeout = (args.timeout.to_secs_f64() * 1000.0) as c_int;
db.busy_timeout(timeout);
if let Some(isolation_level) = &args.isolation_level {
begin_statement_ptr_from_isolation_level(isolation_level, vm)?;
Expand Down
2 changes: 2 additions & 0 deletions crates/vm/src/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod getset;
mod method;
mod number;
mod protocol;
mod time;

pub use argument::{
ArgumentError, FromArgOptional, FromArgs, FuncArgs, IntoFuncArgs, KwArgs, OptionalArg,
Expand All @@ -23,6 +24,7 @@ pub(super) use getset::{IntoPyGetterFunc, IntoPySetterFunc, PyGetterFunc, PySett
pub use method::{HeapMethodDef, PyMethodDef, PyMethodFlags};
pub use number::{ArgIndex, ArgIntoBool, ArgIntoComplex, ArgIntoFloat, ArgPrimitiveIndex, ArgSize};
pub use protocol::{ArgCallable, ArgIterable, ArgMapping, ArgSequence};
pub use time::TimeoutSeconds;

use crate::{PyObject, PyResult, VirtualMachine, builtins::PyStr, convert::TryFromBorrowedObject};
use builtin::{BorrowedParam, OwnedParam, RefParam};
Expand Down
34 changes: 34 additions & 0 deletions crates/vm/src/function/time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::{PyObjectRef, PyResult, TryFromObject, VirtualMachine};

/// A Python timeout value that accepts both `float` and `int`.
///
/// `TimeoutSeconds` implements `FromArgs` so that a built-in function can accept
/// timeout parameters given as either `float` or `int`, normalizing them to `f64`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TimeoutSeconds {
value: f64,
}

impl TimeoutSeconds {
pub const fn new(secs: f64) -> Self {
Self { value: secs }
}

#[inline]
pub fn to_secs_f64(self) -> f64 {
self.value
}
}

impl TryFromObject for TimeoutSeconds {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let value = match super::Either::<f64, i64>::try_from_object(vm, obj)? {
super::Either::A(f) => f,
super::Either::B(i) => i as f64,
};
if value.is_nan() {
return Err(vm.new_value_error("Invalid value NaN (not a number)".to_owned()));
}
Ok(Self { value })
}
}
23 changes: 8 additions & 15 deletions crates/vm/src/stdlib/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) mod _thread {
builtins::{PyDictRef, PyStr, PyTupleRef, PyType, PyTypeRef, PyUtf8StrRef},
common::wtf8::Wtf8Buf,
frame::FrameRef,
function::{ArgCallable, Either, FuncArgs, KwArgs, OptionalArg, PySetterValue},
function::{ArgCallable, FuncArgs, KwArgs, OptionalArg, PySetterValue, TimeoutSeconds},
types::{Constructor, GetAttr, Representable, SetAttr},
};
use alloc::{
Expand Down Expand Up @@ -65,33 +65,26 @@ pub(crate) mod _thread {
struct AcquireArgs {
#[pyarg(any, default = true)]
blocking: bool,
#[pyarg(any, default = Either::A(-1.0))]
timeout: Either<f64, i64>,
#[pyarg(any, default = TimeoutSeconds::new(-1.0))]
timeout: TimeoutSeconds,
}

macro_rules! acquire_lock_impl {
($mu:expr, $args:expr, $vm:expr) => {{
let (mu, args, vm) = ($mu, $args, $vm);
let timeout = match args.timeout {
Either::A(f) => f,
Either::B(i) => i as f64,
};
let timeout = args.timeout.to_secs_f64();
match args.blocking {
true if timeout == -1.0 => {
vm.allow_threads(|| mu.lock());
Ok(true)
}
true if timeout < 0.0 => {
Err(vm.new_value_error("timeout value must be positive".to_owned()))
Err(vm
.new_value_error("timeout value must be a non-negative number".to_owned()))
}
true => {
// modified from std::time::Duration::from_secs_f64 to avoid a panic.
// TODO: put this in the Duration::try_from_object impl, maybe?
let nanos = timeout * 1_000_000_000.0;
if timeout > TIMEOUT_MAX as f64 || nanos < 0.0 || !nanos.is_finite() {
return Err(vm.new_overflow_error(
"timestamp too large to convert to Rust Duration".to_owned(),
));
if timeout > TIMEOUT_MAX {
return Err(vm.new_overflow_error("timeout value is too large".to_owned()));
}

Ok(vm.allow_threads(|| mu.try_lock_for(Duration::from_secs_f64(timeout))))
Expand Down
Loading
X Tutup