aboutsummaryrefslogtreecommitdiff
path: root/src/sync.rs
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-07-01 10:34:08 -0700
committerJoe Wilm <joe@jwilm.com>2016-07-01 10:34:08 -0700
commitae39d38a151f264b09c8e7a698d3838f8aa18dd8 (patch)
treeb234a6dcc409db9e0dee809f837c0110684db38e /src/sync.rs
parentd514b382237d4df2e33503602ec2af4c0cbb2189 (diff)
downloadalacritty-ae39d38a151f264b09c8e7a698d3838f8aa18dd8.tar.gz
alacritty-ae39d38a151f264b09c8e7a698d3838f8aa18dd8.zip
Improve pty reading and renderer synchronization
The pty read thread now runs the parser and directly updates the terminal in the same thread. This obviates the need for a channel which sends every char read from the pty; this is a huge performance boon. Synchronization between the updater and the renderer is now achieved with a PriorityMutex. Previously, an atomic bool was (poorly) used to request the lock on terminal. The PriorityMutex is dead simple to use, and it _Just Works_.
Diffstat (limited to 'src/sync.rs')
-rw-r--r--src/sync.rs94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/sync.rs b/src/sync.rs
new file mode 100644
index 00000000..e341d78e
--- /dev/null
+++ b/src/sync.rs
@@ -0,0 +1,94 @@
+// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Synchronization types
+//!
+//! Most importantly, a priority mutex is included
+use std::ops::{Deref, DerefMut};
+
+use parking_lot::{Mutex, MutexGuard};
+
+/// A priority mutex
+///
+/// A triple locking strategy is used where low priority locks must go through an additional mutex
+/// to access the data. The gist is
+///
+/// Low priority: lock low, lock next, lock data, unlock next, {do work}, unlock data, unlock low
+/// High priority: lock next, lock data, unlock next, {do work}, unlock data
+///
+/// By keeping the low lock active while working on data, a high priority consumer has immediate
+/// access to the next mutex.
+pub struct PriorityMutex<T> {
+ /// Data
+ data: Mutex<T>,
+ /// Next-to-access
+ next: Mutex<()>,
+ /// Low-priority access
+ low: Mutex<()>,
+}
+
+/// Mutex guard for low priority locks
+pub struct LowPriorityMutexGuard<'a, T: 'a> {
+ data: MutexGuard<'a, T>,
+ _low: MutexGuard<'a, ()>,
+}
+
+impl<'a, T> Deref for LowPriorityMutexGuard<'a, T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &T {
+ self.data.deref()
+ }
+}
+
+impl<'a, T> DerefMut for LowPriorityMutexGuard<'a, T> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut T {
+ self.data.deref_mut()
+ }
+}
+
+impl<T> PriorityMutex<T> {
+ /// Create a new priority mutex
+ pub fn new(data: T) -> PriorityMutex<T> {
+ PriorityMutex {
+ data: Mutex::new(data),
+ next: Mutex::new(()),
+ low: Mutex::new(()),
+ }
+ }
+
+ /// Lock the mutex with high priority
+ pub fn lock_high(&self) -> MutexGuard<T> {
+ // Must bind to a temporary or the lock will be freed before going
+ // into data.lock()
+ let _next = self.next.lock();
+ self.data.lock()
+ }
+
+ /// Lock the mutex with low priority
+ pub fn lock_low(&self) -> LowPriorityMutexGuard<T> {
+ let low = self.low.lock();
+ // Must bind to a temporary or the lock will be freed before going
+ // into data.lock()
+ let _next = self.next.lock();
+ let data = self.data.lock();
+
+ LowPriorityMutexGuard {
+ data: data,
+ _low: low,
+ }
+ }
+}