Message382650
I've been wondering if it's worth it to have something like Rust's std::sync::Mutex[1] which is used like this:
let data = Mutex::new(0);
{
let mut unlocked = data.lock().unwrap();
*unlocked += 1;
}
// unlocked is no longer available here, we need to use data.lock() again
Our (Python) [R]Lock is typically used like this:
data_to_protect = whatever()
lock = Lock()
# ...
with lock:
data_to_protect.do_something()
The inconvenience of this is obvious to me and it's more error prone if one forgets the lock when accessing data_to_protect. I wrote a quick prototype to get something like Mutex in Python:
import threading
from contextlib import contextmanager
from typing import Any, cast, Dict, Generator, Generic, Optional, TypeVar
T = TypeVar('T')
class LockedData(Generic[T]):
def __init__(self, data: T, lock: Any = None) -> None:
self._data = data
if lock is None:
lock = threading.Lock()
self._lock = lock
@contextmanager
def unlocked(self, timeout: float = -1.0) -> Generator[T, None, None]:
acquired = None
unlocked = None
try:
acquired = self._lock.acquire(timeout=timeout)
if acquired is False:
raise LockTimeout()
unlocked = UnlockResult(self._data)
yield unlocked
finally:
if acquired is True:
if unlocked is not None:
unlocked._unlocked = False
self._data = unlocked._data
unlocked._data = None
self._lock.release()
class UnlockResult(Generic[T]):
_data: Optional[T]
def __init__(self, data: T) -> None:
self._data = data
self._unlocked = True
@property
def data(self) -> T:
assert self._unlocked
return cast(T, self._data)
@data.setter
def data(self, data: T) -> None:
assert self._unlocked
self._data = data
class LockTimeout(Exception):
pass
if __name__ == '__main__':
locked_dict: LockedData[Dict[str, bool]] = LockedData({})
# Mutating the dictionary
with locked_dict.unlocked() as result:
result.data['hello'] = True
with locked_dict.unlocked() as result:
print(result.data)
# Replacing the dictionary
with locked_dict.unlocked() as result:
result.data = {'a': True, 'b': False}
with locked_dict.unlocked() as result:
print(result.data)
# Trying to access data after context closes
print(result._data)
print(result.data)
Now this is obviously quite far from what Rust offers, as there's nothing to prevent a person from doing something like this:
with locked_dict.unlocked() as result:
data = result.data
print('Oh no, look: %r' % (data,))
but it seems to me it's still an improvement.
[1] https://doc.rust-lang.org/std/sync/struct.Mutex.html |
|
Date |
User |
Action |
Args |
2020-12-07 15:56:57 | jstasiak | set | recipients:
+ jstasiak, pitrou, vstinner, benjamin.peterson |
2020-12-07 15:56:57 | jstasiak | set | messageid: <1607356617.55.0.274484392105.issue42590@roundup.psfhosted.org> |
2020-12-07 15:56:57 | jstasiak | link | issue42590 messages |
2020-12-07 15:56:57 | jstasiak | create | |
|