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
45 changes: 45 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions Lib/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@
'warn', 'warning', 'getLogRecordFactory', 'setLogRecordFactory',
'lastResort', 'raiseExceptions']

# TODO: import threading
import _thread
import threading

__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
Expand Down Expand Up @@ -208,7 +207,7 @@ def _checkLevel(level):
#the lock would already have been acquired - so we need an RLock.
#The same argument applies to Loggers and Manager.loggerDict.
#
_lock = _thread.RLock()
_lock = threading.RLock()

def _acquireLock():
"""
Expand Down Expand Up @@ -844,7 +843,7 @@ def createLock(self):
"""
Acquire a thread lock for serializing access to the underlying I/O.
"""
self.lock = _thread.RLock()
self.lock = threading.RLock()
_register_at_fork_acquire_release(self)

def acquire(self):
Expand Down
1 change: 1 addition & 0 deletions vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ smallbox = "0.8"
bstr = "0.2.12"
crossbeam-utils = "0.7"
generational-arena = "0.2"
parking_lot = { git = "https://github.com/Amanieu/parking_lot" }

## unicode stuff
unicode_names2 = "0.4"
Expand Down
169 changes: 138 additions & 31 deletions vm/src/stdlib/thread.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
/// Implementation of the _thread module, currently noop implementation as RustPython doesn't yet
/// support threading
/// Implementation of the _thread module
use crate::function::PyFuncArgs;
use crate::pyobject::{PyObjectRef, PyResult};
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;

use parking_lot::{
lock_api::{RawMutex as RawMutexT, RawMutexTimed, RawReentrantMutex},
RawMutex, RawThreadId,
};
use std::fmt;
use std::time::Duration;

#[cfg(not(target_os = "windows"))]
const PY_TIMEOUT_MAX: isize = std::isize::MAX;

Expand All @@ -12,49 +19,149 @@ const PY_TIMEOUT_MAX: isize = 0xffffffff * 1_000_000;

const TIMEOUT_MAX: f64 = (PY_TIMEOUT_MAX / 1_000_000_000) as f64;

fn rlock_acquire(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult {
Ok(vm.get_none())
#[derive(FromArgs)]
struct AcquireArgs {
#[pyarg(positional_or_keyword, default = "true")]
waitflag: bool,
#[pyarg(positional_or_keyword, default = "-1.0")]
timeout: f64,
}

fn rlock_release(_zelf: PyObjectRef) {}
#[pyclass(name = "lock")]
struct PyLock {
mu: RawMutex,
}

fn rlock_enter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(vm, args, required = [(instance, None)]);
Ok(instance.clone())
impl PyValue for PyLock {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("_thread", "LockType")
}
}

fn rlock_exit(
// The context manager protocol requires these, but we don't use them
_instance: PyObjectRef,
_exception_type: PyObjectRef,
_exception_value: PyObjectRef,
_traceback: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
Ok(vm.get_none())
impl fmt::Debug for PyLock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("PyLock")
}
}

fn get_ident(_vm: &VirtualMachine) -> u32 {
1
#[pyimpl]
impl PyLock {
#[pymethod]
#[pymethod(name = "acquire_lock")]
#[pymethod(name = "__enter__")]
#[allow(clippy::float_cmp, clippy::match_bool)]
fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult<bool> {
match args.waitflag {
true if args.timeout == -1.0 => {
self.mu.lock();
Ok(true)
}
true if args.timeout < 0.0 => {
Err(vm.new_value_error("timeout value must be positive".to_owned()))
}
true => Ok(self.mu.try_lock_for(Duration::from_secs_f64(args.timeout))),
false if args.timeout != -1.0 => {
Err(vm
.new_value_error("can't specify a timeout for a non-blocking call".to_owned()))
}
false => Ok(self.mu.try_lock()),
}
}
#[pymethod]
#[pymethod(name = "release_lock")]
fn release(&self) {
self.mu.unlock()
}

#[pymethod(magic)]
fn exit(&self, _args: PyFuncArgs) {
self.release()
}

#[pymethod]
fn locked(&self) -> bool {
self.mu.is_locked()
}
}

fn allocate_lock(vm: &VirtualMachine) -> PyResult {
let lock_class = vm.class("_thread", "RLock");
vm.invoke(&lock_class.into_object(), vec![])
type RawRMutex = RawReentrantMutex<RawMutex, RawThreadId>;
#[pyclass(name = "RLock")]
struct PyRLock {
mu: RawRMutex,
}

impl PyValue for PyRLock {
fn class(vm: &VirtualMachine) -> PyClassRef {
vm.class("_thread", "RLock")
}
}

impl fmt::Debug for PyRLock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad("PyRLock")
}
}

#[pyimpl]
impl PyRLock {
#[pyslot]
fn tp_new(cls: PyClassRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
PyRLock {
mu: RawRMutex::INIT,
}
.into_ref_with_type(vm, cls)
}

#[pymethod]
#[pymethod(name = "acquire_lock")]
#[pymethod(name = "__enter__")]
#[allow(clippy::float_cmp, clippy::match_bool)]
fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult<bool> {
match args.waitflag {
true if args.timeout == -1.0 => {
self.mu.lock();
Ok(true)
}
true if args.timeout < 0.0 => {
Err(vm.new_value_error("timeout value must be positive".to_owned()))
}
true => Ok(self.mu.try_lock_for(Duration::from_secs_f64(args.timeout))),
false if args.timeout != -1.0 => {
Err(vm
.new_value_error("can't specify a timeout for a non-blocking call".to_owned()))
}
false => Ok(self.mu.try_lock()),
}
}
#[pymethod]
#[pymethod(name = "release_lock")]
fn release(&self) {
self.mu.unlock()
}

#[pymethod(magic)]
fn exit(&self, _args: PyFuncArgs) {
self.release()
}
}

fn get_ident() -> u64 {
let id = std::thread::current().id();
// TODO: use id.as_u64() once it's stable, until then, ThreadId is just a wrapper
// around NonZeroU64, so this is safe
unsafe { std::mem::transmute(id) }
}

fn allocate_lock() -> PyLock {
PyLock { mu: RawMutex::INIT }
}

pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
let ctx = &vm.ctx;

let rlock_type = py_class!(ctx, "_thread.RLock", ctx.object(), {
"acquire" => ctx.new_method(rlock_acquire),
"release" => ctx.new_method(rlock_release),
"__enter__" => ctx.new_method(rlock_enter),
"__exit__" => ctx.new_method(rlock_exit),
});

py_module!(vm, "_thread", {
"RLock" => rlock_type,
"RLock" => PyRLock::make_class(ctx),
"LockType" => PyLock::make_class(ctx),
"get_ident" => ctx.new_function(get_ident),
"allocate_lock" => ctx.new_function(allocate_lock),
"TIMEOUT_MAX" => ctx.new_float(TIMEOUT_MAX),
Expand Down