1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Copyright (c) The Diem Core Contributors
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

use named_lock::{NamedLock, NamedLockGuard};
use once_cell::sync::Lazy;
use std::sync::{Mutex, MutexGuard};
use whoami::username;

const PACKAGE_LOCK_NAME: &str = "move_pkg_lock";
static PACKAGE_THREAD_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
static PACKAGE_PROCESS_MUTEX: Lazy<NamedLock> = Lazy::new(|| {
    let user_lock_file = format!("{}_{}", PACKAGE_LOCK_NAME, username());
    NamedLock::create(user_lock_file.as_str()).unwrap()
});

/// The package lock is a lock held across threads and processes. This lock is held to ensure that
/// the Move package manager has a consistent (read: serial) view of the file system. Without this
/// lock we can easily get into race conditions around caching and overwriting of packages (e.g.,
/// thread 1 and thread 2 compete to build package P in the same location), as well as downloading
/// of git dependencies (thread 1 starts downloading git dependency, meanwhile thread 2 sees the
/// git directory before it has been fully populated but assumes it has been fully downloaded and
/// starts building the package before the git dependency has been fully downloaded by thread 1.
/// This will then lead to file not found errors). These same issues could occur across processes,
/// this is why we grab both a thread lock and process lock.
pub(crate) struct PackageLock {
    thread_lock: MutexGuard<'static, ()>,
    process_lock: NamedLockGuard<'static>,
}

impl PackageLock {
    pub(crate) fn lock() -> PackageLock {
        let thread_lock = PACKAGE_THREAD_MUTEX.lock().unwrap();
        let process_lock = PACKAGE_PROCESS_MUTEX.lock().unwrap();
        Self {
            thread_lock,
            process_lock,
        }
    }

    pub(crate) fn unlock(self) {
        let Self {
            thread_lock,
            process_lock,
        } = self;
        drop(process_lock);
        drop(thread_lock);
    }
}