You are given a partially completed Python file that implements two synchronization primitives using threading. Your task is to complete the implementation of ModalLock and FairModalLock classes.
`python import threading import time
class ModalLock: def init(self): self._lock = threading.Lock() self._waiters = []
def acquire(self):
# Your implementation here
def release(self):
# Your implementation here
def acquire_timeout(self, timeout):
# Your implementation here
def acquire_nowait(self):
# Your implementation here
def release_and_signal(self):
# Your implementation here
class FairModalLock(ModalLock): def init(self): super().init()
def release_and_signal(self):
# Your implementation here
`
acquire, release, acquire_timeout, acquire_nowait, and release_and_signal methods for the ModalLock class.release_and_signal method for the FairModalLock class.FairModalLock class maintains fairness among waiting threads.threading.Lock and threading.Condition classes to synchronize access to shared resources.ModalLock class.FairModalLock class by maintaining a queue of waiting threads and selecting the next thread in the queue when releasing the lock.`python import threading import time
class ModalLock: def init(self): self._lock = threading.Lock() self._waiters = []
def acquire(self):
with self._lock:
if self._waiters:
self._waiters.pop(0).release()
else:
self._lock.acquire()
def release(self):
with self._lock:
self._lock.release()
def acquire_timeout(self, timeout):
return self._lock.acquire(timeout=timeout)
def acquire_nowait(self):
return self._lock.acquire(timeout=0)
def release_and_signal(self):
with self._lock:
self._lock.release()
if self._waiters:
self._waiters.pop(0).release()
class FairModalLock(ModalLock): def init(self): super().init()
def release_and_signal(self):
with self._lock:
self._lock.release()
if self._waiters:
self._waiters.pop(0).release()
else:
self._lock.acquire()
self._lock.release()
`
This solution implements the required methods for the ModalLock and FairModalLock classes. The FairModalLock class maintains fairness by keeping track of waiting threads and selecting the next thread in the queue when releasing the lock.