Skip to content

Commit 901e766

Browse files
authored
Merge pull request RustPython#1935 from RustPython/coolreader18/_thread-mutexes
Update the _thread module to have actual mutexes
2 parents 970981c + 93f697c commit 901e766

File tree

4 files changed

+187
-35
lines changed

4 files changed

+187
-35
lines changed

Cargo.lock

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/logging/__init__.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737
'warn', 'warning', 'getLogRecordFactory', 'setLogRecordFactory',
3838
'lastResort', 'raiseExceptions']
3939

40-
# TODO: import threading
41-
import _thread
40+
import threading
4241

4342
__author__ = "Vinay Sajip <[email protected]>"
4443
__status__ = "production"
@@ -208,7 +207,7 @@ def _checkLevel(level):
208207
#the lock would already have been acquired - so we need an RLock.
209208
#The same argument applies to Loggers and Manager.loggerDict.
210209
#
211-
_lock = _thread.RLock()
210+
_lock = threading.RLock()
212211

213212
def _acquireLock():
214213
"""
@@ -844,7 +843,7 @@ def createLock(self):
844843
"""
845844
Acquire a thread lock for serializing access to the underlying I/O.
846845
"""
847-
self.lock = _thread.RLock()
846+
self.lock = threading.RLock()
848847
_register_at_fork_acquire_release(self)
849848

850849
def acquire(self):

vm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ smallbox = "0.8"
7171
bstr = "0.2.12"
7272
crossbeam-utils = "0.7"
7373
generational-arena = "0.2"
74+
parking_lot = { git = "https://github.com/Amanieu/parking_lot" }
7475

7576
## unicode stuff
7677
unicode_names2 = "0.4"

vm/src/stdlib/thread.rs

Lines changed: 138 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
/// Implementation of the _thread module, currently noop implementation as RustPython doesn't yet
2-
/// support threading
1+
/// Implementation of the _thread module
32
use crate::function::PyFuncArgs;
4-
use crate::pyobject::{PyObjectRef, PyResult};
3+
use crate::obj::objtype::PyClassRef;
4+
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
55
use crate::vm::VirtualMachine;
66

7+
use parking_lot::{
8+
lock_api::{RawMutex as RawMutexT, RawMutexTimed, RawReentrantMutex},
9+
RawMutex, RawThreadId,
10+
};
11+
use std::fmt;
12+
use std::time::Duration;
13+
714
#[cfg(not(target_os = "windows"))]
815
const PY_TIMEOUT_MAX: isize = std::isize::MAX;
916

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

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

15-
fn rlock_acquire(vm: &VirtualMachine, _args: PyFuncArgs) -> PyResult {
16-
Ok(vm.get_none())
22+
#[derive(FromArgs)]
23+
struct AcquireArgs {
24+
#[pyarg(positional_or_keyword, default = "true")]
25+
waitflag: bool,
26+
#[pyarg(positional_or_keyword, default = "-1.0")]
27+
timeout: f64,
1728
}
1829

19-
fn rlock_release(_zelf: PyObjectRef) {}
30+
#[pyclass(name = "lock")]
31+
struct PyLock {
32+
mu: RawMutex,
33+
}
2034

21-
fn rlock_enter(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
22-
arg_check!(vm, args, required = [(instance, None)]);
23-
Ok(instance.clone())
35+
impl PyValue for PyLock {
36+
fn class(vm: &VirtualMachine) -> PyClassRef {
37+
vm.class("_thread", "LockType")
38+
}
2439
}
2540

26-
fn rlock_exit(
27-
// The context manager protocol requires these, but we don't use them
28-
_instance: PyObjectRef,
29-
_exception_type: PyObjectRef,
30-
_exception_value: PyObjectRef,
31-
_traceback: PyObjectRef,
32-
vm: &VirtualMachine,
33-
) -> PyResult {
34-
Ok(vm.get_none())
41+
impl fmt::Debug for PyLock {
42+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43+
f.pad("PyLock")
44+
}
3545
}
3646

37-
fn get_ident(_vm: &VirtualMachine) -> u32 {
38-
1
47+
#[pyimpl]
48+
impl PyLock {
49+
#[pymethod]
50+
#[pymethod(name = "acquire_lock")]
51+
#[pymethod(name = "__enter__")]
52+
#[allow(clippy::float_cmp, clippy::match_bool)]
53+
fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult<bool> {
54+
match args.waitflag {
55+
true if args.timeout == -1.0 => {
56+
self.mu.lock();
57+
Ok(true)
58+
}
59+
true if args.timeout < 0.0 => {
60+
Err(vm.new_value_error("timeout value must be positive".to_owned()))
61+
}
62+
true => Ok(self.mu.try_lock_for(Duration::from_secs_f64(args.timeout))),
63+
false if args.timeout != -1.0 => {
64+
Err(vm
65+
.new_value_error("can't specify a timeout for a non-blocking call".to_owned()))
66+
}
67+
false => Ok(self.mu.try_lock()),
68+
}
69+
}
70+
#[pymethod]
71+
#[pymethod(name = "release_lock")]
72+
fn release(&self) {
73+
self.mu.unlock()
74+
}
75+
76+
#[pymethod(magic)]
77+
fn exit(&self, _args: PyFuncArgs) {
78+
self.release()
79+
}
80+
81+
#[pymethod]
82+
fn locked(&self) -> bool {
83+
self.mu.is_locked()
84+
}
3985
}
4086

41-
fn allocate_lock(vm: &VirtualMachine) -> PyResult {
42-
let lock_class = vm.class("_thread", "RLock");
43-
vm.invoke(&lock_class.into_object(), vec![])
87+
type RawRMutex = RawReentrantMutex<RawMutex, RawThreadId>;
88+
#[pyclass(name = "RLock")]
89+
struct PyRLock {
90+
mu: RawRMutex,
91+
}
92+
93+
impl PyValue for PyRLock {
94+
fn class(vm: &VirtualMachine) -> PyClassRef {
95+
vm.class("_thread", "RLock")
96+
}
97+
}
98+
99+
impl fmt::Debug for PyRLock {
100+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101+
f.pad("PyRLock")
102+
}
103+
}
104+
105+
#[pyimpl]
106+
impl PyRLock {
107+
#[pyslot]
108+
fn tp_new(cls: PyClassRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
109+
PyRLock {
110+
mu: RawRMutex::INIT,
111+
}
112+
.into_ref_with_type(vm, cls)
113+
}
114+
115+
#[pymethod]
116+
#[pymethod(name = "acquire_lock")]
117+
#[pymethod(name = "__enter__")]
118+
#[allow(clippy::float_cmp, clippy::match_bool)]
119+
fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult<bool> {
120+
match args.waitflag {
121+
true if args.timeout == -1.0 => {
122+
self.mu.lock();
123+
Ok(true)
124+
}
125+
true if args.timeout < 0.0 => {
126+
Err(vm.new_value_error("timeout value must be positive".to_owned()))
127+
}
128+
true => Ok(self.mu.try_lock_for(Duration::from_secs_f64(args.timeout))),
129+
false if args.timeout != -1.0 => {
130+
Err(vm
131+
.new_value_error("can't specify a timeout for a non-blocking call".to_owned()))
132+
}
133+
false => Ok(self.mu.try_lock()),
134+
}
135+
}
136+
#[pymethod]
137+
#[pymethod(name = "release_lock")]
138+
fn release(&self) {
139+
self.mu.unlock()
140+
}
141+
142+
#[pymethod(magic)]
143+
fn exit(&self, _args: PyFuncArgs) {
144+
self.release()
145+
}
146+
}
147+
148+
fn get_ident() -> u64 {
149+
let id = std::thread::current().id();
150+
// TODO: use id.as_u64() once it's stable, until then, ThreadId is just a wrapper
151+
// around NonZeroU64, so this is safe
152+
unsafe { std::mem::transmute(id) }
153+
}
154+
155+
fn allocate_lock() -> PyLock {
156+
PyLock { mu: RawMutex::INIT }
44157
}
45158

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

49-
let rlock_type = py_class!(ctx, "_thread.RLock", ctx.object(), {
50-
"acquire" => ctx.new_method(rlock_acquire),
51-
"release" => ctx.new_method(rlock_release),
52-
"__enter__" => ctx.new_method(rlock_enter),
53-
"__exit__" => ctx.new_method(rlock_exit),
54-
});
55-
56162
py_module!(vm, "_thread", {
57-
"RLock" => rlock_type,
163+
"RLock" => PyRLock::make_class(ctx),
164+
"LockType" => PyLock::make_class(ctx),
58165
"get_ident" => ctx.new_function(get_ident),
59166
"allocate_lock" => ctx.new_function(allocate_lock),
60167
"TIMEOUT_MAX" => ctx.new_float(TIMEOUT_MAX),

0 commit comments

Comments
 (0)