-
Notifications
You must be signed in to change notification settings - Fork 0
feat(tty): serialize terminal output writes with a lock #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
a4e7860
feat(tty): serialize terminal output writes with a lock
tonyfettes 2770694
fix(tty): add write_lock to Windows white-box Tty literal
tonyfettes 67ba977
fix(tty): keep moonbitlang/async at 0.19.1
tonyfettes 9dc7503
docs(plan): record output write lock task (TTY-23)
tonyfettes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| ///| | ||
| /// A mutual-exclusion lock. | ||
| /// | ||
| /// `Lock` wraps a binary semaphore: at most one task can hold it at a time. | ||
| /// Other tasks calling `acquire` block until the holder calls `release`, and | ||
| /// waiters are served first-come-first-serve. Prefer `with_lock`, which pairs | ||
| /// the acquire/release for you and releases even when the action fails. | ||
| struct Lock(@async/semaphore.Semaphore) | ||
|
|
||
| ///| | ||
| /// Create a new, unlocked `Lock`. | ||
| pub fn Lock::new() -> Lock { | ||
| Lock(Semaphore(1)) | ||
| } | ||
|
|
||
| ///| | ||
| /// Acquire the lock, blocking until it is available. | ||
| /// | ||
| /// As a blocking point, `acquire` may be cancelled, in which case the lock is | ||
| /// not held and the cancellation is raised. Every successful `acquire` must be | ||
| /// matched by exactly one `release`. | ||
| pub async fn Lock::acquire(self : Lock) -> Unit { | ||
| self.0.acquire() | ||
| } | ||
|
|
||
| ///| | ||
| /// Try to acquire the lock without blocking. | ||
| /// | ||
| /// Returns `true` and takes the lock if it was free, otherwise returns `false` | ||
| /// immediately. A successful acquisition must be matched by one `release`. | ||
| pub fn Lock::try_acquire(self : Lock) -> Bool { | ||
| self.0.try_acquire() | ||
| } | ||
|
|
||
| ///| | ||
| /// Release the lock, waking the next waiter if any. | ||
| pub fn Lock::release(self : Lock) -> Unit { | ||
| self.0.release() | ||
| } | ||
|
|
||
| ///| | ||
| /// Run `action` while holding the lock, returning its result. | ||
| /// | ||
| /// The lock is released whether `action` returns normally or fails. If | ||
| /// `acquire` is cancelled before the lock is taken, `action` does not run. | ||
| pub async fn[X] Lock::with_lock(self : Lock, action : async () -> X) -> X { | ||
| self.acquire() | ||
| defer self.release() | ||
| action() | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| ///| | ||
| /// A held lock keeps concurrent critical sections from overlapping, even when | ||
| /// the holder suspends. | ||
| async test "with_lock serializes concurrent critical sections" { | ||
| let lock = @lock.Lock::new() | ||
| let mut in_critical = false | ||
| let mut overlaps = 0 | ||
| let order = [] | ||
| @async.with_task_group(group => { | ||
| for id in 0..<5 { | ||
| group.spawn_bg(() => { | ||
| lock.with_lock(() => { | ||
| if in_critical { | ||
| overlaps += 1 | ||
| } | ||
| in_critical = true | ||
| // Suspend while holding the lock; a missing lock would let another task | ||
| // enter here and trip the `overlaps` counter. | ||
| @async.sleep(1) | ||
| order.push(id) | ||
| in_critical = false | ||
| }) | ||
| }) | ||
| } | ||
| }) | ||
| assert_eq(overlaps, 0) | ||
| assert_eq(order.length(), 5) | ||
| } | ||
|
|
||
| ///| | ||
| /// `with_lock` releases the lock even when the action fails. | ||
| async test "with_lock releases on failure" { | ||
| let lock = @lock.Lock::new() | ||
| let failed = try { | ||
| lock.with_lock(() => fail("boom")) | ||
| false | ||
| } catch { | ||
| _ => true | ||
| } | ||
| assert_eq(failed, true) | ||
| // The lock must be free again, so a follow-up acquire succeeds immediately. | ||
| assert_eq(lock.try_acquire(), true) | ||
| lock.release() | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| import { | ||
| "moonbitlang/async/semaphore" @async/semaphore, | ||
| } | ||
|
|
||
| import { | ||
| "moonbitlang/async", | ||
| } for "test" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // Generated using `moon info`, DON'T EDIT IT | ||
| package "moonbit-community/tty/internal/lock" | ||
|
|
||
| // Values | ||
|
|
||
| // Errors | ||
|
|
||
| // Types and methods | ||
| type Lock | ||
| pub async fn Lock::acquire(Self) -> Unit | ||
| pub fn Lock::new() -> Self | ||
| pub fn Lock::release(Self) -> Unit | ||
| pub fn Lock::try_acquire(Self) -> Bool | ||
| pub async fn[X] Lock::with_lock(Self, async () -> X) -> X | ||
|
|
||
| // Type aliases | ||
|
|
||
| // Traits | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.