Rework setting while active to be slightly less hacky.
continuous-integration/drone/push Build was killed Details

This commit is contained in:
Deukhoofd 2022-06-18 18:41:23 +02:00
parent a1051d059e
commit dd535b30af
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
8 changed files with 69 additions and 66 deletions

View File

@ -8,6 +8,7 @@ use crate::static_data::items::item::Item;
use crate::static_data::libraries::static_data::StaticData; use crate::static_data::libraries::static_data::StaticData;
use crate::{PkmnResult, StringKey}; use crate::{PkmnResult, StringKey};
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc;
#[derive(Debug)] #[derive(Debug)]
pub struct DynamicLibrary { pub struct DynamicLibrary {
@ -49,7 +50,7 @@ impl DynamicLibrary {
self.misc_library.deref() self.misc_library.deref()
} }
pub fn load_script(&self, _category: ScriptCategory, _key: &StringKey) -> PkmnResult<Option<Box<dyn Script>>> { pub fn load_script(&self, _category: ScriptCategory, _key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
todo!() todo!()
} }
pub fn load_item_script(&self, _key: &Item) -> PkmnResult<Option<Box<dyn ItemScript>>> { pub fn load_item_script(&self, _key: &Item) -> PkmnResult<Option<Box<dyn ItemScript>>> {

View File

@ -292,7 +292,7 @@ impl<'own, 'library> VolatileScripts<'own> for Battle<'own, 'library> {
&self.volatile_scripts &self.volatile_scripts
} }
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Box<dyn Script>>> { fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
self.library.load_script(ScriptCategory::Battle, key) self.library.load_script(ScriptCategory::Battle, key)
} }
} }

View File

@ -258,7 +258,7 @@ impl<'own, 'library> VolatileScripts<'own> for BattleSide<'own, 'library> {
&self.volatile_scripts &self.volatile_scripts
} }
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Box<dyn Script>>> { fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
self.battle().library().load_script(crate::ScriptCategory::Side, key) self.battle().library().load_script(crate::ScriptCategory::Side, key)
} }
} }

View File

@ -395,15 +395,10 @@ impl<'own, 'library> Pokemon<'own, 'library> {
.load_script(ScriptCategory::Ability, self.active_ability().name()) .load_script(ScriptCategory::Ability, self.active_ability().name())
.unwrap(); .unwrap();
if let Some(ability_script) = ability_script { if let Some(ability_script) = ability_script {
self.ability_script.set(ability_script); self.ability_script
.set(ability_script)
.as_ref()
// Ensure the ability script gets initialized with the parameters for the ability. // Ensure the ability script gets initialized with the parameters for the ability.
self.ability_script()
.get()
.unwrap()
.read()
.as_ref()
.unwrap()
.as_ref()
.on_initialize(self.active_ability().parameters()) .on_initialize(self.active_ability().parameters())
} else { } else {
self.ability_script.clear(); self.ability_script.clear();
@ -581,7 +576,7 @@ impl<'own, 'library> VolatileScripts<'own> for Pokemon<'own, 'library> {
&self.volatile &self.volatile
} }
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Box<dyn Script>>> { fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>> {
self.library.load_script(ScriptCategory::Pokemon, key) self.library.load_script(ScriptCategory::Pokemon, key)
} }
} }

View File

@ -99,7 +99,7 @@ pub trait ScriptSource<'a> {
#[derive(Debug)] #[derive(Debug)]
pub enum ScriptWrapper { pub enum ScriptWrapper {
Script(Weak<RwLock<Option<Box<dyn Script>>>>), Script(Weak<RwLock<Option<Arc<dyn Script>>>>),
Set(Weak<RwLock<ScriptSet>>), Set(Weak<RwLock<ScriptSet>>),
} }
@ -263,7 +263,7 @@ mod tests {
#[test] #[test]
fn script_aggregator_property_iterates_single_script() { fn script_aggregator_property_iterates_single_script() {
let script = ScriptContainer::new(Box::new(TestScript::new())); let script = ScriptContainer::new(Arc::new(TestScript::new()));
let scripts = vec![ScriptWrapper::from(&script)]; let scripts = vec![ScriptWrapper::from(&script)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>); let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
while let Some(v) = aggregator.get_next() { while let Some(v) = aggregator.get_next() {
@ -275,7 +275,7 @@ mod tests {
#[test] #[test]
fn script_aggregator_property_iterates_single_script_with_resets() { fn script_aggregator_property_iterates_single_script_with_resets() {
let script = ScriptContainer::new(Box::new(TestScript::new())); let script = ScriptContainer::new(Arc::new(TestScript::new()));
let scripts = vec![ScriptWrapper::from(&script)]; let scripts = vec![ScriptWrapper::from(&script)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>); let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
for i in 1..11 { for i in 1..11 {
@ -290,9 +290,9 @@ mod tests {
#[test] #[test]
fn script_aggregator_property_iterates_three_script() { fn script_aggregator_property_iterates_three_script() {
let script1 = ScriptContainer::new(Box::new(TestScript::new())); let script1 = ScriptContainer::new(Arc::new(TestScript::new()));
let script2 = ScriptContainer::new(Box::new(TestScript::new())); let script2 = ScriptContainer::new(Arc::new(TestScript::new()));
let script3 = ScriptContainer::new(Box::new(TestScript::new())); let script3 = ScriptContainer::new(Arc::new(TestScript::new()));
let scripts = vec![ let scripts = vec![
ScriptWrapper::from(&script1), ScriptWrapper::from(&script1),
ScriptWrapper::from(&script2), ScriptWrapper::from(&script2),
@ -312,9 +312,9 @@ mod tests {
#[test] #[test]
fn script_aggregator_property_iterates_three_script_with_resets() { fn script_aggregator_property_iterates_three_script_with_resets() {
let script1 = ScriptContainer::new(Box::new(TestScript::new())); let script1 = ScriptContainer::new(Arc::new(TestScript::new()));
let script2 = ScriptContainer::new(Box::new(TestScript::new())); let script2 = ScriptContainer::new(Arc::new(TestScript::new()));
let script3 = ScriptContainer::new(Box::new(TestScript::new())); let script3 = ScriptContainer::new(Arc::new(TestScript::new()));
let scripts = vec![ let scripts = vec![
ScriptWrapper::from(&script1), ScriptWrapper::from(&script1),
ScriptWrapper::from(&script2), ScriptWrapper::from(&script2),
@ -339,9 +339,9 @@ mod tests {
fn script_aggregator_property_iterates_script_set() { fn script_aggregator_property_iterates_script_set() {
let set = Arc::new(RwLock::new(ScriptSet::default())); let set = Arc::new(RwLock::new(ScriptSet::default()));
let mut mut_set = set.write(); let mut mut_set = set.write();
mut_set.add(Box::new(TestScript::new_with_name("test_a"))); mut_set.add(Arc::new(TestScript::new_with_name("test_a")));
mut_set.add(Box::new(TestScript::new_with_name("test_b"))); mut_set.add(Arc::new(TestScript::new_with_name("test_b")));
mut_set.add(Box::new(TestScript::new_with_name("test_c"))); mut_set.add(Arc::new(TestScript::new_with_name("test_c")));
// Drop so we don't have a lock on it anymore. // Drop so we don't have a lock on it anymore.
drop(mut_set); drop(mut_set);
@ -369,9 +369,9 @@ mod tests {
fn script_aggregator_property_iterates_script_set_when_removing_last() { fn script_aggregator_property_iterates_script_set_when_removing_last() {
let set = Arc::new(RwLock::new(ScriptSet::default())); let set = Arc::new(RwLock::new(ScriptSet::default()));
let mut mut_set = set.write(); let mut mut_set = set.write();
mut_set.add(Box::new(TestScript::new_with_name("test_a"))); mut_set.add(Arc::new(TestScript::new_with_name("test_a")));
mut_set.add(Box::new(TestScript::new_with_name("test_b"))); mut_set.add(Arc::new(TestScript::new_with_name("test_b")));
mut_set.add(Box::new(TestScript::new_with_name("test_c"))); mut_set.add(Arc::new(TestScript::new_with_name("test_c")));
// Drop so we don't have a lock on it anymore. // Drop so we don't have a lock on it anymore.
drop(mut_set); drop(mut_set);
@ -415,9 +415,9 @@ mod tests {
fn script_aggregator_property_iterates_script_set_when_removing_middle() { fn script_aggregator_property_iterates_script_set_when_removing_middle() {
let set = Arc::new(RwLock::new(ScriptSet::default())); let set = Arc::new(RwLock::new(ScriptSet::default()));
let mut mut_set = set.write(); let mut mut_set = set.write();
mut_set.add(Box::new(TestScript::new_with_name("test_a"))); mut_set.add(Arc::new(TestScript::new_with_name("test_a")));
mut_set.add(Box::new(TestScript::new_with_name("test_b"))); mut_set.add(Arc::new(TestScript::new_with_name("test_b")));
mut_set.add(Box::new(TestScript::new_with_name("test_c"))); mut_set.add(Arc::new(TestScript::new_with_name("test_c")));
// Drop so we don't have a lock on it anymore. // Drop so we don't have a lock on it anymore.
drop(mut_set); drop(mut_set);
@ -491,7 +491,7 @@ mod tests {
aggregator.reset(); aggregator.reset();
assert!(aggregator.get_next().is_none()); assert!(aggregator.get_next().is_none());
aggregator.reset(); aggregator.reset();
source.script.set(Box::new(TestScript::new())); source.script.set(Arc::new(TestScript::new()));
assert!(aggregator.get_next().is_some()); assert!(aggregator.get_next().is_some());
} }
@ -503,7 +503,7 @@ mod tests {
}; };
let mut aggregator = source.get_script_iterator(); let mut aggregator = source.get_script_iterator();
source.script.set(Box::new(TestScript::new())); source.script.set(Arc::new(TestScript::new()));
assert!(aggregator.get_next().is_some()); assert!(aggregator.get_next().is_some());
aggregator.reset(); aggregator.reset();
source.script.clear(); source.script.clear();

View File

@ -142,7 +142,7 @@ impl Debug for dyn Script {
} }
} }
type ScriptHolder = Arc<RwLock<Option<Box<dyn Script>>>>; type ScriptHolder = Arc<RwLock<Option<Arc<dyn Script>>>>;
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub struct ScriptContainer { pub struct ScriptContainer {
@ -150,7 +150,7 @@ pub struct ScriptContainer {
} }
impl ScriptContainer { impl ScriptContainer {
pub fn new(script: Box<dyn Script>) -> ScriptContainer { pub fn new(script: Arc<dyn Script>) -> ScriptContainer {
Self { Self {
script: Arc::new(RwLock::new(Some(script))), script: Arc::new(RwLock::new(Some(script))),
} }
@ -167,21 +167,20 @@ impl ScriptContainer {
} }
} }
pub fn set(&self, script: Box<dyn Script>) { pub fn set(&self, script: Arc<dyn Script>) -> Arc<dyn Script> {
if let Some(v) = self.script.read().deref() { if let Some(v) = self.script.read().deref() {
v.mark_for_deletion(); v.mark_for_deletion();
} }
if self.script.is_locked() { if self.script.is_locked() {
// This is unfortunate, but when we're replacing this script from itself, we can not delay let holder = self.script.clone();
// it, and as the script is locked, we run into a deadlock here. As such, we force update let new = script.clone();
// it, bypassing the RwLock entirely. A better solution might be possible, but I currently std::thread::spawn(move || {
// cannot think of one. holder.write().replace(new);
unsafe { });
self.script.data_ptr().as_mut().unwrap().replace(script);
}
} else { } else {
self.script.write().replace(script); self.script.write().replace(script.clone());
} };
script
} }
pub fn clear(&self) { pub fn clear(&self) {
@ -215,11 +214,13 @@ impl From<ScriptHolder> for ScriptContainer {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::sync::atomic::AtomicBool; use std::sync::atomic::{AtomicBool, AtomicPtr};
use std::thread::sleep;
use std::time::Duration;
pub struct TestScript { pub struct TestScript {
name: StringKey, name: StringKey,
container: *mut ScriptContainer, container: AtomicPtr<ScriptContainer>,
suppressed_count: AtomicUsize, suppressed_count: AtomicUsize,
marked_for_deletion: AtomicBool, marked_for_deletion: AtomicBool,
} }
@ -228,7 +229,7 @@ mod tests {
fn new() -> Self { fn new() -> Self {
Self { Self {
name: StringKey::new("test"), name: StringKey::new("test"),
container: std::ptr::null_mut::<ScriptContainer>(), container: AtomicPtr::<ScriptContainer>::default(),
suppressed_count: AtomicUsize::new(0), suppressed_count: AtomicUsize::new(0),
marked_for_deletion: Default::default(), marked_for_deletion: Default::default(),
} }
@ -253,7 +254,7 @@ mod tests {
} }
fn on_initialize(&self, _pars: &[EffectParameter]) { fn on_initialize(&self, _pars: &[EffectParameter]) {
unsafe { self.container.as_mut().unwrap().clear() } unsafe { self.container.load(Ordering::Relaxed).as_ref().unwrap().clear() }
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
@ -270,11 +271,13 @@ mod tests {
// then dispose of the script. // then dispose of the script.
#[test] #[test]
fn clear_self_while_active() { fn clear_self_while_active() {
let script = Box::new(TestScript::new()); let script = Arc::new(TestScript::new());
let container = ScriptContainer::new(script); let mut container = ScriptContainer::new(script);
let mut w = container.script.write(); let c = &mut container as *mut ScriptContainer;
let script: &mut TestScript = w.as_mut().unwrap().as_any_mut().downcast_mut().unwrap();
script.container = &container as *const ScriptContainer as *mut ScriptContainer; let w = container.script.read();
let script: &TestScript = w.as_ref().unwrap().as_any().downcast_ref().unwrap();
script.container.store(c, Ordering::SeqCst);
drop(w); drop(w);
// Initialize with the script being taken as read lock. This prevents the script from actually // Initialize with the script being taken as read lock. This prevents the script from actually
// removing itself, as it's still doing things. // removing itself, as it's still doing things.
@ -290,7 +293,7 @@ mod tests {
pub struct ReplaceTestScript { pub struct ReplaceTestScript {
name: StringKey, name: StringKey,
container: *mut ScriptContainer, container: AtomicPtr<ScriptContainer>,
suppressed_count: AtomicUsize, suppressed_count: AtomicUsize,
marked_for_deletion: AtomicBool, marked_for_deletion: AtomicBool,
} }
@ -299,15 +302,15 @@ mod tests {
fn new(name: StringKey) -> Self { fn new(name: StringKey) -> Self {
Self { Self {
name, name,
container: std::ptr::null_mut::<ScriptContainer>(), container: AtomicPtr::<ScriptContainer>::default(),
suppressed_count: AtomicUsize::new(0), suppressed_count: AtomicUsize::new(0),
marked_for_deletion: Default::default(), marked_for_deletion: Default::default(),
} }
} }
fn test(&self, script: Box<dyn Script>) { fn test(&self, script: Arc<dyn Script>) {
unsafe { unsafe {
self.container.as_mut().unwrap().set(script); self.container.load(Ordering::Relaxed).as_ref().unwrap().set(script);
} }
} }
} }
@ -339,14 +342,16 @@ mod tests {
#[test] #[test]
fn replace_self_while_active() { fn replace_self_while_active() {
let script = Box::new(ReplaceTestScript::new("script1".into())); let script = Arc::new(ReplaceTestScript::new("script1".into()));
let container = ScriptContainer::new(script); let mut container = ScriptContainer::new(script);
let mut w = container.script.write(); let c = &mut container as *mut ScriptContainer;
let script: &mut ReplaceTestScript = w.as_mut().unwrap().as_any_mut().downcast_mut().unwrap();
script.container = &container as *const ScriptContainer as *mut ScriptContainer; let w = container.script.read();
let script: &ReplaceTestScript = w.as_ref().unwrap().as_any().downcast_ref().unwrap();
script.container.store(c, Ordering::SeqCst);
drop(w); drop(w);
let script2 = Box::new(ReplaceTestScript::new("script2".into())); let script2 = Arc::new(ReplaceTestScript::new("script2".into()));
container container
.script .script
.read() .read()
@ -356,6 +361,7 @@ mod tests {
.downcast_ref::<ReplaceTestScript>() .downcast_ref::<ReplaceTestScript>()
.unwrap() .unwrap()
.test(script2); .test(script2);
sleep(Duration::from_micros(500));
assert_eq!( assert_eq!(
container.script.read().as_ref().unwrap().name(), container.script.read().as_ref().unwrap().name(),
&StringKey::new("script2") &StringKey::new("script2")

View File

@ -1,6 +1,7 @@
use crate::dynamic_data::script_handling::script::{Script, ScriptContainer}; use crate::dynamic_data::script_handling::script::{Script, ScriptContainer};
use crate::{PkmnResult, StringKey}; use crate::{PkmnResult, StringKey};
use indexmap::IndexMap; use indexmap::IndexMap;
use std::sync::Arc;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ScriptSet { pub struct ScriptSet {
@ -8,7 +9,7 @@ pub struct ScriptSet {
} }
impl ScriptSet { impl ScriptSet {
pub fn add(&mut self, script: Box<dyn Script>) -> ScriptContainer { pub fn add(&mut self, script: Arc<dyn Script>) -> ScriptContainer {
if let Some(lock) = self.scripts.get(script.name()) { if let Some(lock) = self.scripts.get(script.name()) {
if let Some(existing) = lock.get() { if let Some(existing) = lock.get() {
let existing = existing.read(); let existing = existing.read();
@ -24,7 +25,7 @@ impl ScriptSet {
pub fn stack_or_add<'b, F>(&mut self, key: &StringKey, instantiation: &'b F) -> PkmnResult<Option<ScriptContainer>> pub fn stack_or_add<'b, F>(&mut self, key: &StringKey, instantiation: &'b F) -> PkmnResult<Option<ScriptContainer>>
where where
F: Fn() -> PkmnResult<Option<Box<dyn Script>>>, F: Fn() -> PkmnResult<Option<Arc<dyn Script>>>,
{ {
if let Some(lock) = self.scripts.get(key) { if let Some(lock) = self.scripts.get(key) {
if let Some(existing) = lock.get() { if let Some(existing) = lock.get() {

View File

@ -6,7 +6,7 @@ use std::sync::Arc;
pub trait VolatileScripts<'a> { pub trait VolatileScripts<'a> {
fn volatile_scripts(&self) -> &Arc<RwLock<ScriptSet>>; fn volatile_scripts(&self) -> &Arc<RwLock<ScriptSet>>;
fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Box<dyn Script>>>; fn load_volatile_script(&self, key: &StringKey) -> PkmnResult<Option<Arc<dyn Script>>>;
fn has_volatile_script(&self, key: &StringKey) -> bool { fn has_volatile_script(&self, key: &StringKey) -> bool {
self.volatile_scripts().read().has(key) self.volatile_scripts().read().has(key)