@@ -4,7 +4,10 @@ use crate::function::{Args, KwArgs, OptionalArg, PyFuncArgs};
4
4
use crate :: obj:: objdict:: PyDictRef ;
5
5
use crate :: obj:: objtuple:: PyTupleRef ;
6
6
use crate :: obj:: objtype:: PyClassRef ;
7
- use crate :: pyobject:: { Either , PyCallable , PyClassImpl , PyObjectRef , PyRef , PyResult , PyValue } ;
7
+ use crate :: pyobject:: {
8
+ Either , IdProtocol , PyCallable , PyClassImpl , PyObjectRef , PyRef , PyResult , PyValue ,
9
+ TypeProtocol ,
10
+ } ;
8
11
use crate :: vm:: VirtualMachine ;
9
12
10
13
use parking_lot:: {
@@ -16,18 +19,20 @@ use std::io::Write;
16
19
use std:: time:: Duration ;
17
20
use std:: { fmt, thread} ;
18
21
22
+ // PY_TIMEOUT_MAX is a value in microseconds
19
23
#[ cfg( not( target_os = "windows" ) ) ]
20
- const PY_TIMEOUT_MAX : isize = std:: isize:: MAX ;
24
+ const PY_TIMEOUT_MAX : isize = std:: isize:: MAX / 1_000 ;
21
25
22
26
#[ cfg( target_os = "windows" ) ]
23
- const PY_TIMEOUT_MAX : isize = 0xffffffff * 1_000_000 ;
27
+ const PY_TIMEOUT_MAX : isize = 0xffffffff * 1_000 ;
24
28
25
- const TIMEOUT_MAX : f64 = ( PY_TIMEOUT_MAX / 1_000_000_000 ) as f64 ;
29
+ // this is a value in seconds
30
+ const TIMEOUT_MAX : f64 = ( PY_TIMEOUT_MAX / 1_000_000 ) as f64 ;
26
31
27
32
#[ derive( FromArgs ) ]
28
33
struct AcquireArgs {
29
34
#[ pyarg( positional_or_keyword, default = "true" ) ]
30
- waitflag : bool ,
35
+ blocking : bool ,
31
36
#[ pyarg( positional_or_keyword, default = "Either::A(-1.0)" ) ]
32
37
timeout : Either < f64 , isize > ,
33
38
}
@@ -39,7 +44,7 @@ macro_rules! acquire_lock_impl {
39
44
Either :: A ( f) => f,
40
45
Either :: B ( i) => i as f64 ,
41
46
} ;
42
- match args. waitflag {
47
+ match args. blocking {
43
48
true if timeout == -1.0 => {
44
49
mu. lock( ) ;
45
50
Ok ( true )
@@ -48,7 +53,16 @@ macro_rules! acquire_lock_impl {
48
53
Err ( vm. new_value_error( "timeout value must be positive" . to_owned( ) ) )
49
54
}
50
55
true => {
51
- // TODO: respect TIMEOUT_MAX here
56
+ // modified from std::time::Duration::from_secs_f64 to avoid a panic.
57
+ // TODO: put this in the Duration::try_from_object impl, maybe?
58
+ let micros = timeout * 1_000_000.0 ;
59
+ let nanos = timeout * 1_000_000_000.0 ;
60
+ if micros > PY_TIMEOUT_MAX as f64 || nanos < 0.0 || !nanos. is_finite( ) {
61
+ return Err ( vm. new_overflow_error(
62
+ "timestamp too large to convert to Rust Duration" . to_owned( ) ,
63
+ ) ) ;
64
+ }
65
+
52
66
Ok ( mu. try_lock_for( Duration :: from_secs_f64( timeout) ) )
53
67
}
54
68
false if timeout != -1.0 => {
@@ -59,6 +73,21 @@ macro_rules! acquire_lock_impl {
59
73
}
60
74
} } ;
61
75
}
76
+ macro_rules! repr_lock_impl {
77
+ ( $zelf: expr) => { {
78
+ let status = if $zelf. mu. is_locked( ) {
79
+ "locked"
80
+ } else {
81
+ "unlocked"
82
+ } ;
83
+ format!(
84
+ "<{} {} object at {}>" ,
85
+ status,
86
+ $zelf. class( ) . name,
87
+ $zelf. get_id( )
88
+ )
89
+ } } ;
90
+ }
62
91
63
92
#[ pyclass( name = "lock" ) ]
64
93
struct PyLock {
@@ -102,6 +131,11 @@ impl PyLock {
102
131
fn locked ( & self ) -> bool {
103
132
self . mu . is_locked ( )
104
133
}
134
+
135
+ #[ pymethod( magic) ]
136
+ fn repr ( zelf : PyRef < Self > ) -> String {
137
+ repr_lock_impl ! ( zelf)
138
+ }
105
139
}
106
140
107
141
type RawRMutex = RawReentrantMutex < RawMutex , RawThreadId > ;
@@ -149,6 +183,11 @@ impl PyRLock {
149
183
fn exit ( & self , _args : PyFuncArgs ) {
150
184
self . release ( )
151
185
}
186
+
187
+ #[ pymethod( magic) ]
188
+ fn repr ( zelf : PyRef < Self > ) -> String {
189
+ repr_lock_impl ! ( zelf)
190
+ }
152
191
}
153
192
154
193
fn thread_get_ident ( ) -> u64 {
@@ -195,12 +234,16 @@ fn thread_start_new_thread(
195
234
}
196
235
SENTINELS . with ( |sents| {
197
236
for lock in sents. replace ( Default :: default ( ) ) {
198
- lock. release ( )
237
+ lock. mu . unlock ( )
199
238
}
200
- } )
239
+ } ) ;
240
+ vm. state . thread_count . fetch_sub ( 1 ) ;
201
241
} ) ;
202
- res. map ( |handle| thread_to_id ( & handle. thread ( ) ) )
203
- . map_err ( |err| super :: os:: convert_io_error ( vm, err) )
242
+ res. map ( |handle| {
243
+ vm. state . thread_count . fetch_add ( 1 ) ;
244
+ thread_to_id ( & handle. thread ( ) )
245
+ } )
246
+ . map_err ( |err| super :: os:: convert_io_error ( vm, err) )
204
247
}
205
248
206
249
thread_local ! ( static SENTINELS : RefCell <Vec <PyLockRef >> = RefCell :: default ( ) ) ;
@@ -217,6 +260,10 @@ fn thread_stack_size(size: OptionalArg<usize>, vm: &VirtualMachine) -> usize {
217
260
vm. state . stacksize . swap ( size)
218
261
}
219
262
263
+ fn thread_count ( vm : & VirtualMachine ) -> usize {
264
+ vm. state . thread_count . load ( )
265
+ }
266
+
220
267
pub fn make_module ( vm : & VirtualMachine ) -> PyObjectRef {
221
268
let ctx = & vm. ctx ;
222
269
@@ -228,6 +275,7 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
228
275
"start_new_thread" => ctx. new_function( thread_start_new_thread) ,
229
276
"_set_sentinel" => ctx. new_function( thread_set_sentinel) ,
230
277
"stack_size" => ctx. new_function( thread_stack_size) ,
278
+ "_count" => ctx. new_function( thread_count) ,
231
279
"error" => ctx. exceptions. runtime_error. clone( ) ,
232
280
"TIMEOUT_MAX" => ctx. new_float( TIMEOUT_MAX ) ,
233
281
} )
0 commit comments