diff --git a/src/common/button_config.rs b/src/common/button_config.rs
index 81de989..8f1ae4f 100644
--- a/src/common/button_config.rs
+++ b/src/common/button_config.rs
@@ -1,6 +1,7 @@
 use std::collections::HashMap;
 
 use crate::common::*;
+use crate::sync::*;
 use crate::input::{ControllerStyle::*, *};
 use crate::training::frame_counter;
 use crate::training::ui::menu::VANILLA_MENU_ACTIVE;
@@ -198,7 +199,7 @@ lazy_static! {
 fn _combo_passes(p1_controller: Controller, combo: ButtonCombo) -> bool {
     unsafe {
         // Prevent button combos from passing if either the vanilla or mod menu is open
-        if VANILLA_MENU_ACTIVE || QUICK_MENU_ACTIVE {
+        if VANILLA_MENU_ACTIVE || read_rwlock(&QUICK_MENU_ACTIVE) {
             return false;
         }
 
@@ -264,7 +265,7 @@ pub fn handle_final_input_mapping(player_idx: i32, controller_struct: &mut SomeC
                 // Here, we just finished holding start
                 if *start_hold_frames > 0
                     && *start_hold_frames < 10
-                    && unsafe { !QUICK_MENU_ACTIVE }
+                    && !read_rwlock(&QUICK_MENU_ACTIVE)
                     && menu_close_wait_frame == 0
                 {
                     // If we held for fewer than 10 frames, let's let the game know that
diff --git a/src/common/menu.rs b/src/common/menu.rs
index 9ddac59..f875340 100644
--- a/src/common/menu.rs
+++ b/src/common/menu.rs
@@ -7,16 +7,18 @@ use std::ptr::addr_of;
 use lazy_static::lazy_static;
 use parking_lot::Mutex;
 use skyline::nn::hid::GetNpadStyleSet;
+use std::sync::RwLock;
 
 use crate::common::button_config::button_mapping;
 use crate::common::*;
 use crate::events::{Event, EVENT_QUEUE};
 use crate::input::*;
 use crate::logging::*;
+use crate::sync::*;
 use crate::training::frame_counter;
 
 pub const MENU_CLOSE_WAIT_FRAMES: u32 = 15;
-pub static mut QUICK_MENU_ACTIVE: bool = false;
+pub static QUICK_MENU_ACTIVE: RwLock<bool> = RwLock::new(false);
 
 pub unsafe fn menu_condition() -> bool {
     button_config::combo_passes(button_config::ButtonCombo::OpenMenu)
@@ -82,7 +84,7 @@ pub unsafe fn set_menu_from_json(message: &str) {
 
 pub fn spawn_menu() {
     unsafe {
-        QUICK_MENU_ACTIVE = true;
+        assign_rwlock(&QUICK_MENU_ACTIVE, true);
         let mut app = QUICK_MENU_APP.lock();
         app.page = AppPage::SUBMENU;
         *MENU_RECEIVED_INPUT.data_ptr() = true;
@@ -149,7 +151,7 @@ pub fn handle_final_input_mapping(
                 frame_counter::reset_frame_count(*MENU_CLOSE_FRAME_COUNTER);
             }
 
-            if QUICK_MENU_ACTIVE {
+            if read_rwlock(&QUICK_MENU_ACTIVE) {
                 // If we're here, remove all other presses
                 *out = MappedInputs::empty();
 
@@ -166,7 +168,7 @@ pub fn handle_final_input_mapping(
                     .iter()
                     .all(|i| GetNpadStyleSet(i as *const _).flags == 0)
                 {
-                    QUICK_MENU_ACTIVE = false;
+                    assign_rwlock(&QUICK_MENU_ACTIVE, false);
                     return;
                 }
 
@@ -205,7 +207,7 @@ pub fn handle_final_input_mapping(
                     if app.page == AppPage::CLOSE {
                         // Leave menu.
                         frame_counter::start_counting(*MENU_CLOSE_FRAME_COUNTER);
-                        QUICK_MENU_ACTIVE = false;
+                        assign_rwlock(&QUICK_MENU_ACTIVE, false);
                         let menu_json = app.get_serialized_settings_with_defaults();
                         set_menu_from_json(&menu_json);
                         EVENT_QUEUE.push(Event::menu_open(menu_json));
diff --git a/src/lib.rs b/src/lib.rs
index 95f5819..1bd7fdd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -36,6 +36,7 @@ mod hitbox_visualizer;
 mod training;
 
 mod logging;
+mod sync;
 
 fn nro_main(nro: &NroInfo<'_>) {
     if nro.module.isLoaded {
diff --git a/src/sync.rs b/src/sync.rs
new file mode 100644
index 0000000..8619306
--- /dev/null
+++ b/src/sync.rs
@@ -0,0 +1,26 @@
+/// Convenience functions for interacting with RwLocks
+
+use std::sync::{RwLock, RwLockWriteGuard, RwLockReadGuard};
+
+/// Gets a copy of a value inside a RwLock and immediately unlocks
+/// 
+/// Requires <T: Copy> such as a bool or usize
+pub fn read_rwlock<T: Copy>(rwlock: &RwLock<T>) -> T { *rwlock.read().unwrap() }
+
+/// Gets a clone of a value inside a RwLock and immediately unlocks
+/// 
+/// Can be used if <T> is not Copy, such as Vec<u32>
+pub fn read_rwlock_clone<T: Clone>(rwlock: &RwLock<T>) -> T { rwlock.read().unwrap().clone() }
+
+/// Assigns a new value to a RwLock and immediately unlocks
+pub fn assign_rwlock<T>(rwlock: &RwLock<T>, new_val: T) { *rwlock.write().unwrap() = new_val }
+
+/// Locks a RwLock for writing and returns the guard
+/// 
+/// Don't forget to drop the guard as soon as you're finished with it
+pub fn lock_write_rwlock<T>(rwlock: &RwLock<T>) -> RwLockWriteGuard<T> { rwlock.write().unwrap() }
+
+/// Locks a RwLock for reading and returns the guard
+/// 
+/// Don't forget to drop the guard as soon as you're finished with it
+pub fn lock_read_rwlock<T>(rwlock: &RwLock<T>) -> RwLockReadGuard<T> { rwlock.read().unwrap() }
\ No newline at end of file
diff --git a/src/training/input_log.rs b/src/training/input_log.rs
index b47c281..50d18d5 100644
--- a/src/training/input_log.rs
+++ b/src/training/input_log.rs
@@ -2,7 +2,10 @@ use itertools::Itertools;
 use once_cell::sync::Lazy;
 use std::collections::VecDeque;
 
-use crate::common::{input::*, menu::QUICK_MENU_ACTIVE, try_get_module_accessor};
+use crate::common::input::*;
+use crate::menu::QUICK_MENU_ACTIVE;
+use crate::sync::*;
+use crate::try_get_module_accessor;
 use lazy_static::lazy_static;
 use parking_lot::Mutex;
 use skyline::nn::ui2d::ResColor;
@@ -320,7 +323,7 @@ pub fn handle_final_input_mapping(
             return;
         }
 
-        if QUICK_MENU_ACTIVE {
+        if read_rwlock(&QUICK_MENU_ACTIVE) {
             return;
         }
 
diff --git a/src/training/ui/display.rs b/src/training/ui/display.rs
index c85ab08..66486a4 100644
--- a/src/training/ui/display.rs
+++ b/src/training/ui/display.rs
@@ -5,6 +5,7 @@ use smash::ui2d::{SmashPane, SmashTextBox};
 
 use crate::common::menu::QUICK_MENU_ACTIVE;
 use crate::common::TRAINING_MENU_ADDR;
+use crate::sync::*;
 use crate::training::ui;
 macro_rules! display_parent_fmt {
     ($x:ident) => {
@@ -40,7 +41,7 @@ pub unsafe fn draw(root_pane: &Pane) {
     root_pane
         .find_pane_by_name_recursive(display_parent_fmt!(notification_idx))
         .unwrap()
-        .set_visible(notification.is_some() && !QUICK_MENU_ACTIVE);
+        .set_visible(notification.is_some() && !read_rwlock(&QUICK_MENU_ACTIVE));
     if notification.is_none() {
         return;
     }
diff --git a/src/training/ui/input_log.rs b/src/training/ui/input_log.rs
index 49e8cac..909c81c 100644
--- a/src/training/ui/input_log.rs
+++ b/src/training/ui/input_log.rs
@@ -4,15 +4,14 @@ use skyline::nn::ui2d::*;
 use smash::ui2d::{SmashPane, SmashTextBox};
 use training_mod_consts::{InputDisplay, MENU};
 
-use crate::{
-    common::{consts::status_display_name, menu::QUICK_MENU_ACTIVE},
-    training::{
-        input_log::{
-            DirectionStrength, InputLog, DRAW_LOG_BASE_IDX, NUM_LOGS, P1_INPUT_LOGS, WHITE, YELLOW,
-        },
-        ui::{fade_out, menu::VANILLA_MENU_ACTIVE},
-    },
+use crate::common::consts::status_display_name;
+use crate::menu::QUICK_MENU_ACTIVE;
+use crate::sync::*;
+use crate::training::input_log::{
+    DirectionStrength, InputLog, DRAW_LOG_BASE_IDX, NUM_LOGS, P1_INPUT_LOGS, WHITE, YELLOW,
 };
+use crate::training::ui::fade_out;
+use crate::training::ui::menu::VANILLA_MENU_ACTIVE;
 
 macro_rules! log_parent_fmt {
     ($x:ident) => {
@@ -193,7 +192,9 @@ pub unsafe fn draw(root_pane: &Pane) {
         .find_pane_by_name_recursive("TrModInputLog")
         .unwrap();
     logs_pane.set_visible(
-        !QUICK_MENU_ACTIVE && !VANILLA_MENU_ACTIVE && MENU.input_display != InputDisplay::NONE,
+        !read_rwlock(&QUICK_MENU_ACTIVE)
+            && !VANILLA_MENU_ACTIVE
+            && MENU.input_display != InputDisplay::NONE,
     );
     if MENU.input_display == InputDisplay::NONE {
         return;
diff --git a/src/training/ui/menu.rs b/src/training/ui/menu.rs
index 03ffc4c..7d6b376 100644
--- a/src/training/ui/menu.rs
+++ b/src/training/ui/menu.rs
@@ -7,9 +7,13 @@ use training_mod_tui::{
     App, AppPage, ConfirmationState, SliderState, NX_SUBMENU_COLUMNS, NX_SUBMENU_ROWS,
 };
 
-use crate::common::menu::{MENU_CLOSE_FRAME_COUNTER, MENU_CLOSE_WAIT_FRAMES, MENU_RECEIVED_INPUT};
+use crate::common::menu::{
+    MENU_CLOSE_FRAME_COUNTER, MENU_CLOSE_WAIT_FRAMES, MENU_RECEIVED_INPUT, P1_CONTROLLER_STYLE,
+    QUICK_MENU_ACTIVE, QUICK_MENU_APP,
+};
+use crate::input::*;
+use crate::sync::*;
 use crate::training::frame_counter;
-use crate::{common, common::menu::QUICK_MENU_ACTIVE, input::*};
 use training_mod_consts::TOGGLE_MAX;
 
 use super::fade_out;
@@ -464,7 +468,7 @@ pub unsafe fn draw(root_pane: &Pane) {
         != -80.0;
 
     let overall_parent_pane = root_pane.find_pane_by_name_recursive("TrModMenu").unwrap();
-    overall_parent_pane.set_visible(QUICK_MENU_ACTIVE && !VANILLA_MENU_ACTIVE);
+    overall_parent_pane.set_visible(read_rwlock(&QUICK_MENU_ACTIVE) && !VANILLA_MENU_ACTIVE);
     let menu_close_wait_frame = frame_counter::get_frame_count(*MENU_CLOSE_FRAME_COUNTER);
     fade_out(
         overall_parent_pane,
@@ -514,7 +518,7 @@ pub unsafe fn draw(root_pane: &Pane) {
         .find_pane_by_name_recursive("status_R")
         .expect("Unable to find status_R pane");
     // status_r_pane.flags |= 1 << PaneFlag::InfluencedAlpha as u8;
-    status_r_pane.set_visible(!QUICK_MENU_ACTIVE);
+    status_r_pane.set_visible(!read_rwlock(&QUICK_MENU_ACTIVE));
 
     root_pane
         .find_pane_by_name_recursive("TrModSlider")
@@ -524,7 +528,7 @@ pub unsafe fn draw(root_pane: &Pane) {
     // Update menu display
     // Grabbing lock as read-only, essentially
     // We don't really need to change anything, but get_before_selected requires &mut self
-    let app = &mut *crate::common::menu::QUICK_MENU_APP.data_ptr();
+    let app = &mut *QUICK_MENU_APP.data_ptr();
 
     let tab_titles = [
         app.tabs
@@ -538,7 +542,7 @@ pub unsafe fn draw(root_pane: &Pane) {
             .title,
     ];
 
-    let is_gcc = (*common::menu::P1_CONTROLLER_STYLE.data_ptr()) == ControllerStyle::GCController;
+    let is_gcc = (*P1_CONTROLLER_STYLE.data_ptr()) == ControllerStyle::GCController;
     let button_mapping = if is_gcc {
         GCC_BUTTON_MAPPING.clone()
     } else {
diff --git a/src/training/ui/mod.rs b/src/training/ui/mod.rs
index 480ef51..89066c8 100644
--- a/src/training/ui/mod.rs
+++ b/src/training/ui/mod.rs
@@ -10,6 +10,7 @@ use crate::common::offsets::{OFFSET_DRAW, OFFSET_LAYOUT_ARC_MALLOC};
 use crate::common::{is_ready_go, is_training_mode};
 #[cfg(feature = "layout_arc_from_file")]
 use crate::consts::LAYOUT_ARC_PATH;
+use crate::sync::*;
 use crate::training::frame_counter;
 
 mod damage;
@@ -71,7 +72,7 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64)
     {
         // InfluencedAlpha means "Should my children panes' alpha be influenced by mine, as the parent?"
         root_pane.flags |= 1 << PaneFlag::InfluencedAlpha as u8;
-        root_pane.set_visible(MENU.hud == OnOff::ON && !QUICK_MENU_ACTIVE);
+        root_pane.set_visible(MENU.hud == OnOff::ON && !read_rwlock(&QUICK_MENU_ACTIVE));
     }
 
     damage::draw(root_pane, &layout_name);
diff --git a/training_mod_tui/src/structures/stateful_table.rs b/training_mod_tui/src/structures/stateful_table.rs
index de20f03..4b6d2c5 100644
--- a/training_mod_tui/src/structures/stateful_table.rs
+++ b/training_mod_tui/src/structures/stateful_table.rs
@@ -266,7 +266,7 @@ impl<'a, T: Clone + Serialize> Iterator for StatefulTableIteratorMut<'a, T> {
 }
 
 impl<'a, T: Clone + Serialize + 'a> StatefulTable<T> {
-    pub fn iter_mut(&'a mut self) -> StatefulTableIteratorMut<T> {
+    pub fn iter_mut(&'a mut self) -> StatefulTableIteratorMut<'a, T> {
         StatefulTableIteratorMut {
             inner: self.items.iter_mut().flatten(),
         }