mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
feat(unstable): Add file locking APIs (#11746)
This commit adds following unstable APIs: - Deno.flock() - Deno.flockSync() - Deno.funlock() - Deno.funlockSync()
This commit is contained in:
parent
46e4ba38b2
commit
93d83a84db
7 changed files with 283 additions and 0 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -828,6 +828,7 @@ dependencies = [
|
|||
"dlopen",
|
||||
"encoding_rs",
|
||||
"filetime",
|
||||
"fs3",
|
||||
"fwdansi",
|
||||
"http",
|
||||
"hyper",
|
||||
|
@ -1294,6 +1295,17 @@ dependencies = [
|
|||
"syn 1.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs3"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb17cf6ed704f72485332f6ab65257460c4f9f3083934cf402bf9f5b3b600a90"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustc_version 0.2.3",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.0.0"
|
||||
|
|
26
cli/dts/lib.deno.ns.d.ts
vendored
26
cli/dts/lib.deno.ns.d.ts
vendored
|
@ -787,6 +787,32 @@ declare namespace Deno {
|
|||
*/
|
||||
export function fdatasync(rid: number): Promise<void>;
|
||||
|
||||
/** **UNSTABLE**: New API should be tested first.
|
||||
*
|
||||
* Acquire an advisory file-system lock for the provided file. `exclusive`
|
||||
* defaults to `false`.
|
||||
*/
|
||||
export function flock(rid: number, exclusive?: boolean): Promise<void>;
|
||||
|
||||
/** **UNSTABLE**: New API should be tested first.
|
||||
*
|
||||
* Acquire an advisory file-system lock for the provided file. `exclusive`
|
||||
* defaults to `false`.
|
||||
*/
|
||||
export function flockSync(rid: number, exclusive?: boolean): void;
|
||||
|
||||
/** **UNSTABLE**: New API should be tested first.
|
||||
*
|
||||
* Release an advisory file-system lock for the provided file.
|
||||
*/
|
||||
export function funlock(rid: number): Promise<void>;
|
||||
|
||||
/** **UNSTABLE**: New API should be tested first.
|
||||
*
|
||||
* Release an advisory file-system lock for the provided file.
|
||||
*/
|
||||
export function funlockSync(rid: number): void;
|
||||
|
||||
/** Close the given resource ID (rid) which has been previously opened, such
|
||||
* as via opening or creating a file. Closing a file when you are finished
|
||||
* with it is important to avoid leaking resources.
|
||||
|
|
102
cli/tests/unit/flock_test.ts
Normal file
102
cli/tests/unit/flock_test.ts
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
import { assertEquals, unitTest } from "./test_util.ts";
|
||||
|
||||
unitTest(
|
||||
{ perms: { read: true, run: true, hrtime: true } },
|
||||
async function flockFileSync() {
|
||||
const path = "cli/tests/testdata/fixture.json";
|
||||
const script = (exclusive: boolean, wait: number) => `
|
||||
const { rid } = Deno.openSync("${path}");
|
||||
Deno.flockSync(rid, ${exclusive ? "true" : "false"});
|
||||
await new Promise(res => setTimeout(res, ${wait}));
|
||||
Deno.funlockSync(rid);
|
||||
`;
|
||||
const run = (e: boolean, w: number) =>
|
||||
Deno.run({ cmd: [Deno.execPath(), "eval", "--unstable", script(e, w)] });
|
||||
const firstBlocksSecond = async (
|
||||
first: boolean,
|
||||
second: boolean,
|
||||
): Promise<boolean> => {
|
||||
const firstPs = run(first, 1000);
|
||||
await new Promise((res) => setTimeout(res, 250));
|
||||
const start = performance.now();
|
||||
const secondPs = run(second, 0);
|
||||
await secondPs.status();
|
||||
const didBlock = (performance.now() - start) > 500;
|
||||
firstPs.close();
|
||||
secondPs.close();
|
||||
return didBlock;
|
||||
};
|
||||
|
||||
assertEquals(
|
||||
await firstBlocksSecond(true, false),
|
||||
true,
|
||||
"exclusive blocks shared",
|
||||
);
|
||||
assertEquals(
|
||||
await firstBlocksSecond(false, true),
|
||||
true,
|
||||
"shared blocks exclusive",
|
||||
);
|
||||
assertEquals(
|
||||
await firstBlocksSecond(true, true),
|
||||
true,
|
||||
"exclusive blocks exclusive",
|
||||
);
|
||||
assertEquals(
|
||||
await firstBlocksSecond(false, false),
|
||||
false,
|
||||
"shared does not block shared",
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
unitTest(
|
||||
{ perms: { read: true, run: true, hrtime: true } },
|
||||
async function flockFileAsync() {
|
||||
const path = "cli/tests/testdata/fixture.json";
|
||||
const script = (exclusive: boolean, wait: number) => `
|
||||
const { rid } = await Deno.open("${path}");
|
||||
await Deno.flock(rid, ${exclusive ? "true" : "false"});
|
||||
await new Promise(res => setTimeout(res, ${wait}));
|
||||
await Deno.funlock(rid);
|
||||
`;
|
||||
const run = (e: boolean, w: number) =>
|
||||
Deno.run({ cmd: [Deno.execPath(), "eval", "--unstable", script(e, w)] });
|
||||
const firstBlocksSecond = async (
|
||||
first: boolean,
|
||||
second: boolean,
|
||||
): Promise<boolean> => {
|
||||
const firstPs = run(first, 1000);
|
||||
await new Promise((res) => setTimeout(res, 250));
|
||||
const start = performance.now();
|
||||
const secondPs = run(second, 0);
|
||||
await secondPs.status();
|
||||
const didBlock = (performance.now() - start) > 500;
|
||||
firstPs.close();
|
||||
secondPs.close();
|
||||
return didBlock;
|
||||
};
|
||||
|
||||
assertEquals(
|
||||
await firstBlocksSecond(true, false),
|
||||
true,
|
||||
"exclusive blocks shared",
|
||||
);
|
||||
assertEquals(
|
||||
await firstBlocksSecond(false, true),
|
||||
true,
|
||||
"shared blocks exclusive",
|
||||
);
|
||||
assertEquals(
|
||||
await firstBlocksSecond(true, true),
|
||||
true,
|
||||
"exclusive blocks exclusive",
|
||||
);
|
||||
assertEquals(
|
||||
await firstBlocksSecond(false, false),
|
||||
false,
|
||||
"shared does not block shared",
|
||||
);
|
||||
},
|
||||
);
|
|
@ -65,6 +65,7 @@ atty = "0.2.14"
|
|||
dlopen = "0.1.8"
|
||||
encoding_rs = "0.8.28"
|
||||
filetime = "0.2.14"
|
||||
fs3 = "0.5.0"
|
||||
http = "0.2.4"
|
||||
hyper = { version = "0.14.10", features = ["server", "stream", "http1", "http2", "runtime"] }
|
||||
# TODO(lucacasonato): unlock when https://github.com/tkaitchuck/aHash/issues/95 is resolved
|
||||
|
|
|
@ -385,6 +385,22 @@
|
|||
await core.opAsync("op_fsync_async", rid);
|
||||
}
|
||||
|
||||
function flockSync(rid, exclusive) {
|
||||
core.opSync("op_flock_sync", rid, exclusive === true);
|
||||
}
|
||||
|
||||
async function flock(rid, exclusive) {
|
||||
await core.opAsync("op_flock_async", rid, exclusive === true);
|
||||
}
|
||||
|
||||
function funlockSync(rid) {
|
||||
core.opSync("op_funlock_sync", rid);
|
||||
}
|
||||
|
||||
async function funlock(rid) {
|
||||
await core.opAsync("op_funlock_async", rid);
|
||||
}
|
||||
|
||||
window.__bootstrap.fs = {
|
||||
cwd,
|
||||
chdir,
|
||||
|
@ -433,5 +449,9 @@
|
|||
fdatasyncSync,
|
||||
fsync,
|
||||
fsyncSync,
|
||||
flock,
|
||||
flockSync,
|
||||
funlock,
|
||||
funlockSync,
|
||||
};
|
||||
})(this);
|
||||
|
|
|
@ -136,5 +136,9 @@
|
|||
createHttpClient: __bootstrap.fetch.createHttpClient,
|
||||
http: __bootstrap.http,
|
||||
dlopen: __bootstrap.ffi.dlopen,
|
||||
flock: __bootstrap.fs.flock,
|
||||
flockSync: __bootstrap.fs.flockSync,
|
||||
funlock: __bootstrap.fs.funlock,
|
||||
funlockSync: __bootstrap.fs.funlockSync,
|
||||
};
|
||||
})(this);
|
||||
|
|
|
@ -48,6 +48,10 @@ pub fn init() -> Extension {
|
|||
("op_fsync_async", op_async(op_fsync_async)),
|
||||
("op_fstat_sync", op_sync(op_fstat_sync)),
|
||||
("op_fstat_async", op_async(op_fstat_async)),
|
||||
("op_flock_sync", op_sync(op_flock_sync)),
|
||||
("op_flock_async", op_async(op_flock_async)),
|
||||
("op_funlock_sync", op_sync(op_funlock_sync)),
|
||||
("op_funlock_async", op_async(op_funlock_async)),
|
||||
("op_umask", op_sync(op_umask)),
|
||||
("op_chdir", op_sync(op_chdir)),
|
||||
("op_mkdir_sync", op_sync(op_mkdir_sync)),
|
||||
|
@ -346,6 +350,120 @@ async fn op_fstat_async(
|
|||
Ok(get_stat(metadata))
|
||||
}
|
||||
|
||||
fn op_flock_sync(
|
||||
state: &mut OpState,
|
||||
rid: ResourceId,
|
||||
exclusive: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
use fs3::FileExt;
|
||||
super::check_unstable(state, "Deno.flockSync");
|
||||
|
||||
StdFileResource::with(state, rid, |r| match r {
|
||||
Ok(std_file) => {
|
||||
if exclusive {
|
||||
std_file.lock_exclusive()?;
|
||||
} else {
|
||||
std_file.lock_shared()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(type_error("cannot lock this type of resource".to_string())),
|
||||
})
|
||||
}
|
||||
|
||||
async fn op_flock_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
rid: ResourceId,
|
||||
exclusive: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
use fs3::FileExt;
|
||||
super::check_unstable2(&state, "Deno.flock");
|
||||
|
||||
let resource = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<StdFileResource>(rid)?;
|
||||
|
||||
if resource.fs_file.is_none() {
|
||||
return Err(bad_resource_id());
|
||||
}
|
||||
|
||||
let mut fs_file = RcRef::map(&resource, |r| r.fs_file.as_ref().unwrap())
|
||||
.borrow_mut()
|
||||
.await;
|
||||
|
||||
let std_file = (*fs_file)
|
||||
.0
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.try_clone()
|
||||
.await?
|
||||
.into_std()
|
||||
.await;
|
||||
tokio::task::spawn_blocking(move || -> Result<(), AnyError> {
|
||||
if exclusive {
|
||||
std_file.lock_exclusive()?;
|
||||
} else {
|
||||
std_file.lock_shared()?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
fn op_funlock_sync(
|
||||
state: &mut OpState,
|
||||
rid: ResourceId,
|
||||
_: (),
|
||||
) -> Result<(), AnyError> {
|
||||
use fs3::FileExt;
|
||||
super::check_unstable(state, "Deno.funlockSync");
|
||||
|
||||
StdFileResource::with(state, rid, |r| match r {
|
||||
Ok(std_file) => {
|
||||
std_file.unlock()?;
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(type_error("cannot lock this type of resource".to_string())),
|
||||
})
|
||||
}
|
||||
|
||||
async fn op_funlock_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
rid: ResourceId,
|
||||
_: (),
|
||||
) -> Result<(), AnyError> {
|
||||
use fs3::FileExt;
|
||||
super::check_unstable2(&state, "Deno.funlock");
|
||||
|
||||
let resource = state
|
||||
.borrow_mut()
|
||||
.resource_table
|
||||
.get::<StdFileResource>(rid)?;
|
||||
|
||||
if resource.fs_file.is_none() {
|
||||
return Err(bad_resource_id());
|
||||
}
|
||||
|
||||
let mut fs_file = RcRef::map(&resource, |r| r.fs_file.as_ref().unwrap())
|
||||
.borrow_mut()
|
||||
.await;
|
||||
|
||||
let std_file = (*fs_file)
|
||||
.0
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.try_clone()
|
||||
.await?
|
||||
.into_std()
|
||||
.await;
|
||||
tokio::task::spawn_blocking(move || -> Result<(), AnyError> {
|
||||
std_file.unlock()?;
|
||||
Ok(())
|
||||
})
|
||||
.await?
|
||||
}
|
||||
|
||||
fn op_umask(
|
||||
state: &mut OpState,
|
||||
mask: Option<u32>,
|
||||
|
|
Loading…
Reference in a new issue