rwlocker 3.1__tar.gz → 3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rwlocker
3
- Version: 3.1
3
+ Version: 3.2
4
4
  Summary: Advanced, High-Performance, and State-Machine Based Synchronous/Asynchronous Read-Write Locks.
5
5
  Author-email: TahsinCr <TahsinCrs@gmail.com>
6
6
  License: MIT
@@ -486,13 +486,3 @@ Email: TahsinCrs@gmail.com
486
486
  [linkedin-url]: https://linkedin.com/in/TahsinCr
487
487
 
488
488
  [x-url]: https://twitter.com/TahsinCrs
489
-
490
-
491
-
492
- <!-- File URL -->
493
-
494
- [lang-tr-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_tr.md
495
-
496
- [lang-en-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README.md
497
-
498
- [lang-ru-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_ru.md
@@ -457,13 +457,3 @@ Email: TahsinCrs@gmail.com
457
457
  [linkedin-url]: https://linkedin.com/in/TahsinCr
458
458
 
459
459
  [x-url]: https://twitter.com/TahsinCrs
460
-
461
-
462
-
463
- <!-- File URL -->
464
-
465
- [lang-tr-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_tr.md
466
-
467
- [lang-en-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README.md
468
-
469
- [lang-ru-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_ru.md
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rwlocker"
7
- version = "3.1"
7
+ version = "3.2"
8
8
  description = "Advanced, High-Performance, and State-Machine Based Synchronous/Asynchronous Read-Write Locks."
9
9
  readme = {file = "README-pypi.md", content-type = "text/markdown"}
10
10
  license = {text = "MIT"}
@@ -64,4 +64,4 @@ Basic Example:
64
64
  from .thread_rwlock import *
65
65
  from .async_rwlock import *
66
66
 
67
- __version__ = '3.1'
67
+ __version__ = '3.2'
@@ -37,10 +37,9 @@ from collections import deque
37
37
  from abc import ABC, abstractmethod
38
38
  from typing import Callable, Protocol, Optional
39
39
  from types import TracebackType
40
+ import weakref
40
41
 
41
- from . import mixins
42
-
43
- __version__ = '3.1'
42
+ __version__ = '3.2'
44
43
  __all__ = (
45
44
  'AsyncLockable', 'AsyncLockDowngradable', 'AsyncRWLockBase', 'AsyncRWLockWithProxyBase',
46
45
  'AsyncRWLockProxy', 'AsyncRWLockReaderProxy', 'AsyncRWLockWriterProxy',
@@ -280,7 +279,7 @@ class AsyncRWLockWriterProxy(AsyncRWLockProxy):
280
279
  )
281
280
  self._read_lock = rwlock.read
282
281
  self._downgrade_core = rwlock._downgrade_core
283
- self._downgraded_tasks:set[asyncio.Task] = set()
282
+ self._downgraded_tasks:weakref.WeakSet[asyncio.Task] = weakref.WeakSet()
284
283
 
285
284
  def downgrade(self):
286
285
  """
@@ -311,7 +310,7 @@ class AsyncRWLockWriterProxy(AsyncRWLockProxy):
311
310
  self._release_core()
312
311
 
313
312
  # Read and Write Lock
314
- class AsyncRWLockWrite(mixins._RWLockWriteMixin, AsyncRWLockWithProxyBase):
313
+ class AsyncRWLockWrite(AsyncRWLockWithProxyBase):
315
314
  """
316
315
  Write-preferring Asynchronous Read-Write Lock.
317
316
 
@@ -347,8 +346,55 @@ class AsyncRWLockWrite(mixins._RWLockWriteMixin, AsyncRWLockWithProxyBase):
347
346
  super().__init__()
348
347
  self._writer_active = False
349
348
  self._readers_active = 0
349
+
350
+ def _can_read(self) -> bool:
351
+ return not self._writer_active and self.write.those_waiting == 0
352
+
353
+ def _acquire_read_core(self):
354
+ self._readers_active += 1
355
+
356
+ def _release_read_core(self):
357
+ if self._readers_active == 0:
358
+ raise RuntimeError("Unacquired read lock")
359
+ self._readers_active -= 1
360
+ if self._readers_active == 0:
361
+ self.write.condition.notify()
362
+
363
+ def _on_reader_abort(self):
364
+ pass
365
+
366
+ def _can_write(self) -> bool:
367
+ return not self._writer_active and self._readers_active == 0
368
+
369
+ def _acquire_write_core(self):
370
+ self._writer_active = True
371
+
372
+ def _release_write_core(self):
373
+ if not self._writer_active:
374
+ raise RuntimeError("Unacquired write lock")
375
+ self._writer_active = False
376
+ self._on_writer_abort()
377
+
378
+ def _downgrade_core(self):
379
+ if not self._writer_active:
380
+ raise RuntimeError("Cannot downgrade unlocked lock")
381
+ self._writer_active = False
382
+ self._readers_active += 1
383
+ self.read.condition.notify_all()
384
+
385
+ def _on_writer_abort(self):
386
+ if self.write.those_waiting > 0:
387
+ self.write.condition.notify()
388
+ else:
389
+ self.read.condition.notify_all()
390
+
391
+ def _is_write_locked(self) -> bool:
392
+ return self._writer_active
393
+
394
+ def _is_read_locked(self) -> bool:
395
+ return self._readers_active > 0
350
396
 
351
- class AsyncRWLockWriteReentrantWriter(mixins._RWLockWriteReentrantMixin, AsyncRWLockWrite):
397
+ class AsyncRWLockWriteReentrantWriter(AsyncRWLockWrite):
352
398
  """
353
399
  Write-preferring Asynchronous Read-Write Lock with Task-Reentrancy.
354
400
 
@@ -400,7 +446,42 @@ class AsyncRWLockWriteReentrantWriter(mixins._RWLockWriteReentrantMixin, AsyncRW
400
446
  def _clear_current_writer(self) -> None:
401
447
  self._writer_id = None
402
448
 
403
- class AsyncRWLockRead(mixins._RWLockReadMixin, AsyncRWLockWithProxyBase):
449
+ def _can_read(self) -> bool:
450
+ if self._writer_id is not None:
451
+ return self._is_current_writer()
452
+ return self.write.those_waiting == 0
453
+
454
+ def _can_write(self) -> bool:
455
+ if self._writer_id is not None:
456
+ return self._is_current_writer()
457
+ return self._readers_active == 0
458
+
459
+ def _acquire_write_core(self):
460
+ self._set_current_writer()
461
+ self._write_count += 1
462
+
463
+ def _release_write_core(self):
464
+ if not self._is_current_writer():
465
+ raise RuntimeError("Permission denied")
466
+ self._write_count -= 1
467
+ if self._write_count == 0:
468
+ self._clear_current_writer()
469
+ self._on_writer_abort()
470
+
471
+ def _downgrade_core(self):
472
+ if not self._is_current_writer():
473
+ raise RuntimeError("Permission denied")
474
+ if self._write_count > 1:
475
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
476
+ self._clear_current_writer()
477
+ self._write_count = 0
478
+ self._readers_active += 1
479
+ self.read.condition.notify_all()
480
+
481
+ def _is_write_locked(self) -> bool:
482
+ return self._writer_id is not None
483
+
484
+ class AsyncRWLockRead(AsyncRWLockWithProxyBase):
404
485
  """
405
486
  Read-preferring Asynchronous Read-Write Lock.
406
487
 
@@ -436,8 +517,56 @@ class AsyncRWLockRead(mixins._RWLockReadMixin, AsyncRWLockWithProxyBase):
436
517
  super().__init__()
437
518
  self._writer_active = False
438
519
  self._readers_active = 0
520
+
521
+ def _can_read(self) -> bool:
522
+ return not self._writer_active
523
+
524
+ def _acquire_read_core(self):
525
+ self._readers_active += 1
526
+
527
+ def _release_read_core(self):
528
+ if self._readers_active == 0:
529
+ raise RuntimeError("Unacquired read lock")
530
+ self._readers_active -= 1
531
+ if self._readers_active == 0:
532
+ self.write.condition.notify()
533
+
534
+ def _on_reader_abort(self):
535
+ if self.read.those_waiting == 0 and self._readers_active == 0:
536
+ self.write.condition.notify()
537
+
538
+ def _can_write(self) -> bool:
539
+ return not self._writer_active and self._readers_active == 0 and self.read.those_waiting == 0
540
+
541
+ def _acquire_write_core(self):
542
+ self._writer_active = True
543
+
544
+ def _release_write_core(self):
545
+ if not self._writer_active:
546
+ raise RuntimeError("Unacquired write lock")
547
+ self._writer_active = False
548
+ self._on_writer_abort()
549
+
550
+ def _downgrade_core(self):
551
+ if not self._writer_active:
552
+ raise RuntimeError("Cannot downgrade unlocked lock")
553
+ self._writer_active = False
554
+ self._readers_active += 1
555
+ self.read.condition.notify_all()
556
+
557
+ def _on_writer_abort(self):
558
+ if self.read.those_waiting > 0:
559
+ self.read.condition.notify_all()
560
+ else:
561
+ self.write.condition.notify()
562
+
563
+ def _is_write_locked(self) -> bool:
564
+ return self._writer_active
565
+
566
+ def _is_read_locked(self) -> bool:
567
+ return self._readers_active > 0
439
568
 
440
- class AsyncRWLockReadReentrantWriter(mixins._RWLockReadReentrantMixin, AsyncRWLockRead):
569
+ class AsyncRWLockReadReentrantWriter(AsyncRWLockRead):
441
570
  """
442
571
  Read-preferring Asynchronous Read-Write Lock with Task-Reentrancy.
443
572
 
@@ -487,7 +616,42 @@ class AsyncRWLockReadReentrantWriter(mixins._RWLockReadReentrantMixin, AsyncRWLo
487
616
  def _clear_current_writer(self) -> None:
488
617
  self._writer_id = None
489
618
 
490
- class AsyncRWLockFair(mixins._RWLockFairMixin, AsyncRWLockWithProxyBase):
619
+ def _can_read(self) -> bool:
620
+ if self._writer_id is not None:
621
+ return self._is_current_writer()
622
+ return True
623
+
624
+ def _can_write(self) -> bool:
625
+ if self._writer_id is not None:
626
+ return self._is_current_writer()
627
+ return self._readers_active == 0 and self.read.those_waiting == 0
628
+
629
+ def _acquire_write_core(self):
630
+ self._set_current_writer()
631
+ self._write_count += 1
632
+
633
+ def _release_write_core(self):
634
+ if not self._is_current_writer():
635
+ raise RuntimeError("Permission denied")
636
+ self._write_count -= 1
637
+ if self._write_count == 0:
638
+ self._clear_current_writer()
639
+ self._on_writer_abort()
640
+
641
+ def _downgrade_core(self):
642
+ if not self._is_current_writer():
643
+ raise RuntimeError("Permission denied")
644
+ if self._write_count > 1:
645
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
646
+ self._clear_current_writer()
647
+ self._write_count = 0
648
+ self._readers_active += 1
649
+ self.read.condition.notify_all()
650
+
651
+ def _is_write_locked(self) -> bool:
652
+ return self._writer_id is not None
653
+
654
+ class AsyncRWLockFair(AsyncRWLockWithProxyBase):
491
655
  """
492
656
  Fair (First-In-First-Out) Asynchronous Read-Write Lock.
493
657
 
@@ -524,8 +688,66 @@ class AsyncRWLockFair(mixins._RWLockFairMixin, AsyncRWLockWithProxyBase):
524
688
  self._writer_active = False
525
689
  self._readers_turn = False
526
690
  self._readers_active = 0
691
+
692
+ def _can_read(self) -> bool:
693
+ if self._writer_active:
694
+ return False
695
+ return self._readers_turn or self.write.those_waiting == 0
696
+
697
+ def _acquire_read_core(self):
698
+ self._readers_active += 1
699
+
700
+ def _release_read_core(self):
701
+ if self._readers_active == 0:
702
+ raise RuntimeError("Unacquired read lock")
703
+ self._readers_active -= 1
704
+ if self._readers_active == 0:
705
+ self._readers_turn = False
706
+ self.write.condition.notify()
707
+
708
+ def _on_reader_abort(self):
709
+ if self._readers_turn and self.read.those_waiting == 0 and self._readers_active == 0:
710
+ self._readers_turn = False
711
+ self.write.condition.notify()
712
+
713
+ def _can_write(self) -> bool:
714
+ if self._writer_active or self._readers_active > 0:
715
+ return False
716
+ if self._readers_turn and self.read.those_waiting > 0:
717
+ return False
718
+ return True
719
+
720
+ def _acquire_write_core(self):
721
+ self._writer_active = True
722
+
723
+ def _release_write_core(self):
724
+ if not self._writer_active:
725
+ raise RuntimeError("Unacquired write lock")
726
+ self._writer_active = False
727
+ self._on_writer_abort()
728
+
729
+ def _downgrade_core(self):
730
+ if not self._writer_active:
731
+ raise RuntimeError("Cannot downgrade unlocked lock")
732
+ self._writer_active = False
733
+ self._readers_active += 1
734
+ self._readers_turn = True
735
+ self.read.condition.notify_all()
736
+
737
+ def _on_writer_abort(self):
738
+ if self.read.those_waiting > 0:
739
+ self._readers_turn = True
740
+ self.read.condition.notify_all()
741
+ else:
742
+ self.write.condition.notify()
743
+
744
+ def _is_write_locked(self) -> bool:
745
+ return self._writer_active
746
+
747
+ def _is_read_locked(self) -> bool:
748
+ return self._readers_active > 0
527
749
 
528
- class AsyncRWLockFairReentrantWriter(mixins._RWLockFairReentrantMixin, AsyncRWLockFair):
750
+ class AsyncRWLockFairReentrantWriter(AsyncRWLockFair):
529
751
  """
530
752
  Fair (First-In-First-Out) Asynchronous Read-Write Lock with Task-Reentrancy.
531
753
 
@@ -575,6 +797,50 @@ class AsyncRWLockFairReentrantWriter(mixins._RWLockFairReentrantMixin, AsyncRWLo
575
797
  def _clear_current_writer(self) -> None:
576
798
  self._writer_id = None
577
799
 
800
+ def _can_read(self) -> bool:
801
+ if self._writer_id is not None:
802
+ if self._is_current_writer():
803
+ return True
804
+ return False
805
+ return self._readers_turn or self.write.those_waiting == 0
806
+
807
+ def _can_write(self) -> bool:
808
+ if self._writer_id is not None:
809
+ if self._is_current_writer():
810
+ return True
811
+ return False
812
+ if self._readers_active > 0:
813
+ return False
814
+ if self._readers_turn and self.read.those_waiting > 0:
815
+ return False
816
+ return True
817
+
818
+ def _acquire_write_core(self):
819
+ self._set_current_writer()
820
+ self._write_count += 1
821
+
822
+ def _release_write_core(self):
823
+ if not self._is_current_writer():
824
+ raise RuntimeError("Permission denied")
825
+ self._write_count -= 1
826
+ if self._write_count == 0:
827
+ self._clear_current_writer()
828
+ self._on_writer_abort()
829
+
830
+ def _downgrade_core(self):
831
+ if not self._is_current_writer():
832
+ raise RuntimeError("Permission denied")
833
+ if self._write_count > 1:
834
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
835
+ self._clear_current_writer()
836
+ self._write_count = 0
837
+ self._readers_active += 1
838
+ self._readers_turn = True
839
+ self.read.condition.notify_all()
840
+
841
+ def _is_write_locked(self) -> bool:
842
+ return self._writer_id is not None
843
+
578
844
  # Standart Lock Adapter
579
845
  class AsyncLock(AsyncRWLockBase):
580
846
  """
@@ -632,7 +898,7 @@ class AsyncRWConditionBase(ABC):
632
898
  """
633
899
  __slots__ = ('_lock', 'read', 'write')
634
900
 
635
- def __init__(self, lock: Optional[AsyncRWLockBase] = None):
901
+ def __init__(self, lock: Optional[AsyncLockable] = None):
636
902
  self._lock = AsyncLock() if lock is None else lock
637
903
  self.write: AsyncConditionLockable = asyncio.Condition(self._lock)
638
904
  self.read: AsyncConditionLockable = self.write
@@ -934,7 +1200,7 @@ class AsyncRWConditionWriterProxy(AsyncRWConditionProxy):
934
1200
  self._lock_proxy.downgrade()
935
1201
 
936
1202
  # Read Write Lock For Condition
937
- class AsyncRWCondition(mixins._RWConditionMixin, AsyncRWConditionWithProxyBase):
1203
+ class AsyncRWCondition(AsyncRWConditionWithProxyBase):
938
1204
  """
939
1205
  Standard implementation of an Asynchronous Read-Write Condition variable.
940
1206
 
@@ -977,6 +1243,12 @@ class AsyncRWCondition(mixins._RWConditionMixin, AsyncRWConditionWithProxyBase):
977
1243
  self._waiters: deque[asyncio.Future] = deque()
978
1244
  super().__init__(lock)
979
1245
 
1246
+ def _is_owned_read(self) -> bool:
1247
+ return self._lock._is_read_locked()
1248
+
1249
+ def _is_owned_write(self) -> bool:
1250
+ return self._lock._is_write_locked()
1251
+
980
1252
  def _add_waiter(self) -> asyncio.Future:
981
1253
  loop = asyncio.get_running_loop()
982
1254
  waiter = loop.create_future()
@@ -1039,7 +1311,7 @@ class AsyncCondition(AsyncRWConditionBase):
1039
1311
  ```
1040
1312
  """
1041
1313
  __slots__ = ()
1042
- def __init__(self, lock: Optional[AsyncRWLockBase] = None):
1314
+ def __init__(self, lock: Optional[AsyncLockable] = None):
1043
1315
  if lock is not None and isinstance(lock, AsyncRWLockWithProxyBase):
1044
1316
  raise TypeError("Standard 'AsyncCondition' adapters cannot be used with proxy-based RWLocks. Use 'AsyncRWCondition' instead.")
1045
1317
  super().__init__(lock)
@@ -37,9 +37,7 @@ from abc import ABC, abstractmethod
37
37
  from typing import Callable, Protocol, Optional
38
38
  from types import TracebackType
39
39
 
40
- from . import mixins
41
-
42
- __version__ = '3.1'
40
+ __version__ = '3.2'
43
41
  __all__ = (
44
42
  'Lockable', 'LockDowngradable', 'RWLockBase', 'RWLockWithProxyBase',
45
43
  'RWLockProxy', 'RWLockReaderProxy', 'RWLockWriterProxy',
@@ -306,7 +304,7 @@ class RWLockWriterProxy(RWLockProxy):
306
304
  Proxy specifically handling Writer logic, extending capabilities with
307
305
  atomic state degradation (downgrading).
308
306
  """
309
- __slots__ = ('_read_lock', '_downgrade_core', '_downgraded_threads')
307
+ __slots__ = ('_read_lock', '_downgrade_core', '_downgraded_thread_id')
310
308
  def __init__(self, rwlock: RWLockWithProxyBase):
311
309
  super().__init__(
312
310
  lock=rwlock._lock,
@@ -318,7 +316,7 @@ class RWLockWriterProxy(RWLockProxy):
318
316
  )
319
317
  self._read_lock = rwlock.read
320
318
  self._downgrade_core = rwlock._downgrade_core
321
- self._downgraded_threads:set[int] = set()
319
+ self._downgraded_thread_id: Optional[int] = None
322
320
 
323
321
  def downgrade(self):
324
322
  """
@@ -332,7 +330,7 @@ class RWLockWriterProxy(RWLockProxy):
332
330
  """
333
331
  with self._lock:
334
332
  self._downgrade_core()
335
- self._downgraded_threads.add(threading.get_ident())
333
+ self._downgraded_thread_id = threading.get_ident()
336
334
 
337
335
  def release(self):
338
336
  """
@@ -341,17 +339,14 @@ class RWLockWriterProxy(RWLockProxy):
341
339
  and routes the release to the reader core to prevent deadlocks.
342
340
  """
343
341
  with self._lock:
344
- if self._downgraded_threads:
345
- try:
346
- self._downgraded_threads.remove(threading.get_ident())
347
- self._read_lock._release_core()
348
- return
349
- except KeyError:
350
- pass
342
+ if self._downgraded_thread_id == threading.get_ident():
343
+ self._downgraded_thread_id = None
344
+ self._read_lock._release_core()
345
+ return
351
346
  self._release_core()
352
347
 
353
348
  # Read and Write Lock
354
- class RWLockWrite(mixins._RWLockWriteMixin, RWLockWithProxyBase):
349
+ class RWLockWrite(RWLockWithProxyBase):
355
350
  """
356
351
  Write-preferring Read-Write Lock.
357
352
 
@@ -387,8 +382,55 @@ class RWLockWrite(mixins._RWLockWriteMixin, RWLockWithProxyBase):
387
382
  super().__init__(lock)
388
383
  self._writer_active = False
389
384
  self._readers_active = 0
385
+
386
+ def _can_read(self) -> bool:
387
+ return not self._writer_active and self.write.those_waiting == 0
388
+
389
+ def _acquire_read_core(self):
390
+ self._readers_active += 1
391
+
392
+ def _release_read_core(self):
393
+ if self._readers_active == 0:
394
+ raise RuntimeError("Unacquired read lock")
395
+ self._readers_active -= 1
396
+ if self._readers_active == 0:
397
+ self.write.condition.notify()
398
+
399
+ def _on_reader_abort(self):
400
+ pass
401
+
402
+ def _can_write(self) -> bool:
403
+ return not self._writer_active and self._readers_active == 0
390
404
 
391
- class RWLockWriteReentrantWriter(mixins._RWLockWriteReentrantMixin, RWLockWrite):
405
+ def _acquire_write_core(self):
406
+ self._writer_active = True
407
+
408
+ def _release_write_core(self):
409
+ if not self._writer_active:
410
+ raise RuntimeError("Unacquired write lock")
411
+ self._writer_active = False
412
+ self._on_writer_abort()
413
+
414
+ def _downgrade_core(self):
415
+ if not self._writer_active:
416
+ raise RuntimeError("Cannot downgrade unlocked lock")
417
+ self._writer_active = False
418
+ self._readers_active += 1
419
+ self.read.condition.notify_all()
420
+
421
+ def _on_writer_abort(self):
422
+ if self.write.those_waiting > 0:
423
+ self.write.condition.notify()
424
+ else:
425
+ self.read.condition.notify_all()
426
+
427
+ def _is_write_locked(self) -> bool:
428
+ return self._writer_active
429
+
430
+ def _is_read_locked(self) -> bool:
431
+ return self._readers_active > 0
432
+
433
+ class RWLockWriteReentrantWriter(RWLockWrite):
392
434
  """
393
435
  Write-preferring Read-Write Lock with Write-Reentrancy support.
394
436
 
@@ -440,7 +482,42 @@ class RWLockWriteReentrantWriter(mixins._RWLockWriteReentrantMixin, RWLockWrite)
440
482
  def _clear_current_writer(self) -> None:
441
483
  self._writer_id = None
442
484
 
443
- class RWLockRead(mixins._RWLockReadMixin, RWLockWithProxyBase):
485
+ def _can_read(self) -> bool:
486
+ if self._writer_id is not None:
487
+ return self._is_current_writer()
488
+ return self.write.those_waiting == 0
489
+
490
+ def _can_write(self) -> bool:
491
+ if self._writer_id is not None:
492
+ return self._is_current_writer()
493
+ return self._readers_active == 0
494
+
495
+ def _acquire_write_core(self):
496
+ self._set_current_writer()
497
+ self._write_count += 1
498
+
499
+ def _release_write_core(self):
500
+ if not self._is_current_writer():
501
+ raise RuntimeError("Permission denied")
502
+ self._write_count -= 1
503
+ if self._write_count == 0:
504
+ self._clear_current_writer()
505
+ self._on_writer_abort()
506
+
507
+ def _downgrade_core(self):
508
+ if not self._is_current_writer():
509
+ raise RuntimeError("Permission denied")
510
+ if self._write_count > 1:
511
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
512
+ self._clear_current_writer()
513
+ self._write_count = 0
514
+ self._readers_active += 1
515
+ self.read.condition.notify_all()
516
+
517
+ def _is_write_locked(self) -> bool:
518
+ return self._writer_id is not None
519
+
520
+ class RWLockRead(RWLockWithProxyBase):
444
521
  """
445
522
  Read-preferring Read-Write Lock.
446
523
 
@@ -478,7 +555,55 @@ class RWLockRead(mixins._RWLockReadMixin, RWLockWithProxyBase):
478
555
  self._writer_active = False
479
556
  self._readers_active = 0
480
557
 
481
- class RWLockReadReentrantWriter(mixins._RWLockReadReentrantMixin, RWLockRead):
558
+ def _can_read(self) -> bool:
559
+ return not self._writer_active
560
+
561
+ def _acquire_read_core(self):
562
+ self._readers_active += 1
563
+
564
+ def _release_read_core(self):
565
+ if self._readers_active == 0:
566
+ raise RuntimeError("Unacquired read lock")
567
+ self._readers_active -= 1
568
+ if self._readers_active == 0:
569
+ self.write.condition.notify()
570
+
571
+ def _on_reader_abort(self):
572
+ if self.read.those_waiting == 0 and self._readers_active == 0:
573
+ self.write.condition.notify()
574
+
575
+ def _can_write(self) -> bool:
576
+ return not self._writer_active and self._readers_active == 0 and self.read.those_waiting == 0
577
+
578
+ def _acquire_write_core(self):
579
+ self._writer_active = True
580
+
581
+ def _release_write_core(self):
582
+ if not self._writer_active:
583
+ raise RuntimeError("Unacquired write lock")
584
+ self._writer_active = False
585
+ self._on_writer_abort()
586
+
587
+ def _downgrade_core(self):
588
+ if not self._writer_active:
589
+ raise RuntimeError("Cannot downgrade unlocked lock")
590
+ self._writer_active = False
591
+ self._readers_active += 1
592
+ self.read.condition.notify_all()
593
+
594
+ def _on_writer_abort(self):
595
+ if self.read.those_waiting > 0:
596
+ self.read.condition.notify_all()
597
+ else:
598
+ self.write.condition.notify()
599
+
600
+ def _is_write_locked(self) -> bool:
601
+ return self._writer_active
602
+
603
+ def _is_read_locked(self) -> bool:
604
+ return self._readers_active > 0
605
+
606
+ class RWLockReadReentrantWriter(RWLockRead):
482
607
  """
483
608
  Read-preferring Read-Write Lock with Write-Reentrancy support.
484
609
 
@@ -527,7 +652,42 @@ class RWLockReadReentrantWriter(mixins._RWLockReadReentrantMixin, RWLockRead):
527
652
  def _clear_current_writer(self) -> None:
528
653
  self._writer_id = None
529
654
 
530
- class RWLockFair(mixins._RWLockFairMixin, RWLockWithProxyBase):
655
+ def _can_read(self) -> bool:
656
+ if self._writer_id is not None:
657
+ return self._is_current_writer()
658
+ return True
659
+
660
+ def _can_write(self) -> bool:
661
+ if self._writer_id is not None:
662
+ return self._is_current_writer()
663
+ return self._readers_active == 0 and self.read.those_waiting == 0
664
+
665
+ def _acquire_write_core(self):
666
+ self._set_current_writer()
667
+ self._write_count += 1
668
+
669
+ def _release_write_core(self):
670
+ if not self._is_current_writer():
671
+ raise RuntimeError("Permission denied")
672
+ self._write_count -= 1
673
+ if self._write_count == 0:
674
+ self._clear_current_writer()
675
+ self._on_writer_abort()
676
+
677
+ def _downgrade_core(self):
678
+ if not self._is_current_writer():
679
+ raise RuntimeError("Permission denied")
680
+ if self._write_count > 1:
681
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
682
+ self._clear_current_writer()
683
+ self._write_count = 0
684
+ self._readers_active += 1
685
+ self.read.condition.notify_all()
686
+
687
+ def _is_write_locked(self) -> bool:
688
+ return self._writer_id is not None
689
+
690
+ class RWLockFair(RWLockWithProxyBase):
531
691
  """
532
692
  Fair (First-In-First-Out) Read-Write Lock.
533
693
 
@@ -564,8 +724,66 @@ class RWLockFair(mixins._RWLockFairMixin, RWLockWithProxyBase):
564
724
  self._writer_active = False
565
725
  self._readers_turn = False
566
726
  self._readers_active = 0
727
+
728
+ def _can_read(self) -> bool:
729
+ if self._writer_active:
730
+ return False
731
+ return self._readers_turn or self.write.those_waiting == 0
732
+
733
+ def _acquire_read_core(self):
734
+ self._readers_active += 1
735
+
736
+ def _release_read_core(self):
737
+ if self._readers_active == 0:
738
+ raise RuntimeError("Unacquired read lock")
739
+ self._readers_active -= 1
740
+ if self._readers_active == 0:
741
+ self._readers_turn = False
742
+ self.write.condition.notify()
743
+
744
+ def _on_reader_abort(self):
745
+ if self._readers_turn and self.read.those_waiting == 0 and self._readers_active == 0:
746
+ self._readers_turn = False
747
+ self.write.condition.notify()
748
+
749
+ def _can_write(self) -> bool:
750
+ if self._writer_active or self._readers_active > 0:
751
+ return False
752
+ if self._readers_turn and self.read.those_waiting > 0:
753
+ return False
754
+ return True
755
+
756
+ def _acquire_write_core(self):
757
+ self._writer_active = True
758
+
759
+ def _release_write_core(self):
760
+ if not self._writer_active:
761
+ raise RuntimeError("Unacquired write lock")
762
+ self._writer_active = False
763
+ self._on_writer_abort()
764
+
765
+ def _downgrade_core(self):
766
+ if not self._writer_active:
767
+ raise RuntimeError("Cannot downgrade unlocked lock")
768
+ self._writer_active = False
769
+ self._readers_active += 1
770
+ self._readers_turn = True
771
+ self.read.condition.notify_all()
772
+
773
+ def _on_writer_abort(self):
774
+ if self.read.those_waiting > 0:
775
+ self._readers_turn = True
776
+ self.read.condition.notify_all()
777
+ else:
778
+ self.write.condition.notify()
779
+
780
+ def _is_write_locked(self) -> bool:
781
+ return self._writer_active
782
+
783
+ def _is_read_locked(self) -> bool:
784
+ return self._readers_active > 0
567
785
 
568
- class RWLockFairReentrantWriter(mixins._RWLockFairReentrantMixin, RWLockFair):
786
+ class RWLockFairReentrantWriter(RWLockFair):
569
787
  """
570
788
  Fair (First-In-First-Out) Read-Write Lock with Write-Reentrancy support.
571
789
 
@@ -615,6 +833,50 @@ class RWLockFairReentrantWriter(mixins._RWLockFairReentrantMixin, RWLockFair):
615
833
  def _clear_current_writer(self) -> None:
616
834
  self._writer_id = None
617
835
 
836
+ def _can_read(self) -> bool:
837
+ if self._writer_id is not None:
838
+ if self._is_current_writer():
839
+ return True
840
+ return False
841
+ return self._readers_turn or self.write.those_waiting == 0
842
+
843
+ def _can_write(self) -> bool:
844
+ if self._writer_id is not None:
845
+ if self._is_current_writer():
846
+ return True
847
+ return False
848
+ if self._readers_active > 0:
849
+ return False
850
+ if self._readers_turn and self.read.those_waiting > 0:
851
+ return False
852
+ return True
853
+
854
+ def _acquire_write_core(self):
855
+ self._set_current_writer()
856
+ self._write_count += 1
857
+
858
+ def _release_write_core(self):
859
+ if not self._is_current_writer():
860
+ raise RuntimeError("Permission denied")
861
+ self._write_count -= 1
862
+ if self._write_count == 0:
863
+ self._clear_current_writer()
864
+ self._on_writer_abort()
865
+
866
+ def _downgrade_core(self):
867
+ if not self._is_current_writer():
868
+ raise RuntimeError("Permission denied")
869
+ if self._write_count > 1:
870
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
871
+ self._clear_current_writer()
872
+ self._write_count = 0
873
+ self._readers_active += 1
874
+ self._readers_turn = True
875
+ self.read.condition.notify_all()
876
+
877
+ def _is_write_locked(self) -> bool:
878
+ return self._writer_id is not None
879
+
618
880
  # Standart Lock Adapter
619
881
  class Lock(RWLockBase):
620
882
  """
@@ -671,7 +933,7 @@ class RWConditionBase(ABC):
671
933
  `.write` proxy. Designed to act as a common type hint and contract.
672
934
  """
673
935
  __slots__ = ('_lock', 'read', 'write')
674
- def __init__(self, lock: Optional[RWLockBase] = None):
936
+ def __init__(self, lock: Optional[Lockable] = None):
675
937
  self._lock = Lock() if lock is None else lock
676
938
  self.write: ConditionLockable = threading.Condition(self._lock)
677
939
  self.read: ConditionLockable = self.write
@@ -966,7 +1228,7 @@ class RWConditionWriterProxy(RWConditionProxy):
966
1228
  self._lock_proxy.downgrade()
967
1229
 
968
1230
  # Read Write Lock For Condition
969
- class RWCondition(mixins._RWConditionMixin, RWConditionWithProxyBase):
1231
+ class RWCondition(RWConditionWithProxyBase):
970
1232
  """
971
1233
  Standard implementation of a Read-Write Condition variable.
972
1234
 
@@ -1008,6 +1270,12 @@ class RWCondition(mixins._RWConditionMixin, RWConditionWithProxyBase):
1008
1270
  self._internal_lock = threading.Lock()
1009
1271
  super().__init__(lock)
1010
1272
 
1273
+ def _is_owned_read(self) -> bool:
1274
+ return self._lock._is_read_locked()
1275
+
1276
+ def _is_owned_write(self) -> bool:
1277
+ return self._lock._is_write_locked()
1278
+
1011
1279
  def _add_waiter(self) -> threading.Lock:
1012
1280
  waiter = threading.Lock()
1013
1281
  waiter.acquire()
@@ -1080,7 +1348,7 @@ class Condition(RWConditionBase):
1080
1348
  ```
1081
1349
  """
1082
1350
  __slots__ = ()
1083
- def __init__(self, lock: Optional[RWLockBase] = None):
1351
+ def __init__(self, lock: Optional[Lockable] = None):
1084
1352
  if lock is not None and isinstance(lock, RWLockWithProxyBase):
1085
1353
  raise TypeError("Standard 'Condition' adapters cannot be used with proxy-based RWLocks. Use 'RWCondition' instead.")
1086
1354
  super().__init__(lock)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rwlocker
3
- Version: 3.1
3
+ Version: 3.2
4
4
  Summary: Advanced, High-Performance, and State-Machine Based Synchronous/Asynchronous Read-Write Locks.
5
5
  Author-email: TahsinCr <TahsinCrs@gmail.com>
6
6
  License: MIT
@@ -486,13 +486,3 @@ Email: TahsinCrs@gmail.com
486
486
  [linkedin-url]: https://linkedin.com/in/TahsinCr
487
487
 
488
488
  [x-url]: https://twitter.com/TahsinCrs
489
-
490
-
491
-
492
- <!-- File URL -->
493
-
494
- [lang-tr-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_tr.md
495
-
496
- [lang-en-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README.md
497
-
498
- [lang-ru-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_ru.md
@@ -4,7 +4,6 @@ README-pypi.md
4
4
  pyproject.toml
5
5
  rwlocker/__init__.py
6
6
  rwlocker/async_rwlock.py
7
- rwlocker/mixins.py
8
7
  rwlocker/thread_rwlock.py
9
8
  rwlocker.egg-info/PKG-INFO
10
9
  rwlocker.egg-info/SOURCES.txt
@@ -1,305 +0,0 @@
1
- """
2
- Shared state-machine Mixins for both Thread and Async RWLock implementations.
3
- These mixins encapsulate the core scheduling algorithms (Write-Pref, Read-Pref, Fair)
4
- to eliminate code duplication across synchronous and asynchronous domains.
5
- """
6
-
7
- class _RWLockWriteMixin:
8
- __slots__ = ()
9
-
10
- def _can_read(self) -> bool:
11
- return not self._writer_active and self.write.those_waiting == 0
12
-
13
- def _acquire_read_core(self):
14
- self._readers_active += 1
15
-
16
- def _release_read_core(self):
17
- if self._readers_active == 0:
18
- raise RuntimeError("Unacquired read lock")
19
- self._readers_active -= 1
20
- if self._readers_active == 0:
21
- self.write.condition.notify()
22
-
23
- def _on_reader_abort(self):
24
- pass
25
-
26
- def _can_write(self) -> bool:
27
- return not self._writer_active and self._readers_active == 0
28
-
29
- def _acquire_write_core(self):
30
- self._writer_active = True
31
-
32
- def _release_write_core(self):
33
- if not self._writer_active:
34
- raise RuntimeError("Unacquired write lock")
35
- self._writer_active = False
36
- self._on_writer_abort()
37
-
38
- def _downgrade_core(self):
39
- if not self._writer_active:
40
- raise RuntimeError("Cannot downgrade unlocked lock")
41
- self._writer_active = False
42
- self._readers_active += 1
43
- self.read.condition.notify_all()
44
-
45
- def _on_writer_abort(self):
46
- if self.write.those_waiting > 0:
47
- self.write.condition.notify()
48
- else:
49
- self.read.condition.notify_all()
50
-
51
- def _is_write_locked(self) -> bool:
52
- return self._writer_active
53
-
54
- def _is_read_locked(self) -> bool:
55
- return self._readers_active > 0
56
-
57
-
58
- class _RWLockWriteReentrantMixin:
59
- __slots__ = ()
60
-
61
- def _can_read(self) -> bool:
62
- if self._writer_id is not None:
63
- return self._is_current_writer()
64
- return self.write.those_waiting == 0
65
-
66
- def _can_write(self) -> bool:
67
- if self._writer_id is not None:
68
- return self._is_current_writer()
69
- return self._readers_active == 0
70
-
71
- def _acquire_write_core(self):
72
- self._set_current_writer()
73
- self._write_count += 1
74
-
75
- def _release_write_core(self):
76
- if not self._is_current_writer():
77
- raise RuntimeError("Permission denied")
78
- self._write_count -= 1
79
- if self._write_count == 0:
80
- self._clear_current_writer()
81
- self._on_writer_abort()
82
-
83
- def _downgrade_core(self):
84
- if not self._is_current_writer():
85
- raise RuntimeError("Permission denied")
86
- if self._write_count > 1:
87
- raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
88
- self._clear_current_writer()
89
- self._write_count = 0
90
- self._readers_active += 1
91
- self.read.condition.notify_all()
92
-
93
- def _is_write_locked(self) -> bool:
94
- return self._writer_id is not None
95
-
96
-
97
- class _RWLockReadMixin:
98
- __slots__ = ()
99
-
100
- def _can_read(self) -> bool:
101
- return not self._writer_active
102
-
103
- def _acquire_read_core(self):
104
- self._readers_active += 1
105
-
106
- def _release_read_core(self):
107
- if self._readers_active == 0:
108
- raise RuntimeError("Unacquired read lock")
109
- self._readers_active -= 1
110
- if self._readers_active == 0:
111
- self.write.condition.notify()
112
-
113
- def _on_reader_abort(self):
114
- if self.read.those_waiting == 0 and self._readers_active == 0:
115
- self.write.condition.notify()
116
-
117
- def _can_write(self) -> bool:
118
- return not self._writer_active and self._readers_active == 0 and self.read.those_waiting == 0
119
-
120
- def _acquire_write_core(self):
121
- self._writer_active = True
122
-
123
- def _release_write_core(self):
124
- if not self._writer_active:
125
- raise RuntimeError("Unacquired write lock")
126
- self._writer_active = False
127
- self._on_writer_abort()
128
-
129
- def _downgrade_core(self):
130
- if not self._writer_active:
131
- raise RuntimeError("Cannot downgrade unlocked lock")
132
- self._writer_active = False
133
- self._readers_active += 1
134
- self.read.condition.notify_all()
135
-
136
- def _on_writer_abort(self):
137
- if self.read.those_waiting > 0:
138
- self.read.condition.notify_all()
139
- else:
140
- self.write.condition.notify()
141
-
142
- def _is_write_locked(self) -> bool:
143
- return self._writer_active
144
-
145
- def _is_read_locked(self) -> bool:
146
- return self._readers_active > 0
147
-
148
-
149
- class _RWLockReadReentrantMixin:
150
- __slots__ = ()
151
-
152
- def _can_read(self) -> bool:
153
- if self._writer_id is not None:
154
- return self._is_current_writer()
155
- return True
156
-
157
- def _can_write(self) -> bool:
158
- if self._writer_id is not None:
159
- return self._is_current_writer()
160
- return self._readers_active == 0 and self.read.those_waiting == 0
161
-
162
- def _acquire_write_core(self):
163
- self._set_current_writer()
164
- self._write_count += 1
165
-
166
- def _release_write_core(self):
167
- if not self._is_current_writer():
168
- raise RuntimeError("Permission denied")
169
- self._write_count -= 1
170
- if self._write_count == 0:
171
- self._clear_current_writer()
172
- self._on_writer_abort()
173
-
174
- def _downgrade_core(self):
175
- if not self._is_current_writer():
176
- raise RuntimeError("Permission denied")
177
- if self._write_count > 1:
178
- raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
179
- self._clear_current_writer()
180
- self._write_count = 0
181
- self._readers_active += 1
182
- self.read.condition.notify_all()
183
-
184
- def _is_write_locked(self) -> bool:
185
- return self._writer_id is not None
186
-
187
-
188
- class _RWLockFairMixin:
189
- __slots__ = ()
190
-
191
- def _can_read(self) -> bool:
192
- if self._writer_active:
193
- return False
194
- return self._readers_turn or self.write.those_waiting == 0
195
-
196
- def _acquire_read_core(self):
197
- self._readers_active += 1
198
-
199
- def _release_read_core(self):
200
- if self._readers_active == 0:
201
- raise RuntimeError("Unacquired read lock")
202
- self._readers_active -= 1
203
- if self._readers_active == 0:
204
- self._readers_turn = False
205
- self.write.condition.notify()
206
-
207
- def _on_reader_abort(self):
208
- if self._readers_turn and self.read.those_waiting == 0 and self._readers_active == 0:
209
- self._readers_turn = False
210
- self.write.condition.notify()
211
-
212
- def _can_write(self) -> bool:
213
- if self._writer_active or self._readers_active > 0:
214
- return False
215
- if self._readers_turn and self.read.those_waiting > 0:
216
- return False
217
- return True
218
-
219
- def _acquire_write_core(self):
220
- self._writer_active = True
221
-
222
- def _release_write_core(self):
223
- if not self._writer_active:
224
- raise RuntimeError("Unacquired write lock")
225
- self._writer_active = False
226
- self._on_writer_abort()
227
-
228
- def _downgrade_core(self):
229
- if not self._writer_active:
230
- raise RuntimeError("Cannot downgrade unlocked lock")
231
- self._writer_active = False
232
- self._readers_active += 1
233
- self._readers_turn = True
234
- self.read.condition.notify_all()
235
-
236
- def _on_writer_abort(self):
237
- if self.read.those_waiting > 0:
238
- self._readers_turn = True
239
- self.read.condition.notify_all()
240
- else:
241
- self.write.condition.notify()
242
-
243
- def _is_write_locked(self) -> bool:
244
- return self._writer_active
245
-
246
- def _is_read_locked(self) -> bool:
247
- return self._readers_active > 0
248
-
249
-
250
- class _RWLockFairReentrantMixin:
251
- __slots__ = ()
252
-
253
- def _can_read(self) -> bool:
254
- if self._writer_id is not None:
255
- if self._is_current_writer():
256
- return True
257
- return False
258
- return self._readers_turn or self.write.those_waiting == 0
259
-
260
- def _can_write(self) -> bool:
261
- if self._writer_id is not None:
262
- if self._is_current_writer():
263
- return True
264
- return False
265
- if self._readers_active > 0:
266
- return False
267
- if self._readers_turn and self.read.those_waiting > 0:
268
- return False
269
- return True
270
-
271
- def _acquire_write_core(self):
272
- self._set_current_writer()
273
- self._write_count += 1
274
-
275
- def _release_write_core(self):
276
- if not self._is_current_writer():
277
- raise RuntimeError("Permission denied")
278
- self._write_count -= 1
279
- if self._write_count == 0:
280
- self._clear_current_writer()
281
- self._on_writer_abort()
282
-
283
- def _downgrade_core(self):
284
- if not self._is_current_writer():
285
- raise RuntimeError("Permission denied")
286
- if self._write_count > 1:
287
- raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
288
- self._clear_current_writer()
289
- self._write_count = 0
290
- self._readers_active += 1
291
- self._readers_turn = True
292
- self.read.condition.notify_all()
293
-
294
- def _is_write_locked(self) -> bool:
295
- return self._writer_id is not None
296
-
297
-
298
- class _RWConditionMixin:
299
- __slots__ = ()
300
-
301
- def _is_owned_read(self) -> bool:
302
- return self._lock._is_read_locked()
303
-
304
- def _is_owned_write(self) -> bool:
305
- return self._lock._is_write_locked()
File without changes
File without changes
File without changes