Preventing deadlock by replacing lock with safe_lock (#852)

* Preventing deadlock by replacing `lock` with `safe_lock`

* Fixing unused code warnings in wasm SafeLock
This commit is contained in:
b-guild
2025-10-05 10:34:06 +03:00
committed by GitHub
parent 9dcdd9c28b
commit 89e7cdbe28
38 changed files with 358 additions and 183 deletions
+7 -4
View File
@@ -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
+5 -5
View File
@@ -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<Box<dyn BaseImportOptions>> {
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();
+3 -3
View File
@@ -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,
+6 -6
View File
@@ -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<Box<dyn BaseImportOptions>> {
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
+4 -4
View File
@@ -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<UiNode> {
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::<Vec<_>>();
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())
+4 -2
View File
@@ -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.
+11 -11
View File
@@ -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<TextureResource> {
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::<InspectorPlugin>();
*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::<InspectorPlugin>();
*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::<InspectorPlugin>();
*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());
@@ -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<T: Reflect> PropertyEditorDefinition for NodeHandlePropertyEditorDefinition
) -> Result<PropertyEditorInstance, InspectorError> {
let value = ctx.property_info.cast_value::<Handle<T>>()?;
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)
+3 -3
View File
@@ -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(),
+30 -29
View File
@@ -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::<Vec<_>>();
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<f32>,
_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(
+4 -3
View File
@@ -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,
+12 -12
View File
@@ -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);
+6 -6
View File
@@ -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;
}
+4 -4
View File
@@ -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,
+2
View File
@@ -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;
+4 -3
View File
@@ -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<T: Reflect + Clone> Reflect for parking_lot::Mutex<T> {
impl_reflect_inner_mutability!(self, { self.lock() }, { self.into_inner() });
impl_reflect_inner_mutability!(self, { self.safe_lock() }, { self.into_inner() });
}
impl<T: Reflect + Clone> Reflect for parking_lot::RwLock<T> {
@@ -453,7 +454,7 @@ impl<T: Reflect + Clone> Reflect for parking_lot::RwLock<T> {
#[allow(clippy::mut_mutex_lock)]
impl<T: Reflect + Clone> Reflect for std::sync::Mutex<T> {
impl_reflect_inner_mutability!(self, { self.lock().unwrap() }, { self.into_inner() });
impl_reflect_inner_mutability!(self, { self.safe_lock().unwrap() }, { self.into_inner() });
}
impl<T: Reflect + Clone> Reflect for std::sync::RwLock<T> {
@@ -461,7 +462,7 @@ impl<T: Reflect + Clone> Reflect for std::sync::RwLock<T> {
}
impl<T: Reflect + Clone> Reflect for Arc<parking_lot::Mutex<T>> {
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()
+132
View File
@@ -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<T: ?Sized> SafeLock for Mutex<T> {
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<T: ?Sized> SafeLock for std::sync::Mutex<T> {
type Output<'a>
= std::sync::LockResult<std::sync::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<'_> {
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<dyn Any>) -> Option<String> {
match message.downcast_ref::<&str>() {
Some(&str) => Some(str.into()),
None => message.downcast::<String>().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);
}
}
+6 -5
View File
@@ -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<S: AsRef<str>>(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<String> 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()
}
}
+2 -1
View File
@@ -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<TaskResult> {
self.receiver.lock().try_recv().ok()
self.receiver.safe_lock().try_recv().ok()
}
}
+10 -7
View File
@@ -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<Node, Ctx> GraphNodeConstructorContainer<Node, Ctx> {
{
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<Node, Ctx>) {
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<Node> {
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<Node, Ctx> GraphNodeConstructorContainer<Node, Ctx> {
/// Returns the inner map of the node constructors.
pub fn map(&self) -> MutexGuard<'_, FxHashMap<Uuid, GraphNodeConstructor<Node, Ctx>>> {
self.map.lock()
self.map.safe_lock()
}
}
+2 -4
View File
@@ -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();
+3 -3
View File
@@ -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();
+11 -7
View File
@@ -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<f32>, 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<u32>) {
@@ -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
+8 -4
View File
@@ -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
}
+6 -6
View File
@@ -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<Script> {
self.map
.lock()
.safe_lock()
.get_mut(type_uuid)
.map(|c| (c.constructor)())
}
/// Returns inner map of script constructors.
pub fn map(&self) -> MutexGuard<BTreeMap<Uuid, ScriptConstructor>> {
self.map.lock()
self.map.safe_lock()
}
}
+2 -2
View File
@@ -15,7 +15,7 @@ use fyrox_core::{
sstorage::ImmutableString,
uuid::{uuid, Uuid},
visitor::{prelude::*, RegionGuard},
TypeUuidProvider,
SafeLock, TypeUuidProvider,
};
use fyrox_graphics::gpu_program::SamplerFallback;
use fyrox_resource::{
@@ -1171,7 +1171,7 @@ pub fn visit_old_material(region: &mut RegionGuard) -> Option<MaterialResource>
return Some(MaterialResource::new_ok(
Uuid::new_v4(),
Default::default(),
old_material.lock().clone(),
old_material.safe_lock().clone(),
));
}
}
+6 -6
View File
@@ -23,7 +23,7 @@
//! on deserialization to create a default instance of corresponding resource.
use crate::{
core::{parking_lot::Mutex, uuid::Uuid, TypeUuidProvider},
core::{parking_lot::Mutex, uuid::Uuid, SafeLock, TypeUuidProvider},
ResourceData,
};
use fxhash::FxHashMap;
@@ -62,7 +62,7 @@ impl ResourceConstructorContainer {
where
T: ResourceData + Default + TypeUuidProvider,
{
let previous = self.map.lock().insert(
let previous = self.map.safe_lock().insert(
<T as TypeUuidProvider>::type_uuid(),
ResourceDataConstructor {
callback: Box::new(|| Box::<T>::default()),
@@ -75,26 +75,26 @@ impl ResourceConstructorContainer {
/// Adds custom type constructor.
pub fn add_custom(&self, type_uuid: Uuid, constructor: ResourceDataConstructor) {
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 resource data using provided type UUID. It may fail if there is no
/// resource data constructor for specified type UUID.
pub fn try_create(&self, type_uuid: &Uuid) -> Option<Box<dyn ResourceData>> {
self.map
.lock()
.safe_lock()
.get_mut(type_uuid)
.map(|c| c.create_instance())
}
/// 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.
+4 -3
View File
@@ -23,6 +23,7 @@
use crate::core::{
parking_lot::Mutex,
pool::{Handle, Pool},
SafeLock,
};
use crate::UntypedResource;
use std::{
@@ -72,17 +73,17 @@ impl ResourceEventBroadcaster {
/// Adds an event sender to the broadcaster and returns its handle. If the receiver part of the
/// channel dies, the sender part is automatically removed from the broadcaster.
pub fn add(&self, sender: ResourceEventSender) -> Handle<ResourceEventSender> {
self.container.lock().spawn(sender)
self.container.safe_lock().spawn(sender)
}
/// Removes an event sender by its handle.
pub fn remove(&self, handle: Handle<ResourceEventSender>) -> ResourceEventSender {
self.container.lock().free(handle)
self.container.safe_lock().free(handle)
}
/// Sends an event to all "subscribers" in the broadcaster.
pub fn broadcast(&self, event: ResourceEvent) {
let mut container = self.container.lock();
let mut container = self.container.safe_lock();
container.retain(|sender| sender.send(event.clone()).is_ok());
}
+2 -1
View File
@@ -22,6 +22,7 @@
use crate::{collect_used_resources, state::ResourceState, untyped::UntypedResource};
use fxhash::FxHashSet;
use fyrox_core::SafeLock;
/// A node of [`ResourceDependencyGraph`].
pub struct ResourceGraphNode {
@@ -40,7 +41,7 @@ impl ResourceGraphNode {
// Look for dependent resources.
let mut dependent_resources = FxHashSet::default();
let header = resource.0.lock();
let header = resource.0.safe_lock();
if let ResourceState::Ok { ref data, .. } = header.state {
(**data).as_reflect(&mut |entity| {
collect_used_resources(entity, &mut dependent_resources);
+6 -5
View File
@@ -38,7 +38,7 @@ use crate::{
};
use fxhash::FxHashSet;
pub use fyrox_core as core;
use fyrox_core::{combine_uuids, log::Log};
use fyrox_core::{combine_uuids, log::Log, SafeLock};
use std::any::Any;
use std::path::PathBuf;
use std::{
@@ -408,7 +408,7 @@ where
#[inline]
pub fn data_ref(&self) -> ResourceDataRef<'_, T> {
ResourceDataRef {
guard: self.untyped.0.lock(),
guard: self.untyped.0.safe_lock(),
phantom: Default::default(),
}
}
@@ -703,7 +703,8 @@ mod tests {
};
use fyrox_core::{
append_extension, futures::executor::block_on, io::FileError, parking_lot::Mutex,
reflect::prelude::*, task::TaskPool, uuid, visitor::prelude::*, TypeUuidProvider, Uuid,
reflect::prelude::*, task::TaskPool, uuid, visitor::prelude::*, SafeLock, TypeUuidProvider,
Uuid,
};
use ron::ser::PrettyConfig;
use serde::{Deserialize, Serialize};
@@ -854,7 +855,7 @@ mod tests {
resource_manager
.state()
.resource_registry
.lock()
.safe_lock()
.set_path(Path::new(TEST_FOLDER2).join("resources.registry"));
resource_manager.add_loader(MyDataLoader {});
resource_manager.update_or_load_registry();
@@ -877,7 +878,7 @@ mod tests {
resource_manager
.state()
.resource_registry
.lock()
.safe_lock()
.set_path(Path::new(TEST_FOLDER3).join("resources.registry"));
resource_manager.add_loader(MyDataLoader {});
resource_manager.update_or_load_registry();
+18 -8
View File
@@ -32,6 +32,7 @@ use crate::{
state::WakersList,
};
use fxhash::FxHashSet;
use fyrox_core::SafeLock;
use ron::ser::PrettyConfig;
use std::{
collections::BTreeMap,
@@ -127,24 +128,33 @@ pub struct ResourceRegistryStatusFlag(Arc<Mutex<RegistryReadyFlagData>>);
impl ResourceRegistryStatusFlag {
/// Returns current status of the registry.
pub fn status(&self) -> ResourceRegistryStatus {
self.0.lock().status
self.0.safe_lock().status
}
/// Marks the registry as loaded.
pub fn mark_as_loaded(&self) {
let mut lock = self.0.lock();
fn mark_as(&self, status: ResourceRegistryStatus) {
let mut lock = self.0.safe_lock();
lock.status = ResourceRegistryStatus::Loaded;
lock.status = status;
for waker in lock.wakers.drain(..) {
waker.wake();
}
}
/// Marks the registry as loaded.
pub fn mark_as_loaded(&self) {
self.mark_as(ResourceRegistryStatus::Loaded);
}
/// Marks the registry as unknown, due to an error.
pub fn mark_as_unknown(&self) {
self.mark_as(ResourceRegistryStatus::Unknown);
}
/// Marks the registry as loading. This method should be used before trying to load a registry
/// from an external source.
pub fn mark_as_loading(&self) {
self.0.lock().status = ResourceRegistryStatus::Loading;
self.0.safe_lock().status = ResourceRegistryStatus::Loading;
}
}
@@ -152,7 +162,7 @@ impl Future for ResourceRegistryStatusFlag {
type Output = ResourceRegistryStatus;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut lock = self.0.lock();
let mut lock = self.0.safe_lock();
match lock.status {
ResourceRegistryStatus::Unknown => Poll::Ready(ResourceRegistryStatus::Unknown),
@@ -503,7 +513,7 @@ impl ResourceRegistry {
continue;
}
if !loaders.lock().is_supported_resource(&path) {
if !loaders.safe_lock().is_supported_resource(&path) {
if path
.extension()
.is_some_and(|ext| ext != "meta" && ext != "registry")
+5 -1
View File
@@ -326,7 +326,11 @@ impl Visit for UntypedResource {
// 1) The registry is not loaded.
// 2) You're trying to deserialize the resource handle manually without a proper
// environment.
assert_eq!(status_flag.status(), ResourceRegistryStatus::Loaded);
assert_eq!(
status_flag.status(),
ResourceRegistryStatus::Loaded,
"Resource registry is not loaded"
);
let path = match inner_lock.old_format_path {
None => {
+2 -1
View File
@@ -42,6 +42,7 @@ use fyrox_core::{
reflect::prelude::*,
uuid_provider,
visitor::prelude::*,
SafeLock,
};
use std::{
sync::{Arc, Mutex, MutexGuard},
@@ -370,7 +371,7 @@ impl SoundContext {
/// You'll get a deadlock, so general rule here is to not store result of this method
/// anywhere.
pub fn state(&self) -> MutexGuard<'_, State> {
self.state.as_ref().unwrap().lock().unwrap()
self.state.as_ref().unwrap().safe_lock().unwrap()
}
/// Creates deep copy instead of shallow which is done by clone().
+2 -1
View File
@@ -26,6 +26,7 @@
use crate::context::{SoundContext, SAMPLE_RATE};
use fyrox_core::visitor::{Visit, VisitResult, Visitor};
use fyrox_core::SafeLock;
use std::error::Error;
use std::sync::{Arc, Mutex, MutexGuard};
@@ -103,7 +104,7 @@ impl SoundEngine {
/// Provides direct access to actual engine data.
pub fn state(&self) -> MutexGuard<State> {
self.0.lock().unwrap()
self.0.safe_lock().unwrap()
}
}
+15 -14
View File
@@ -37,6 +37,7 @@ use crate::{
type_traits::prelude::*,
uuid_provider,
visitor::prelude::*,
SafeLock,
},
curve::key::{CurveKeyView, CurveKeyViewContainer},
define_constructor,
@@ -121,35 +122,35 @@ pub struct CurveTransformCell(Mutex<CurveTransform>);
impl Clone for CurveTransformCell {
fn clone(&self) -> Self {
Self(Mutex::new(self.0.lock().clone()))
Self(Mutex::new(self.0.safe_lock().clone()))
}
}
impl CurveTransformCell {
/// Position of the center of the curve editor in the curve coordinate space.
pub fn position(&self) -> Vector2<f32> {
self.0.lock().position
self.0.safe_lock().position
}
/// Scape of the curve editor: multiply curve space units by this to get screen space units.
pub fn scale(&self) -> Vector2<f32> {
self.0.lock().scale
self.0.safe_lock().scale
}
/// Location of the curve editor on the screen, in screen space units.
pub fn bounds(&self) -> Rect<f32> {
self.0.lock().bounds
self.0.safe_lock().bounds
}
/// Modify the current position. Call this when the center of the curve view should change.
pub fn set_position(&self, position: Vector2<f32>) {
self.0.lock().position = position
self.0.safe_lock().position = position
}
/// Modify the current zoom of the curve view.
pub fn set_scale(&self, scale: Vector2<f32>) {
self.0.lock().scale = scale;
self.0.safe_lock().scale = scale;
}
/// Update the bounds of the curve view. Call this to ensure the CurveTransform accurately
/// reflects the actual size of the widget being drawn.
pub fn set_bounds(&self, bounds: Rect<f32>) {
self.0.lock().bounds = bounds;
self.0.safe_lock().bounds = bounds;
}
/// Just like [CurveTransformCell::y_step_iter] but for x-coordinates.
/// Iterate through a list of x-coordinates across the width of the bounds.
@@ -159,7 +160,7 @@ impl CurveTransformCell {
/// This iterator indates where grid lines should be drawn to make a curve easier
/// to read for the user.
pub fn x_step_iter(&self, grid_size: f32) -> StepIterator {
self.0.lock().x_step_iter(grid_size)
self.0.safe_lock().x_step_iter(grid_size)
}
/// Just like [CurveTransformCell::x_step_iter] but for y-coordinates.
/// Iterate through a list of y-coordinates across the width of the bounds.
@@ -169,31 +170,31 @@ impl CurveTransformCell {
/// This iterator indates where grid lines should be drawn to make a curve easier
/// to read for the user.
pub fn y_step_iter(&self, grid_size: f32) -> StepIterator {
self.0.lock().y_step_iter(grid_size)
self.0.safe_lock().y_step_iter(grid_size)
}
/// Construct the transformation matrices to reflect the current position, scale, and bounds.
pub fn update_transform(&self) {
self.0.lock().update_transform();
self.0.safe_lock().update_transform();
}
/// Transform a point on the curve into a point in the local coordinate space of the widget.
pub fn curve_to_local(&self) -> MappedMutexGuard<Matrix3<f32>> {
MutexGuard::map(self.0.lock(), |t| &mut t.curve_to_local)
MutexGuard::map(self.0.safe_lock(), |t| &mut t.curve_to_local)
}
/// Transform a point on the curve into a point on the screen.
pub fn curve_to_screen(&self) -> MappedMutexGuard<Matrix3<f32>> {
MutexGuard::map(self.0.lock(), |t| &mut t.curve_to_screen)
MutexGuard::map(self.0.safe_lock(), |t| &mut t.curve_to_screen)
}
/// Transform a point in the local coordinate space of the widget into a point in the coordinate space of the curve.
/// After the transformation, the x-coordinate could be a key location and the y-coordinate could be a key value.
/// Y-coordinates are flipped so that positive-y becomes the up direction.
pub fn local_to_curve(&self) -> MappedMutexGuard<Matrix3<f32>> {
MutexGuard::map(self.0.lock(), |t| &mut t.local_to_curve)
MutexGuard::map(self.0.safe_lock(), |t| &mut t.local_to_curve)
}
/// Transform a point on the screen into a point in the coordinate space of the curve.
/// After the transformation, the x-coordinate could be a key location and the y-coordinate could be a key value.
/// Y-coordinates are flipped so that positive-y becomes the up direction.
pub fn screen_to_curve(&self) -> MappedMutexGuard<Matrix3<f32>> {
MutexGuard::map(self.0.lock(), |t| &mut t.screen_to_curve)
MutexGuard::map(self.0.safe_lock(), |t| &mut t.screen_to_curve)
}
}
+3 -2
View File
@@ -39,6 +39,7 @@ use crate::{
sstorage::ImmutableString,
uuid::Uuid,
visitor::prelude::*,
SafeLock,
},
decorator::Decorator,
dropdown_list::DropdownList,
@@ -761,7 +762,7 @@ impl PropertyEditorDefinitionContainer {
self.definitions.write().insert(
definition.value_type_id(),
PropertyEditorDefinitionContainerEntry {
source_type_id: *self.context_type_id.lock(),
source_type_id: *self.context_type_id.safe_lock(),
property_editor: definition,
},
)
@@ -786,7 +787,7 @@ impl PropertyEditorDefinitionContainer {
self.definitions.write().insert(
definition.value_type_id(),
PropertyEditorDefinitionContainerEntry {
source_type_id: *self.context_type_id.lock(),
source_type_id: *self.context_type_id.safe_lock(),
property_editor: Box::new(definition),
},
)
+4 -3
View File
@@ -36,6 +36,7 @@ use crate::{
uuid_provider,
variable::InheritableVariable,
visitor::prelude::*,
SafeLock,
},
define_constructor,
draw::{CommandTexture, Draw, DrawingContext},
@@ -503,7 +504,7 @@ impl TextBox {
let mut str = str.replace("\r\n", "\n");
str.retain(|c| c == '\n' || !c.is_control());
if let Some(filter) = self.filter.as_ref() {
let filter = &mut *filter.lock();
let filter = &mut *filter.safe_lock();
str.retain(filter);
}
str
@@ -516,7 +517,7 @@ impl TextBox {
.filter(|c| !c.is_control())
.collect();
if let Some(filter) = self.filter.as_ref() {
let filter = &mut *filter.lock();
let filter = &mut *filter.safe_lock();
str.retain(filter);
}
str
@@ -1106,7 +1107,7 @@ impl Control for TextBox {
for symbol in text.chars() {
let insert = !symbol.is_control()
&& if let Some(filter) = self.filter.as_ref() {
let filter = &mut *filter.lock();
let filter = &mut *filter.safe_lock();
filter(symbol)
} else {
true
+2 -2
View File
@@ -33,7 +33,7 @@ use crate::{
reflect::prelude::*,
uuid::Uuid,
visitor::prelude::*,
ImmutableString,
ImmutableString, SafeLock,
},
core::{parking_lot::Mutex, variable::InheritableVariable},
define_constructor,
@@ -1805,7 +1805,7 @@ impl Widget {
#[inline]
pub fn user_data_cloned<T: Clone + 'static>(&self) -> Option<T> {
self.user_data.as_ref().and_then(|v| {
let guard = v.lock();
let guard = v.safe_lock();
guard.downcast_ref::<T>().cloned()
})
}