@@ -35,7 +35,7 @@ public static void PerformModuleReload(PythonContext/*!*/ context, PythonDiction
35
35
36
36
#region Public API Surface
37
37
38
- public static double TIMEOUT_MAX = 0 ; // TODO: fill this with a proper value
38
+ public static double TIMEOUT_MAX = Math . Floor ( TimeSpan . MaxValue . TotalSeconds ) ;
39
39
40
40
[ System . Diagnostics . CodeAnalysis . SuppressMessage ( "Microsoft.Security" , "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes" ) ]
41
41
public static readonly PythonType LockType = DynamicHelpers . GetPythonTypeFromType ( typeof ( @lock ) ) ;
@@ -138,23 +138,33 @@ public static object _set_sentinel(CodeContext context) {
138
138
139
139
#endregion
140
140
141
+ #nullable enable
142
+
141
143
[ PythonType , PythonHidden ]
142
144
public sealed class @lock {
143
- private AutoResetEvent blockEvent ;
144
- private Thread curHolder ;
145
+ private AutoResetEvent ? blockEvent ;
146
+ private Thread ? curHolder ;
145
147
146
148
public object __enter__ ( ) {
147
- acquire ( true , - 1 ) ;
149
+ acquire ( ) ;
148
150
return this ;
149
151
}
150
152
151
153
public void __exit__ ( CodeContext /*!*/ context , [ NotNone ] params object [ ] args ) {
152
154
release ( context ) ;
153
155
}
154
156
155
- public bool acquire ( bool blocking = true , float timeout = - 1 ) {
157
+ public bool acquire ( bool blocking = true , double timeout = - 1 ) {
158
+ var timespan = Timeout . InfiniteTimeSpan ;
159
+
160
+ if ( timeout != - 1 ) {
161
+ if ( ! blocking ) throw PythonOps . ValueError ( "can't specify a timeout for a non-blocking call" ) ;
162
+ if ( timeout < 0 ) throw PythonOps . ValueError ( "timeout value must be a non-negative number" ) ;
163
+ timespan = TimeSpan . FromSeconds ( timeout ) ;
164
+ }
165
+
156
166
for ( ; ; ) {
157
- if ( Interlocked . CompareExchange < Thread > ( ref curHolder , Thread . CurrentThread , null ) == null ) {
167
+ if ( Interlocked . CompareExchange ( ref curHolder , Thread . CurrentThread , null ) is null ) {
158
168
return true ;
159
169
}
160
170
if ( ! blocking ) {
@@ -166,16 +176,16 @@ public bool acquire(bool blocking = true, float timeout = -1) {
166
176
CreateBlockEvent ( ) ;
167
177
continue ;
168
178
}
169
- if ( ! blockEvent . WaitOne ( timeout < 0 ? Timeout . InfiniteTimeSpan : TimeSpan . FromSeconds ( timeout ) ) ) {
179
+ if ( ! blockEvent . WaitOne ( timespan ) ) {
170
180
return false ;
171
181
}
172
182
GC . KeepAlive ( this ) ;
173
183
}
174
184
}
175
185
176
186
public void release ( CodeContext /*!*/ context ) {
177
- if ( Interlocked . Exchange < Thread > ( ref curHolder , null ) == null ) {
178
- throw PythonExceptions . CreateThrowable ( ( PythonType ) context . LanguageContext . GetModuleState ( "threaderror" ) , "lock isn't held" , null ) ;
187
+ if ( Interlocked . Exchange ( ref curHolder , null ) is null ) {
188
+ throw PythonOps . RuntimeError ( "release unlocked lock" ) ;
179
189
}
180
190
if ( blockEvent != null ) {
181
191
// if this isn't set yet we race, it's handled in Acquire()
@@ -184,18 +194,135 @@ public void release(CodeContext/*!*/ context) {
184
194
}
185
195
}
186
196
187
- public bool locked ( ) {
188
- return curHolder != null ;
197
+ public bool locked ( )
198
+ => curHolder is not null ;
199
+
200
+ public string __repr__ ( ) {
201
+ if ( curHolder is null ) {
202
+ return $ "<unlocked _thread.lock object at 0x{ IdDispenser . GetId ( this ) : X16} >";
203
+ }
204
+ return $ "<locked _thread.lock object at 0x{ IdDispenser . GetId ( this ) : X16} >";
189
205
}
190
206
191
207
private void CreateBlockEvent ( ) {
192
208
AutoResetEvent are = new AutoResetEvent ( false ) ;
193
- if ( Interlocked . CompareExchange < AutoResetEvent > ( ref blockEvent , are , null ) != null ) {
209
+ if ( Interlocked . CompareExchange ( ref blockEvent , are , null ) is not null ) {
194
210
are . Close ( ) ;
195
211
}
196
212
}
197
213
}
198
214
215
+ [ PythonType ]
216
+ public sealed class RLock {
217
+ private AutoResetEvent ? blockEvent ;
218
+ private Thread ? curHolder ;
219
+ private int count ;
220
+
221
+ public object __enter__ ( ) {
222
+ acquire ( ) ;
223
+ return this ;
224
+ }
225
+
226
+ public void __exit__ ( CodeContext /*!*/ context , [ NotNone ] params object [ ] args ) {
227
+ release ( ) ;
228
+ }
229
+
230
+ public bool acquire ( bool blocking = true , double timeout = - 1 ) {
231
+ var timespan = Timeout . InfiniteTimeSpan ;
232
+
233
+ if ( timeout != - 1 ) {
234
+ if ( ! blocking ) throw PythonOps . ValueError ( "can't specify a timeout for a non-blocking call" ) ;
235
+ if ( timeout < 0 ) throw PythonOps . ValueError ( "timeout value must be a non-negative number" ) ;
236
+ timespan = TimeSpan . FromSeconds ( timeout ) ;
237
+ }
238
+
239
+ var currentThread = Thread . CurrentThread ;
240
+
241
+ for ( ; ; ) {
242
+ var previousThread = Interlocked . CompareExchange ( ref curHolder , currentThread , null ) ;
243
+ if ( previousThread == currentThread ) {
244
+ count ++ ;
245
+ return true ;
246
+ }
247
+ if ( previousThread is null ) {
248
+ count = 1 ;
249
+ return true ;
250
+ }
251
+ if ( ! blocking ) {
252
+ return false ;
253
+ }
254
+ if ( blockEvent is null ) {
255
+ // try again in case someone released us, checked the block
256
+ // event and discovered it was null so they didn't set it.
257
+ CreateBlockEvent ( ) ;
258
+ continue ;
259
+ }
260
+ if ( ! blockEvent . WaitOne ( timespan ) ) {
261
+ return false ;
262
+ }
263
+ GC . KeepAlive ( this ) ;
264
+ }
265
+ }
266
+
267
+ public void release ( ) {
268
+ var currentThread = Thread . CurrentThread ;
269
+
270
+ if ( curHolder != currentThread ) {
271
+ throw PythonOps . RuntimeError ( "cannot release un-acquired lock" ) ;
272
+ }
273
+ if ( -- count > 0 ) {
274
+ return ;
275
+ }
276
+
277
+ if ( Interlocked . Exchange ( ref curHolder , null ) is null ) {
278
+ throw PythonOps . RuntimeError ( "release unlocked lock" ) ;
279
+ }
280
+ if ( blockEvent is not null ) {
281
+ // if this isn't set yet we race, it's handled in acquire()
282
+ blockEvent . Set ( ) ;
283
+ GC . KeepAlive ( this ) ;
284
+ }
285
+ }
286
+
287
+ public string __repr__ ( ) {
288
+ if ( curHolder is null ) {
289
+ return $ "<unlocked _thread.RLock object owner=0 count=0 at 0x{ IdDispenser . GetId ( this ) : X16} >";
290
+ }
291
+ return $ "<locked _thread.RLock object owner={ curHolder ? . ManagedThreadId } count={ count } at 0x{ IdDispenser . GetId ( this ) : X16} >";
292
+ }
293
+
294
+ public void _acquire_restore ( [ NotNone ] PythonTuple state ) {
295
+ acquire ( ) ;
296
+ count = ( int ) state [ 0 ] ! ;
297
+ curHolder = ( Thread ? ) state [ 1 ] ;
298
+ }
299
+
300
+ public PythonTuple _release_save ( ) {
301
+ var count = Interlocked . Exchange ( ref this . count , 0 ) ;
302
+ if ( count == 0 ) {
303
+ throw PythonOps . RuntimeError ( "cannot release un-acquired lock" ) ;
304
+ }
305
+
306
+ // release
307
+ var owner = Interlocked . Exchange ( ref curHolder , null ) ;
308
+ blockEvent ? . Set ( ) ;
309
+
310
+ return PythonTuple . MakeTuple ( count , owner ) ;
311
+ }
312
+
313
+ public bool _is_owned ( )
314
+ => curHolder == Thread . CurrentThread ;
315
+
316
+ private void CreateBlockEvent ( ) {
317
+ AutoResetEvent are = new AutoResetEvent ( false ) ;
318
+ if ( Interlocked . CompareExchange ( ref blockEvent , are , null ) != null ) {
319
+ are . Close ( ) ;
320
+ }
321
+ }
322
+ }
323
+
324
+ #nullable restore
325
+
199
326
#region Internal Implementation details
200
327
201
328
private static Thread CreateThread ( CodeContext /*!*/ context , ThreadStart start ) {
0 commit comments