From 4c6ff5f8cc84d3cc1660e141d40bb109ba2c84ef Mon Sep 17 00:00:00 2001 From: Markus Heikkinen Date: Wed, 31 Oct 2018 12:36:35 +0200 Subject: [PATCH] Check for recursively locking mutexes --- Cargo.lock | 73 +++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/aiscript.rs | 72 ++++++++++++++++---------------- src/globals.rs | 22 +++++----- src/idle_orders.rs | 2 +- src/lib.rs | 10 +++-- src/recurse_checked_mutex.rs | 79 ++++++++++++++++++++++++++++++++++++ 7 files changed, 208 insertions(+), 51 deletions(-) create mode 100644 src/recurse_checked_mutex.rs diff --git a/Cargo.lock b/Cargo.lock index ef413c7..3a2caaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "samase_shim 0.5.0 (git+https://github.com/neivv/samase_plugin)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -441,6 +442,15 @@ name = "libc" version = "0.2.43" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lock_api" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.4.1" @@ -532,6 +542,35 @@ dependencies = [ "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "owning_ref" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parking_lot_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pkg-config" version = "0.3.14" @@ -628,6 +667,14 @@ name = "rustc-demangle" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "samase_plugin" version = "0.5.0" @@ -677,6 +724,19 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" version = "1.0.58" @@ -728,6 +788,11 @@ dependencies = [ "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "0.13.11" @@ -902,6 +967,7 @@ dependencies = [ "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum lde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f3d8de5fa220652b2416936dcd274fc86c461be26f0cda1d42dba845e53f09b" "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum lyon_geom 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0f181b2b51b8f2edd7b37a97718e848933dcc7420c8cde081ba95dbb475b6975" "checksum lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9dc8e0746b7cca11960b602f7fe037bb067746a01eab4aa502fed1494544843" @@ -914,6 +980,9 @@ dependencies = [ "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" "checksum objc 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "9833ab0efe5361b1e2122a0544a5d3359576911a42cb098c2e59be8650807367" +"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" +"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" +"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "3d7b7eaaa90b4a90a932a9ea6666c95a389e424eff347f0f793979289429feee" @@ -926,17 +995,21 @@ dependencies = [ "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26" "checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum samase_plugin 0.5.0 (git+https://github.com/neivv/samase_plugin)" = "" "checksum samase_shim 0.5.0 (git+https://github.com/neivv/samase_plugin)" = "" "checksum same-file 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10f7794e2fda7f594866840e95f5c5962e886e228e68b6505885811a94dd728c" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)" = "34e9df8efbe7a2c12ceec1fc8744d56ae3374d8ae325f4a0028949d16433d554" "checksum serde_derive 1.0.58 (registry+https://github.com/rust-lang/crates.io-index)" = "ac38f51a52a556cd17545798e29536885fb1a3fa63d6399f5ef650f4a7d35901" "checksum servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a088f8d775a5c5314aae09bd77340bc9c67d72b9a45258be34c83548b4814cd9" "checksum servo-fontconfig-sys 4.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b46d201addcfbd25c1798ad1281d98c40743824e0b0f1e611bd3d5d0d31a7b8d" "checksum servo-freetype-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d00ab791f66cd2ec58e72c91b6076cee20fac560463aa871404eb31dfc9c4ff" "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" +"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.15.14 (registry+https://github.com/rust-lang/crates.io-index)" = "baaba45c6bf60fe29aaf241fa33306c0b75c801edea8378263a8f043b09a5634" "checksum synstructure 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec37f4fab4bafaf6b5621c1d54e6aa5d4d059a8f84929e87abfdd7f9f04c6db2" diff --git a/Cargo.toml b/Cargo.toml index 7384387..c2e0fb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ lazy_static = "1.0" libc = "0.2" log = "0.4" memoffset = "0.2.1" +parking_lot = "0.6" scopeguard = "0.3.2" serde = { version = "1.0.27", features = ["rc"] } serde_derive = "1.0.27" diff --git a/src/aiscript.rs b/src/aiscript.rs index 24c21c2..ab577e4 100644 --- a/src/aiscript.rs +++ b/src/aiscript.rs @@ -6,11 +6,12 @@ use std::path::{Component, Path, PathBuf}; use std::ptr::null_mut; use std::slice; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use bincode; use byteorder::{WriteBytesExt, LE}; use directories::UserDirs; +use parking_lot::Mutex; use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; use smallvec::SmallVec; @@ -60,11 +61,11 @@ unsafe fn aiscript_unit_search(game: Game) -> Arc { let frame = game.frame_count() as usize; if CACHED_UNIT_SEARCH_FRAME.load(Ordering::Relaxed) != frame { let search = Arc::new(UnitSearch::from_bw()); - *CACHED_UNIT_SEARCH.lock().unwrap() = Some(search.clone()); + *CACHED_UNIT_SEARCH.lock() = Some(search.clone()); CACHED_UNIT_SEARCH_FRAME.store(frame, Ordering::Relaxed); search } else { - let guard = CACHED_UNIT_SEARCH.lock().unwrap(); + let guard = CACHED_UNIT_SEARCH.lock(); guard.as_ref().unwrap().clone() } } @@ -121,7 +122,8 @@ impl AttackTimeoutState { pub unsafe extern fn attack_timeout(script: *mut bw::AiScript) { let mut read = ScriptData::new(script); let timeout = read.read_u32(); - Globals::get().attack_timeouts[(*script).player as usize].value = Some(timeout); + let mut globals = Globals::get("ais attack_timeout"); + globals.attack_timeouts[(*script).player as usize].value = Some(timeout); } pub unsafe fn attack_timeouts_frame_hook(globals: &mut Globals, game: Game) { @@ -389,7 +391,7 @@ pub unsafe extern fn set_town_id(script: *mut bw::AiScript) { return; } }; - let mut globals = Globals::get(); + let mut globals = Globals::get("ais set_id"); globals.town_ids.swap_retain(|x| x.town != town); globals.town_ids.push(TownId { town, @@ -402,7 +404,7 @@ pub unsafe extern fn remove_build(script: *mut bw::AiScript) { let amount = read.read_u8(); let mut unit_id = read.read_unit_match(); let id = read.read_u8(); - let globals = Globals::get(); + let globals = Globals::get("ais remove_build"); let town = match id { 255 => Town::from_ptr((*script).town), _ => globals @@ -472,7 +474,7 @@ pub unsafe extern fn max_workers(script: *mut bw::AiScript) { return; } }; - let mut globals = Globals::get(); + let mut globals = Globals::get("ais max_workers"); globals.max_workers.swap_retain(|x| x.town != town); if count != 255 { globals.max_workers.push(MaxWorkers { @@ -495,7 +497,7 @@ pub unsafe extern fn under_attack(script: *mut bw::AiScript) { let mut read = ScriptData::new(script); let mode = read.read_u8(); let player = (*script).player as usize; - let mut globals = Globals::get(); + let mut globals = Globals::get("ais under_attack"); globals.under_attack_mode[player] = match mode { 0 => Some(false), 1 => None, @@ -607,7 +609,7 @@ pub unsafe extern fn aicontrol(script: *mut bw::AiScript) { let mut read = ScriptData::new(script); let mode = read.read_u8(); let player = (*script).player as usize; - let mut globals = Globals::get(); + let mut globals = Globals::get("ais aicontrol"); let out = &mut globals.ai_mode[player]; match mode { @@ -767,7 +769,7 @@ pub unsafe extern fn supply(script: *mut bw::AiScript) { Max, } let old_pos = (*script).pos - 1; - let mut globals = Globals::get(); + let mut globals = Globals::get("ais supply"); let game = Game::get(); let mut read = ScriptData::new(script); let players = read.read_player_match(game); @@ -889,7 +891,7 @@ pub unsafe extern fn resources_command(script: *mut bw::AiScript) { } let old_pos = (*script).pos - 1; let game = Game::get(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais resources"); let mut read = ScriptData::new(script); let players = read.read_player_match(game); let modifier = read.read_modifier(); @@ -994,7 +996,7 @@ pub unsafe extern fn reveal_area(script: *mut bw::AiScript) { } let mut read = ScriptData::new(script); let game = Game::get(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais reveal_area"); let players = read.read_player_match(game); let mut src = read.read_position(); let radius = read.read_u16(); @@ -1042,7 +1044,7 @@ fn get_bank_path(name: &str) -> Option { pub unsafe extern fn save_bank(script: *mut bw::AiScript) { let mut read = ScriptData::new(script); let name = read.read_string(); - let globals = Globals::get(); + let globals = Globals::get("ais save_bank"); let name = String::from_utf8_lossy(name).to_string(); let path = match get_bank_path(&name) { Some(s) => s, @@ -1067,7 +1069,7 @@ pub unsafe extern fn save_bank(script: *mut bw::AiScript) { pub unsafe extern fn load_bank(script: *mut bw::AiScript) { let mut read = ScriptData::new(script); let name = read.read_string(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais load_bank"); globals.bank.reset(); let name = String::from_utf8_lossy(name); let path = match get_bank_path(&name) { @@ -1100,7 +1102,7 @@ unsafe fn add_bank_data( amount: u32, dest: u32, ) { - let mut globals = Globals::get(); + let mut globals = Globals::get("ais bank_data"); let globals = &mut *globals; match modifier.ty { ModifierType::Read(read) => { @@ -1284,7 +1286,7 @@ pub unsafe fn ai_attack_focus_hook( orig: &Fn(*mut bw::Unit, u32) -> u32, ) -> u32 { if (*unit).player < 8 { - let globals = Globals::get(); + let globals = Globals::get("ai focus unit hook"); let ai_mode = globals.ai_mode[(*unit).player as usize]; if !ai_mode.focus_disabled_units { return 0; @@ -1299,7 +1301,7 @@ pub unsafe fn unit_name_hook(unit_id: u32, orig: &Fn(u32) -> *const u8) -> *cons Some(s) => s, None => return orig(unit_id), }; - let globals = Globals::get(); + let globals = Globals::get("unit name hook"); let name_match = globals .renamed_units .states @@ -1344,7 +1346,7 @@ pub unsafe extern fn unit_name(script: *mut bw::AiScript) { bw::print_text("unit_name is not supported in SCR"); return; } - let mut globals = Globals::get(); + let mut globals = Globals::get("ais unit_name"); let rename_status = RenameStatus { area: src.area, @@ -1369,7 +1371,7 @@ pub unsafe extern fn deaths(script: *mut bw::AiScript) { let mut units = read.read_unit_match(); let dest = read.read_jump_pos(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais deaths"); match modifier.ty { ModifierType::Read(read) => { let sum = units @@ -1426,7 +1428,7 @@ pub unsafe extern fn wait_rand(script: *mut bw::AiScript) { let mut read = ScriptData::new(script); let mut r1 = read.read_u32(); let mut r2 = read.read_u32(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais wait_rand"); if r1 > r2 { mem::swap(&mut r1, &mut r2); } @@ -1444,7 +1446,7 @@ pub unsafe extern fn kills_command(script: *mut bw::AiScript) { let amount = read.read_u32(); let mut units = read.read_unit_match(); let dest = read.read_jump_pos(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais kills"); match modifier.ty { ModifierType::Read(read) => { @@ -1512,7 +1514,7 @@ pub unsafe fn increment_deaths( let amount = 1; let player = (*target).player; { - let mut globals = Globals::get(); + let mut globals = Globals::get("increment deaths hook"); let kpos = globals::KCPos::new(attacker_p_id, player, unit_id); globals.kills_table.try_add(kpos, amount); } @@ -1596,7 +1598,7 @@ pub unsafe extern fn upgrade_jump(script: *mut bw::AiScript) { } } ModifierType::Write(w) => { - let mut globals = Globals::get(); + let mut globals = Globals::get("ais upgrade_jump"); for player in players.players() { let old = game.upgrade_level(player, upgrade); let new = w.apply(u32::from(old), u32::from(level), &mut globals.rng); @@ -1608,7 +1610,7 @@ pub unsafe extern fn upgrade_jump(script: *mut bw::AiScript) { pub unsafe extern fn load_bunkers(script: *mut bw::AiScript) { // load_bunkers(area, load_unit, quantity, bunker_unit, bunker_quantity, priority) - let mut globals = Globals::get(); + let mut globals = Globals::get("ais load_bunkers"); let mut read = ScriptData::new(script); let player = (*script).player; let mut src = read.read_position(); @@ -1635,7 +1637,7 @@ pub unsafe extern fn load_bunkers(script: *mut bw::AiScript) { pub unsafe extern fn unit_avail(script: *mut bw::AiScript) { let old_pos = (*script).pos - 1; let game = Game::get(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais unit_avail"); let mut read = ScriptData::new(script); let players = read.read_player_match(game); let modifier = read.read_modifier(); @@ -1726,7 +1728,7 @@ pub unsafe extern fn tech_jump(script: *mut bw::AiScript) { } } ModifierType::Write(w) => { - let mut globals = Globals::get(); + let mut globals = Globals::get("ais tech_jump"); for player in players.players() { let old = game.tech_researched(player, tech); let new = w.apply(u32::from(old), u32::from(level), &mut globals.rng); @@ -1770,7 +1772,7 @@ pub unsafe extern fn tech_avail(script: *mut bw::AiScript) { } } ModifierType::Write(w) => { - let mut globals = Globals::get(); + let mut globals = Globals::get("ais tech_avail"); for player in players.players() { let old = game.tech_available(player, tech); let new = w.apply(u32::from(old), u32::from(level), &mut globals.rng); @@ -1785,7 +1787,7 @@ pub unsafe extern fn random_call(script: *mut bw::AiScript) { let chance = read.read_u8(); let dest = read.read_jump_pos(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais random_call"); let random = globals.rng.synced_rand(0..256); if u32::from(chance) > random { let ret = (*script).pos; @@ -1802,7 +1804,7 @@ pub unsafe extern fn attack_rand(script: *mut bw::AiScript) { if r1 > r2 { mem::swap(&mut r1, &mut r2); } - let mut globals = Globals::get(); + let mut globals = Globals::get("ais attack_rand"); let random = globals.rng.synced_rand(r1..r2 + 1); add_to_attack_force((*script).player as u8, UnitId(unit), random); } @@ -2494,7 +2496,7 @@ pub fn serialize_scripts( ) -> Result { use serde::ser::SerializeSeq; - let mut state = globals::save_state(); + let mut state = globals::save_state("serialize_scripts"); let state = state .as_mut() .expect("Serializing AI scripts without state init"); @@ -2733,7 +2735,7 @@ pub unsafe fn ai_spellcast_hook( unit: *mut bw::Unit, orig: &Fn(bool, *mut bw::Unit) -> u32, ) -> u32 { - let globals = Globals::get(); + let globals = Globals::get("ai spellcast hook"); let ai_mode = &globals.ai_mode[(*unit).player as usize]; if !ai_mode.retaliation && revenge { 0 @@ -2793,7 +2795,7 @@ pub unsafe fn choose_building_placement( let unit_id = UnitId(unit_id as u16); let player = builder.player(); - let globals = Globals::get(); + let globals = Globals::get("place building hook"); let unit_search = UnitSearch::from_bw(); if let Some(town_id) = globals.town_ids.iter().find(|x| x.town.0 == town) { let game = Game::get(); @@ -2966,7 +2968,7 @@ unsafe extern fn add_layout( } }; - let mut globals = Globals::get(); + let mut globals = Globals::get("ais base_layout"); let town = Town::from_ptr((*script).town); if let Some(town) = town { let layout = BaseLayout { @@ -3027,7 +3029,7 @@ pub unsafe extern fn guard_command(script: *mut bw::AiScript) { let quantity = read.read_u8(); let death_limit = read.read_u8(); let priority = read.read_u8(); - let mut globals = Globals::get(); + let mut globals = Globals::get("ais guard"); for _n in 0..quantity { let guards = samase::guard_ais().add((*script).player as usize); let old_first_active = (*guards).first; @@ -3092,7 +3094,7 @@ pub unsafe extern fn create_script(script: *mut bw::AiScript) { } }; - let mut globals = Globals::get(); + let mut globals = Globals::get("ais create_script"); let flags = ((*script).flags & 1) | ((resarea as u32) << 3); let first_ai_script = bw::first_ai_script(); let script = globals.ai_scripts.alloc(Script { diff --git a/src/globals.rs b/src/globals.rs index 95c4f1d..a9cdef9 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -1,7 +1,6 @@ use std::ffi::CString; use std::ptr::null_mut; use std::slice; -use std::sync::{Mutex, MutexGuard}; use bincode; @@ -14,13 +13,14 @@ use aiscript::{ use block_alloc::BlockAllocSet; use bw; use idle_orders::IdleOrders; +use recurse_checked_mutex::{Mutex, MutexGuard}; use rng::Rng; use swap_retain::SwapRetain; use unit::{self, Unit}; lazy_static! { static ref GLOBALS: Mutex = Mutex::new(Globals::new()); - static ref SAVE_STATE: Mutex> = Mutex::new(None); + static ref SAVE_STATE: Mutex> = Default::default(); } pub struct SaveState { @@ -387,18 +387,18 @@ impl Globals { // Should only be called on hook start to prevent deadlocks. // Should also have something to detect/typesystem level block misuse :l - pub fn get() -> MutexGuard<'static, Globals> { - GLOBALS.lock().unwrap() + pub fn get(caller: &'static str) -> MutexGuard<'static, Globals> { + GLOBALS.lock(caller) } } -pub fn save_state() -> MutexGuard<'static, Option> { - SAVE_STATE.lock().unwrap() +pub fn save_state(caller: &'static str) -> MutexGuard<'static, Option> { + SAVE_STATE.lock(caller) } pub unsafe extern fn init_game() { aiscript::invalidate_cached_unit_search(); - *GLOBALS.lock().unwrap() = Globals::new(); + *GLOBALS.lock("init") = Globals::new(); } pub unsafe extern fn wrap_save( @@ -409,7 +409,7 @@ pub unsafe extern fn wrap_save( orig: unsafe extern fn(*const u8, u32), ) { trace!("Saving.."); - let mut globals = GLOBALS.lock().unwrap(); + let mut globals = GLOBALS.lock("before save"); aiscript::claim_bw_allocated_scripts(&mut globals); let first_ai_script = bw::first_ai_script(); @@ -417,7 +417,7 @@ pub unsafe extern fn wrap_save( defer!({ bw::set_first_ai_script(first_ai_script); }); - *SAVE_STATE.lock().unwrap() = Some(SaveState { + *SAVE_STATE.lock("init save state") = Some(SaveState { first_ai_script: SendPtr(first_ai_script), }); drop(globals); @@ -426,7 +426,7 @@ pub unsafe extern fn wrap_save( } pub unsafe extern fn save(set_data: unsafe extern fn(*const u8, usize)) { - let globals = GLOBALS.lock().unwrap(); + let globals = GLOBALS.lock("save"); unit::init_save_mapping(); aiscript::init_save_mapping(); defer!(aiscript::clear_save_mapping()); @@ -457,6 +457,6 @@ pub unsafe extern fn load(ptr: *const u8, len: usize) -> u32 { return 0; } }; - *GLOBALS.lock().unwrap() = data; + *GLOBALS.lock("load") = data; 1 } diff --git a/src/idle_orders.rs b/src/idle_orders.rs index 91c9d26..75f70ca 100644 --- a/src/idle_orders.rs +++ b/src/idle_orders.rs @@ -567,7 +567,7 @@ pub unsafe extern fn idle_orders(script: *mut bw::AiScript) { } return; } - let mut globals = Globals::get(); + let mut globals = Globals::get("ais idle_orders"); let orders = &mut globals.idle_orders; let deathrattle = target_flags.simple & 0x40 != 0; if delete_flags != 0 { diff --git a/src/lib.rs b/src/lib.rs index efeb9a2..794a243 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,7 @@ extern crate libc; extern crate log; #[macro_use] extern crate memoffset; +extern crate parking_lot; extern crate rand; #[macro_use] extern crate scopeguard; @@ -62,6 +63,7 @@ mod globals; mod idle_orders; mod list; mod order; +mod recurse_checked_mutex; mod rng; mod swap_retain; mod unit; @@ -211,7 +213,7 @@ pub extern fn Initialize() { unsafe extern fn frame_hook() { let search = unit_search::UnitSearch::from_bw(); - let mut globals = Globals::get(); + let mut globals = Globals::get("frame hook"); let globals = &mut *globals; let game = game::Game::get(); aiscript::claim_bw_allocated_scripts(globals); @@ -252,7 +254,7 @@ unsafe extern fn frame_hook() { } unsafe extern fn frame_hook_after() { - let mut globals = Globals::get(); + let mut globals = Globals::get("frame hook after"); aiscript::update_towns(&mut globals); aiscript::attack_timeouts_frame_hook_after(&mut globals); } @@ -265,14 +267,14 @@ static FIRST_STEP_ORDER_OF_FRAME: AtomicBool = ATOMIC_BOOL_INIT; unsafe extern fn step_order_hook(u: *mut c_void, orig: unsafe extern fn(*mut c_void)) { if FIRST_STEP_ORDER_OF_FRAME.load(Ordering::Relaxed) { FIRST_STEP_ORDER_OF_FRAME.store(false, Ordering::Relaxed); - let globals = Globals::get(); + let globals = Globals::get("step order hook (start)"); aiscript::clean_unsatisfiable_requests(&globals.ai_mode); } let unit = unit::Unit(u as *mut bw::Unit); match unit.order() { order::id::DIE => { - let mut globals = Globals::get(); + let mut globals = Globals::get("step order hook (die)"); globals.idle_orders.unit_removed(unit); globals.bunker_states.unit_removed(unit); } diff --git a/src/recurse_checked_mutex.rs b/src/recurse_checked_mutex.rs new file mode 100644 index 0000000..c79d2a0 --- /dev/null +++ b/src/recurse_checked_mutex.rs @@ -0,0 +1,79 @@ +use std::cell::UnsafeCell; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use winapi::um::processthreadsapi::GetCurrentThreadId; + +pub struct Mutex { + inner: parking_lot::Mutex, + locking_thread: AtomicUsize, + locking_call: UnsafeCell<&'static str>, +} + +pub struct MutexGuard<'a, T: 'a> { + mutex: &'a Mutex, + guard: parking_lot::MutexGuard<'a, T>, +} + +unsafe impl Sync for Mutex {} + +impl Mutex { + pub fn new(value: T) -> Mutex { + Mutex { + inner: parking_lot::Mutex::new(value), + locking_thread: AtomicUsize::new(!0), + locking_call: UnsafeCell::new(""), + } + } + + pub fn lock<'s>(&'s self, locking_call: &'static str) -> MutexGuard<'s, T> { + let self_thread_id; + unsafe { + self_thread_id = GetCurrentThreadId(); + if self.locking_thread.load(Ordering::Relaxed) as u32 == self_thread_id { + panic!( + "Thread {} to lock a mutex recursively from '{}', it was already locked by '{}'", + self_thread_id, locking_call, *self.locking_call.get(), + ); + } + } + let guard = MutexGuard { + mutex: self, + guard: self.inner.lock(), + }; + self.locking_thread + .store(self_thread_id as usize, Ordering::Relaxed); + unsafe { + *self.locking_call.get() = locking_call; + } + guard + } +} + +impl Default for Mutex { + fn default() -> Self { + Mutex { + inner: Default::default(), + locking_thread: AtomicUsize::new(!0), + locking_call: UnsafeCell::new(""), + } + } +} + +impl<'a, T: 'a> Drop for MutexGuard<'a, T> { + fn drop(&mut self) { + self.mutex.locking_thread.store(!0, Ordering::Relaxed); + } +} + +impl<'a, T: 'a> std::ops::Deref for MutexGuard<'a, T> { + type Target = T; + fn deref(&self) -> &T { + &self.guard + } +} + +impl<'a, T: 'a> std::ops::DerefMut for MutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.guard + } +}