mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 14:28:17 +03:00
repolock: add py bindings for RepoLocker
Summary: Expose the RepoLocker via the pyrepo bindings. Python will make use of this to allow sharing of locks between Python and Rust (i.e. allow locks to be re-entrant across Python -> Rust boundary). I added a new RepoLockHandle.count() method to enable some sanity checks in the bindings. In particular, we make sure that when the Python code acquires a lock, it isn't already acquired. Python has its own logic for storing the lock on the repo, so it should only try to acquire once. In general Python assumes it is the top level locker, so I want to catch any craziness where Rust acquires a lock and then calls into Python and the lock is re-acquired. Similarly, we check that when Python releases a lock, the lock is _actually_ released (i.e. there isn't some other Rust code still holding a reference to the lock handle). Reviewed By: quark-zju Differential Revision: D45510004 fbshipit-source-id: 8625da19b82b4cd92a052bca56e6e46affe1bc08
This commit is contained in:
parent
9f78d6b183
commit
48fe991547
@ -8,6 +8,7 @@ edition = "2021"
|
||||
cpython_ext = { path = "../../../../lib/cpython-ext" }
|
||||
cpython = { version = "0.7", default-features = false }
|
||||
repo = { path = "../../../../lib/repo" }
|
||||
repolock = { path = "../../../../lib/repolock" }
|
||||
util = { path = "../../../../lib/util" }
|
||||
parking_lot = "0.11.2"
|
||||
pyconfigloader = { path = "../pyconfigloader" }
|
||||
@ -16,4 +17,3 @@ pyedenapi = { path = "../pyedenapi" }
|
||||
pymetalog = { path = "../pymetalog" }
|
||||
pyworkingcopy = { path = "../pyworkingcopy" }
|
||||
workingcopy = { path = "../../../../lib/workingcopy" }
|
||||
|
||||
|
@ -8,8 +8,10 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
extern crate repo as rsrepo;
|
||||
extern crate repolock as rsrepolock;
|
||||
extern crate workingcopy as rsworkingcopy;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -30,6 +32,7 @@ pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
|
||||
let name = [package, "repo"].join(".");
|
||||
let m = PyModule::new(py, &name)?;
|
||||
m.add_class::<repo>(py)?;
|
||||
m.add_class::<repolock>(py)?;
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
@ -127,4 +130,40 @@ py_class!(pub class repo |py| {
|
||||
def dotpath(&self) -> PyResult<PyPathBuf> {
|
||||
self.inner(py).read().dot_hg_path().try_into().map_pyerr(py)
|
||||
}
|
||||
|
||||
def trywlock(&self, wc_dot_hg: PyPathBuf) -> PyResult<repolock> {
|
||||
let lock = self.inner(py).read().locker().try_lock_working_copy(wc_dot_hg.to_path_buf()).map_pyerr(py)?;
|
||||
if lock.count() > 1 {
|
||||
Err(PyErr::new::<exc::ValueError, _>(py, "lock is already locked"))
|
||||
} else {
|
||||
repolock::create_instance(py, Cell::new(Some(lock)))
|
||||
}
|
||||
}
|
||||
|
||||
def trylock(&self) -> PyResult<repolock> {
|
||||
let lock = self.inner(py).read().locker().try_lock_store().map_pyerr(py)?;
|
||||
if lock.count() > 1 {
|
||||
Err(PyErr::new::<exc::ValueError, _>(py, "lock is already locked"))
|
||||
} else {
|
||||
repolock::create_instance(py, Cell::new(Some(lock)))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
py_class!(pub class repolock |py| {
|
||||
data lock: Cell<Option<rsrepolock::RepoLockHandle>>;
|
||||
|
||||
def unlock(&self) -> PyResult<PyNone> {
|
||||
if let Some(f) = self.lock(py).replace(None) {
|
||||
let count = f.count();
|
||||
drop(f);
|
||||
if count == 1 {
|
||||
Ok(PyNone)
|
||||
} else {
|
||||
Err(PyErr::new::<exc::ValueError, _>(py, "lock is still locked"))
|
||||
}
|
||||
} else {
|
||||
Err(PyErr::new::<exc::ValueError, _>(py, "lock is already unlocked"))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -233,6 +233,22 @@ impl RepoLockHandle {
|
||||
wc_path: Some(wc_path),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return ref count for this lock handle. Ref count of 1 means lock will be
|
||||
/// relased when this handle is dropped.
|
||||
pub fn count(&self) -> u64 {
|
||||
let locker = self.locker.lock();
|
||||
if self.store {
|
||||
locker.store_lock.as_ref().unwrap().1.get()
|
||||
} else {
|
||||
locker
|
||||
.wc_locks
|
||||
.get(self.wc_path.as_ref().unwrap())
|
||||
.unwrap()
|
||||
.1
|
||||
.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RepoLockHandle {
|
||||
|
Loading…
Reference in New Issue
Block a user