Skip to content

Commit

Permalink
dcparser: Wrap DC File in Arc and Mutex for safe shared mutability
Browse files Browse the repository at this point in the history
Okay. Things are getting messy. But, at least it's safe, right? I would
get rid of the 'interior' Arc<Mutex<>>'s inside DC element structs, but
even those require to be wrapped in a Mutex in order to be wrapped in
Arc's, and Arc pointers seem to be the only viable solution to keeping
multiple shared references to different DC elements within other DC
elements, which is vital for elements like molecular fields, etc.
  • Loading branch information
maxrdz committed Feb 26, 2024
1 parent db5676b commit 122537e
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 23 deletions.
6 changes: 3 additions & 3 deletions donet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ fn main() -> std::io::Result<()> {
info!("libdonet: DC read of {:?}", dc_check_files);
let dc_read: DCReadResult = read_dc_files(dc_check_files.to_owned());

if let Ok(mut dc_file) = dc_read {
let h: u32 = dc_file.get_hash();
if let Ok(dc_file) = dc_read {
let h: u32 = dc_file.lock().unwrap().get_hash();
let sh: i32 = h as i32;
let ph: String = dc_file.get_pretty_hash();
let ph: String = dc_file.lock().unwrap().get_pretty_hash();
info!("No issues found. File hash is {} (signed {}, hex {})", h, sh, ph);
return Ok(());
}
Expand Down
9 changes: 9 additions & 0 deletions libdonet/src/dcfield.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pub trait DCFieldInterface {
fn generate_hash(&self, hashgen: &mut DCHashGenerator);

fn get_field_id(&self) -> globals::FieldId;
fn get_field_name(&self) -> String;
fn get_dclass(&self) -> Arc<Mutex<DClass>>;

fn set_field_id(&mut self, id: globals::FieldId);
Expand Down Expand Up @@ -165,16 +166,23 @@ impl DCFieldInterface for DCField {
self.field_id
}

#[inline(always)]
fn get_field_name(&self) -> String {
self.field_name.clone()
}

fn get_dclass(&self) -> Arc<Mutex<DClass>> {
assert!(self.parent_is_dclass);
// clone option to unwrap w/o move, and clone Arc to return
self.dclass.clone().unwrap().clone()
}

#[inline(always)]
fn set_field_id(&mut self, id: globals::FieldId) {
self.field_id = id
}

#[inline(always)]
fn set_field_name(&mut self, name: String) {
self.field_name = name
}
Expand All @@ -191,6 +199,7 @@ impl DCFieldInterface for DCField {
self.default_value_stale = false;
}

#[inline(always)]
fn set_bogus_field(&mut self, is_bogus: bool) {
self.bogus_field = is_bogus
}
Expand Down
18 changes: 18 additions & 0 deletions libdonet/src/dcfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub struct DCFile {
pub trait DCFileInterface {
fn get_hash(&mut self) -> globals::DCFileHash;
fn generate_hash(&mut self, hashgen: &mut DCHashGenerator);
fn semantic_analysis(&self) -> Result<(), ()>;
fn get_pretty_hash(&mut self) -> String;
fn add_field(&mut self, field: DCField); // assigns unique ID for the whole DC file

Expand Down Expand Up @@ -133,6 +134,23 @@ impl DCFileInterface for DCFile {
}
}

/// Performs a semantic analysis on the object and its children
/// DC elements. In Panda, this is done on the go as you build the
/// DC file tree. Due to how we build it in memory, (and the fact
/// that we link all the objects together until we reduce to the
/// root production in the CFG) we have to perform this analysis
/// until the very end when all the elements are in the DCF struct.
fn semantic_analysis(&self) -> Result<(), ()> {
// Run semantic analysis chain of all distributed class objects.
// This should include semantic analysis for DC fields as well.
for dclass in &self.dclasses {
let locked_dclass: MutexGuard<'_, DClass> = dclass.lock().unwrap();
locked_dclass.semantic_analysis()?;
}
// TODO!
Ok(())
}

/// Returns a string with the hash as a pretty format hexadecimal.
fn get_pretty_hash(&mut self) -> String {
format!("0x{:0width$x}", self.get_hash(), width = 8) // 2 hex / byte = 8 hex
Expand Down
56 changes: 53 additions & 3 deletions libdonet/src/dclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
use crate::dcatomic::{DCAtomicField, DCAtomicFieldInterface};
use crate::dcfield::{ClassField, DCFieldInterface};
use crate::dcfile::DCFile;
use crate::dcmolecular::DCMolecularFieldInterface;
use crate::globals;
use crate::hashgen::DCHashGenerator;
use multimap::MultiMap;
Expand All @@ -29,8 +31,14 @@ use std::sync::{Arc, Mutex, MutexGuard};
pub type FieldName2Field = MultiMap<String, Arc<Mutex<ClassField>>>;
pub type FieldId2Field = MultiMap<globals::FieldId, Arc<Mutex<ClassField>>>;

/// Represents a Distributed Class defined in the DC file.
/// Contains a map of DC Fields, as well as atomic and
/// molecular fields that are declared within the class.
/// Also stores other properties such as its hierarchy.
#[derive(Debug)]
pub struct DClass {
dcfile: Arc<Mutex<DCFile>>, // read comment below. should reference REAL dcf by parse end.
dcf_assigned: bool, // due to how the parser works, we assign it 'til the end.
class_name: String,
class_id: globals::DClassId,
is_struct: bool,
Expand All @@ -46,8 +54,11 @@ pub struct DClass {
pub trait DClassInterface {
fn new(name: &str) -> Self;
fn generate_hash(&mut self, hashgen: &mut DCHashGenerator);
fn semantic_analysis(&self) -> Result<(), ()>;

fn set_parent(&mut self, parent: Arc<Mutex<DClass>>);
fn set_dcfile(&mut self, dcf: Arc<Mutex<DCFile>>);
fn add_parent(&mut self, parent: Arc<Mutex<DClass>>);
fn add_class_field(&mut self, field: ClassField);

fn get_name(&mut self) -> String;
fn get_dclass_id(&mut self) -> globals::DClassId;
Expand All @@ -61,6 +72,8 @@ pub trait DClassInterface {
impl DClassInterface for DClass {
fn new(name: &str) -> Self {
DClass {
dcfile: Arc::new(Mutex::new(DCFile::new())),
dcf_assigned: false,
class_name: name.to_owned(),
class_id: 0, // assigned later
is_struct: false,
Expand Down Expand Up @@ -103,40 +116,77 @@ impl DClassInterface for DClass {
match &field.deref() {
ClassField::Field(field) => field.generate_hash(hashgen),
ClassField::Atomic(atomic) => atomic.generate_hash(hashgen),
ClassField::Molecular(_) => todo!(),
ClassField::Molecular(molecular) => molecular.generate_hash(hashgen),
}
}
}

fn set_parent(&mut self, parent: Arc<Mutex<DClass>>) {
/// Performs a semantic analysis on the object and its children.
fn semantic_analysis(&self) -> Result<(), ()> {
assert!(
self.dcf_assigned,
"No DC file pointer found in '{}' dclass!",
self.class_name,
);
// TODO!
Ok(())
}

fn set_dcfile(&mut self, dcf: Arc<Mutex<DCFile>>) {
assert!(
!self.dcf_assigned,
"Tried to reassign DC file pointer to '{}' class",
self.class_name
);
self.dcfile = dcf;
self.dcf_assigned = true;
}

#[inline(always)]
fn add_parent(&mut self, parent: Arc<Mutex<DClass>>) {
self.class_parents.push(parent);
}

/// Adds a newly allocated DC field to this class. The field structure
/// in memory is moved into ownership of this class structure, and is
/// wrapped in a Mutex and an Arc pointer to pass references to other
/// elements, such as molecular fields.
fn add_class_field(&mut self, field: ClassField) {
self.fields.push(Arc::new(Mutex::new(field)));
}

#[inline(always)]
fn get_name(&mut self) -> String {
self.class_name.clone()
}

#[inline(always)]
fn get_dclass_id(&mut self) -> globals::DClassId {
self.class_id
}

#[inline(always)]
fn set_dclass_id(&mut self, id: globals::DClassId) {
self.class_id = id;
}

#[inline(always)]
fn get_num_parents(&mut self) -> usize {
self.class_parents.len()
}

#[inline(always)]
fn get_parent(&mut self, index: usize) -> Option<Arc<Mutex<DClass>>> {
// copy the reference inside the option instead of a reference to the reference
self.class_parents.get(index).cloned()
}

#[inline(always)]
fn has_constructor(&mut self) -> bool {
self.constructor.is_some()
}

#[inline(always)]
fn get_constructor(&mut self) -> Option<Arc<Mutex<DCAtomicField>>> {
self.constructor.clone()
}
Expand Down
33 changes: 21 additions & 12 deletions libdonet/src/dcparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::dclexer::DCToken::*;
use crate::dclexer::{DCToken, Span};
use crate::dcstruct;
use plex::parser;
use std::sync::{Arc, Mutex};

/* To write the DC file elements to memory just as Panda and Astron do, I
* initially stored the DCFile struct on static memory as mutable. This required
Expand Down Expand Up @@ -80,33 +81,40 @@ parser! {
}

// root production of the grammar
dc_file: DCFile {
dc_file: Arc<Mutex<DCFile>> {
type_declarations[tds] => {
let mut dc_file: DCFile = DCFile::new();

// Allocates a DC File struct on the heap; Wrapped in a Mutex for mutability.
let dc_file: Arc<Mutex<DCFile>> = Arc::new(Mutex::new(DCFile::new()));

for type_declaration in tds {
match type_declaration {
TypeDeclaration::PythonImport(imports) => {
for import in imports {
dc_file.add_python_import(import);
dc_file.lock().unwrap().add_python_import(import);
}
},
TypeDeclaration::KeywordType(keyword) => {
dc_file.add_keyword(keyword);
dc_file.lock().unwrap().add_keyword(keyword);
},
TypeDeclaration::StructType(_) => {},
TypeDeclaration::SwitchType(_) => {},
TypeDeclaration::DClassType(mut dclass) => {
use dclass::DClassInterface;

let next_class_id: usize = dc_file.get_num_dclasses();
dclass.set_dcfile(dc_file.clone());

let next_class_id: usize = dc_file.lock().unwrap().get_num_dclasses();
dclass.set_dclass_id(next_class_id.try_into().unwrap());

dc_file.add_dclass(dclass);
dc_file.lock().unwrap().add_dclass(dclass);
},
TypeDeclaration::TypedefType(_) => {},
}
}
// TODO: maybe properly handle semantic errors in the future
assert!(dc_file.lock().unwrap().semantic_analysis().is_ok());

dc_file
},
}
Expand Down Expand Up @@ -683,19 +691,20 @@ parser! {

pub fn parse<I: Iterator<Item = (DCToken, Span)>>(
i: I,
) -> Result<DCFile, (Option<(DCToken, Span)>, &'static str)> {
) -> Result<Arc<Mutex<DCFile>>, (Option<(DCToken, Span)>, &'static str)> {
parse_(i)
}

#[cfg(test)]
mod unit_testing {
use super::parse;
use super::{Arc, Mutex};
use crate::dcfile::*;
use crate::dclexer::Lexer;

fn parse_dcfile_string(input: &str) -> DCFile {
fn parse_dcfile_string(input: &str) -> Arc<Mutex<DCFile>> {
let lexer = Lexer::new(input).inspect(|tok| eprintln!("token: {:?}", tok));
let dc_file: DCFile = parse(lexer).unwrap();
let dc_file: Arc<Mutex<DCFile>> = parse(lexer).unwrap();
eprintln!("{:#?}", dc_file); // pretty print DC element tree to stderr
dc_file
}
Expand All @@ -711,14 +720,14 @@ mod unit_testing {
* that may be lexed as tokens other than Id/Module.
*/
from db.char import DistributedDonut\n";
let mut dc_file = parse_dcfile_string(dc_file);
let dc_file = parse_dcfile_string(dc_file);

let expected_num_imports: usize = 10;
let mut imports: Vec<DCImport> = vec![];
assert_eq!(dc_file.get_num_imports(), expected_num_imports);
assert_eq!(dc_file.lock().unwrap().get_num_imports(), expected_num_imports);

for i in 0..expected_num_imports {
imports.push(dc_file.get_python_import(i));
imports.push(dc_file.lock().unwrap().get_python_import(i));
}

assert_eq!(imports[0].python_module, "example_views");
Expand Down
3 changes: 2 additions & 1 deletion libdonet/src/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub const BCHAN_DBSERVERS: Channel = 13;

cfg_if! {
if #[cfg(feature = "dcfile")] {
use std::sync::{Arc, Mutex};

// DC File Constants
pub static HISTORICAL_DC_KEYWORDS: &[&str] = &[
Expand All @@ -79,7 +80,7 @@ cfg_if! {
ParseError(ParseError),
FileError(std::io::Error),
}
pub type DCReadResult = Result<dcfile::DCFile, DCReadError>;
pub type DCReadResult = Result<Arc<Mutex<dcfile::DCFile>>, DCReadError>;
}
}

Expand Down
9 changes: 5 additions & 4 deletions libdonet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ cfg_if! {
///
/// let dc_read: DCReadResult = read_dc_files(vec![dc_file.into()]);
///
/// if let Ok(mut dc_file) = dc_read {
/// println!("{}", dc_file.get_pretty_hash()); // Print the DC File Hash
/// if let Ok(dc_file) = dc_read {
/// println!("{}", dc_file.lock().unwrap().get_pretty_hash()); // Print the DC File Hash
///
/// let avatar_class: Arc<Mutex<DClass>> = dc_file.get_dclass_by_id(3);
/// let avatar_class: Arc<Mutex<DClass>> = dc_file.lock().unwrap().get_dclass_by_id(3);
/// let mut locked_class: MutexGuard<'_, DClass> = avatar_class.lock().unwrap();
///
/// println!("{}", locked_class.get_name());
Expand All @@ -139,6 +139,7 @@ pub fn read_dc_files(file_paths: Vec<String>) -> globals::DCReadResult {
use crate::dcparser::parse;
use std::fs::File;
use std::io::Read;
use std::sync::{Arc, Mutex};

let mut file_results: Vec<Result<File, std::io::Error>> = vec![];
let mut lexer_input: String = String::new();
Expand All @@ -161,7 +162,7 @@ pub fn read_dc_files(file_paths: Vec<String>) -> globals::DCReadResult {
}

let lexer: Lexer<'_> = Lexer::new(&lexer_input);
let res: Result<dcfile::DCFile, globals::ParseError> = parse(lexer);
let res: Result<Arc<Mutex<dcfile::DCFile>>, globals::ParseError> = parse(lexer);

if let Ok(res_ok) = res {
Ok(res_ok)
Expand Down

0 comments on commit 122537e

Please sign in to comment.