diff --git a/editor/src/asset/creator.rs b/editor/src/asset/creator.rs index 116e52bbd..6ccab48fc 100644 --- a/editor/src/asset/creator.rs +++ b/editor/src/asset/creator.rs @@ -24,7 +24,7 @@ use crate::{ manager::ResourceManager, untyped::{ResourceKind, UntypedResource}, }, - core::{log::Log, make_pretty_type_name, pool::Handle, Uuid}, + core::{log::Log, make_pretty_type_name, pool::Handle, SafeLock, Uuid}, engine::Engine, gui::{ button::{ButtonBuilder, ButtonMessage}, @@ -59,7 +59,7 @@ pub struct ResourceCreator { impl ResourceCreator { pub fn new(ctx: &mut BuildContext, resource_manager: &ResourceManager) -> Self { let rm_state = resource_manager.state(); - let mut constructors = rm_state.constructors_container.map.lock(); + let mut constructors = rm_state.constructors_container.map.safe_lock(); let mut items = Vec::new(); let mut supported_resource_data_uuids = Vec::new(); for (uuid, constructor) in constructors.iter_mut() { @@ -196,7 +196,7 @@ impl ResourceCreator { .supported_resource_data_uuids .get(self.selected.unwrap_or_default()) { - let loaders = resource_manager_state.loaders.lock(); + let loaders = resource_manager_state.loaders.safe_lock(); if let Some(loader) = loaders .iter() .find(|loader| &loader.data_type_uuid() == data_type_uuid) @@ -222,7 +222,10 @@ impl ResourceCreator { } else if let Some(ButtonMessage::Click) = message.data() { if message.destination() == self.ok { let resource_manager_state = engine.resource_manager.state(); - let mut constructors = resource_manager_state.constructors_container.map.lock(); + let mut constructors = resource_manager_state + .constructors_container + .map + .safe_lock(); if let Some(mut instance) = self .supported_resource_data_uuids diff --git a/editor/src/asset/mod.rs b/editor/src/asset/mod.rs index 563c6ba87..c89c3c876 100644 --- a/editor/src/asset/mod.rs +++ b/editor/src/asset/mod.rs @@ -33,7 +33,7 @@ use crate::{ asset::{manager::ResourceManager, options::BaseImportOptions}, core::{ append_extension, err, futures::executor::block_on, log::Log, make_relative_path, - ok_or_continue, pool::Handle, some_or_continue, + ok_or_continue, pool::Handle, some_or_continue, SafeLock, }, engine::Engine, graph::{BaseSceneGraph, SceneGraph}, @@ -175,7 +175,7 @@ pub struct AssetBrowser { fn is_path_in_registry(path: &Path, resource_manager: &ResourceManager) -> bool { let rm_state = resource_manager.state(); - let registry = rm_state.resource_registry.lock(); + let registry = rm_state.resource_registry.safe_lock(); if let Some(registry_directory) = registry.directory() { if let Ok(canonical_registry_path) = registry_directory.canonicalize() { if let Ok(canonical_path) = path.canonicalize() { @@ -200,7 +200,7 @@ fn is_supported_resource(ext: &OsStr, resource_manager: &ResourceManager) -> boo resource_manager .state() .loaders - .lock() + .safe_lock() .iter() .any(|loader| loader.supports_extension(ext)) } @@ -812,7 +812,7 @@ impl AssetBrowser { let search_text = search_text.to_lowercase(); let registry = engine.resource_manager.state().resource_registry.clone(); - let registry = registry.lock(); + let registry = registry.safe_lock(); let mut paths = Vec::new(); for resource_path in registry.inner().values() { let file_stem = some_or_continue!(resource_path @@ -885,7 +885,7 @@ impl AssetBrowser { resource_manager: &ResourceManager, ) -> Option> { let rm_state = resource_manager.state(); - let loaders = rm_state.loaders.lock(); + let loaders = rm_state.loaders.safe_lock(); for loader in loaders.iter() { if loader.supports_extension(&extension.to_string_lossy()) { return loader.default_import_options(); diff --git a/editor/src/asset/preview/cache.rs b/editor/src/asset/preview/cache.rs index 708d87b01..a2023aa70 100644 --- a/editor/src/asset/preview/cache.rs +++ b/editor/src/asset/preview/cache.rs @@ -25,7 +25,7 @@ use crate::{ }, fyrox::{ asset::untyped::UntypedResource, - core::{futures::executor::block_on, parking_lot::Mutex, pool::Handle, Uuid}, + core::{futures::executor::block_on, parking_lot::Mutex, pool::Handle, SafeLock, Uuid}, engine::Engine, fxhash::FxHashMap, gui::{message::MessageDirection, UiNode}, @@ -57,7 +57,7 @@ impl AssetPreviewCache { for request in receiver.iter() { let resource = request.resource.clone(); if block_on(resource).is_ok() { - queue.lock().push_back(request); + queue.safe_lock().push_back(request); } } }); @@ -108,7 +108,7 @@ impl AssetPreviewCache { ) { let mut generated = 0; let queue = self.queue.clone(); - let mut queue = queue.lock(); + let mut queue = queue.safe_lock(); while let Some(request) = queue.pop_back() { let IconRequest { widget_handle, diff --git a/editor/src/asset/selection.rs b/editor/src/asset/selection.rs index 09109dd01..b91409086 100644 --- a/editor/src/asset/selection.rs +++ b/editor/src/asset/selection.rs @@ -23,7 +23,7 @@ use crate::{ command::{make_command, Command, SetPropertyCommand}, fyrox::{ asset::{manager::ResourceManager, options::BaseImportOptions, ResourceData}, - core::{futures::executor::block_on, reflect::Reflect}, + core::{futures::executor::block_on, reflect::Reflect, SafeLock}, engine::Engine, gui::inspector::PropertyChanged, scene::SceneContainer, @@ -59,7 +59,7 @@ struct SaveResourceCommand { impl SaveResourceCommand { fn save(&self) { - let mut guard = self.resource.0.lock(); + let mut guard = self.resource.0.safe_lock(); if let Some(data) = guard.state.data_mut() { Log::verify(data.save(&self.path)); } @@ -98,7 +98,7 @@ fn load_import_options_or_default( ) -> Option> { if let Some(extension) = resource_path.extension() { let rm_state = resource_manager.state(); - let loaders = rm_state.loaders.lock(); + let loaders = rm_state.loaders.safe_lock(); for loader in loaders.iter() { if loader.supports_extension(&extension.to_string_lossy()) { return if let Some(import_options) = block_on(loader.try_load_import_settings( @@ -163,7 +163,7 @@ impl SelectionContainer for AssetSelection { block_on(self.resource_manager.request_untyped(&resource.path)) { if !self.resource_manager.is_built_in_resource(&resource) { - let guard = resource.0.lock(); + let guard = resource.0.safe_lock(); if let Some(data) = guard.state.data_ref() { callback(&*data.0 as &dyn Reflect, false) } @@ -205,7 +205,7 @@ impl SelectionContainer for AssetSelection { let resource2 = resource.clone(); if !self.resource_manager.is_built_in_resource(&resource) { if let Some(command) = make_command(args, move |_| { - let mut guard = resource.0.lock(); + let mut guard = resource.0.safe_lock(); let data = &mut **guard.state.data_mut()?; // SAFETY: This is safe, because the closure owns its own copy of // resource strong ref, and the entity getter is used only once per @@ -264,7 +264,7 @@ impl SelectionContainer for AssetSelection { path.to_string(), value, move |_| { - let mut guard = resource.0.lock(); + let mut guard = resource.0.safe_lock(); let data = &mut **guard.state.data_mut()?; // SAFETY: This is safe, because the closure owns its own copy of // resource strong ref, and the entity getter is used only once per diff --git a/editor/src/asset/selector.rs b/editor/src/asset/selector.rs index a2540db32..223ff8994 100644 --- a/editor/src/asset/selector.rs +++ b/editor/src/asset/selector.rs @@ -28,7 +28,7 @@ use crate::{ asset::{manager::ResourceManager, untyped::UntypedResource, TypedResourceData}, core::{ algebra::Vector2, log::Log, pool::Handle, reflect::prelude::*, type_traits::prelude::*, - visitor::prelude::*, + visitor::prelude::*, SafeLock, }, graph::SceneGraph, gui::{ @@ -307,8 +307,8 @@ impl<'a> AssetSelectorBuilder<'a> { ctx: &mut BuildContext, ) -> Handle { let state = resource_manager.state(); - let loaders = state.loaders.lock(); - let registry = state.resource_registry.lock(); + let loaders = state.loaders.safe_lock(); + let registry = state.resource_registry.safe_lock(); let mut supported_resource_paths = loaders .iter() @@ -337,7 +337,7 @@ impl<'a> AssetSelectorBuilder<'a> { .collect::>(); supported_resource_paths.extend(state.built_in_resources.values().filter_map(|res| { - let resource_state = res.resource.0.lock(); + let resource_state = res.resource.0.safe_lock(); if self .asset_types .contains(&resource_state.state.data_ref()?.type_uuid()) diff --git a/editor/src/export/asset.rs b/editor/src/export/asset.rs index 1ae177ce6..ad946015c 100644 --- a/editor/src/export/asset.rs +++ b/editor/src/export/asset.rs @@ -23,7 +23,9 @@ use crate::export::utils; use fyrox::{ asset::manager::ResourceManager, - core::{futures::executor, futures::future::join_all, log::Log, platform::TargetPlatform}, + core::{ + futures::executor, futures::future::join_all, log::Log, platform::TargetPlatform, SafeLock, + }, }; use std::{fs, io, path::Path}; @@ -38,7 +40,7 @@ pub fn copy_and_convert_assets( if convert { let rm = resource_manager.state(); let io = rm.resource_io.clone(); - let loaders = rm.loaders.lock(); + let loaders = rm.loaders.safe_lock(); let mut tasks = Vec::new(); // Iterate over the file system and try to convert all the supported resources. diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 01a44e8ce..e7128c712 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -78,7 +78,7 @@ use crate::{ task::TaskPool, uuid::Uuid, watcher::FileSystemWatcher, - TypeUuidProvider, + SafeLock, TypeUuidProvider, }, dpi::{PhysicalPosition, PhysicalSize}, engine::{ @@ -232,7 +232,7 @@ lazy_static! { } pub fn load_texture_internal(data: &[u8]) -> Option { - let mut cache = EDITOR_TEXTURE_CACHE.lock(); + let mut cache = EDITOR_TEXTURE_CACHE.safe_lock(); // Editor use data that is embedded in the binary, so each such piece of data will have fixed // location in memory. This fact allows us to cache the resources and skip redundant loading if @@ -2095,7 +2095,7 @@ impl Editor { } } - let mut loading_scenes = self.loading_scenes.lock(); + let mut loading_scenes = self.loading_scenes.safe_lock(); if loading_scenes.contains(&scene_path) { return; } @@ -2121,7 +2121,7 @@ impl Editor { resource_manager, ) .await; - loading_scenes.lock().remove(&scene_path); + loading_scenes.safe_lock().remove(&scene_path); match result { Ok(loader) => { let scene = loader.0.finish().await; @@ -2145,7 +2145,7 @@ impl Editor { &FsResourceIo, ) .await; - loading_scenes.lock().remove(&scene_path); + loading_scenes.safe_lock().remove(&scene_path); match result { Ok(ui) => { sender.send(Message::AddUiScene { @@ -2487,7 +2487,7 @@ impl Editor { let ui = self.engine.user_interfaces.first_mut(); if let Some(loading_window) = self.scene_loading_window.take() { - let set = self.loading_scenes.lock(); + let set = self.loading_scenes.safe_lock(); self.scene_loading_window = loading_window.update(&set, ui); } @@ -2498,7 +2498,7 @@ impl Editor { } } - if !self.loading_scenes.lock().is_empty() { + if !self.loading_scenes.safe_lock().is_empty() { self.update_loop_state.request_update_in_next_frame(); } @@ -2843,7 +2843,7 @@ impl Editor { P: Plugin + 'static, { let inspector = self.plugins.get::(); - *inspector.property_editors.context_type_id.lock() = plugin.type_id(); + *inspector.property_editors.context_type_id.safe_lock() = plugin.type_id(); inspector .property_editors .merge(plugin.register_property_editors()); @@ -2883,7 +2883,7 @@ impl Editor { { let plugin = self.engine.add_dynamic_plugin_custom(plugin); let inspector = self.plugins.get::(); - *inspector.property_editors.context_type_id.lock() = plugin.type_id(); + *inspector.property_editors.context_type_id.safe_lock() = plugin.type_id(); inspector .property_editors .merge(plugin.register_property_editors()); @@ -2903,7 +2903,7 @@ impl Editor { // Keep the editor active if user holds any mouse button. || self.engine.user_interfaces.first().captured_node().is_some() // Keep the editor active until it fully loads all the queued scenes. - ||!self.loading_scenes.lock().is_empty() + ||!self.loading_scenes.safe_lock().is_empty() } fn on_resumed(&mut self, event_loop: &ActiveEventLoop) { @@ -3231,7 +3231,7 @@ fn update(editor: &mut Editor, event_loop: &ActiveEventLoop) { if need_reload_plugins { let on_plugin_reloaded = |plugin: &dyn Plugin| { let inspector = editor.plugins.get_mut::(); - *inspector.property_editors.context_type_id.lock() = plugin.type_id(); + *inspector.property_editors.context_type_id.safe_lock() = plugin.type_id(); inspector .property_editors .merge(plugin.register_property_editors()); diff --git a/editor/src/plugins/inspector/editors/handle.rs b/editor/src/plugins/inspector/editors/handle.rs index d02e76d27..6cfb5aca0 100644 --- a/editor/src/plugins/inspector/editors/handle.rs +++ b/editor/src/plugins/inspector/editors/handle.rs @@ -23,7 +23,7 @@ use crate::{ fyrox::{ core::{ color::Color, pool::ErasedHandle, pool::Handle, reflect::prelude::*, - type_traits::prelude::*, visitor::prelude::*, + type_traits::prelude::*, visitor::prelude::*, SafeLock, }, graph::BaseSceneGraph, gui::{ @@ -523,7 +523,7 @@ impl PropertyEditorDefinition for NodeHandlePropertyEditorDefinition ) -> Result { let value = ctx.property_info.cast_value::>()?; - let sender = self.sender.lock().unwrap().clone(); + let sender = self.sender.safe_lock().unwrap().clone(); let editor = HandlePropertyEditorBuilder::new(WidgetBuilder::new(), sender.clone()) .with_value(*value) diff --git a/editor/src/plugins/material/editor.rs b/editor/src/plugins/material/editor.rs index b1506619a..31547169c 100644 --- a/editor/src/plugins/material/editor.rs +++ b/editor/src/plugins/material/editor.rs @@ -28,7 +28,7 @@ use crate::{ asset::{core::pool::Handle, manager::ResourceManager, state::ResourceState}, core::{ color::Color, log::Log, parking_lot::Mutex, reflect::prelude::*, - type_traits::prelude::*, uuid_provider, visitor::prelude::*, + type_traits::prelude::*, uuid_provider, visitor::prelude::*, SafeLock, }, graph::BaseSceneGraph, gui::{ @@ -224,7 +224,7 @@ fn make_name(resource_manager: &ResourceManager, material: &MaterialResource) -> if let Some(path) = resource_manager .state() .resource_registry - .lock() + .safe_lock() .uuid_to_path_buf(resource_uuid) { format!( @@ -394,7 +394,7 @@ impl PropertyEditorDefinition for MaterialPropertyEditorDefinition { Ok(PropertyEditorInstance::Simple { editor: MaterialFieldEditorBuilder::new(WidgetBuilder::new()).build( ctx.build_context, - self.sender.lock().clone(), + self.sender.safe_lock().clone(), value.clone(), environment.icon_request_sender.clone(), environment.resource_manager.clone(), diff --git a/editor/src/plugins/tilemap/interaction_mode.rs b/editor/src/plugins/tilemap/interaction_mode.rs index 8676727c4..b84f5eb76 100644 --- a/editor/src/plugins/tilemap/interaction_mode.rs +++ b/editor/src/plugins/tilemap/interaction_mode.rs @@ -23,6 +23,7 @@ use commands::{MoveMapTileCommand, SetMapTilesCommand}; use fyrox::{ asset::untyped::UntypedResource, + core::SafeLock, fxhash::FxHashMap, scene::tilemap::{ brush::TileMapBrushResource, @@ -193,13 +194,13 @@ impl TileMapInteractionMode { pub fn sync_to_state(&mut self) { let state = self.state.lock(); if state.selection_node() != self.tile_map { - self.select_effect.lock().positions.clear(); + self.select_effect.safe_lock().positions.clear(); self.selecting.clear(); } match state.drawing_mode { DrawingMode::Draw => { - let mut overlay = self.overlay_effect.lock(); - let mut erase_overlay = self.erase_select_effect.lock(); + let mut overlay = self.overlay_effect.safe_lock(); + let mut erase_overlay = self.erase_select_effect.safe_lock(); overlay.tiles.clear(); erase_overlay.positions.clear(); let stamp = &state.stamp; @@ -215,8 +216,8 @@ impl TileMapInteractionMode { } } DrawingMode::Erase => { - let mut overlay = self.overlay_effect.lock(); - let mut erase_overlay = self.erase_select_effect.lock(); + let mut overlay = self.overlay_effect.safe_lock(); + let mut erase_overlay = self.erase_select_effect.safe_lock(); overlay.tiles.clear(); erase_overlay.positions.clear(); if state.stamp.is_empty() { @@ -227,18 +228,18 @@ impl TileMapInteractionMode { } DrawingMode::Pick => { if self.mouse_mode == MouseMode::None { - self.overlay_effect.lock().tiles.clear(); - self.erase_select_effect.lock().positions.clear(); + self.overlay_effect.safe_lock().tiles.clear(); + self.erase_select_effect.safe_lock().positions.clear(); } } _ => { - self.overlay_effect.lock().tiles.clear(); - self.erase_select_effect.lock().positions.clear(); + self.overlay_effect.safe_lock().tiles.clear(); + self.erase_select_effect.safe_lock().positions.clear(); } } } fn delete(&mut self) { - let sel = &self.select_effect.lock().positions; + let sel = &self.select_effect.safe_lock().positions; if sel.is_empty() { return; } @@ -497,8 +498,8 @@ impl InteractionMode for TileMapInteractionMode { let Some(tiles) = tiles_guard.as_loaded_ref() else { return; }; - let mut overlay = self.overlay_effect.lock(); - let mut erase_overlay = self.erase_select_effect.lock(); + let mut overlay = self.overlay_effect.safe_lock(); + let mut erase_overlay = self.erase_select_effect.safe_lock(); if let Some(grid_coord) = grid_coord { overlay.active = true; overlay.offset = grid_coord; @@ -514,10 +515,10 @@ impl InteractionMode for TileMapInteractionMode { DrawingMode::Pick => { if mods.alt { self.mouse_mode = MouseMode::Dragging; - let erased_area = &mut self.erase_effect.lock().positions; + let erased_area = &mut self.erase_effect.safe_lock().positions; overlay.tiles.clear(); erased_area.clear(); - let selected = &self.select_effect.lock().positions; + let selected = &self.select_effect.safe_lock().positions; for pos in selected.iter() { let Some(handle) = tiles.get(*pos) else { continue; @@ -535,7 +536,7 @@ impl InteractionMode for TileMapInteractionMode { state.set_node(self.tile_map); update_select( tile_map, - &mut self.select_effect.lock().positions, + &mut self.select_effect.safe_lock().positions, &self.selecting, &mut state, grid_coord, @@ -545,7 +546,7 @@ impl InteractionMode for TileMapInteractionMode { } mode => { self.mouse_mode = MouseMode::Drawing; - let update = &mut self.update_effect.lock().update; + let update = &mut self.update_effect.safe_lock().update; draw(update, tiles, mode, &state, grid_coord, grid_coord); drop(tiles_guard); if state.stamp.brush().is_some() { @@ -594,7 +595,7 @@ impl InteractionMode for TileMapInteractionMode { match self.mouse_mode { MouseMode::None => (), MouseMode::Dragging => { - let overlay = &mut self.overlay_effect.lock().tiles; + let overlay = &mut self.overlay_effect.safe_lock().tiles; if let (Some(start), Some(end)) = (start, end) { let offset = end - start; if offset != Vector2::new(0, 0) { @@ -603,7 +604,7 @@ impl InteractionMode for TileMapInteractionMode { .copied() .map(|p| p + start) .collect::>(); - let selected = &mut self.select_effect.lock().positions; + let selected = &mut self.select_effect.safe_lock().positions; selected.clear(); selected.extend(tiles.iter().map(|p| p + offset)); self.sender.do_command(MoveMapTileCommand::new( @@ -614,17 +615,17 @@ impl InteractionMode for TileMapInteractionMode { } } overlay.clear(); - self.erase_effect.lock().positions.clear(); + self.erase_effect.safe_lock().positions.clear(); } MouseMode::Drawing => { let state = self.state.lock(); if let DrawingMode::Pick = self.current_tool { self.selecting - .clone_from(&self.select_effect.lock().positions); + .clone_from(&self.select_effect.safe_lock().positions); } else if let Some(tile_set) = state.tile_set.as_ref().or(tile_map.tile_set()).cloned() { - let update_source = &mut self.update_effect.lock().update; + let update_source = &mut self.update_effect.safe_lock().update; let tile_map_context = TileMapContext { node: tile_map_handle, scene: scene_handle, @@ -681,8 +682,8 @@ impl InteractionMode for TileMapInteractionMode { let grid_coord = self.pick_grid(scene, game_scene, mouse_position, frame_size); - let mut overlay = self.overlay_effect.lock(); - let mut erase_overlay = self.erase_select_effect.lock(); + let mut overlay = self.overlay_effect.safe_lock(); + let mut erase_overlay = self.erase_select_effect.safe_lock(); if let Some(grid_coord) = grid_coord { overlay.active = true; overlay.offset = grid_coord; @@ -691,7 +692,7 @@ impl InteractionMode for TileMapInteractionMode { overlay.active = false; erase_overlay.offset = None; } - self.cursor_effect.lock().position = grid_coord; + self.cursor_effect.safe_lock().position = grid_coord; let Some(grid_coord) = grid_coord else { return; @@ -731,14 +732,14 @@ impl InteractionMode for TileMapInteractionMode { drop(tiles_guard); update_select( tile_map, - &mut self.select_effect.lock().positions, + &mut self.select_effect.safe_lock().positions, &self.selecting, &mut state.into_mut("TileMap select"), start, end, ); } else { - let update = &mut self.update_effect.lock().update; + let update = &mut self.update_effect.safe_lock().update; draw(update, tiles, self.current_tool, &state, start, end); drop(tiles_guard); if let Some(brush) = state.stamp.brush() { @@ -765,9 +766,9 @@ impl InteractionMode for TileMapInteractionMode { _frame_size: Vector2, _settings: &Settings, ) { - self.overlay_effect.lock().active = false; - self.erase_select_effect.lock().offset = None; - self.cursor_effect.lock().position = None; + self.overlay_effect.safe_lock().active = false; + self.erase_select_effect.safe_lock().offset = None; + self.cursor_effect.safe_lock().position = None; } fn update( diff --git a/editor/src/plugins/tilemap/palette.rs b/editor/src/plugins/tilemap/palette.rs index 794b5e884..d82a1dcf9 100644 --- a/editor/src/plugins/tilemap/palette.rs +++ b/editor/src/plugins/tilemap/palette.rs @@ -22,6 +22,7 @@ //! is responsible for displaying a grid of tiles where the user may select tiles, //! drag tiles, and use drawing tools upon the tiles. +use fyrox::core::SafeLock; use fyrox::scene::tilemap::brush::TileMapBrushResource; use fyrox::scene::tilemap::tileset::OptionTileSet; use fyrox::scene::tilemap::{ResourceTilePosition, RotTileHandle}; @@ -731,7 +732,7 @@ impl PaletteWidget { self.editable && self.kind == TilePaletteStage::Tiles && state.drawing_mode == DrawingMode::Editor - && editor.lock().slice_mode() + && editor.safe_lock().slice_mode() } else { false }; @@ -763,7 +764,7 @@ impl PaletteWidget { DrawingMode::Editor => { if let Some(editor) = &state.active_editor { if let &Some(page) = &self.page { - editor.lock().highlight( + editor.safe_lock().highlight( &mut self.highlight, page, &self.content, @@ -1363,7 +1364,7 @@ impl PaletteWidget { DrawingMode::Editor => { if let Some(editor) = &state.active_editor { if let Some(handle) = TileDefinitionHandle::try_new(page, end) { - let editor = editor.lock(); + let editor = editor.safe_lock(); editor.draw_tile( handle, sub_pos, diff --git a/editor/src/plugins/tilemap/tile_inspector.rs b/editor/src/plugins/tilemap/tile_inspector.rs index 100a34f14..39e323950 100644 --- a/editor/src/plugins/tilemap/tile_inspector.rs +++ b/editor/src/plugins/tilemap/tile_inspector.rs @@ -33,7 +33,7 @@ use fyrox::{ asset::ResourceDataRef, core::{ algebra::Vector2, pool::Handle, reflect::prelude::*, type_traits::prelude::*, - visitor::prelude::*, + visitor::prelude::*, SafeLock, }, gui::{ button::{Button, ButtonMessage}, @@ -597,7 +597,7 @@ impl PropertyEditors { let mut editors = Vec::default(); make_property_editors(state, &mut editors, ctx); let content = StackPanelBuilder::new( - WidgetBuilder::new().with_children(editors.iter().map(|v| v.1.lock().handle())), + WidgetBuilder::new().with_children(editors.iter().map(|v| v.1.safe_lock().handle())), ) .build(ctx); Self { @@ -618,14 +618,14 @@ impl PropertyEditors { // The list has changed somehow, so remove the old editors and construct an all new editor for each layer. for (_, editor) in self.editors.iter() { ui.send_message(WidgetMessage::remove( - editor.lock().handle(), + editor.safe_lock().handle(), MessageDirection::ToWidget, )); } make_property_editors(state, &mut self.editors, &mut ui.build_ctx()); for (_, editor) in self.editors.iter() { ui.send_message(WidgetMessage::link( - editor.lock().handle(), + editor.safe_lock().handle(), MessageDirection::ToWidget, self.content, )); @@ -633,7 +633,7 @@ impl PropertyEditors { } else { // The list has not changed, so just sync each editor because one of the layers may have changed. for (_, editor) in self.editors.iter() { - editor.lock().sync_to_model(state, ui); + editor.safe_lock().sync_to_model(state, ui); } } } @@ -672,7 +672,7 @@ impl ColliderEditors { let mut editors = Vec::default(); make_collider_editors(state, &mut editors, ctx); let content = StackPanelBuilder::new( - WidgetBuilder::new().with_children(editors.iter().map(|v| v.1.lock().handle())), + WidgetBuilder::new().with_children(editors.iter().map(|v| v.1.safe_lock().handle())), ) .build(ctx); Self { @@ -693,14 +693,14 @@ impl ColliderEditors { // The list has changed somehow, so remove the old editors and construct an all new editor for each layer. for (_, editor) in self.editors.iter() { ui.send_message(WidgetMessage::remove( - editor.lock().handle(), + editor.safe_lock().handle(), MessageDirection::ToWidget, )); } make_collider_editors(state, &mut self.editors, &mut ui.build_ctx()); for (_, editor) in self.editors.iter() { ui.send_message(WidgetMessage::link( - editor.lock().handle(), + editor.safe_lock().handle(), MessageDirection::ToWidget, self.content, )); @@ -708,7 +708,7 @@ impl ColliderEditors { } else { // The list has not changed, so just sync each editor because one of the layers may have changed. for (_, editor) in self.editors.iter() { - editor.lock().sync_to_model(state, ui); + editor.safe_lock().sync_to_model(state, ui); } } } @@ -907,7 +907,7 @@ impl TileInspector { .with_child(tile_size_inspector.handle) .with_child(animation_speed_inspector.handle) .with_child(create_tile) - .with_children(tile_editors.iter().map(|e| e.lock().handle())) + .with_children(tile_editors.iter().map(|e| e.safe_lock().handle())) .with_child(property_editors.handle) .with_child(collider_editors.handle) .with_child(macro_inspector.handle()), @@ -1109,7 +1109,7 @@ impl TileInspector { .chain(self.property_editors.iter()) .chain(self.collider_editors.iter()); for editor in iter { - editor.lock().handle_ui_message( + editor.safe_lock().handle_ui_message( &mut tile_editor_state, message, ui, @@ -1153,7 +1153,7 @@ impl TileInspector { .chain(self.property_editors.iter()) .chain(self.collider_editors.iter()); for editor_ref in iter { - let draw_button = editor_ref.lock().draw_button(); + let draw_button = editor_ref.safe_lock().draw_button(); if message.destination() == draw_button { if tile_editor_state.is_active_editor(editor_ref) { tile_editor_state.set_active_editor(None); diff --git a/editor/src/plugins/tilemap/wfc.rs b/editor/src/plugins/tilemap/wfc.rs index e46390d7b..e8c02a3b6 100644 --- a/editor/src/plugins/tilemap/wfc.rs +++ b/editor/src/plugins/tilemap/wfc.rs @@ -22,7 +22,7 @@ use std::hash::Hash; use fyrox::{ asset::{untyped::UntypedResource, Resource, ResourceData}, - core::swap_hash_map_entry, + core::{swap_hash_map_entry, SafeLock}, fxhash::FxHashMap, gui::{ button::ButtonMessage, @@ -1154,7 +1154,7 @@ impl CommandTrait for WaveFunctionTaskCommand { } fn execute(&mut self, _context: &mut dyn CommandContext) { - let mut data_guard = self.task_data.lock(); + let mut data_guard = self.task_data.safe_lock(); if data_guard.state == WfcTaskState::Finished { write_propagator_to_tile_data( &data_guard.constraint, @@ -1180,7 +1180,7 @@ impl CommandTrait for WaveFunctionTaskCommand { } fn revert(&mut self, _context: &mut dyn CommandContext) { - let mut data_guard = self.task_data.lock(); + let mut data_guard = self.task_data.safe_lock(); if data_guard.state != WfcTaskState::Finished { data_guard.state = WfcTaskState::Cancelled; } @@ -1201,10 +1201,10 @@ fn run_wfc( max_attempts: u32, data: TileMapDataResource, ) { - let attempts = task_data.lock().attempts; + let attempts = task_data.safe_lock().attempts; let mut rng = thread_rng(); for i in attempts..max_attempts { - let mut guard = task_data.lock(); + let mut guard = task_data.safe_lock(); let task_data = guard.deref_mut(); if task_data.state == WfcTaskState::Cancelled { task_data.attempts = i; @@ -1228,5 +1228,5 @@ fn run_wfc( write_propagator_to_tile_data(&task_data.constraint, &task_data.working_propagator, &data); } Log::err(format!("WFC failed after {max_attempts} attempts")); - task_data.lock().state = WfcTaskState::Finished; + task_data.safe_lock().state = WfcTaskState::Finished; } diff --git a/fyrox-build-tools/src/build.rs b/fyrox-build-tools/src/build.rs index fbed02cd8..0931d2ac1 100644 --- a/fyrox-build-tools/src/build.rs +++ b/fyrox-build-tools/src/build.rs @@ -24,7 +24,7 @@ use fyrox_resource::untyped::ResourceKind; use fyrox_ui::{ border::BorderBuilder, button::{ButtonBuilder, ButtonMessage}, - core::{parking_lot::Mutex, pool::Handle}, + core::{parking_lot::Mutex, pool::Handle, SafeLock}, grid::{Column, GridBuilder, Row}, image::ImageBuilder, message::{MessageDirection, UiMessage}, @@ -229,7 +229,7 @@ impl BuildWindow { let pipe: &mut dyn BufRead = &mut BufReader::new(&mut pipe); while reader_active.load(Ordering::SeqCst) { for line in pipe.lines().take(10).flatten() { - let mut log_guard = log.lock(); + let mut log_guard = log.safe_lock(); log_guard.push_str(&line); log_guard.push('\n'); log_changed.store(true, Ordering::SeqCst); @@ -241,7 +241,7 @@ impl BuildWindow { pub fn reset(&mut self, ui: &UserInterface) { self.active.store(false, Ordering::SeqCst); self.changed.store(false, Ordering::SeqCst); - self.log.lock().clear(); + self.log.safe_lock().clear(); ui.send_message(TextMessage::text( self.log_text, MessageDirection::ToWidget, @@ -262,7 +262,7 @@ impl BuildWindow { ui.send_message(TextMessage::text( self.log_text, MessageDirection::ToWidget, - self.log.lock().clone(), + self.log.safe_lock().clone(), )); ui.send_message(ScrollViewerMessage::scroll_to_end( self.scroll_viewer, diff --git a/fyrox-core/src/lib.rs b/fyrox-core/src/lib.rs index 630482c02..7137383fc 100644 --- a/fyrox-core/src/lib.rs +++ b/fyrox-core/src/lib.rs @@ -44,6 +44,7 @@ pub use uuid; use crate::visitor::{Visit, VisitResult, Visitor}; use bytemuck::Pod; use fxhash::FxHashMap; +pub use safelock::*; use std::collections::hash_map::Entry; use std::ffi::OsString; use std::hash::Hasher; @@ -66,6 +67,7 @@ pub mod pool; pub mod quadtree; pub mod rectpack; pub mod reflect; +mod safelock; pub mod sparse; pub mod sstorage; pub mod task; diff --git a/fyrox-core/src/reflect/std_impls.rs b/fyrox-core/src/reflect/std_impls.rs index f44014b5a..dd3e9a6da 100644 --- a/fyrox-core/src/reflect/std_impls.rs +++ b/fyrox-core/src/reflect/std_impls.rs @@ -25,6 +25,7 @@ use crate::{ reflect::{blank_reflect, prelude::*}, sstorage::ImmutableString, uuid::Uuid, + SafeLock, }; use fyrox_core_derive::impl_reflect; use std::{ @@ -444,7 +445,7 @@ macro_rules! impl_reflect_inner_mutability { } impl Reflect for parking_lot::Mutex { - impl_reflect_inner_mutability!(self, { self.lock() }, { self.into_inner() }); + impl_reflect_inner_mutability!(self, { self.safe_lock() }, { self.into_inner() }); } impl Reflect for parking_lot::RwLock { @@ -453,7 +454,7 @@ impl Reflect for parking_lot::RwLock { #[allow(clippy::mut_mutex_lock)] impl Reflect for std::sync::Mutex { - impl_reflect_inner_mutability!(self, { self.lock().unwrap() }, { self.into_inner() }); + impl_reflect_inner_mutability!(self, { self.safe_lock().unwrap() }, { self.into_inner() }); } impl Reflect for std::sync::RwLock { @@ -461,7 +462,7 @@ impl Reflect for std::sync::RwLock { } impl Reflect for Arc> { - impl_reflect_inner_mutability!(self, { self.lock() }, { + impl_reflect_inner_mutability!(self, { self.safe_lock() }, { Arc::into_inner(*self) .expect("Value cannot be shared!") .into_inner() diff --git a/fyrox-core/src/safelock.rs b/fyrox-core/src/safelock.rs new file mode 100644 index 000000000..9da444253 --- /dev/null +++ b/fyrox-core/src/safelock.rs @@ -0,0 +1,132 @@ +// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! This is an extension for Mutex-like objects that creates a `safe_lock` method +//! to replace `lock` and put a time limit on locking, preventing a game from +//! permanently freezing due to a deadlock. + +use parking_lot::{Mutex, MutexGuard}; +#[allow(unused_imports)] +use std::sync::TryLockError; +use std::time::Duration; + +#[allow(dead_code)] +const PANIC_MESSAGE: &str = "lock timeout"; + +/// Trait for lockable objects that can panic if they take too long +/// to lock. Panicking is preferable over freezing. +pub trait SafeLock { + const TIMEOUT: Duration = Duration::from_secs(10); + type Output<'a> + where + Self: 'a; + /// Attempt to lock the object, with a limit on how long locking may block for, + /// panicking if the time limit is exceeded. It panics instead of freezing. + fn safe_lock(&self) -> Self::Output<'_>; +} + +impl SafeLock for Mutex { + type Output<'a> + = MutexGuard<'a, T> + where + T: 'a; + #[cfg(target_arch = "wasm32")] + fn safe_lock(&self) -> Self::Output<'_> { + self.lock() + } + #[cfg(not(target_arch = "wasm32"))] + fn safe_lock(&self) -> Self::Output<'_> { + self.try_lock_for(Self::TIMEOUT).expect(PANIC_MESSAGE) + } +} + +impl SafeLock for std::sync::Mutex { + type Output<'a> + = std::sync::LockResult> + where + T: 'a; + + #[cfg(target_arch = "wasm32")] + fn safe_lock(&self) -> Self::Output<'_> { + self.lock() + } + #[cfg(not(target_arch = "wasm32"))] + fn safe_lock(&self) -> Self::Output<'_> { + let start = std::time::Instant::now(); + loop { + match self.try_lock() { + Ok(guard) => return Ok(guard), + Err(TryLockError::WouldBlock) => (), + Err(TryLockError::Poisoned(err)) => return Err(err), + } + std::thread::yield_now(); + if start.elapsed() > Self::TIMEOUT { + std::panic::panic_any(PANIC_MESSAGE); + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::{ + any::Any, + panic::{catch_unwind, AssertUnwindSafe}, + }; + fn panic_to_string(message: Box) -> Option { + match message.downcast_ref::<&str>() { + Some(&str) => Some(str.into()), + None => message.downcast::().ok().map(|s| *s), + } + } + #[test] + fn successful_lock_parking_lot() { + let mutex = Mutex::new(()); + drop(mutex.safe_lock()); + } + #[test] + fn successful_lock_std() { + let mutex = std::sync::Mutex::new(()); + drop(mutex.safe_lock()); + } + #[test] + fn failed_lock_parking_lot() { + let mutex = Mutex::new(()); + let _guard = mutex.safe_lock(); + let panic_message = catch_unwind(AssertUnwindSafe(|| mutex.safe_lock())) + .expect_err("safe_lock did not panic"); + let Some(message) = panic_to_string(panic_message) else { + panic!("safe_lock panicked with wrong type"); + }; + assert_eq!(message, PANIC_MESSAGE); + } + #[test] + fn failed_lock_std() { + let mutex = std::sync::Mutex::new(()); + let _guard = mutex.safe_lock(); + let panic_message = + catch_unwind(|| mutex.safe_lock()).expect_err("safe_lock did not panic"); + let Some(message) = panic_to_string(panic_message) else { + panic!("safe_lock panicked with wrong type"); + }; + assert_eq!(message, PANIC_MESSAGE); + } +} diff --git a/fyrox-core/src/sstorage.rs b/fyrox-core/src/sstorage.rs index 7a3d5439a..b7ce2abd7 100644 --- a/fyrox-core/src/sstorage.rs +++ b/fyrox-core/src/sstorage.rs @@ -27,6 +27,7 @@ use crate::{ parking_lot::Mutex, uuid_provider, visitor::{Visit, VisitResult, Visitor}, + SafeLock, }; use fxhash::{FxHashMap, FxHasher}; pub use fyrox_core_derive::TypeUuidProvider; @@ -86,7 +87,7 @@ impl Visit for ImmutableString { // Deduplicate on deserialization. if visitor.is_reading() { - *self = SSTORAGE.lock().insert(string); + *self = SSTORAGE.safe_lock().insert(string); } Ok(()) @@ -159,7 +160,7 @@ impl ImmutableString { /// memory allocator. #[inline] pub fn new>(string: S) -> ImmutableString { - SSTORAGE.lock().insert(string) + SSTORAGE.safe_lock().insert(string) } /// Returns unique identifier of the string. Keep in mind that uniqueness is guaranteed only @@ -189,13 +190,13 @@ impl From<&str> for ImmutableString { impl From for ImmutableString { fn from(value: String) -> Self { - SSTORAGE.lock().insert_owned(value) + SSTORAGE.safe_lock().insert_owned(value) } } impl From<&String> for ImmutableString { fn from(value: &String) -> Self { - SSTORAGE.lock().insert(value) + SSTORAGE.safe_lock().insert(value) } } @@ -269,7 +270,7 @@ impl ImmutableStringStorage { impl ImmutableStringStorage { /// Returns total amount of immutable strings in the storage. pub fn entry_count() -> usize { - SSTORAGE.lock().vec.len() + SSTORAGE.safe_lock().vec.len() } } diff --git a/fyrox-core/src/task.rs b/fyrox-core/src/task.rs index 33f39ea5f..3be0b0d44 100644 --- a/fyrox-core/src/task.rs +++ b/fyrox-core/src/task.rs @@ -20,6 +20,7 @@ #[cfg(not(target_arch = "wasm32"))] use crate::futures::executor::ThreadPool; +use crate::SafeLock; use parking_lot::Mutex; use std::{ any::Any, @@ -152,6 +153,6 @@ impl TaskPool { #[inline] pub fn next_task_result(&self) -> Option { - self.receiver.lock().try_recv().ok() + self.receiver.safe_lock().try_recv().ok() } } diff --git a/fyrox-graph/src/constructor.rs b/fyrox-graph/src/constructor.rs index 03eddac1c..49dbd21c3 100644 --- a/fyrox-graph/src/constructor.rs +++ b/fyrox-graph/src/constructor.rs @@ -23,7 +23,7 @@ use fyrox_core::pool::Handle; use fyrox_core::{ parking_lot::{Mutex, MutexGuard}, reflect::prelude::*, - TypeUuidProvider, Uuid, + SafeLock, TypeUuidProvider, Uuid, }; use std::sync::Arc; @@ -138,30 +138,33 @@ impl GraphNodeConstructorContainer { { let previous = self .map - .lock() + .safe_lock() .insert(Inner::type_uuid(), Inner::constructor()); assert!(previous.is_none()); } /// Adds custom type constructor. pub fn add_custom(&self, type_uuid: Uuid, constructor: GraphNodeConstructor) { - self.map.lock().insert(type_uuid, constructor); + self.map.safe_lock().insert(type_uuid, constructor); } /// Unregisters type constructor. pub fn remove(&self, type_uuid: Uuid) { - self.map.lock().remove(&type_uuid); + self.map.safe_lock().remove(&type_uuid); } /// Makes an attempt to create a node using provided type UUID. It may fail if there is no /// node constructor for specified type UUID. pub fn try_create(&self, type_uuid: &Uuid) -> Option { - self.map.lock().get_mut(type_uuid).map(|c| (c.default)()) + self.map + .safe_lock() + .get_mut(type_uuid) + .map(|c| (c.default)()) } /// Returns total amount of constructors. pub fn len(&self) -> usize { - self.map.lock().len() + self.map.safe_lock().len() } /// Returns true if the container is empty. @@ -171,6 +174,6 @@ impl GraphNodeConstructorContainer { /// Returns the inner map of the node constructors. pub fn map(&self) -> MutexGuard<'_, FxHashMap>> { - self.map.lock() + self.map.safe_lock() } } diff --git a/fyrox-impl/src/scene/mod.rs b/fyrox-impl/src/scene/mod.rs index acc3643f6..056663920 100644 --- a/fyrox-impl/src/scene/mod.rs +++ b/fyrox-impl/src/scene/mod.rs @@ -82,6 +82,7 @@ use crate::{ utils::navmesh::Navmesh, }; use fxhash::FxHashSet; +use fyrox_core::SafeLock; use std::{ fmt::{Display, Formatter}, ops::{Index, IndexMut}, @@ -348,7 +349,7 @@ impl SceneLoader { let registry_status = resource_manager .state() .resource_registry - .lock() + .safe_lock() .status_flag(); // Wait until the registry is fully loaded. let registry_status = registry_status.await; @@ -414,9 +415,6 @@ impl SceneLoader { let exclusion_list = used_resources .iter() .filter(|res| { - // Calling resource_uuid means locking the header. - // To minimize the number of locks we hold at once, get the UUID first, - // before we lock the resource manager and registry. let uuid = res.resource_uuid(); let state = self.resource_manager.state(); let registry = state.resource_registry.lock(); diff --git a/fyrox-impl/src/scene/node/mod.rs b/fyrox-impl/src/scene/node/mod.rs index 1bd20ae13..9dac8f6eb 100644 --- a/fyrox-impl/src/scene/node/mod.rs +++ b/fyrox-impl/src/scene/node/mod.rs @@ -641,7 +641,7 @@ mod test { uuid::{uuid, Uuid}, variable::InheritableVariable, visitor::{prelude::*, Visitor}, - TypeUuidProvider, + SafeLock, TypeUuidProvider, }, engine::{self, SerializationContext}, resource::model::{Model, ModelResourceExtension}, @@ -746,7 +746,7 @@ mod test { resource_manager .state() .resource_registry - .lock() + .safe_lock() .set_path("test_output/resources.registry"); let serialization_context = SerializationContext::new(); @@ -794,7 +794,7 @@ mod test { mesh.set_cast_shadows(false); save_scene(&mut derived, derived_asset_path); let registry = resource_manager.state().resource_registry.clone(); - let mut registry = registry.lock(); + let mut registry = registry.safe_lock(); let mut ctx = registry.modify(); ctx.write_metadata(Uuid::new_v4(), derived_asset_path) .unwrap(); diff --git a/fyrox-impl/src/scene/terrain/mod.rs b/fyrox-impl/src/scene/terrain/mod.rs index d1fa48317..27e0f4e5e 100644 --- a/fyrox-impl/src/scene/terrain/mod.rs +++ b/fyrox-impl/src/scene/terrain/mod.rs @@ -34,6 +34,7 @@ use crate::{ uuid::{uuid, Uuid}, variable::InheritableVariable, visitor::prelude::*, + SafeLock, }, graphics::ElementRange, material::MaterialResourceExtension, @@ -524,7 +525,7 @@ impl Chunk { return; }; let count = heightmap.data_ref().modifications_count(); - let mut quad_tree = self.quad_tree.lock(); + let mut quad_tree = self.quad_tree.safe_lock(); if count != quad_tree.height_mod_count() { *quad_tree = make_quad_tree(&self.heightmap, self.height_map_size, self.block_size); } @@ -818,9 +819,12 @@ impl Chunk { pub fn debug_draw(&self, transform: &Matrix4, ctx: &mut SceneDrawingContext) { let transform = *transform * Matrix4::new_translation(&self.position()); - self.quad_tree - .lock() - .debug_draw(&transform, self.height_map_size, self.physical_size, ctx) + self.quad_tree.safe_lock().debug_draw( + &transform, + self.height_map_size, + self.physical_size, + ctx, + ) } fn set_block_size(&mut self, block_size: Vector2) { @@ -833,7 +837,7 @@ impl Chunk { if self.heightmap.is_none() { return; } - *self.quad_tree.lock() = + *self.quad_tree.safe_lock() = make_quad_tree(&self.heightmap, self.height_map_size, self.block_size); } } @@ -2099,7 +2103,7 @@ impl Terrain { drop(texture_modifier); drop(texture_data); - *chunk.quad_tree.lock() = + *chunk.quad_tree.safe_lock() = make_quad_tree(&chunk.heightmap, chunk.height_map_size, chunk.block_size); } @@ -2616,7 +2620,7 @@ impl NodeTrait for Terrain { // The first element of the list is the furthest distance, where the lowest LOD is used. // The formula used to produce this list has been chosen arbitrarily based on what seems to produce // the best results in the render. - let quad_tree = chunk.quad_tree.lock(); + let quad_tree = chunk.quad_tree.safe_lock(); let levels = (0..=quad_tree.max_level) .map(|n| { ctx.observer_position.z_far diff --git a/fyrox-impl/src/scene/tilemap/mod.rs b/fyrox-impl/src/scene/tilemap/mod.rs index c25579084..f99a8186d 100644 --- a/fyrox-impl/src/scene/tilemap/mod.rs +++ b/fyrox-impl/src/scene/tilemap/mod.rs @@ -63,7 +63,7 @@ use crate::{ type_traits::prelude::*, variable::InheritableVariable, visitor::prelude::*, - ImmutableString, + ImmutableString, SafeLock, }, graph::{constructor::ConstructorProvider, BaseSceneGraph}, material::{Material, MaterialResource, STANDARD_2D}, @@ -1421,7 +1421,7 @@ impl NodeTrait for TileMap { let mut tile_set_lock = TileSetRef::new(tile_set_resource); let tile_set = tile_set_lock.as_loaded(); - let mut hidden_tiles = self.hidden_tiles.lock(); + let mut hidden_tiles = self.hidden_tiles.safe_lock(); hidden_tiles.clear(); let bounds = ctx @@ -1440,7 +1440,9 @@ impl NodeTrait for TileMap { }; for effect in self.before_effects.iter() { - effect.lock().render_special_tiles(&mut tile_render_context); + effect + .safe_lock() + .render_special_tiles(&mut tile_render_context); } let bounds = tile_render_context.visible_bounds(); let Some(tiles) = self.tiles.as_ref().map(|r| r.data_ref()) else { @@ -1465,7 +1467,9 @@ impl NodeTrait for TileMap { } } for effect in self.after_effects.iter() { - effect.lock().render_special_tiles(&mut tile_render_context); + effect + .safe_lock() + .render_special_tiles(&mut tile_render_context); } RdcControlFlow::Continue } diff --git a/fyrox-impl/src/script/constructor.rs b/fyrox-impl/src/script/constructor.rs index 2437d46a3..8d5a617ff 100644 --- a/fyrox-impl/src/script/constructor.rs +++ b/fyrox-impl/src/script/constructor.rs @@ -24,7 +24,7 @@ use crate::{ core::{ parking_lot::{Mutex, MutexGuard}, uuid::Uuid, - TypeUuidProvider, + SafeLock, TypeUuidProvider, }, script::{Script, ScriptTrait}, }; @@ -68,7 +68,7 @@ impl ScriptConstructorContainer { where T: TypeUuidProvider + ScriptTrait + Default, { - let old = self.map.lock().insert( + let old = self.map.safe_lock().insert( T::type_uuid(), ScriptConstructor { constructor: Box::new(|| Script::new(T::default())), @@ -89,7 +89,7 @@ impl ScriptConstructorContainer { type_uuid: Uuid, constructor: ScriptConstructor, ) -> Result<(), String> { - let mut map = self.map.lock(); + let mut map = self.map.safe_lock(); if let Some(old) = map.get(&type_uuid) { return Err(format!( "cannot add {} ({}) because its uuid is already used by {} ({})", @@ -102,20 +102,20 @@ impl ScriptConstructorContainer { /// Unregisters type constructor. pub fn remove(&self, type_uuid: Uuid) { - self.map.lock().remove(&type_uuid); + self.map.safe_lock().remove(&type_uuid); } /// Makes an attempt to create a script using provided type UUID. It may fail if there is no /// script constructor for specified type UUID. pub fn try_create(&self, type_uuid: &Uuid) -> Option