Skip to content

Commit

Permalink
chore(core): move file lock to rust
Browse files Browse the repository at this point in the history
  • Loading branch information
AgentEnder committed Dec 19, 2024
1 parent b6f3289 commit d14b40b
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 76 deletions.
8 changes: 8 additions & 0 deletions packages/nx/src/native/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ export declare class ChildProcess {
onOutput(callback: (message: string) => void): void
}

export declare class FileLock {
locked: boolean
constructor(lockFilePath: string)
lock(): void
unlock(): void
wait(timeout?: number | undefined | null): Promise<void>
}

export declare class HashPlanner {
constructor(nxJson: NxJson, projectGraph: ExternalObject<ProjectGraph>)
getPlans(taskIds: Array<string>, taskGraph: TaskGraph): Record<string, string[]>
Expand Down
1 change: 1 addition & 0 deletions packages/nx/src/native/native-bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ if (!nativeBinding) {
}

module.exports.ChildProcess = nativeBinding.ChildProcess
module.exports.FileLock = nativeBinding.FileLock
module.exports.HashPlanner = nativeBinding.HashPlanner
module.exports.ImportResult = nativeBinding.ImportResult
module.exports.NxCache = nativeBinding.NxCache
Expand Down
85 changes: 85 additions & 0 deletions packages/nx/src/native/utils/file_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::Path;
use std::time::Duration;

#[napi]
#[derive(Clone)]
pub struct FileLock {
#[napi]
pub locked: bool,

lock_file_path: String,
}

#[napi]
impl FileLock {
#[napi(constructor)]
pub fn new(lock_file_path: String) -> anyhow::Result<Self> {
let locked = Path::new(&lock_file_path).exists();
Ok(Self {
locked,
lock_file_path,
})
}

#[napi]
pub fn lock(&mut self) -> anyhow::Result<()> {
if self.locked {
anyhow::bail!("File {} is already locked", self.lock_file_path)
}
let mut lock_file = File::create(&self.lock_file_path)?;
lock_file.write_all(b"")?;
self.locked = true;
Ok(())
}

#[napi]
pub fn unlock(&mut self) -> anyhow::Result<()> {
if !self.locked {
anyhow::bail!("File {} is not locked", self.lock_file_path)
}
fs::remove_file(&self.lock_file_path).or_else(|err| {
if err.kind() == io::ErrorKind::NotFound {
Ok(())
} else {
Err(err)
}
})?;
self.locked = false;
Ok(())
}

#[napi]
pub async fn wait(&self, timeout: Option<i32>) -> Result<(), napi::Error> {
if !self.locked {
return Ok(());
}

let start = std::time::Instant::now();
let duration = timeout.map(|t| Duration::from_secs(u64::try_from(t).unwrap()));

loop {
if !self.locked || !Path::new(&self.lock_file_path).exists() {
break Ok(());
}

if let Some(duration) = duration {
if start.elapsed() > duration {
break Err(napi::Error::from_reason("Timeout waiting for lock"));
}
}

std::thread::sleep(Duration::from_millis(2));
}
}
}

// Ensure the lock file is removed when the FileLock is dropped
impl Drop for FileLock {
fn drop(&mut self) {
if self.locked {
let _ = self.unlock();
}
}
}
1 change: 1 addition & 0 deletions packages/nx/src/native/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ pub use normalize_trait::Normalize;
#[cfg_attr(target_arch = "wasm32", path = "atomics/wasm.rs")]
pub mod atomics;
pub mod ci;
pub mod file_lock;

pub use atomics::*;
18 changes: 15 additions & 3 deletions packages/nx/src/project-graph/project-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
} from './utils/retrieve-workspace-files';
import { getPlugins } from './plugins/get-plugins';
import { logger } from '../utils/logger';
import { FileLock } from '../utils/file-lock';
import { FileLock } from '../native';
import { join } from 'path';
import { workspaceDataDirectory } from '../utils/cache-directory';

Expand Down Expand Up @@ -275,7 +275,18 @@ export async function createProjectGraphAndSourceMapsAsync(
performance.mark('create-project-graph-async:start');

if (!daemonClient.enabled()) {
const lock = new FileLock(join(workspaceDataDirectory, 'project-graph'));
const lock = new FileLock(
join(workspaceDataDirectory, 'project-graph.lock')
);

function cleanupFileLock() {
try {
lock.unlock();
} catch {}
}

process.on('exit', cleanupFileLock);

if (lock.locked) {
logger.verbose(
'Waiting for graph construction in another process to complete'
Expand Down Expand Up @@ -316,10 +327,11 @@ export async function createProjectGraphAndSourceMapsAsync(
'create-project-graph-async:start',
'create-project-graph-async:end'
);
lock.unlock();
return res;
} catch (e) {
handleProjectGraphError(opts, e);
} finally {
lock.unlock();
}
} else {
try {
Expand Down
73 changes: 0 additions & 73 deletions packages/nx/src/utils/file-lock.ts

This file was deleted.

0 comments on commit d14b40b

Please sign in to comment.