rwlocker 3.0__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.0
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
@@ -112,29 +112,33 @@ If you use the lock objects directly like a standard lock without specifying the
112
112
 
113
113
  ## 📊 Performance and Benchmark Results
114
114
 
115
- `rwlocker` unlocks the system's true potential during Network and Database I/O operations where Python's GIL (Global Interpreter Lock) is released. In aggressive scenario tests conducted with zero OS sleep interference, it overwhelmingly outperformed standard locks.
115
+ The performance results demonstrate `rwlocker`'s true potential during Network and Database I/O operations where Python's GIL (Global Interpreter Lock) is released or context-switched.
116
116
 
117
- **Summary of Performance Outputs:**
117
+ **🖥️ Test Environment:** All tests were executed on an **Intel Core i7-12700H (2.4GHz)** processor running **EndeavourOS (Arch-based Linux)**, using **Python 3.14.3** and the experimental **Free-Threading (3.14.3t)** interpreters.
118
118
 
119
- * **🚀 Read-Heavy Scenario (100 Readers, 2 Writers):**
120
- While standard locks queue readers single-file and choke the system, `rwlocker` allows readers to access the data simultaneously. This achieves **~37x FASTER** speed and throughput in **Threading** and **~30x FASTER** in **Asyncio**.
121
- * **⚖️ Balanced Scenario (50 Readers, 50 Writers):**
122
- Thanks to the Fair state machine, read operations are squeezed in parallel between write queues. It increases performance by **2x** compared to standard locks without creating a system bottleneck.
123
- * **🛡️ Write-Heavy Scenario (2 Readers, 100 Writers):**
124
- Even though write operations inherently cannot be executed concurrently (in parallel), thanks to `rwlocker`'s zero-allocation smart proxy architecture, it runs **7-8% faster** than standard `C`-based locks. Even the O(1) cost "ReentrantWriter" (reentrancy) feature adds almost no overhead to performance.
119
+ **🧪 Methodology:** To ensure absolute precision and zero margin of error, all reader and writer entities (threads or async tasks) are spawned in advance and held at a starting line using a synchronization `Event`. Once the event triggers, they execute simultaneously. The workloads strictly follow an `IOBoundScenario` that enforces a precise `time.sleep(0.001)` or `asyncio.sleep(0.001)` delay to accurately simulate real network/database I/O latency.
125
120
 
126
- **Condition Variable Performance Outputs:**
121
+ ### 1. Read-Write Lock (RWLock) Benchmarks
127
122
 
128
- Standard library Condition structures lock up the CPU during multiple wake-ups due to O(N) scanning. `rwlocker`'s O(1) queue architecture absolutely crushes the standard library at this point.
123
+ Standard locks force threads to queue single-file even if they are only reading data. `rwlocker` unleashes concurrent read access.
129
124
 
130
- * **📣 Massive Broadcast (1 Writer, 100 Readers):**
131
- When a single writer updates the database and wakes up hundreds of waiting readers (`notify_all`); thanks to our O(1) architecture, **~65x FASTER** throughput (Ops/sec) is achieved in **Threading**, and **~70x FASTER** in **Asyncio**. The system is saved from entering a "Cache Stampede".
132
- * **🔀 Balanced Pub/Sub (50 Writers, 50 Readers):**
133
- In mixed waiting and waking scenarios, our Condition locks with the `Write-Pref` strategy ran **~2x FASTER** than the standard library.
134
- * **📉 Write-Heavy Limit (Stress Test - 100 Writers, 2 Readers):**
135
- In this brutal scenario where writers constantly block each other and call `notify()`, C-based standard locks utilize their raw speed advantage. `rwlocker`'s Write-Pref model holds its ground neck-and-neck (1.0x) with the standard lock, while the Fair and Read-Pref models intentionally slow down (0.5x - 0.7x) for the sake of maintaining fairness.
125
+ **Synchronous (Thread) RWLock Performance:**
126
+ In the Read-Heavy scenario, standard C-based locks choke the system, whereas `rwlocker` achieves up to **~35x speedup**. Even in Write-Heavy workloads where parallelism isn't inherently possible, `rwlocker` matches or slightly outperforms the C-baseline thanks to its zero-allocation fast-paths.
136
127
 
137
- *(Note: All lock, adapter, and condition classes have passed a massive suite of **266 different unit tests** covering reentrancy, deadlock, timeout, OS interrupts, O(N) leaks, and cancellation safety scenarios with 0 errors, and this entire test suite was completed in just **8.5 seconds**.)*
128
+ **Asynchronous (Asyncio) RWLock Performance:**
129
+ `rwlocker` achieves **~30x faster throughput** by allowing thousands of reader tasks to await I/O simultaneously without blocking one another.
130
+
131
+ ### 2. Condition Variable (RWCondition) Benchmarks
132
+
133
+ Standard `Condition` variables iterate through all sleeping threads/tasks one by one `O(N)` during a broadcast (`notify_all`), causing massive CPU spikes and "Cache Stampedes". `rwlocker` completely eradicates this with its pure `O(1)` queueing architecture.
134
+
135
+ **Synchronous (Thread) RWCondition Performance:**
136
+ When 100 sleeping readers are awakened simultaneously, `rwlocker` processes them instantly without locking the OS. This architectural leap results in a mind-blowing **~45x speedup** compared to the standard library's `threading.Condition`.
137
+
138
+ **Asynchronous (Asyncio) RWCondition Performance:**
139
+ In event-driven caching systems, waking up hundreds of waiting web requests simultaneously is a major bottleneck. The `AsyncRWCondition` completely bypasses standard asyncio constraints, reaching up to **~45x higher throughput** during massive broadcast scenarios, completely saving the loop from freezing.
140
+
141
+ *(Note: All lock, adapter, and condition classes have passed a massive suite of **315 different unit tests** covering reentrancy, deadlock, timeout, OS interrupts, O(N) leaks, and cancellation safety scenarios with 0 errors, and this entire test suite was completed in just **13.2 seconds**.)*
138
142
 
139
143
 
140
144
  <br/>
@@ -469,7 +473,7 @@ Email: TahsinCrs@gmail.com
469
473
 
470
474
  [issues-url]: https://github.com/TahsinCr/python-rwlocker/issues
471
475
 
472
- [examples-url]: https://github.com/TahsinCr/python-rwlocker/wiki
476
+ [examples-url]: https://github.com/TahsinCr/python-rwlocker/tree/main/examples
473
477
 
474
478
  [license-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/LICENSE
475
479
 
@@ -482,11 +486,3 @@ Email: TahsinCrs@gmail.com
482
486
  [linkedin-url]: https://linkedin.com/in/TahsinCr
483
487
 
484
488
  [x-url]: https://twitter.com/TahsinCrs
485
-
486
-
487
-
488
- <!-- File URL -->
489
-
490
- [lang-tr-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_TR.md
491
-
492
- [lang-en-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README.md
@@ -83,29 +83,33 @@ If you use the lock objects directly like a standard lock without specifying the
83
83
 
84
84
  ## 📊 Performance and Benchmark Results
85
85
 
86
- `rwlocker` unlocks the system's true potential during Network and Database I/O operations where Python's GIL (Global Interpreter Lock) is released. In aggressive scenario tests conducted with zero OS sleep interference, it overwhelmingly outperformed standard locks.
86
+ The performance results demonstrate `rwlocker`'s true potential during Network and Database I/O operations where Python's GIL (Global Interpreter Lock) is released or context-switched.
87
87
 
88
- **Summary of Performance Outputs:**
88
+ **🖥️ Test Environment:** All tests were executed on an **Intel Core i7-12700H (2.4GHz)** processor running **EndeavourOS (Arch-based Linux)**, using **Python 3.14.3** and the experimental **Free-Threading (3.14.3t)** interpreters.
89
89
 
90
- * **🚀 Read-Heavy Scenario (100 Readers, 2 Writers):**
91
- While standard locks queue readers single-file and choke the system, `rwlocker` allows readers to access the data simultaneously. This achieves **~37x FASTER** speed and throughput in **Threading** and **~30x FASTER** in **Asyncio**.
92
- * **⚖️ Balanced Scenario (50 Readers, 50 Writers):**
93
- Thanks to the Fair state machine, read operations are squeezed in parallel between write queues. It increases performance by **2x** compared to standard locks without creating a system bottleneck.
94
- * **🛡️ Write-Heavy Scenario (2 Readers, 100 Writers):**
95
- Even though write operations inherently cannot be executed concurrently (in parallel), thanks to `rwlocker`'s zero-allocation smart proxy architecture, it runs **7-8% faster** than standard `C`-based locks. Even the O(1) cost "ReentrantWriter" (reentrancy) feature adds almost no overhead to performance.
90
+ **🧪 Methodology:** To ensure absolute precision and zero margin of error, all reader and writer entities (threads or async tasks) are spawned in advance and held at a starting line using a synchronization `Event`. Once the event triggers, they execute simultaneously. The workloads strictly follow an `IOBoundScenario` that enforces a precise `time.sleep(0.001)` or `asyncio.sleep(0.001)` delay to accurately simulate real network/database I/O latency.
96
91
 
97
- **Condition Variable Performance Outputs:**
92
+ ### 1. Read-Write Lock (RWLock) Benchmarks
98
93
 
99
- Standard library Condition structures lock up the CPU during multiple wake-ups due to O(N) scanning. `rwlocker`'s O(1) queue architecture absolutely crushes the standard library at this point.
94
+ Standard locks force threads to queue single-file even if they are only reading data. `rwlocker` unleashes concurrent read access.
100
95
 
101
- * **📣 Massive Broadcast (1 Writer, 100 Readers):**
102
- When a single writer updates the database and wakes up hundreds of waiting readers (`notify_all`); thanks to our O(1) architecture, **~65x FASTER** throughput (Ops/sec) is achieved in **Threading**, and **~70x FASTER** in **Asyncio**. The system is saved from entering a "Cache Stampede".
103
- * **🔀 Balanced Pub/Sub (50 Writers, 50 Readers):**
104
- In mixed waiting and waking scenarios, our Condition locks with the `Write-Pref` strategy ran **~2x FASTER** than the standard library.
105
- * **📉 Write-Heavy Limit (Stress Test - 100 Writers, 2 Readers):**
106
- In this brutal scenario where writers constantly block each other and call `notify()`, C-based standard locks utilize their raw speed advantage. `rwlocker`'s Write-Pref model holds its ground neck-and-neck (1.0x) with the standard lock, while the Fair and Read-Pref models intentionally slow down (0.5x - 0.7x) for the sake of maintaining fairness.
96
+ **Synchronous (Thread) RWLock Performance:**
97
+ In the Read-Heavy scenario, standard C-based locks choke the system, whereas `rwlocker` achieves up to **~35x speedup**. Even in Write-Heavy workloads where parallelism isn't inherently possible, `rwlocker` matches or slightly outperforms the C-baseline thanks to its zero-allocation fast-paths.
107
98
 
108
- *(Note: All lock, adapter, and condition classes have passed a massive suite of **266 different unit tests** covering reentrancy, deadlock, timeout, OS interrupts, O(N) leaks, and cancellation safety scenarios with 0 errors, and this entire test suite was completed in just **8.5 seconds**.)*
99
+ **Asynchronous (Asyncio) RWLock Performance:**
100
+ `rwlocker` achieves **~30x faster throughput** by allowing thousands of reader tasks to await I/O simultaneously without blocking one another.
101
+
102
+ ### 2. Condition Variable (RWCondition) Benchmarks
103
+
104
+ Standard `Condition` variables iterate through all sleeping threads/tasks one by one `O(N)` during a broadcast (`notify_all`), causing massive CPU spikes and "Cache Stampedes". `rwlocker` completely eradicates this with its pure `O(1)` queueing architecture.
105
+
106
+ **Synchronous (Thread) RWCondition Performance:**
107
+ When 100 sleeping readers are awakened simultaneously, `rwlocker` processes them instantly without locking the OS. This architectural leap results in a mind-blowing **~45x speedup** compared to the standard library's `threading.Condition`.
108
+
109
+ **Asynchronous (Asyncio) RWCondition Performance:**
110
+ In event-driven caching systems, waking up hundreds of waiting web requests simultaneously is a major bottleneck. The `AsyncRWCondition` completely bypasses standard asyncio constraints, reaching up to **~45x higher throughput** during massive broadcast scenarios, completely saving the loop from freezing.
111
+
112
+ *(Note: All lock, adapter, and condition classes have passed a massive suite of **315 different unit tests** covering reentrancy, deadlock, timeout, OS interrupts, O(N) leaks, and cancellation safety scenarios with 0 errors, and this entire test suite was completed in just **13.2 seconds**.)*
109
113
 
110
114
 
111
115
  <br/>
@@ -440,7 +444,7 @@ Email: TahsinCrs@gmail.com
440
444
 
441
445
  [issues-url]: https://github.com/TahsinCr/python-rwlocker/issues
442
446
 
443
- [examples-url]: https://github.com/TahsinCr/python-rwlocker/wiki
447
+ [examples-url]: https://github.com/TahsinCr/python-rwlocker/tree/main/examples
444
448
 
445
449
  [license-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/LICENSE
446
450
 
@@ -453,11 +457,3 @@ Email: TahsinCrs@gmail.com
453
457
  [linkedin-url]: https://linkedin.com/in/TahsinCr
454
458
 
455
459
  [x-url]: https://twitter.com/TahsinCrs
456
-
457
-
458
-
459
- <!-- File URL -->
460
-
461
- [lang-tr-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_TR.md
462
-
463
- [lang-en-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README.md
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "rwlocker"
7
- version = "3.0"
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.0'
67
+ __version__ = '3.2'
@@ -37,8 +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
- __version__ = '3.0'
42
+ __version__ = '3.2'
42
43
  __all__ = (
43
44
  'AsyncLockable', 'AsyncLockDowngradable', 'AsyncRWLockBase', 'AsyncRWLockWithProxyBase',
44
45
  'AsyncRWLockProxy', 'AsyncRWLockReaderProxy', 'AsyncRWLockWriterProxy',
@@ -180,6 +181,7 @@ class _AsyncWaitQueue:
180
181
  for fut in waiters:
181
182
  if not fut.done():
182
183
  fut.set_result(True)
184
+ waiters.clear()
183
185
 
184
186
  class AsyncRWLockProxy:
185
187
  """
@@ -277,7 +279,7 @@ class AsyncRWLockWriterProxy(AsyncRWLockProxy):
277
279
  )
278
280
  self._read_lock = rwlock.read
279
281
  self._downgrade_core = rwlock._downgrade_core
280
- self._downgraded_tasks:set[asyncio.Task] = set()
282
+ self._downgraded_tasks:weakref.WeakSet[asyncio.Task] = weakref.WeakSet()
281
283
 
282
284
  def downgrade(self):
283
285
  """
@@ -299,11 +301,12 @@ class AsyncRWLockWriterProxy(AsyncRWLockProxy):
299
301
  the release logic to the reader core, avoiding Deadlocks and RuntimeErrors.
300
302
  """
301
303
  if self._downgraded_tasks:
302
- task = asyncio.current_task()
303
- if task in self._downgraded_tasks:
304
- self._downgraded_tasks.remove(task)
304
+ try:
305
+ self._downgraded_tasks.remove(asyncio.current_task())
305
306
  self._read_lock._release_core()
306
307
  return
308
+ except KeyError:
309
+ pass
307
310
  self._release_core()
308
311
 
309
312
  # Read and Write Lock
@@ -344,17 +347,17 @@ class AsyncRWLockWrite(AsyncRWLockWithProxyBase):
344
347
  self._writer_active = False
345
348
  self._readers_active = 0
346
349
 
347
- def _can_read(self) -> bool:
350
+ def _can_read(self) -> bool:
348
351
  return not self._writer_active and self.write.those_waiting == 0
349
352
 
350
- def _acquire_read_core(self):
353
+ def _acquire_read_core(self):
351
354
  self._readers_active += 1
352
355
 
353
356
  def _release_read_core(self):
354
- if self._readers_active == 0:
357
+ if self._readers_active == 0:
355
358
  raise RuntimeError("Unacquired read lock")
356
359
  self._readers_active -= 1
357
- if self._readers_active == 0:
360
+ if self._readers_active == 0:
358
361
  self.write.condition.notify()
359
362
 
360
363
  def _on_reader_abort(self):
@@ -365,7 +368,7 @@ class AsyncRWLockWrite(AsyncRWLockWithProxyBase):
365
368
 
366
369
  def _acquire_write_core(self):
367
370
  self._writer_active = True
368
-
371
+
369
372
  def _release_write_core(self):
370
373
  if not self._writer_active:
371
374
  raise RuntimeError("Unacquired write lock")
@@ -390,7 +393,7 @@ class AsyncRWLockWrite(AsyncRWLockWithProxyBase):
390
393
 
391
394
  def _is_read_locked(self) -> bool:
392
395
  return self._readers_active > 0
393
-
396
+
394
397
  class AsyncRWLockWriteReentrantWriter(AsyncRWLockWrite):
395
398
  """
396
399
  Write-preferring Asynchronous Read-Write Lock with Task-Reentrancy.
@@ -434,36 +437,47 @@ class AsyncRWLockWriteReentrantWriter(AsyncRWLockWrite):
434
437
  self._writer_id: Optional[asyncio.Task] = None
435
438
  self._write_count: int = 0
436
439
 
440
+ def _is_current_writer(self) -> bool:
441
+ return self._writer_id is asyncio.current_task()
442
+
443
+ def _set_current_writer(self) -> None:
444
+ self._writer_id = asyncio.current_task()
445
+
446
+ def _clear_current_writer(self) -> None:
447
+ self._writer_id = None
448
+
437
449
  def _can_read(self) -> bool:
438
- if self._writer_id is asyncio.current_task():
439
- return True
440
- return self._writer_id is None and self.write.those_waiting == 0
450
+ if self._writer_id is not None:
451
+ return self._is_current_writer()
452
+ return self.write.those_waiting == 0
441
453
 
442
454
  def _can_write(self) -> bool:
443
- return (self._writer_id is None and self._readers_active == 0) or self._writer_id is asyncio.current_task()
455
+ if self._writer_id is not None:
456
+ return self._is_current_writer()
457
+ return self._readers_active == 0
444
458
 
445
459
  def _acquire_write_core(self):
446
- self._writer_id = asyncio.current_task()
460
+ self._set_current_writer()
447
461
  self._write_count += 1
448
462
 
449
463
  def _release_write_core(self):
450
- if self._writer_id is not asyncio.current_task():
464
+ if not self._is_current_writer():
451
465
  raise RuntimeError("Permission denied")
452
466
  self._write_count -= 1
453
467
  if self._write_count == 0:
454
- self._writer_id = None
468
+ self._clear_current_writer()
455
469
  self._on_writer_abort()
456
470
 
457
471
  def _downgrade_core(self):
458
- if self._writer_id is not asyncio.current_task():
472
+ if not self._is_current_writer():
459
473
  raise RuntimeError("Permission denied")
460
474
  if self._write_count > 1:
461
- raise RuntimeError("Cannot downgrade a nested write lock.")
462
- self._writer_id = None
475
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
476
+ self._clear_current_writer()
463
477
  self._write_count = 0
464
478
  self._readers_active += 1
465
479
  self.read.condition.notify_all()
466
-
480
+
467
481
  def _is_write_locked(self) -> bool:
468
482
  return self._writer_id is not None
469
483
 
@@ -504,43 +518,44 @@ class AsyncRWLockRead(AsyncRWLockWithProxyBase):
504
518
  self._writer_active = False
505
519
  self._readers_active = 0
506
520
 
507
- def _can_read(self) -> bool:
521
+ def _can_read(self) -> bool:
508
522
  return not self._writer_active
509
523
 
510
- def _acquire_read_core(self):
524
+ def _acquire_read_core(self):
511
525
  self._readers_active += 1
512
526
 
513
527
  def _release_read_core(self):
514
- if self._readers_active == 0:
528
+ if self._readers_active == 0:
515
529
  raise RuntimeError("Unacquired read lock")
516
530
  self._readers_active -= 1
517
- if self._readers_active == 0:
531
+ if self._readers_active == 0:
518
532
  self.write.condition.notify()
519
533
 
520
534
  def _on_reader_abort(self):
521
- if self.read.those_waiting == 0 and self._readers_active == 0:
535
+ if self.read.those_waiting == 0 and self._readers_active == 0:
522
536
  self.write.condition.notify()
523
537
 
524
538
  def _can_write(self) -> bool:
525
539
  return not self._writer_active and self._readers_active == 0 and self.read.those_waiting == 0
526
540
 
527
- def _acquire_write_core(self):
541
+ def _acquire_write_core(self):
528
542
  self._writer_active = True
529
543
 
530
544
  def _release_write_core(self):
531
- if not self._writer_active:
545
+ if not self._writer_active:
532
546
  raise RuntimeError("Unacquired write lock")
533
547
  self._writer_active = False
534
548
  self._on_writer_abort()
549
+
535
550
  def _downgrade_core(self):
536
- if not self._writer_active:
551
+ if not self._writer_active:
537
552
  raise RuntimeError("Cannot downgrade unlocked lock")
538
553
  self._writer_active = False
539
554
  self._readers_active += 1
540
555
  self.read.condition.notify_all()
541
556
 
542
557
  def _on_writer_abort(self):
543
- if self.read.those_waiting > 0:
558
+ if self.read.those_waiting > 0:
544
559
  self.read.condition.notify_all()
545
560
  else:
546
561
  self.write.condition.notify()
@@ -550,7 +565,7 @@ class AsyncRWLockRead(AsyncRWLockWithProxyBase):
550
565
 
551
566
  def _is_read_locked(self) -> bool:
552
567
  return self._readers_active > 0
553
-
568
+
554
569
  class AsyncRWLockReadReentrantWriter(AsyncRWLockRead):
555
570
  """
556
571
  Read-preferring Asynchronous Read-Write Lock with Task-Reentrancy.
@@ -592,32 +607,43 @@ class AsyncRWLockReadReentrantWriter(AsyncRWLockRead):
592
607
  self._writer_id: Optional[asyncio.Task] = None
593
608
  self._write_count: int = 0
594
609
 
610
+ def _is_current_writer(self) -> bool:
611
+ return self._writer_id is asyncio.current_task()
612
+
613
+ def _set_current_writer(self) -> None:
614
+ self._writer_id = asyncio.current_task()
615
+
616
+ def _clear_current_writer(self) -> None:
617
+ self._writer_id = None
618
+
595
619
  def _can_read(self) -> bool:
596
- if self._writer_id is asyncio.current_task():
597
- return True
598
- return self._writer_id is None
620
+ if self._writer_id is not None:
621
+ return self._is_current_writer()
622
+ return True
599
623
 
600
624
  def _can_write(self) -> bool:
601
- return (self._writer_id is None and self._readers_active == 0 and self.read.those_waiting == 0) or self._writer_id is asyncio.current_task()
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
602
628
 
603
629
  def _acquire_write_core(self):
604
- self._writer_id = asyncio.current_task()
630
+ self._set_current_writer()
605
631
  self._write_count += 1
606
632
 
607
633
  def _release_write_core(self):
608
- if self._writer_id is not asyncio.current_task():
634
+ if not self._is_current_writer():
609
635
  raise RuntimeError("Permission denied")
610
636
  self._write_count -= 1
611
637
  if self._write_count == 0:
612
- self._writer_id = None
638
+ self._clear_current_writer()
613
639
  self._on_writer_abort()
614
640
 
615
641
  def _downgrade_core(self):
616
- if self._writer_id is not asyncio.current_task():
642
+ if not self._is_current_writer():
617
643
  raise RuntimeError("Permission denied")
618
644
  if self._write_count > 1:
619
- raise RuntimeError("Cannot downgrade a nested write lock.")
620
- self._writer_id = None
645
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
646
+ self._clear_current_writer()
621
647
  self._write_count = 0
622
648
  self._readers_active += 1
623
649
  self.read.condition.notify_all()
@@ -668,11 +694,11 @@ class AsyncRWLockFair(AsyncRWLockWithProxyBase):
668
694
  return False
669
695
  return self._readers_turn or self.write.those_waiting == 0
670
696
 
671
- def _acquire_read_core(self):
697
+ def _acquire_read_core(self):
672
698
  self._readers_active += 1
673
699
 
674
700
  def _release_read_core(self):
675
- if self._readers_active == 0:
701
+ if self._readers_active == 0:
676
702
  raise RuntimeError("Unacquired read lock")
677
703
  self._readers_active -= 1
678
704
  if self._readers_active == 0:
@@ -685,9 +711,9 @@ class AsyncRWLockFair(AsyncRWLockWithProxyBase):
685
711
  self.write.condition.notify()
686
712
 
687
713
  def _can_write(self) -> bool:
688
- if self._writer_active or self._readers_active > 0:
714
+ if self._writer_active or self._readers_active > 0:
689
715
  return False
690
- if self._readers_turn and self.read.those_waiting > 0:
716
+ if self._readers_turn and self.read.those_waiting > 0:
691
717
  return False
692
718
  return True
693
719
 
@@ -695,13 +721,13 @@ class AsyncRWLockFair(AsyncRWLockWithProxyBase):
695
721
  self._writer_active = True
696
722
 
697
723
  def _release_write_core(self):
698
- if not self._writer_active:
724
+ if not self._writer_active:
699
725
  raise RuntimeError("Unacquired write lock")
700
726
  self._writer_active = False
701
727
  self._on_writer_abort()
702
728
 
703
729
  def _downgrade_core(self):
704
- if not self._writer_active:
730
+ if not self._writer_active:
705
731
  raise RuntimeError("Cannot downgrade unlocked lock")
706
732
  self._writer_active = False
707
733
  self._readers_active += 1
@@ -712,7 +738,7 @@ class AsyncRWLockFair(AsyncRWLockWithProxyBase):
712
738
  if self.read.those_waiting > 0:
713
739
  self._readers_turn = True
714
740
  self.read.condition.notify_all()
715
- else:
741
+ else:
716
742
  self.write.condition.notify()
717
743
 
718
744
  def _is_write_locked(self) -> bool:
@@ -720,7 +746,7 @@ class AsyncRWLockFair(AsyncRWLockWithProxyBase):
720
746
 
721
747
  def _is_read_locked(self) -> bool:
722
748
  return self._readers_active > 0
723
-
749
+
724
750
  class AsyncRWLockFairReentrantWriter(AsyncRWLockFair):
725
751
  """
726
752
  Fair (First-In-First-Out) Asynchronous Read-Write Lock with Task-Reentrancy.
@@ -762,46 +788,57 @@ class AsyncRWLockFairReentrantWriter(AsyncRWLockFair):
762
788
  self._writer_id: Optional[asyncio.Task] = None
763
789
  self._write_count: int = 0
764
790
 
791
+ def _is_current_writer(self) -> bool:
792
+ return self._writer_id is asyncio.current_task()
793
+
794
+ def _set_current_writer(self) -> None:
795
+ self._writer_id = asyncio.current_task()
796
+
797
+ def _clear_current_writer(self) -> None:
798
+ self._writer_id = None
799
+
765
800
  def _can_read(self) -> bool:
766
- if self._writer_id is asyncio.current_task():
767
- return True
768
801
  if self._writer_id is not None:
802
+ if self._is_current_writer():
803
+ return True
769
804
  return False
770
805
  return self._readers_turn or self.write.those_waiting == 0
771
806
 
772
807
  def _can_write(self) -> bool:
773
- if self._writer_id is asyncio.current_task():
774
- return True
775
- if self._writer_id is not None or self._readers_active > 0:
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:
776
813
  return False
777
- if self._readers_turn and self.read.those_waiting > 0:
814
+ if self._readers_turn and self.read.those_waiting > 0:
778
815
  return False
779
816
  return True
780
817
 
781
818
  def _acquire_write_core(self):
782
- self._writer_id = asyncio.current_task()
819
+ self._set_current_writer()
783
820
  self._write_count += 1
784
821
 
785
822
  def _release_write_core(self):
786
- if self._writer_id is not asyncio.current_task():
823
+ if not self._is_current_writer():
787
824
  raise RuntimeError("Permission denied")
788
825
  self._write_count -= 1
789
826
  if self._write_count == 0:
790
- self._writer_id = None
827
+ self._clear_current_writer()
791
828
  self._on_writer_abort()
792
829
 
793
830
  def _downgrade_core(self):
794
- if self._writer_id is not asyncio.current_task():
831
+ if not self._is_current_writer():
795
832
  raise RuntimeError("Permission denied")
796
833
  if self._write_count > 1:
797
- raise RuntimeError("Cannot downgrade a nested write lock.")
798
- self._writer_id = None
834
+ raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
835
+ self._clear_current_writer()
799
836
  self._write_count = 0
800
837
  self._readers_active += 1
801
838
  self._readers_turn = True
802
839
  self.read.condition.notify_all()
803
840
 
804
- def _is_write_locked(self) -> bool:
841
+ def _is_write_locked(self) -> bool:
805
842
  return self._writer_id is not None
806
843
 
807
844
  # Standart Lock Adapter
@@ -861,7 +898,7 @@ class AsyncRWConditionBase(ABC):
861
898
  """
862
899
  __slots__ = ('_lock', 'read', 'write')
863
900
 
864
- def __init__(self, lock: Optional[AsyncRWLockBase] = None):
901
+ def __init__(self, lock: Optional[AsyncLockable] = None):
865
902
  self._lock = AsyncLock() if lock is None else lock
866
903
  self.write: AsyncConditionLockable = asyncio.Condition(self._lock)
867
904
  self.read: AsyncConditionLockable = self.write
@@ -1274,7 +1311,7 @@ class AsyncCondition(AsyncRWConditionBase):
1274
1311
  ```
1275
1312
  """
1276
1313
  __slots__ = ()
1277
- def __init__(self, lock: Optional[AsyncRWLockBase] = None):
1314
+ def __init__(self, lock: Optional[AsyncLockable] = None):
1278
1315
  if lock is not None and isinstance(lock, AsyncRWLockWithProxyBase):
1279
1316
  raise TypeError("Standard 'AsyncCondition' adapters cannot be used with proxy-based RWLocks. Use 'AsyncRWCondition' instead.")
1280
1317
  super().__init__(lock)
@@ -37,7 +37,7 @@ from abc import ABC, abstractmethod
37
37
  from typing import Callable, Protocol, Optional
38
38
  from types import TracebackType
39
39
 
40
- __version__ = '3.0'
40
+ __version__ = '3.2'
41
41
  __all__ = (
42
42
  'Lockable', 'LockDowngradable', 'RWLockBase', 'RWLockWithProxyBase',
43
43
  'RWLockProxy', 'RWLockReaderProxy', 'RWLockWriterProxy',
@@ -304,7 +304,7 @@ class RWLockWriterProxy(RWLockProxy):
304
304
  Proxy specifically handling Writer logic, extending capabilities with
305
305
  atomic state degradation (downgrading).
306
306
  """
307
- __slots__ = ('_read_lock', '_downgrade_core', '_downgraded_threads')
307
+ __slots__ = ('_read_lock', '_downgrade_core', '_downgraded_thread_id')
308
308
  def __init__(self, rwlock: RWLockWithProxyBase):
309
309
  super().__init__(
310
310
  lock=rwlock._lock,
@@ -316,7 +316,7 @@ class RWLockWriterProxy(RWLockProxy):
316
316
  )
317
317
  self._read_lock = rwlock.read
318
318
  self._downgrade_core = rwlock._downgrade_core
319
- self._downgraded_threads:set[int] = set()
319
+ self._downgraded_thread_id: Optional[int] = None
320
320
 
321
321
  def downgrade(self):
322
322
  """
@@ -330,7 +330,7 @@ class RWLockWriterProxy(RWLockProxy):
330
330
  """
331
331
  with self._lock:
332
332
  self._downgrade_core()
333
- self._downgraded_threads.add(threading.get_ident())
333
+ self._downgraded_thread_id = threading.get_ident()
334
334
 
335
335
  def release(self):
336
336
  """
@@ -339,15 +339,13 @@ class RWLockWriterProxy(RWLockProxy):
339
339
  and routes the release to the reader core to prevent deadlocks.
340
340
  """
341
341
  with self._lock:
342
- if self._downgraded_threads:
343
- ident = threading.get_ident()
344
- if ident in self._downgraded_threads:
345
- self._downgraded_threads.remove(ident)
346
- self._read_lock._release_core()
347
- return
342
+ if self._downgraded_thread_id == threading.get_ident():
343
+ self._downgraded_thread_id = None
344
+ self._read_lock._release_core()
345
+ return
348
346
  self._release_core()
349
347
 
350
- # Read and Write Lock
348
+ # Read and Write Lock
351
349
  class RWLockWrite(RWLockWithProxyBase):
352
350
  """
353
351
  Write-preferring Read-Write Lock.
@@ -431,7 +429,7 @@ class RWLockWrite(RWLockWithProxyBase):
431
429
 
432
430
  def _is_read_locked(self) -> bool:
433
431
  return self._readers_active > 0
434
-
432
+
435
433
  class RWLockWriteReentrantWriter(RWLockWrite):
436
434
  """
437
435
  Write-preferring Read-Write Lock with Write-Reentrancy support.
@@ -475,36 +473,47 @@ class RWLockWriteReentrantWriter(RWLockWrite):
475
473
  self._writer_id: Optional[int] = None
476
474
  self._write_count: int = 0
477
475
 
476
+ def _is_current_writer(self) -> bool:
477
+ return self._writer_id == threading.get_ident()
478
+
479
+ def _set_current_writer(self) -> None:
480
+ self._writer_id = threading.get_ident()
481
+
482
+ def _clear_current_writer(self) -> None:
483
+ self._writer_id = None
484
+
478
485
  def _can_read(self) -> bool:
479
- if self._writer_id == threading.get_ident():
480
- return True
481
- return self._writer_id is None and self.write.those_waiting == 0
486
+ if self._writer_id is not None:
487
+ return self._is_current_writer()
488
+ return self.write.those_waiting == 0
482
489
 
483
490
  def _can_write(self) -> bool:
484
- return (self._writer_id is None and self._readers_active == 0) or self._writer_id == threading.get_ident()
491
+ if self._writer_id is not None:
492
+ return self._is_current_writer()
493
+ return self._readers_active == 0
485
494
 
486
495
  def _acquire_write_core(self):
487
- self._writer_id = threading.get_ident()
496
+ self._set_current_writer()
488
497
  self._write_count += 1
489
498
 
490
499
  def _release_write_core(self):
491
- if self._writer_id != threading.get_ident():
500
+ if not self._is_current_writer():
492
501
  raise RuntimeError("Permission denied")
493
502
  self._write_count -= 1
494
503
  if self._write_count == 0:
495
- self._writer_id = None
504
+ self._clear_current_writer()
496
505
  self._on_writer_abort()
497
506
 
498
507
  def _downgrade_core(self):
499
- if self._writer_id != threading.get_ident():
508
+ if not self._is_current_writer():
500
509
  raise RuntimeError("Permission denied")
501
510
  if self._write_count > 1:
502
511
  raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
503
- self._writer_id = None
512
+ self._clear_current_writer()
504
513
  self._write_count = 0
505
514
  self._readers_active += 1
506
515
  self.read.condition.notify_all()
507
-
516
+
508
517
  def _is_write_locked(self) -> bool:
509
518
  return self._writer_id is not None
510
519
 
@@ -634,32 +643,43 @@ class RWLockReadReentrantWriter(RWLockRead):
634
643
  self._writer_id: Optional[int] = None
635
644
  self._write_count: int = 0
636
645
 
646
+ def _is_current_writer(self) -> bool:
647
+ return self._writer_id == threading.get_ident()
648
+
649
+ def _set_current_writer(self) -> None:
650
+ self._writer_id = threading.get_ident()
651
+
652
+ def _clear_current_writer(self) -> None:
653
+ self._writer_id = None
654
+
637
655
  def _can_read(self) -> bool:
638
- if self._writer_id == threading.get_ident():
639
- return True
640
- return self._writer_id is None
656
+ if self._writer_id is not None:
657
+ return self._is_current_writer()
658
+ return True
641
659
 
642
660
  def _can_write(self) -> bool:
643
- return (self._writer_id is None and self._readers_active == 0 and self.read.those_waiting == 0) or self._writer_id == threading.get_ident()
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
644
664
 
645
665
  def _acquire_write_core(self):
646
- self._writer_id = threading.get_ident()
666
+ self._set_current_writer()
647
667
  self._write_count += 1
648
668
 
649
669
  def _release_write_core(self):
650
- if self._writer_id != threading.get_ident():
670
+ if not self._is_current_writer():
651
671
  raise RuntimeError("Permission denied")
652
672
  self._write_count -= 1
653
673
  if self._write_count == 0:
654
- self._writer_id = None
674
+ self._clear_current_writer()
655
675
  self._on_writer_abort()
656
676
 
657
677
  def _downgrade_core(self):
658
- if self._writer_id != threading.get_ident():
678
+ if not self._is_current_writer():
659
679
  raise RuntimeError("Permission denied")
660
680
  if self._write_count > 1:
661
681
  raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
662
- self._writer_id = None
682
+ self._clear_current_writer()
663
683
  self._write_count = 0
664
684
  self._readers_active += 1
665
685
  self.read.condition.notify_all()
@@ -762,7 +782,7 @@ class RWLockFair(RWLockWithProxyBase):
762
782
 
763
783
  def _is_read_locked(self) -> bool:
764
784
  return self._readers_active > 0
765
-
785
+
766
786
  class RWLockFairReentrantWriter(RWLockFair):
767
787
  """
768
788
  Fair (First-In-First-Out) Read-Write Lock with Write-Reentrancy support.
@@ -804,40 +824,51 @@ class RWLockFairReentrantWriter(RWLockFair):
804
824
  self._writer_id: Optional[int] = None
805
825
  self._write_count: int = 0
806
826
 
827
+ def _is_current_writer(self) -> bool:
828
+ return self._writer_id == threading.get_ident()
829
+
830
+ def _set_current_writer(self) -> None:
831
+ self._writer_id = threading.get_ident()
832
+
833
+ def _clear_current_writer(self) -> None:
834
+ self._writer_id = None
835
+
807
836
  def _can_read(self) -> bool:
808
- if self._writer_id == threading.get_ident():
809
- return True
810
- if self._writer_id is not None:
837
+ if self._writer_id is not None:
838
+ if self._is_current_writer():
839
+ return True
811
840
  return False
812
841
  return self._readers_turn or self.write.those_waiting == 0
813
842
 
814
843
  def _can_write(self) -> bool:
815
- if self._writer_id == threading.get_ident():
816
- return True
817
- if self._writer_id is not None or self._readers_active > 0:
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:
818
849
  return False
819
850
  if self._readers_turn and self.read.those_waiting > 0:
820
851
  return False
821
852
  return True
822
853
 
823
854
  def _acquire_write_core(self):
824
- self._writer_id = threading.get_ident()
855
+ self._set_current_writer()
825
856
  self._write_count += 1
826
857
 
827
858
  def _release_write_core(self):
828
- if self._writer_id != threading.get_ident():
859
+ if not self._is_current_writer():
829
860
  raise RuntimeError("Permission denied")
830
861
  self._write_count -= 1
831
862
  if self._write_count == 0:
832
- self._writer_id = None
863
+ self._clear_current_writer()
833
864
  self._on_writer_abort()
834
865
 
835
866
  def _downgrade_core(self):
836
- if self._writer_id != threading.get_ident():
867
+ if not self._is_current_writer():
837
868
  raise RuntimeError("Permission denied")
838
869
  if self._write_count > 1:
839
870
  raise RuntimeError("Cannot downgrade a nested write lock. Release inner locks first.")
840
- self._writer_id = None
871
+ self._clear_current_writer()
841
872
  self._write_count = 0
842
873
  self._readers_active += 1
843
874
  self._readers_turn = True
@@ -902,7 +933,7 @@ class RWConditionBase(ABC):
902
933
  `.write` proxy. Designed to act as a common type hint and contract.
903
934
  """
904
935
  __slots__ = ('_lock', 'read', 'write')
905
- def __init__(self, lock: Optional[RWLockBase] = None):
936
+ def __init__(self, lock: Optional[Lockable] = None):
906
937
  self._lock = Lock() if lock is None else lock
907
938
  self.write: ConditionLockable = threading.Condition(self._lock)
908
939
  self.read: ConditionLockable = self.write
@@ -1238,7 +1269,7 @@ class RWCondition(RWConditionWithProxyBase):
1238
1269
  self._waiters = deque()
1239
1270
  self._internal_lock = threading.Lock()
1240
1271
  super().__init__(lock)
1241
-
1272
+
1242
1273
  def _is_owned_read(self) -> bool:
1243
1274
  return self._lock._is_read_locked()
1244
1275
 
@@ -1317,7 +1348,7 @@ class Condition(RWConditionBase):
1317
1348
  ```
1318
1349
  """
1319
1350
  __slots__ = ()
1320
- def __init__(self, lock: Optional[RWLockBase] = None):
1351
+ def __init__(self, lock: Optional[Lockable] = None):
1321
1352
  if lock is not None and isinstance(lock, RWLockWithProxyBase):
1322
1353
  raise TypeError("Standard 'Condition' adapters cannot be used with proxy-based RWLocks. Use 'RWCondition' instead.")
1323
1354
  super().__init__(lock)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rwlocker
3
- Version: 3.0
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
@@ -112,29 +112,33 @@ If you use the lock objects directly like a standard lock without specifying the
112
112
 
113
113
  ## 📊 Performance and Benchmark Results
114
114
 
115
- `rwlocker` unlocks the system's true potential during Network and Database I/O operations where Python's GIL (Global Interpreter Lock) is released. In aggressive scenario tests conducted with zero OS sleep interference, it overwhelmingly outperformed standard locks.
115
+ The performance results demonstrate `rwlocker`'s true potential during Network and Database I/O operations where Python's GIL (Global Interpreter Lock) is released or context-switched.
116
116
 
117
- **Summary of Performance Outputs:**
117
+ **🖥️ Test Environment:** All tests were executed on an **Intel Core i7-12700H (2.4GHz)** processor running **EndeavourOS (Arch-based Linux)**, using **Python 3.14.3** and the experimental **Free-Threading (3.14.3t)** interpreters.
118
118
 
119
- * **🚀 Read-Heavy Scenario (100 Readers, 2 Writers):**
120
- While standard locks queue readers single-file and choke the system, `rwlocker` allows readers to access the data simultaneously. This achieves **~37x FASTER** speed and throughput in **Threading** and **~30x FASTER** in **Asyncio**.
121
- * **⚖️ Balanced Scenario (50 Readers, 50 Writers):**
122
- Thanks to the Fair state machine, read operations are squeezed in parallel between write queues. It increases performance by **2x** compared to standard locks without creating a system bottleneck.
123
- * **🛡️ Write-Heavy Scenario (2 Readers, 100 Writers):**
124
- Even though write operations inherently cannot be executed concurrently (in parallel), thanks to `rwlocker`'s zero-allocation smart proxy architecture, it runs **7-8% faster** than standard `C`-based locks. Even the O(1) cost "ReentrantWriter" (reentrancy) feature adds almost no overhead to performance.
119
+ **🧪 Methodology:** To ensure absolute precision and zero margin of error, all reader and writer entities (threads or async tasks) are spawned in advance and held at a starting line using a synchronization `Event`. Once the event triggers, they execute simultaneously. The workloads strictly follow an `IOBoundScenario` that enforces a precise `time.sleep(0.001)` or `asyncio.sleep(0.001)` delay to accurately simulate real network/database I/O latency.
125
120
 
126
- **Condition Variable Performance Outputs:**
121
+ ### 1. Read-Write Lock (RWLock) Benchmarks
127
122
 
128
- Standard library Condition structures lock up the CPU during multiple wake-ups due to O(N) scanning. `rwlocker`'s O(1) queue architecture absolutely crushes the standard library at this point.
123
+ Standard locks force threads to queue single-file even if they are only reading data. `rwlocker` unleashes concurrent read access.
129
124
 
130
- * **📣 Massive Broadcast (1 Writer, 100 Readers):**
131
- When a single writer updates the database and wakes up hundreds of waiting readers (`notify_all`); thanks to our O(1) architecture, **~65x FASTER** throughput (Ops/sec) is achieved in **Threading**, and **~70x FASTER** in **Asyncio**. The system is saved from entering a "Cache Stampede".
132
- * **🔀 Balanced Pub/Sub (50 Writers, 50 Readers):**
133
- In mixed waiting and waking scenarios, our Condition locks with the `Write-Pref` strategy ran **~2x FASTER** than the standard library.
134
- * **📉 Write-Heavy Limit (Stress Test - 100 Writers, 2 Readers):**
135
- In this brutal scenario where writers constantly block each other and call `notify()`, C-based standard locks utilize their raw speed advantage. `rwlocker`'s Write-Pref model holds its ground neck-and-neck (1.0x) with the standard lock, while the Fair and Read-Pref models intentionally slow down (0.5x - 0.7x) for the sake of maintaining fairness.
125
+ **Synchronous (Thread) RWLock Performance:**
126
+ In the Read-Heavy scenario, standard C-based locks choke the system, whereas `rwlocker` achieves up to **~35x speedup**. Even in Write-Heavy workloads where parallelism isn't inherently possible, `rwlocker` matches or slightly outperforms the C-baseline thanks to its zero-allocation fast-paths.
136
127
 
137
- *(Note: All lock, adapter, and condition classes have passed a massive suite of **266 different unit tests** covering reentrancy, deadlock, timeout, OS interrupts, O(N) leaks, and cancellation safety scenarios with 0 errors, and this entire test suite was completed in just **8.5 seconds**.)*
128
+ **Asynchronous (Asyncio) RWLock Performance:**
129
+ `rwlocker` achieves **~30x faster throughput** by allowing thousands of reader tasks to await I/O simultaneously without blocking one another.
130
+
131
+ ### 2. Condition Variable (RWCondition) Benchmarks
132
+
133
+ Standard `Condition` variables iterate through all sleeping threads/tasks one by one `O(N)` during a broadcast (`notify_all`), causing massive CPU spikes and "Cache Stampedes". `rwlocker` completely eradicates this with its pure `O(1)` queueing architecture.
134
+
135
+ **Synchronous (Thread) RWCondition Performance:**
136
+ When 100 sleeping readers are awakened simultaneously, `rwlocker` processes them instantly without locking the OS. This architectural leap results in a mind-blowing **~45x speedup** compared to the standard library's `threading.Condition`.
137
+
138
+ **Asynchronous (Asyncio) RWCondition Performance:**
139
+ In event-driven caching systems, waking up hundreds of waiting web requests simultaneously is a major bottleneck. The `AsyncRWCondition` completely bypasses standard asyncio constraints, reaching up to **~45x higher throughput** during massive broadcast scenarios, completely saving the loop from freezing.
140
+
141
+ *(Note: All lock, adapter, and condition classes have passed a massive suite of **315 different unit tests** covering reentrancy, deadlock, timeout, OS interrupts, O(N) leaks, and cancellation safety scenarios with 0 errors, and this entire test suite was completed in just **13.2 seconds**.)*
138
142
 
139
143
 
140
144
  <br/>
@@ -469,7 +473,7 @@ Email: TahsinCrs@gmail.com
469
473
 
470
474
  [issues-url]: https://github.com/TahsinCr/python-rwlocker/issues
471
475
 
472
- [examples-url]: https://github.com/TahsinCr/python-rwlocker/wiki
476
+ [examples-url]: https://github.com/TahsinCr/python-rwlocker/tree/main/examples
473
477
 
474
478
  [license-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/LICENSE
475
479
 
@@ -482,11 +486,3 @@ Email: TahsinCrs@gmail.com
482
486
  [linkedin-url]: https://linkedin.com/in/TahsinCr
483
487
 
484
488
  [x-url]: https://twitter.com/TahsinCrs
485
-
486
-
487
-
488
- <!-- File URL -->
489
-
490
- [lang-tr-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README_TR.md
491
-
492
- [lang-en-url]: https://github.com/TahsinCr/python-rwlocker/blob/main/README.md
File without changes
File without changes
File without changes