summaryrefslogtreecommitdiff
path: root/cli/util/sync/sync_read_async_write_lock.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-06-05 15:17:35 -0400
committerGitHub <noreply@github.com>2024-06-05 15:17:35 -0400
commit1b355d8a87a3ad43bf240aa66b88eb98c1cd777f (patch)
tree0422d22a6a0e40873eea4c1ef3eff9d6648f1c51 /cli/util/sync/sync_read_async_write_lock.rs
parent7ed90a20d04982ae15a52ae2378cbffd4b6839df (diff)
refactor(npm): improve locking around updating npm resolution (#24104)
Introduces a `SyncReadAsyncWriteLock` to make it harder to write to the npm resolution without first waiting async in a queue. For the npm resolution, reading synchronously is fine, but when updating, someone should wait async, clone the data, then write the data at the end back.
Diffstat (limited to 'cli/util/sync/sync_read_async_write_lock.rs')
-rw-r--r--cli/util/sync/sync_read_async_write_lock.rs62
1 files changed, 62 insertions, 0 deletions
diff --git a/cli/util/sync/sync_read_async_write_lock.rs b/cli/util/sync/sync_read_async_write_lock.rs
new file mode 100644
index 000000000..8bd211aa7
--- /dev/null
+++ b/cli/util/sync/sync_read_async_write_lock.rs
@@ -0,0 +1,62 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::parking_lot::RwLock;
+use deno_core::parking_lot::RwLockReadGuard;
+use deno_core::parking_lot::RwLockWriteGuard;
+
+use super::TaskQueue;
+use super::TaskQueuePermit;
+
+/// A lock that can be read synchronously at any time (including when
+/// being written to), but must write asynchronously.
+pub struct SyncReadAsyncWriteLockWriteGuard<'a, T: Send + Sync> {
+ _update_permit: TaskQueuePermit<'a>,
+ data: &'a RwLock<T>,
+}
+
+impl<'a, T: Send + Sync> SyncReadAsyncWriteLockWriteGuard<'a, T> {
+ pub fn read(&self) -> RwLockReadGuard<'_, T> {
+ self.data.read()
+ }
+
+ /// Warning: Only `write()` with data you created within this
+ /// write this `SyncReadAsyncWriteLockWriteGuard`.
+ ///
+ /// ```rs
+ /// let mut data = lock.write().await;
+ ///
+ /// let mut data = data.read().clone();
+ /// data.value = 2;
+ /// *data.write() = data;
+ /// ```
+ pub fn write(&self) -> RwLockWriteGuard<'_, T> {
+ self.data.write()
+ }
+}
+
+/// A lock that can only be
+pub struct SyncReadAsyncWriteLock<T: Send + Sync> {
+ data: RwLock<T>,
+ update_queue: TaskQueue,
+}
+
+impl<T: Send + Sync> SyncReadAsyncWriteLock<T> {
+ pub fn new(data: T) -> Self {
+ Self {
+ data: RwLock::new(data),
+ update_queue: TaskQueue::default(),
+ }
+ }
+
+ pub fn read(&self) -> RwLockReadGuard<'_, T> {
+ self.data.read()
+ }
+
+ pub async fn acquire(&self) -> SyncReadAsyncWriteLockWriteGuard<'_, T> {
+ let update_permit = self.update_queue.acquire().await;
+ SyncReadAsyncWriteLockWriteGuard {
+ _update_permit: update_permit,
+ data: &self.data,
+ }
+ }
+}