1
- /// Implementation of the _thread module, currently noop implementation as RustPython doesn't yet
2
- /// support threading
1
+ /// Implementation of the _thread module
3
2
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 } ;
5
5
use crate :: vm:: VirtualMachine ;
6
6
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
+
7
14
#[ cfg( not( target_os = "windows" ) ) ]
8
15
const PY_TIMEOUT_MAX : isize = std:: isize:: MAX ;
9
16
@@ -12,49 +19,149 @@ const PY_TIMEOUT_MAX: isize = 0xffffffff * 1_000_000;
12
19
13
20
const TIMEOUT_MAX : f64 = ( PY_TIMEOUT_MAX / 1_000_000_000 ) as f64 ;
14
21
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 ,
17
28
}
18
29
19
- fn rlock_release ( _zelf : PyObjectRef ) { }
30
+ #[ pyclass( name = "lock" ) ]
31
+ struct PyLock {
32
+ mu : RawMutex ,
33
+ }
20
34
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
+ }
24
39
}
25
40
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
+ }
35
45
}
36
46
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
+ }
39
85
}
40
86
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 }
44
157
}
45
158
46
159
pub fn make_module ( vm : & VirtualMachine ) -> PyObjectRef {
47
160
let ctx = & vm. ctx ;
48
161
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
-
56
162
py_module ! ( vm, "_thread" , {
57
- "RLock" => rlock_type,
163
+ "RLock" => PyRLock :: make_class( ctx) ,
164
+ "LockType" => PyLock :: make_class( ctx) ,
58
165
"get_ident" => ctx. new_function( get_ident) ,
59
166
"allocate_lock" => ctx. new_function( allocate_lock) ,
60
167
"TIMEOUT_MAX" => ctx. new_float( TIMEOUT_MAX ) ,
0 commit comments