<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/// Implementation of Quartz (CoreGraphics) fonts.

extern crate core_foundation;
extern crate core_graphics;
extern crate core_text;

use app_units::Au;
use byteorder::{BigEndian, ByteOrder};
use core_foundation::base::CFIndex;
use core_foundation::data::CFData;
use core_foundation::string::UniChar;
use core_graphics::font::CGGlyph;
use core_graphics::geometry::CGRect;
use core_text::font::CTFont;
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
use core_text::font_descriptor::kCTFontDefaultOrientation;
use font::{FontHandleMethods, FontMetrics, FontTableMethods, FontTableTag, FractionalPixel};
use font::{GPOS, GSUB, KERN};
use platform::font_template::FontTemplateData;
use platform::macos::font_context::FontContextHandle;
use std::{fmt, ptr};
use std::ops::Range;
use std::sync::Arc;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;

const KERN_PAIR_LEN: usize = 6;

pub struct FontTable {
    data: CFData,
}

// assumes 72 points per inch, and 96 px per inch
fn px_to_pt(px: f64) -> f64 {
    px / 96. * 72.
}

// assumes 72 points per inch, and 96 px per inch
fn pt_to_px(pt: f64) -> f64 {
    pt / 72. * 96.
}

fn au_from_pt(pt: f64) -> Au {
    Au::from_f64_px(pt_to_px(pt))
}

impl FontTable {
    pub fn wrap(data: CFData) -> FontTable {
        FontTable { data: data }
    }
}

impl FontTableMethods for FontTable {
    fn buffer(&self) -> &[u8] {
        self.data.bytes()
    }
}

#[derive(Debug)]
pub struct FontHandle {
    font_data: Arc<FontTemplateData>,
    ctfont: CTFont,
    h_kern_subtable: Option<CachedKernTable>,
    can_do_fast_shaping: bool,
}

impl FontHandle {
    /// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or
    /// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case.
    fn find_h_kern_subtable(&self) -> Option<CachedKernTable> {
        let font_table = match self.table_for_tag(KERN) {
            Some(table) => table,
            None => return None
        };

        let mut result = CachedKernTable {
            font_table: font_table,
            pair_data_range: 0..0,
            px_per_font_unit: 0.0,
        };

        // Look for a subtable with horizontal kerning in format 0.
        // https://www.microsoft.com/typography/otspec/kern.htm
        const KERN_COVERAGE_HORIZONTAL_FORMAT_0: u16 = 1;
        const SUBTABLE_HEADER_LEN: usize = 6;
        const FORMAT_0_HEADER_LEN: usize = 8;
        {
            let table = result.font_table.buffer();
            let version = BigEndian::read_u16(table);
            if version != 0 {
                return None;
            }
            let num_subtables = BigEndian::read_u16(&table[2..]);
            let mut start = 4;
            for _ in 0..num_subtables {
                // TODO: Check the subtable version number?
                let len = BigEndian::read_u16(&table[start + 2..]) as usize;
                let cov = BigEndian::read_u16(&table[start + 4..]);
                let end = start + len;
                if cov == KERN_COVERAGE_HORIZONTAL_FORMAT_0 {
                    // Found a matching subtable.
                    if result.pair_data_range.len() > 0 {
                        debug!("Found multiple horizontal kern tables. Disable fast path.");
                        return None;
                    }
                    // Read the subtable header.
                    let subtable_start = start + SUBTABLE_HEADER_LEN;
                    let n_pairs = BigEndian::read_u16(&table[subtable_start..]) as usize;
                    let pair_data_start = subtable_start + FORMAT_0_HEADER_LEN;

                    result.pair_data_range = pair_data_start..end;
                    if result.pair_data_range.len() != n_pairs * KERN_PAIR_LEN {
                        debug!("Bad data in kern header. Disable fast path.");
                        return None;
                    }

                    let pt_per_font_unit = self.ctfont.pt_size() as f64 /
                                           self.ctfont.units_per_em() as f64;
                    result.px_per_font_unit = pt_to_px(pt_per_font_unit);
                }
                start = end;
            }
        }
        if result.pair_data_range.len() > 0 {
            Some(result)
        } else {
            None
        }
    }
}

struct CachedKernTable {
    font_table: FontTable,
    pair_data_range: Range<usize>,
    px_per_font_unit: f64,
}

impl CachedKernTable {
    /// Search for a glyph pair in the kern table and return the corresponding value.
    fn binary_search(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> Option<i16> {
        let pairs = &self.font_table.buffer()[self.pair_data_range.clone()];

        let query = first_glyph << 16 | second_glyph;
        let (mut start, mut end) = (0, pairs.len() / KERN_PAIR_LEN);
        while start < end {
            let i = (start + end) / 2;
            let key = BigEndian::read_u32(&pairs[i * KERN_PAIR_LEN..]);
            if key > query {
                end = i;
            } else if key < query {
                start = i + 1;
            } else {
                return Some(BigEndian::read_i16(&pairs[i * KERN_PAIR_LEN + 4..]));
            }
        }
        None
    }
}

impl fmt::Debug for CachedKernTable {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "CachedKernTable")
    }
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(_fctx: &FontContextHandle,
                         template: Arc<FontTemplateData>,
                         pt_size: Option<Au>)
                         -> Result<FontHandle, ()> {
        let size = match pt_size {
            Some(s) => s.to_f64_px(),
            None => 0.0
        };
        match template.ctfont(size) {
            Some(ref ctfont) => {
                let mut handle = FontHandle {
                    font_data: template.clone(),
                    ctfont: ctfont.clone_with_font_size(size),
                    h_kern_subtable: None,
                    can_do_fast_shaping: false,
                };
                handle.h_kern_subtable = handle.find_h_kern_subtable();
                // TODO (#11310): Implement basic support for GPOS and GSUB.
                handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() &&
                                             handle.table_for_tag(GPOS).is_none() &&
                                             handle.table_for_tag(GSUB).is_none();
                Ok(handle)
            }
            None => {
                Err(())
            }
        }
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> String {
        self.ctfont.family_name()
    }

    fn face_name(&self) -> String {
        self.ctfont.face_name()
    }

    fn is_italic(&self) -> bool {
        self.ctfont.symbolic_traits().is_italic()
    }

    fn boldness(&self) -> font_weight::T {
        let normalized = self.ctfont.all_traits().normalized_weight();  // [-1.0, 1.0]
        let normalized = if normalized <= 0.0 {
            4.0 + normalized * 3.0  // [1.0, 4.0]
        } else {
            4.0 + normalized * 5.0  // [4.0, 9.0]
        }; // [1.0, 9.0], centered on 4.0
        match normalized.round() as u32 {
            1 => font_weight::T::Weight100,
            2 => font_weight::T::Weight200,
            3 => font_weight::T::Weight300,
            4 => font_weight::T::Weight400,
            5 => font_weight::T::Weight500,
            6 => font_weight::T::Weight600,
            7 => font_weight::T::Weight700,
            8 => font_weight::T::Weight800,
            _ => font_weight::T::Weight900,
        }
    }

    fn stretchiness(&self) -> font_stretch::T {
        let normalized = self.ctfont.all_traits().normalized_width();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_stretch::T::ultra_condensed,
            v if v < 2.0 => font_stretch::T::extra_condensed,
            v if v < 3.0 => font_stretch::T::condensed,
            v if v < 4.0 => font_stretch::T::semi_condensed,
            v if v < 5.0 => font_stretch::T::normal,
            v if v < 6.0 => font_stretch::T::semi_expanded,
            v if v < 7.0 => font_stretch::T::expanded,
            v if v < 8.0 => font_stretch::T::extra_expanded,
            _ => font_stretch::T::ultra_expanded,
        }
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let characters: [UniChar; 1] = [codepoint as UniChar];
        let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
        let count: CFIndex = 1;

        let result = self.ctfont.get_glyphs_for_characters(&characters[0],
                                                           &mut glyphs[0],
                                                           count);

        if !result {
            // No glyph for this character
            return None;
        }

        assert!(glyphs[0] != 0); // FIXME: error handling
        return Some(glyphs[0] as GlyphId);
    }

    fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
        if let Some(ref table) = self.h_kern_subtable {
            if let Some(font_units) = table.binary_search(first_glyph, second_glyph) {
                return font_units as f64 * table.px_per_font_unit;
            }
        }
        0.0
    }

    fn can_do_fast_shaping(&self) -> bool {
        self.can_do_fast_shaping
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        let glyphs = [glyph as CGGlyph];
        let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
                                                          &glyphs[0],
                                                          ptr::null_mut(),
                                                          1);
        Some(advance as FractionalPixel)
    }

    fn metrics(&self) -> FontMetrics {
        let bounding_rect: CGRect = self.ctfont.bounding_box();
        let ascent = self.ctfont.ascent() as f64;
        let descent = self.ctfont.descent() as f64;
        let em_size = Au::from_f64_px(self.ctfont.pt_size() as f64);
        let leading = self.ctfont.leading() as f64;

        let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent);
        let line_gap = (ascent + descent + leading + 0.5).floor();

        let max_advance_width = au_from_pt(bounding_rect.size.width as f64);
        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map(Au::from_f64_px)
                                  .unwrap_or(max_advance_width);

        let metrics =  FontMetrics {
            underline_size:   au_from_pt(self.ctfont.underline_thickness() as f64),
            // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
            // directly.
            //
            // see also: https://bugs.webkit.org/show_bug.cgi?id=16768
            // see also: https://bugreports.qt-project.org/browse/QTBUG-13364
            underline_offset: au_from_pt(self.ctfont.underline_position() as f64),
            strikeout_size:   Au(0), // FIXME(Issue #942)
            strikeout_offset: Au(0), // FIXME(Issue #942)
            leading:          au_from_pt(leading),
            x_height:         au_from_pt(self.ctfont.x_height() as f64),
            em_size:          em_size,
            ascent:           au_from_pt(ascent * scale),
            descent:          au_from_pt(descent * scale),
            max_advance:      max_advance_width,
            average_advance:  average_advance,
            line_gap:         Au::from_f64_px(line_gap),
        };
        debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        let result: Option<CFData> = self.ctfont.get_font_table(tag);
        result.and_then(|data| {
            Some(FontTable::wrap(data))
        })
    }
}
<commit_msg>gfx: On the Mac, take the scale into account when determining the x-height of a font.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/// Implementation of Quartz (CoreGraphics) fonts.

extern crate core_foundation;
extern crate core_graphics;
extern crate core_text;

use app_units::Au;
use byteorder::{BigEndian, ByteOrder};
use core_foundation::base::CFIndex;
use core_foundation::data::CFData;
use core_foundation::string::UniChar;
use core_graphics::font::CGGlyph;
use core_graphics::geometry::CGRect;
use core_text::font::CTFont;
use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors};
use core_text::font_descriptor::kCTFontDefaultOrientation;
use font::{FontHandleMethods, FontMetrics, FontTableMethods, FontTableTag, FractionalPixel};
use font::{GPOS, GSUB, KERN};
use platform::font_template::FontTemplateData;
use platform::macos::font_context::FontContextHandle;
use std::{fmt, ptr};
use std::ops::Range;
use std::sync::Arc;
use style::computed_values::{font_stretch, font_weight};
use text::glyph::GlyphId;

const KERN_PAIR_LEN: usize = 6;

pub struct FontTable {
    data: CFData,
}

// assumes 72 points per inch, and 96 px per inch
fn px_to_pt(px: f64) -> f64 {
    px / 96. * 72.
}

// assumes 72 points per inch, and 96 px per inch
fn pt_to_px(pt: f64) -> f64 {
    pt / 72. * 96.
}

fn au_from_pt(pt: f64) -> Au {
    Au::from_f64_px(pt_to_px(pt))
}

impl FontTable {
    pub fn wrap(data: CFData) -> FontTable {
        FontTable { data: data }
    }
}

impl FontTableMethods for FontTable {
    fn buffer(&self) -> &[u8] {
        self.data.bytes()
    }
}

#[derive(Debug)]
pub struct FontHandle {
    font_data: Arc<FontTemplateData>,
    ctfont: CTFont,
    h_kern_subtable: Option<CachedKernTable>,
    can_do_fast_shaping: bool,
}

impl FontHandle {
    /// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or
    /// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case.
    fn find_h_kern_subtable(&self) -> Option<CachedKernTable> {
        let font_table = match self.table_for_tag(KERN) {
            Some(table) => table,
            None => return None
        };

        let mut result = CachedKernTable {
            font_table: font_table,
            pair_data_range: 0..0,
            px_per_font_unit: 0.0,
        };

        // Look for a subtable with horizontal kerning in format 0.
        // https://www.microsoft.com/typography/otspec/kern.htm
        const KERN_COVERAGE_HORIZONTAL_FORMAT_0: u16 = 1;
        const SUBTABLE_HEADER_LEN: usize = 6;
        const FORMAT_0_HEADER_LEN: usize = 8;
        {
            let table = result.font_table.buffer();
            let version = BigEndian::read_u16(table);
            if version != 0 {
                return None;
            }
            let num_subtables = BigEndian::read_u16(&table[2..]);
            let mut start = 4;
            for _ in 0..num_subtables {
                // TODO: Check the subtable version number?
                let len = BigEndian::read_u16(&table[start + 2..]) as usize;
                let cov = BigEndian::read_u16(&table[start + 4..]);
                let end = start + len;
                if cov == KERN_COVERAGE_HORIZONTAL_FORMAT_0 {
                    // Found a matching subtable.
                    if result.pair_data_range.len() > 0 {
                        debug!("Found multiple horizontal kern tables. Disable fast path.");
                        return None;
                    }
                    // Read the subtable header.
                    let subtable_start = start + SUBTABLE_HEADER_LEN;
                    let n_pairs = BigEndian::read_u16(&table[subtable_start..]) as usize;
                    let pair_data_start = subtable_start + FORMAT_0_HEADER_LEN;

                    result.pair_data_range = pair_data_start..end;
                    if result.pair_data_range.len() != n_pairs * KERN_PAIR_LEN {
                        debug!("Bad data in kern header. Disable fast path.");
                        return None;
                    }

                    let pt_per_font_unit = self.ctfont.pt_size() as f64 /
                                           self.ctfont.units_per_em() as f64;
                    result.px_per_font_unit = pt_to_px(pt_per_font_unit);
                }
                start = end;
            }
        }
        if result.pair_data_range.len() > 0 {
            Some(result)
        } else {
            None
        }
    }
}

struct CachedKernTable {
    font_table: FontTable,
    pair_data_range: Range<usize>,
    px_per_font_unit: f64,
}

impl CachedKernTable {
    /// Search for a glyph pair in the kern table and return the corresponding value.
    fn binary_search(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> Option<i16> {
        let pairs = &self.font_table.buffer()[self.pair_data_range.clone()];

        let query = first_glyph << 16 | second_glyph;
        let (mut start, mut end) = (0, pairs.len() / KERN_PAIR_LEN);
        while start < end {
            let i = (start + end) / 2;
            let key = BigEndian::read_u32(&pairs[i * KERN_PAIR_LEN..]);
            if key > query {
                end = i;
            } else if key < query {
                start = i + 1;
            } else {
                return Some(BigEndian::read_i16(&pairs[i * KERN_PAIR_LEN + 4..]));
            }
        }
        None
    }
}

impl fmt::Debug for CachedKernTable {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "CachedKernTable")
    }
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(_fctx: &FontContextHandle,
                         template: Arc<FontTemplateData>,
                         pt_size: Option<Au>)
                         -> Result<FontHandle, ()> {
        let size = match pt_size {
            Some(s) => s.to_f64_px(),
            None => 0.0
        };
        match template.ctfont(size) {
            Some(ref ctfont) => {
                let mut handle = FontHandle {
                    font_data: template.clone(),
                    ctfont: ctfont.clone_with_font_size(size),
                    h_kern_subtable: None,
                    can_do_fast_shaping: false,
                };
                handle.h_kern_subtable = handle.find_h_kern_subtable();
                // TODO (#11310): Implement basic support for GPOS and GSUB.
                handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() &&
                                             handle.table_for_tag(GPOS).is_none() &&
                                             handle.table_for_tag(GSUB).is_none();
                Ok(handle)
            }
            None => {
                Err(())
            }
        }
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> String {
        self.ctfont.family_name()
    }

    fn face_name(&self) -> String {
        self.ctfont.face_name()
    }

    fn is_italic(&self) -> bool {
        self.ctfont.symbolic_traits().is_italic()
    }

    fn boldness(&self) -> font_weight::T {
        let normalized = self.ctfont.all_traits().normalized_weight();  // [-1.0, 1.0]
        let normalized = if normalized <= 0.0 {
            4.0 + normalized * 3.0  // [1.0, 4.0]
        } else {
            4.0 + normalized * 5.0  // [4.0, 9.0]
        }; // [1.0, 9.0], centered on 4.0
        match normalized.round() as u32 {
            1 => font_weight::T::Weight100,
            2 => font_weight::T::Weight200,
            3 => font_weight::T::Weight300,
            4 => font_weight::T::Weight400,
            5 => font_weight::T::Weight500,
            6 => font_weight::T::Weight600,
            7 => font_weight::T::Weight700,
            8 => font_weight::T::Weight800,
            _ => font_weight::T::Weight900,
        }
    }

    fn stretchiness(&self) -> font_stretch::T {
        let normalized = self.ctfont.all_traits().normalized_width();  // [-1.0, 1.0]
        let normalized = (normalized + 1.0) / 2.0 * 9.0;  // [0.0, 9.0]
        match normalized {
            v if v < 1.0 => font_stretch::T::ultra_condensed,
            v if v < 2.0 => font_stretch::T::extra_condensed,
            v if v < 3.0 => font_stretch::T::condensed,
            v if v < 4.0 => font_stretch::T::semi_condensed,
            v if v < 5.0 => font_stretch::T::normal,
            v if v < 6.0 => font_stretch::T::semi_expanded,
            v if v < 7.0 => font_stretch::T::expanded,
            v if v < 8.0 => font_stretch::T::extra_expanded,
            _ => font_stretch::T::ultra_expanded,
        }
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let characters: [UniChar; 1] = [codepoint as UniChar];
        let mut glyphs: [CGGlyph; 1] = [0 as CGGlyph];
        let count: CFIndex = 1;

        let result = self.ctfont.get_glyphs_for_characters(&characters[0],
                                                           &mut glyphs[0],
                                                           count);

        if !result {
            // No glyph for this character
            return None;
        }

        assert!(glyphs[0] != 0); // FIXME: error handling
        return Some(glyphs[0] as GlyphId);
    }

    fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
        if let Some(ref table) = self.h_kern_subtable {
            if let Some(font_units) = table.binary_search(first_glyph, second_glyph) {
                return font_units as f64 * table.px_per_font_unit;
            }
        }
        0.0
    }

    fn can_do_fast_shaping(&self) -> bool {
        self.can_do_fast_shaping
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        let glyphs = [glyph as CGGlyph];
        let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation,
                                                          &glyphs[0],
                                                          ptr::null_mut(),
                                                          1);
        Some(advance as FractionalPixel)
    }

    fn metrics(&self) -> FontMetrics {
        let bounding_rect: CGRect = self.ctfont.bounding_box();
        let ascent = self.ctfont.ascent() as f64;
        let descent = self.ctfont.descent() as f64;
        let em_size = Au::from_f64_px(self.ctfont.pt_size() as f64);
        let leading = self.ctfont.leading() as f64;

        let scale = px_to_pt(self.ctfont.pt_size() as f64) / (ascent + descent);
        let line_gap = (ascent + descent + leading + 0.5).floor();

        let max_advance_width = au_from_pt(bounding_rect.size.width as f64);
        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map(Au::from_f64_px)
                                  .unwrap_or(max_advance_width);

        let metrics = FontMetrics {
            underline_size:   au_from_pt(self.ctfont.underline_thickness() as f64),
            // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table
            // directly.
            //
            // see also: https://bugs.webkit.org/show_bug.cgi?id=16768
            // see also: https://bugreports.qt-project.org/browse/QTBUG-13364
            underline_offset: au_from_pt(self.ctfont.underline_position() as f64),
            strikeout_size:   Au(0), // FIXME(Issue #942)
            strikeout_offset: Au(0), // FIXME(Issue #942)
            leading:          au_from_pt(leading),
            x_height:         au_from_pt((self.ctfont.x_height() as f64) * scale),
            em_size:          em_size,
            ascent:           au_from_pt(ascent * scale),
            descent:          au_from_pt(descent * scale),
            max_advance:      max_advance_width,
            average_advance:  average_advance,
            line_gap:         Au::from_f64_px(line_gap),
        };
        debug!("Font metrics (@{} pt): {:?}", self.ctfont.pt_size() as f64, metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        let result: Option<CFData> = self.ctfont.get_font_table(tag);
        result.and_then(|data| {
            Some(FontTable::wrap(data))
        })
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::DOMStringMapBinding;
use dom::bindings::codegen::Bindings::DOMStringMapBinding::DOMStringMapMethods;
use dom::bindings::error::ErrorResult;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::htmlelement::HTMLElement;
use dom::node::window_from_node;
use util::str::DOMString;

#[dom_struct]
pub struct DOMStringMap {
    reflector_: Reflector,
    element: JS<HTMLElement>,
}

impl DOMStringMap {
    fn new_inherited(element: &HTMLElement) -> DOMStringMap {
        DOMStringMap {
            reflector_: Reflector::new(),
            element: JS::from_ref(element),
        }
    }

    pub fn new(element: &HTMLElement) -> Root<DOMStringMap> {
        let window = window_from_node(element);
        reflect_dom_object(box DOMStringMap::new_inherited(element),
                           GlobalRef::Window(window.r()),
                           DOMStringMapBinding::Wrap)
    }
}

// https://html.spec.whatwg.org/multipage/#domstringmap
impl DOMStringMapMethods for DOMStringMap {
    // https://html.spec.whatwg.org/multipage/#dom-domstringmap-removeitem
    fn NamedDeleter(&self, name: DOMString) {
        self.element.delete_custom_attr(name)
    }

    // https://html.spec.whatwg.org/multipage/#dom-domstringmap-setitem
    fn NamedSetter(&self, name: DOMString, value: DOMString) -> ErrorResult {
        self.element.set_custom_attr(name, value)
    }

    // https://html.spec.whatwg.org/multipage/#dom-domstringmap-nameditem
    fn NamedGetter(&self, name: DOMString, found: &mut bool) -> DOMString {
        match self.element.get_custom_attr(name) {
            Some(value) => {
                *found = true;
                value.clone()
            },
            None => {
                *found = false;
                DOMString::new()
            }
        }
    }

    // https://html.spec.whatwg.org/multipage/#the-domstringmap-interface:supported-property-names
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        self.element.supported_prop_names_custom_attr().iter().cloned().collect()
    }
}
<commit_msg>Reduce 'match' body, remove unnecessary clone<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::DOMStringMapBinding;
use dom::bindings::codegen::Bindings::DOMStringMapBinding::DOMStringMapMethods;
use dom::bindings::error::ErrorResult;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::htmlelement::HTMLElement;
use dom::node::window_from_node;
use util::str::DOMString;

#[dom_struct]
pub struct DOMStringMap {
    reflector_: Reflector,
    element: JS<HTMLElement>,
}

impl DOMStringMap {
    fn new_inherited(element: &HTMLElement) -> DOMStringMap {
        DOMStringMap {
            reflector_: Reflector::new(),
            element: JS::from_ref(element),
        }
    }

    pub fn new(element: &HTMLElement) -> Root<DOMStringMap> {
        let window = window_from_node(element);
        reflect_dom_object(box DOMStringMap::new_inherited(element),
                           GlobalRef::Window(window.r()),
                           DOMStringMapBinding::Wrap)
    }
}

// https://html.spec.whatwg.org/multipage/#domstringmap
impl DOMStringMapMethods for DOMStringMap {
    // https://html.spec.whatwg.org/multipage/#dom-domstringmap-removeitem
    fn NamedDeleter(&self, name: DOMString) {
        self.element.delete_custom_attr(name)
    }

    // https://html.spec.whatwg.org/multipage/#dom-domstringmap-setitem
    fn NamedSetter(&self, name: DOMString, value: DOMString) -> ErrorResult {
        self.element.set_custom_attr(name, value)
    }

    // https://html.spec.whatwg.org/multipage/#dom-domstringmap-nameditem
    fn NamedGetter(&self, name: DOMString, found: &mut bool) -> DOMString {
        let attr = self.element.get_custom_attr(name);
        *found = attr.is_some();
        attr.unwrap_or_default()
    }

    // https://html.spec.whatwg.org/multipage/#the-domstringmap-interface:supported-property-names
    fn SupportedPropertyNames(&self) -> Vec<DOMString> {
        self.element.supported_prop_names_custom_attr().iter().cloned().collect()
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use async_trait::async_trait;
use future::Either;
use futures::{future, Future, TryFutureExt};
use iml_orm::{
    sfa::{
        SfaClassError, SfaController, SfaDiskDrive, SfaEnclosure, SfaJob, SfaPowerSupply,
        SfaStorageSystem,
    },
    tokio_diesel::{AsyncError, AsyncRunQueryDsl as _},
    AsyncRunQueryDslPostgres, Changeable, DbPool, Executable, GetChanges as _, Identifiable,
    Upserts,
};
use iml_request_retry::{retry_future, RetryAction, RetryPolicy};
use iml_tracing::tracing;
use std::{convert::TryInto as _, fmt::Debug, time::Duration};
use thiserror::Error;
use tokio::time;
use url::Url;
use wbem_client::{resp::Instance, Client, ClientExt};

#[derive(Error, Debug)]
enum ImlSfaError {
    #[error(transparent)]
    WbemClient(#[from] wbem_client::WbemClientError),
    #[error(transparent)]
    SfaClass(#[from] SfaClassError),
    #[error(transparent)]
    Async(#[from] AsyncError),
    #[error(transparent)]
    ImlOrm(#[from] iml_orm::ImlOrmError),
}

fn create_retry_endpoint_policy<E: Debug>(len: u32) -> impl RetryPolicy<E> + Send {
    move |k: u32, e| {
        if k < len {
            RetryAction::RetryNow
        } else {
            RetryAction::ReturnError(e)
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), ImlSfaError> {
    iml_tracing::init();

    let pool = iml_orm::pool()?;

    let endpoints = iml_manager_env::get_sfa_endpoints();

    let endpoints = match endpoints {
        Some(x) => x,
        None => {
            tracing::info!("No endpoints found, exiting.");

            return Ok(());
        }
    };

    let client = wbem_client::get_client(true)?;

    let mut interval = time::interval(Duration::from_secs(5));

    let mut old_drives = SfaDiskDrive::all()
        .load_async::<SfaDiskDrive>(&pool)
        .await?;

    let mut old_enclosures = SfaEnclosure::all()
        .load_async::<SfaEnclosure>(&pool)
        .await?;

    let mut old_jobs = SfaJob::all().load_async::<SfaJob>(&pool).await?;

    let mut old_power_supplies = SfaPowerSupply::all()
        .load_async::<SfaPowerSupply>(&pool)
        .await?;

    let mut old_controllers = SfaController::all()
        .load_async::<SfaController>(&pool)
        .await?;

    loop {
        interval.tick().await;

        let mut fut1_policy = create_retry_endpoint_policy(endpoints[0].len() as u32);
        let fut1 = retry_future(
            |c| client.fetch_sfa_enclosures(endpoints[0][c as usize].clone()),
            &mut fut1_policy,
        );

        let mut fut2_policy = create_retry_endpoint_policy(endpoints[0].len() as u32);
        let fut2 = retry_future(
            |c| client.fetch_sfa_storage_system(endpoints[0][c as usize].clone()),
            &mut fut2_policy,
        );

        let mut fut3_policy = create_retry_endpoint_policy(endpoints[0].len() as u32);
        let fut3 = retry_future(
            |c| client.fetch_sfa_disk_drives(endpoints[0][c as usize].clone()),
            &mut fut3_policy,
        );

        let mut fut4_policy = create_retry_endpoint_policy(endpoints[0].len() as u32);
        let fut4 = retry_future(
            |c| client.fetch_sfa_jobs(endpoints[0][c as usize].clone()),
            &mut fut4_policy,
        );

        let mut fut5_policy = create_retry_endpoint_policy(endpoints[0].len() as u32);
        let fut5 = retry_future(
            |c| client.fetch_sfa_power_supply(endpoints[0][c as usize].clone()),
            &mut fut5_policy,
        );

        let (new_enclosures, x, new_drives, new_jobs, new_power_supplies) =
            future::try_join5(fut1, fut2, fut3, fut4, fut5).await?;

        let mut policy = create_retry_endpoint_policy(endpoints[0].len() as u32);
        let new_controllers = retry_future(
            |c| client.fetch_sfa_controllers(endpoints[0][c as usize].clone()),
            &mut policy,
        )
        .await?;

        tracing::trace!("SfaStorageSystem {:?}", x);
        tracing::trace!("SfaEnclosures {:?}", new_enclosures);
        tracing::trace!("SfaDiskDrives {:?}", new_drives);
        tracing::trace!("SfaJobs {:?}", new_jobs);
        tracing::trace!("SfaPowerSupply {:?}", new_power_supplies);
        tracing::trace!("SfaController {:?}", new_controllers);

        let (enclosure_upsert, enclosure_remove) = diff_items(
            &new_enclosures,
            &old_enclosures,
            &pool,
            SfaEnclosure::batch_upsert,
            SfaEnclosure::batch_delete,
        );

        let (drive_upsert, drive_remove) = diff_items(
            &new_drives,
            &old_drives,
            &pool,
            SfaDiskDrive::batch_upsert,
            SfaDiskDrive::batch_delete,
        );

        let (job_upsert, job_remove) = diff_items(
            &new_jobs,
            &old_jobs,
            &pool,
            SfaJob::batch_upsert,
            SfaJob::batch_delete,
        );

        let (power_supply_upsert, power_supply_remove) = diff_items(
            &new_power_supplies,
            &old_power_supplies,
            &pool,
            SfaPowerSupply::batch_upsert,
            SfaPowerSupply::batch_delete,
        );

        let (controller_upsert, controller_remove) = diff_items(
            &new_controllers,
            &old_controllers,
            &pool,
            SfaController::batch_upsert,
            SfaController::batch_delete,
        );

        SfaStorageSystem::upsert(x).execute_async(&pool).await?;

        enclosure_upsert.await?;

        controller_upsert.await?;

        controller_remove.await?;

        power_supply_upsert.await?;

        power_supply_remove.await?;

        drive_upsert.await?;

        job_upsert.await?;

        job_remove.await?;

        drive_remove.await?;

        enclosure_remove.await?;

        old_drives = new_drives;

        old_enclosures = new_enclosures;

        old_jobs = new_jobs;

        old_power_supplies = new_power_supplies;

        old_controllers = new_controllers;
    }
}

type UpsertFn<'a, T, R> = fn(Upserts<&'a T>) -> R;
type DeleteFn<'a, T, R> = fn(Vec<&'a T>) -> R;

fn diff_items<
    'a,
    T: Identifiable + Changeable,
    R1: AsyncRunQueryDslPostgres + Executable + 'a,
    R2: AsyncRunQueryDslPostgres + Executable + 'a,
>(
    new: &'a Vec<T>,
    old: &'a Vec<T>,
    pool: &'a DbPool,
    upsert_fn: UpsertFn<'a, T, R1>,
    delete_fn: DeleteFn<'a, T, R2>,
) -> (
    impl Future<Output = Result<(), ImlSfaError>> + 'a,
    impl Future<Output = Result<(), ImlSfaError>> + 'a,
) {
    let (upsert, remove) = new.get_changes(&old);

    let upserts = if let Some(upsert) = upsert {
        tracing::debug!("{} changed Enclosures, performing Upsert", upsert.0.len());
        Either::Left(
            upsert_fn(upsert)
                .execute_async(pool)
                .map_ok(drop)
                .err_into(),
        )
    } else {
        Either::Right(future::ok(()))
    };

    let removals = if let Some(remove) = remove {
        tracing::debug!("{} removed Items, performing Deletion", remove.0.len());

        Either::Left(
            delete_fn(remove.0)
                .execute_async(pool)
                .map_ok(drop)
                .err_into(),
        )
    } else {
        Either::Right(future::ok(()))
    };

    (upserts, removals)
}

#[async_trait(?Send)]
trait SfaClassExt: ClientExt {
    async fn fetch_sfa_storage_system(&self, url: Url) -> Result<SfaStorageSystem, ImlSfaError>;
    async fn fetch_sfa_enclosures(&self, url: Url) -> Result<Vec<SfaEnclosure>, ImlSfaError>;
    async fn fetch_sfa_disk_drives(&self, url: Url) -> Result<Vec<SfaDiskDrive>, ImlSfaError>;
    async fn fetch_sfa_jobs(&self, url: Url) -> Result<Vec<SfaJob>, ImlSfaError>;
    async fn fetch_sfa_power_supply(&self, url: Url) -> Result<Vec<SfaPowerSupply>, ImlSfaError>;
    async fn fetch_sfa_controllers(&self, url: Url) -> Result<Vec<SfaController>, ImlSfaError>;
}

#[async_trait(?Send)]
impl SfaClassExt for Client {
    async fn fetch_sfa_storage_system(&self, url: Url) -> Result<SfaStorageSystem, ImlSfaError> {
        let x = self
            .get_instance(url, "root/ddn", "DDN_SFAStorageSystem")
            .await?;

        let x: SfaStorageSystem = x.try_into()?;

        Ok(x)
    }
    async fn fetch_sfa_enclosures(&self, url: Url) -> Result<Vec<SfaEnclosure>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAEnclosure")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_disk_drives(&self, url: Url) -> Result<Vec<SfaDiskDrive>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFADiskDrive")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_jobs(&self, url: Url) -> Result<Vec<SfaJob>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAJob")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_power_supply(&self, url: Url) -> Result<Vec<SfaPowerSupply>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAPowerSupply")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_controllers(&self, url: Url) -> Result<Vec<SfaController>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAController")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
}
<commit_msg>Fix policy index in sfa (#1979)<commit_after>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use async_trait::async_trait;
use future::Either;
use futures::{future, Future, TryFutureExt};
use iml_orm::{
    sfa::{
        SfaClassError, SfaController, SfaDiskDrive, SfaEnclosure, SfaJob, SfaPowerSupply,
        SfaStorageSystem,
    },
    tokio_diesel::{AsyncError, AsyncRunQueryDsl as _},
    AsyncRunQueryDslPostgres, Changeable, DbPool, Executable, GetChanges as _, Identifiable,
    Upserts,
};
use iml_request_retry::{retry_future, RetryAction, RetryPolicy};
use iml_tracing::tracing;
use std::{convert::TryInto as _, fmt::Debug, time::Duration};
use thiserror::Error;
use tokio::time;
use url::Url;
use wbem_client::{resp::Instance, Client, ClientExt};

#[derive(Error, Debug)]
enum ImlSfaError {
    #[error(transparent)]
    WbemClient(#[from] wbem_client::WbemClientError),
    #[error(transparent)]
    SfaClass(#[from] SfaClassError),
    #[error(transparent)]
    Async(#[from] AsyncError),
    #[error(transparent)]
    ImlOrm(#[from] iml_orm::ImlOrmError),
}

fn create_retry_endpoint_policy<E: Debug>(len: u32) -> impl RetryPolicy<E> + Send {
    move |k: u32, e| {
        if k < len {
            RetryAction::RetryNow
        } else {
            RetryAction::ReturnError(e)
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), ImlSfaError> {
    iml_tracing::init();

    let pool = iml_orm::pool()?;

    let endpoints = iml_manager_env::get_sfa_endpoints();

    let endpoints = match endpoints {
        Some(x) => x,
        None => {
            tracing::info!("No endpoints found, exiting.");

            return Ok(());
        }
    };

    let client = wbem_client::get_client(true)?;

    let mut interval = time::interval(Duration::from_secs(5));

    let mut old_drives = SfaDiskDrive::all()
        .load_async::<SfaDiskDrive>(&pool)
        .await?;

    let mut old_enclosures = SfaEnclosure::all()
        .load_async::<SfaEnclosure>(&pool)
        .await?;

    let mut old_jobs = SfaJob::all().load_async::<SfaJob>(&pool).await?;

    let mut old_power_supplies = SfaPowerSupply::all()
        .load_async::<SfaPowerSupply>(&pool)
        .await?;

    let mut old_controllers = SfaController::all()
        .load_async::<SfaController>(&pool)
        .await?;

    loop {
        interval.tick().await;

        let mut fut1_policy = create_retry_endpoint_policy((endpoints[0].len() - 1) as u32);
        let fut1 = retry_future(
            |c| client.fetch_sfa_enclosures(endpoints[0][c as usize].clone()),
            &mut fut1_policy,
        );

        let mut fut2_policy = create_retry_endpoint_policy((endpoints[0].len() - 1) as u32);
        let fut2 = retry_future(
            |c| client.fetch_sfa_storage_system(endpoints[0][c as usize].clone()),
            &mut fut2_policy,
        );

        let mut fut3_policy = create_retry_endpoint_policy((endpoints[0].len() - 1) as u32);
        let fut3 = retry_future(
            |c| client.fetch_sfa_disk_drives(endpoints[0][c as usize].clone()),
            &mut fut3_policy,
        );

        let mut fut4_policy = create_retry_endpoint_policy((endpoints[0].len() - 1) as u32);
        let fut4 = retry_future(
            |c| client.fetch_sfa_jobs(endpoints[0][c as usize].clone()),
            &mut fut4_policy,
        );

        let mut fut5_policy = create_retry_endpoint_policy((endpoints[0].len() - 1) as u32);
        let fut5 = retry_future(
            |c| client.fetch_sfa_power_supply(endpoints[0][c as usize].clone()),
            &mut fut5_policy,
        );

        let (new_enclosures, x, new_drives, new_jobs, new_power_supplies) =
            future::try_join5(fut1, fut2, fut3, fut4, fut5).await?;

        let mut policy = create_retry_endpoint_policy((endpoints[0].len() - 1) as u32);
        let new_controllers = retry_future(
            |c| client.fetch_sfa_controllers(endpoints[0][c as usize].clone()),
            &mut policy,
        )
        .await?;

        tracing::trace!("SfaStorageSystem {:?}", x);
        tracing::trace!("SfaEnclosures {:?}", new_enclosures);
        tracing::trace!("SfaDiskDrives {:?}", new_drives);
        tracing::trace!("SfaJobs {:?}", new_jobs);
        tracing::trace!("SfaPowerSupply {:?}", new_power_supplies);
        tracing::trace!("SfaController {:?}", new_controllers);

        let (enclosure_upsert, enclosure_remove) = diff_items(
            &new_enclosures,
            &old_enclosures,
            &pool,
            SfaEnclosure::batch_upsert,
            SfaEnclosure::batch_delete,
        );

        let (drive_upsert, drive_remove) = diff_items(
            &new_drives,
            &old_drives,
            &pool,
            SfaDiskDrive::batch_upsert,
            SfaDiskDrive::batch_delete,
        );

        let (job_upsert, job_remove) = diff_items(
            &new_jobs,
            &old_jobs,
            &pool,
            SfaJob::batch_upsert,
            SfaJob::batch_delete,
        );

        let (power_supply_upsert, power_supply_remove) = diff_items(
            &new_power_supplies,
            &old_power_supplies,
            &pool,
            SfaPowerSupply::batch_upsert,
            SfaPowerSupply::batch_delete,
        );

        let (controller_upsert, controller_remove) = diff_items(
            &new_controllers,
            &old_controllers,
            &pool,
            SfaController::batch_upsert,
            SfaController::batch_delete,
        );

        SfaStorageSystem::upsert(x).execute_async(&pool).await?;

        enclosure_upsert.await?;

        controller_upsert.await?;

        controller_remove.await?;

        power_supply_upsert.await?;

        power_supply_remove.await?;

        drive_upsert.await?;

        job_upsert.await?;

        job_remove.await?;

        drive_remove.await?;

        enclosure_remove.await?;

        old_drives = new_drives;

        old_enclosures = new_enclosures;

        old_jobs = new_jobs;

        old_power_supplies = new_power_supplies;

        old_controllers = new_controllers;
    }
}

type UpsertFn<'a, T, R> = fn(Upserts<&'a T>) -> R;
type DeleteFn<'a, T, R> = fn(Vec<&'a T>) -> R;

fn diff_items<
    'a,
    T: Identifiable + Changeable,
    R1: AsyncRunQueryDslPostgres + Executable + 'a,
    R2: AsyncRunQueryDslPostgres + Executable + 'a,
>(
    new: &'a Vec<T>,
    old: &'a Vec<T>,
    pool: &'a DbPool,
    upsert_fn: UpsertFn<'a, T, R1>,
    delete_fn: DeleteFn<'a, T, R2>,
) -> (
    impl Future<Output = Result<(), ImlSfaError>> + 'a,
    impl Future<Output = Result<(), ImlSfaError>> + 'a,
) {
    let (upsert, remove) = new.get_changes(&old);

    let upserts = if let Some(upsert) = upsert {
        tracing::debug!("{} changed Enclosures, performing Upsert", upsert.0.len());
        Either::Left(
            upsert_fn(upsert)
                .execute_async(pool)
                .map_ok(drop)
                .err_into(),
        )
    } else {
        Either::Right(future::ok(()))
    };

    let removals = if let Some(remove) = remove {
        tracing::debug!("{} removed Items, performing Deletion", remove.0.len());

        Either::Left(
            delete_fn(remove.0)
                .execute_async(pool)
                .map_ok(drop)
                .err_into(),
        )
    } else {
        Either::Right(future::ok(()))
    };

    (upserts, removals)
}

#[async_trait(?Send)]
trait SfaClassExt: ClientExt {
    async fn fetch_sfa_storage_system(&self, url: Url) -> Result<SfaStorageSystem, ImlSfaError>;
    async fn fetch_sfa_enclosures(&self, url: Url) -> Result<Vec<SfaEnclosure>, ImlSfaError>;
    async fn fetch_sfa_disk_drives(&self, url: Url) -> Result<Vec<SfaDiskDrive>, ImlSfaError>;
    async fn fetch_sfa_jobs(&self, url: Url) -> Result<Vec<SfaJob>, ImlSfaError>;
    async fn fetch_sfa_power_supply(&self, url: Url) -> Result<Vec<SfaPowerSupply>, ImlSfaError>;
    async fn fetch_sfa_controllers(&self, url: Url) -> Result<Vec<SfaController>, ImlSfaError>;
}

#[async_trait(?Send)]
impl SfaClassExt for Client {
    async fn fetch_sfa_storage_system(&self, url: Url) -> Result<SfaStorageSystem, ImlSfaError> {
        let x = self
            .get_instance(url, "root/ddn", "DDN_SFAStorageSystem")
            .await?;

        let x: SfaStorageSystem = x.try_into()?;

        Ok(x)
    }
    async fn fetch_sfa_enclosures(&self, url: Url) -> Result<Vec<SfaEnclosure>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAEnclosure")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_disk_drives(&self, url: Url) -> Result<Vec<SfaDiskDrive>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFADiskDrive")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_jobs(&self, url: Url) -> Result<Vec<SfaJob>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAJob")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_power_supply(&self, url: Url) -> Result<Vec<SfaPowerSupply>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAPowerSupply")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
    async fn fetch_sfa_controllers(&self, url: Url) -> Result<Vec<SfaController>, ImlSfaError> {
        let x = self.fetch_sfa_storage_system(url.clone());

        let ys = self
            .enumerate_instances(url, "root/ddn", "DDN_SFAController")
            .err_into();

        let (x, ys) = future::try_join(x, ys).await?;

        let ys = Vec::<Instance>::from(ys)
            .into_iter()
            .map(|y| (x.uuid.clone(), y).try_into())
            .collect::<Result<Vec<_>, _>>()?;

        Ok(ys)
    }
}
<|endoftext|>


<commit_before>#![macro_escape]

#[macro_export]
macro_rules! msg_send(
	($obj:expr $name:ident) => ({
		let sel_name = stringify!($name);
		let sel = ::objc::runtime::Sel::register(sel_name);
		let ptr = ::objc::to_ptr(&$obj);
		::objc::runtime::objc_msgSend(ptr, sel)
	});
	($obj:expr $($name:ident : $arg:expr)+) => ({
		let sel_name = concat!($(stringify!($name), ':'),+);
		let sel = ::objc::runtime::Sel::register(sel_name);
		let ptr = ::objc::to_ptr(&$obj);
		::objc::runtime::objc_msgSend(ptr, sel $(,$arg)+)
	});
)

#[macro_export]
macro_rules! object_struct(
	($name:ident<$($t:ident),+>) => (
		object_struct!($name, $($t),+)
	);
	($name:ident $(,$t:ident)*) => (
		pub struct $name<$($t),*> {
			nocopy: ::std::kinds::marker::NoCopy,
		}

		impl<$($t),*> ::objc::runtime::Message for $name<$($t),*> { }

		impl<$($t),*> ::objc::foundation::INSObject for $name<$($t),*> {
			fn class_name() -> ::objc::ClassName<$name<$($t),*>> {
				::objc::ClassName(stringify!($name))
			}
		}

		impl<$($t),*> ::std::cmp::PartialEq for $name<$($t),*> {
			fn eq(&self, other: &$name<$($t),*>) -> bool {
				use objc::foundation::INSObject;
				self.is_equal(other)
			}
		}

		impl<$($t),*> ::std::cmp::Eq for $name<$($t),*> { }

		impl<$($t),*> ::std::hash::Hash for $name<$($t),*> {
			fn hash(&self, state: &mut ::std::hash::sip::SipState) {
				use objc::foundation::INSObject;
				self.hash_code().hash(state);
			}
		}

		impl<$($t),*> ::std::fmt::Show for $name<$($t),*> {
			fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
				use objc::foundation::{INSObject, INSString};
				self.description().as_str().fmt(f)
			}
		}
	);
)

#[macro_export]
macro_rules! method(
	// Void no arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident;
		$body:block
	) => ({
		method!(, stringify!($name), $body, $name, (), $self_name: $self_ty,)
	});
	// No arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident
		-> $ret_ty:ty $body:block
	) => ({
		method!(, stringify!($name), $body, $name, $ret_ty, $self_name: $self_ty,)
	});
	// Void with arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident : ($first_arg_ty:ty) $first_arg_name:ident
		$(, $next_name:ident : ($next_arg_ty:ty) $next_arg_name:ident)*;
		$body:block
	) => ({
		let sel_name = concat!(stringify!($name), ':', $(stringify!($next_name), ':'),*);
		method!(, sel_name, $body, $name, (), $self_name: $self_ty,
			$first_arg_name: $first_arg_ty$(, $next_arg_name: $next_arg_ty)*)
	});
	// Arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident : ($first_arg_ty:ty) $first_arg_name:ident
		$(, $next_name:ident : ($next_arg_ty:ty) $next_arg_name:ident)*
		-> $ret_ty:ty $body:block
	) => ({
		let sel_name = concat!(stringify!($name), ':', $(stringify!($next_name), ':'),*);
		method!(, sel_name, $body, $name, $ret_ty, $self_name: $self_ty,
			$first_arg_name: $first_arg_ty$(, $next_arg_name: $next_arg_ty)*)
	});
	// Preceding comma is necessary to disambiguate
	(, $sel_name:expr, $body:block, $fn_name:ident, $ret_ty:ty, $self_name:ident : $self_ty:ty, $($arg_name:ident : $arg_ty:ty),*) => ({
		let sel = ::objc::runtime::Sel::register($sel_name);

		#[allow(non_snake_case_functions)]
		extern fn $fn_name($self_name: $self_ty, _cmd: ::objc::runtime::Sel $(, $arg_name: $arg_ty)*) -> $ret_ty $body
		let imp: ::objc::runtime::Imp = unsafe { ::std::mem::transmute($fn_name) };

		let mut types = ::objc::encode::<$ret_ty>().to_string();
		types.push_str(::objc::encode::<$self_ty>());
		types.push_str(::objc::encode::<::objc::runtime::Sel>());
		$(types.push_str(::objc::encode::<$arg_ty>());)*

		::objc::MethodDecl { sel: sel, imp: imp, types: types }
	});
)
<commit_msg>Split out an object_impl macro.<commit_after>#![macro_escape]

#[macro_export]
macro_rules! msg_send(
	($obj:expr $name:ident) => ({
		let sel_name = stringify!($name);
		let sel = ::objc::runtime::Sel::register(sel_name);
		let ptr = ::objc::to_ptr(&$obj);
		::objc::runtime::objc_msgSend(ptr, sel)
	});
	($obj:expr $($name:ident : $arg:expr)+) => ({
		let sel_name = concat!($(stringify!($name), ':'),+);
		let sel = ::objc::runtime::Sel::register(sel_name);
		let ptr = ::objc::to_ptr(&$obj);
		::objc::runtime::objc_msgSend(ptr, sel $(,$arg)+)
	});
)

#[macro_export]
macro_rules! object_struct(
	($name:ident<$($t:ident),+>) => (
		object_struct!($name, $($t),+)
	);
	($name:ident $(,$t:ident)*) => (
		pub struct $name<$($t),*> {
			nocopy: ::std::kinds::marker::NoCopy,
		}

		object_impl!($name $(,$t)*)
	);
)

#[macro_export]
macro_rules! object_impl(
	($name:ident<$($t:ident),+>) => (
		object_impl!($name, $($t),+)
	);
	($name:ident $(,$t:ident)*) => (
		impl<$($t),*> ::objc::runtime::Message for $name<$($t),*> { }

		impl<$($t),*> ::objc::foundation::INSObject for $name<$($t),*> {
			fn class_name() -> ::objc::ClassName<$name<$($t),*>> {
				::objc::ClassName(stringify!($name))
			}
		}

		impl<$($t),*> ::std::cmp::PartialEq for $name<$($t),*> {
			fn eq(&self, other: &$name<$($t),*>) -> bool {
				use objc::foundation::INSObject;
				self.is_equal(other)
			}
		}

		impl<$($t),*> ::std::cmp::Eq for $name<$($t),*> { }

		impl<$($t),*> ::std::hash::Hash for $name<$($t),*> {
			fn hash(&self, state: &mut ::std::hash::sip::SipState) {
				use objc::foundation::INSObject;
				self.hash_code().hash(state);
			}
		}

		impl<$($t),*> ::std::fmt::Show for $name<$($t),*> {
			fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
				use objc::foundation::{INSObject, INSString};
				self.description().as_str().fmt(f)
			}
		}
	);
)

#[macro_export]
macro_rules! method(
	// Void no arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident;
		$body:block
	) => ({
		method!(, stringify!($name), $body, $name, (), $self_name: $self_ty,)
	});
	// No arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident
		-> $ret_ty:ty $body:block
	) => ({
		method!(, stringify!($name), $body, $name, $ret_ty, $self_name: $self_ty,)
	});
	// Void with arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident : ($first_arg_ty:ty) $first_arg_name:ident
		$(, $next_name:ident : ($next_arg_ty:ty) $next_arg_name:ident)*;
		$body:block
	) => ({
		let sel_name = concat!(stringify!($name), ':', $(stringify!($next_name), ':'),*);
		method!(, sel_name, $body, $name, (), $self_name: $self_ty,
			$first_arg_name: $first_arg_ty$(, $next_arg_name: $next_arg_ty)*)
	});
	// Arguments
	(
		($self_ty:ty)$self_name:ident
		, $name:ident : ($first_arg_ty:ty) $first_arg_name:ident
		$(, $next_name:ident : ($next_arg_ty:ty) $next_arg_name:ident)*
		-> $ret_ty:ty $body:block
	) => ({
		let sel_name = concat!(stringify!($name), ':', $(stringify!($next_name), ':'),*);
		method!(, sel_name, $body, $name, $ret_ty, $self_name: $self_ty,
			$first_arg_name: $first_arg_ty$(, $next_arg_name: $next_arg_ty)*)
	});
	// Preceding comma is necessary to disambiguate
	(, $sel_name:expr, $body:block, $fn_name:ident, $ret_ty:ty, $self_name:ident : $self_ty:ty, $($arg_name:ident : $arg_ty:ty),*) => ({
		let sel = ::objc::runtime::Sel::register($sel_name);

		#[allow(non_snake_case_functions)]
		extern fn $fn_name($self_name: $self_ty, _cmd: ::objc::runtime::Sel $(, $arg_name: $arg_ty)*) -> $ret_ty $body
		let imp: ::objc::runtime::Imp = unsafe { ::std::mem::transmute($fn_name) };

		let mut types = ::objc::encode::<$ret_ty>().to_string();
		types.push_str(::objc::encode::<$self_ty>());
		types.push_str(::objc::encode::<::objc::runtime::Sel>());
		$(types.push_str(::objc::encode::<$arg_ty>());)*

		::objc::MethodDecl { sel: sel, imp: imp, types: types }
	});
)
<|endoftext|>


<commit_before>use std::collections::HashMap;
use objective::functions::Function;
use objective::constraints::Constraint;
use objective::constraints::SystemOfConstraints;
use tableau::tables::Table;

pub fn get_initial_table_from(fun: &Function, constraints: &SystemOfConstraints) -> Table {
    let mut column_names: HashMap<String, usize> = HashMap::new();
    // We have number of rows equal to the number of regular constraints plus a
    // row for the function we are maxising or minimising.
    let mut num_rows: usize = 1;
    // We need the length of rows and we should be able to index into the table
    // with respect to a variable name.
    for constraint in constraints.system() {
        match constraint {
            &Constraint::Regular(ref ref_cell) => {
                num_rows += 1;
                let exp = ref_cell.borrow();
                for var in exp.lhs() {
                    let map_len = column_names.len();
                    column_names.entry(var.name().to_string()).or_insert(map_len);
                }
            }
            &Constraint::NonNegative(_) => continue,
        }
    }
    // ... and don't forget about the constant on the right.
    let map_len = column_names.len();
    column_names.insert("RHS".to_string(), map_len);
    for var in fun.exp().borrow().rhs() {
        let map_len = column_names.len();
        column_names.entry(var.name().to_string()).or_insert(map_len);
    }
    let mut rows: Vec<Vec<f64>> = vec![vec![0.0; column_names.len()]; num_rows];
    // Populate the table
    let mut row_index = 0;
    for constraint in constraints.system() {
        match constraint {
            &Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                for var in exp.lhs() {
                    rows[row_index][*column_names.get(var.name()).unwrap()] = var.get_data();
                }
                // ... and don't forget about the constant on the right.
                let last_column = rows[row_index].len() - 1;
                rows[row_index][last_column] = exp.rhs()[0].get_data();
                row_index += 1;
            }
            &Constraint::NonNegative(_) => continue,
        }
    }
    for var in fun.exp().borrow().rhs() {
        rows[row_index][*column_names.get(var.name()).unwrap()] = var.get_data();
    }
    // Set the value of Z to be initially 0 in the table.
    rows[row_index][map_len] = 0.0;
    Table::new(column_names, rows)
}
<commit_msg>Get max expression not original expression.<commit_after>use std::collections::HashMap;
use objective::functions::Function;
use objective::constraints::Constraint;
use objective::constraints::SystemOfConstraints;
use tableau::tables::Table;

pub fn get_initial_table_from(fun: &Function, constraints: &SystemOfConstraints) -> Table {
    let mut column_names: HashMap<String, usize> = HashMap::new();
    // We have number of rows equal to the number of regular constraints plus a
    // row for the function we are maxising or minimising.
    let mut num_rows: usize = 1;
    // We need the length of rows and we should be able to index into the table
    // with respect to a variable name.
    for constraint in constraints.system() {
        match constraint {
            &Constraint::Regular(ref ref_cell) => {
                num_rows += 1;
                let exp = ref_cell.borrow();
                for var in exp.lhs() {
                    let map_len = column_names.len();
                    column_names.entry(var.name().to_string()).or_insert(map_len);
                }
            }
            &Constraint::NonNegative(_) => continue,
        }
    }
    // ... and don't forget about the constant on the right.
    let map_len = column_names.len();
    column_names.insert("RHS".to_string(), map_len);
    for var in fun.exp().borrow().rhs() {
        let map_len = column_names.len();
        column_names.entry(var.name().to_string()).or_insert(map_len);
    }
    let mut rows: Vec<Vec<f64>> = vec![vec![0.0; column_names.len()]; num_rows];
    // Populate the table
    let mut row_index = 0;
    for constraint in constraints.system() {
        match constraint {
            &Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                for var in exp.lhs() {
                    rows[row_index][*column_names.get(var.name()).unwrap()] = var.get_data();
                }
                // ... and don't forget about the constant on the right.
                let last_column = rows[row_index].len() - 1;
                rows[row_index][last_column] = exp.rhs()[0].get_data();
                row_index += 1;
            }
            &Constraint::NonNegative(_) => continue,
        }
    }
    for var in fun.exp_max().borrow().rhs() {
        rows[row_index][*column_names.get(var.name()).unwrap()] = var.get_data();
    }
    // Set the value of Z to be initially 0 in the table.
    rows[row_index][map_len] = 0.0;
    Table::new(column_names, rows)
}
<|endoftext|>


<commit_before>//! Utility for managing concurrent access to the application state
//!
//! # Background
//!
//! Requests come in to the server in a certain order. Each request has a certain set of
//! "data dependencies". These dependencies are the parts of the state that the request wants to
//! read or modify. We want to avoid partially updating state or ending up in an inconsistent
//! state, so it is important that each request waits until **all** of the data it needs is
//! available before it operates on that data. Additionally, requests that do not depend on any of
//! the same data should be able to run concurrently.
//!
//! For example, if two turtles are drawing separate lines, they should be able to draw those lines
//! at the same time. On the other hand, if a turtle is drawing a line and then a clear request
//! is sent from another thread, the turtle should be able to finish drawing that line before the
//! clear request is executed. If in the meantime another request pertaining to a different turtle
//! comes in, that request should wait until the clear request has completed.
//!
//! More precisely, we are trying to enforce that:
//! * requests that depend on an overlapping subset of app data execute in the order in which they
//!   arrive in the request queue
//! * requests that are independent of each other (no shared data dependencies), may execute
//!   concurrently without having to wait for one another
//!
//! It is tough to do this with just a `Vec<Mutex<T>>` because while tokio's Mutex does guarantee
//! FIFO order, there is no way to put something back on the front of the mutex queue in the case
//! where *all* of its data dependencies aren't ready. Also, if we were to use a Mutex, it would
//! be impossible to lock the data in between request executions. That would mean that rendering
//! would be blocked until all requests have been processed. To deal with that we would need some
//! kind of prioritized locking system that allows a lock from the renderer to override any other
//! locks from requests.
//!
//! # Access Control
//!
//! The `AccessControl` data structure below is responsible for enforcing the properties outlined
//! above. Each piece of data shared by the renderer and the requests is stored behind a separate
//! mutex. To ensure that the renderer has priority over requests when locking the data, it does
//! not use the `AccessControl` type. Instead, it is given direct access to those mutexes and is
//! able to lock them whenever it needs to. Requests on the other hand are required to use the
//! `AccessControl` type to explicitly declare the data that they need. The `AccessControl` type
//! decides when to give each request permission to begin locking the data.
//!
//! Only a single request is ever given permission to begin locking a given set of mutexes.
//! Multiple requests with disjoint sets of data may be given permission at the same time to lock
//! their respective pieces of data. In all cases, the `AccessControl` type guarantees that only up
//! to one waiter will ever be in a mutex's queue or currently holding a mutex's lock. This is
//! important because it means that the renderer is guaranteed to always get priority access to the
//! data immediately after the current set of running requests. It can lock the data almost right
//! away without waiting for all queued requests to complete.
//!
//! Some requests do not complete "instantly" and may even have long await points (e.g. for an
//! animation delay). If not handled correctly, this can block rendering as a single request could
//! hold on to a lock for too long. To avoid this, the `AccessControl` type does not actually lock
//! all of the data as soon as a request is given permission to access it. Turtle data for example
//! is provided as the mutexes holding the data, not as mutex guards which would hold the locks.
//! This allows a request that needs long await points to unlock the data before those await points
//! begin. This enables the renderer to continue while still allowing requests to maintain access
//! over a longer period of time.
//!
//! The key here is that access to the data isn't bound by the amount of time the data is locked.
//! Instead, the `AccessControl` type provides the *opportunity* to lock, and waits for the request
//! to give up that opportunity before passing it on to the next request. Requests may unlock data
//! and still re-lock it later without worrying about losing their place to another request that
//! came afterwards.
//!
//! # Example
//!
//! Example: Suppose there are N = 4 turtles and you have the following requests:
//! - Request R1 depends on turtles: 1, 2, 3
//! - Request R2 depends on turtles: 4
//! - Request R3 depends on turtles: 3, 4
//! - Request R4 depends on turtles: 1, 2, 3, 4
//! - Request R5 depends on turtles: 1
//!
//! Expected behaviour:
//! 1. R1 and R2 execute concurrently, no shared dependencies
//! 2. R3 waits on both R1 and R2
//! 3. R4 waits on R3 (and implicitly R1 and R2)
//! 4. R5 waits on R4
//!
//! Conceptually, you can imagine that there is a queue for each turtle's data. The requests can
//! be sorted into those queues like so:
//!
//! 1: R1, R4, R5
//! 2: R1, R4
//! 3: R1, R3, R4
//! 4: R2, R3, R4
//!
//! The key here is that each request is listed in the order that it was in the original queue. A
//! request cannot execute until the request before it is done. A request can't be done until it's
//! at the front of all the queues it is in.

mod data_guard;
mod data_request;
mod resources;

pub use data_guard::*;
pub use data_request::*;
pub use resources::*;

use std::sync::Arc;

use tokio::sync::oneshot;
use parking_lot::Mutex;

use super::app::{App, TurtleId};

type SharedResources = Arc<Mutex<Resources>>;

/// Manages access to the app state, enforcing the rules around sequential consistency and
/// concurrent access
#[derive(Debug)]
pub struct AccessControl {
    app: Arc<App>,
    resources: SharedResources,
}

impl AccessControl {
    /// Creates a struct that will manage access to the given App data
    ///
    /// This struct assumes that App will only be managed here. That is, App should never be
    /// mutated without first going through this struct. Reading data externally is fine.
    pub async fn new(app: Arc<App>) -> Self {
        assert_eq!(app.turtles_len().await, 0,
            "bug: access control assumes that turtles are only added through itself");

        let resources = SharedResources::default();

        Self {app, resources}
    }

    /// Adds a new turtle to the application state
    ///
    /// This does not need any ordering protection because no call to `get()` is allowed to depend
    /// on the data for a turtle that hasn't been created yet.
    pub async fn add_turtle(&self) -> TurtleId {
        let id = self.app.add_turtle().await;

        id
    }

    /// Requests the opportunity to potentially read or modify the drawing and all turtles
    ///
    /// See `get()` for more information
    pub async fn get_all(&self, data_req_queued: oneshot::Sender<()>) -> (DrawingData, Vec<TurtleData>) {
        // Record the IDs of the turtles that are currently available. This is necessary to
        // guarantee the soundness of requesting "all" of the turtles. No request is allowed to
        // depend on turtles that haven't been created yet, so "all" must be treated as all turtles
        // that currently exist when the request was sent. Only those turtles will be accessed, even
        // if more are added while the request waits.
        let turtles: Vec<_> = self.app.turtle_ids().await.collect();

        self.get((FetchDrawing, turtles), data_req_queued).await
    }

    /// Requests the opportunity to potentially read or modify the drawing
    ///
    /// See `get()` for more information
    pub async fn get_drawing(&self, data_req_queued: oneshot::Sender<()>) -> DrawingData {
        self.get(FetchDrawing, data_req_queued).await
    }

    /// Requests the opportunity to potentially read or modify some of the
    /// application state
    ///
    /// A message will be sent through `data_req_queued` when the data requests
    /// have been queued and the next call to get() may proceed.
    ///
    /// # Deadlock Warning
    ///
    /// **Warning:** If used incorrectly, this method can deadlock the program. For example, if the
    /// same resource is requested twice, one of the requests will be fulfilled, but the other will
    /// cause a deadlock since the same resource cannot be held twice.
    pub async fn get<R: DataRequest>(
        &self,
        data_req: R,
        data_req_queued: oneshot::Sender<()>,
    ) -> <R as DataRequest>::Output {
        // Request all the necessary data

        let mut pending_request: Option<(Arc<PendingDataRequest>, _)> = None;
        let generate_data_request = || match &pending_request {
            Some((req, _)) => {
                req.add_needed_resource();
                req.clone()
            },

            None => {
                let (sender, receiver) = oneshot::channel();
                let req = Arc::new(PendingDataRequest::new(sender));
                pending_request = Some((req.clone(), receiver));
                req
            },
        };

        {
            let mut res = self.resources.lock();
            data_req.poll_resources(&mut res, generate_data_request);
        }

        // Signal that all data requests have been queued and the next call to get() can proceed.
        // This is necessary since we can only guarantee request ordering if each request gets to
        // this point before the next call to `get`. Without this, we would have a race condition
        // between all callers of get() where the order would be randomly determined based on the
        // order that the tasks calling get() are scheduled.
        //
        // Ignoring errors since this just means that whoever was waiting to find out when the
        // requests have been queued no longer needs to know.
        data_req_queued.send(()).unwrap_or(());

        // Wait for any data that is not available yet
        if let Some((_, data_ready_receiver)) = pending_request {
            data_ready_receiver.await
                .expect("bug: tasks should notify about data being available before they are dropped");
        }

        // Fetch all the data that was requested (should only be contended by renderer)

        data_req.fetch_resources(&self.app, &self.resources).await
    }
}
<commit_msg>Added a note about why all the resources are behind a single Mutex rather than multiple Mutexes per resource<commit_after>//! Utility for managing concurrent access to the application state
//!
//! # Background
//!
//! Requests come in to the server in a certain order. Each request has a certain set of
//! "data dependencies". These dependencies are the parts of the state that the request wants to
//! read or modify. We want to avoid partially updating state or ending up in an inconsistent
//! state, so it is important that each request waits until **all** of the data it needs is
//! available before it operates on that data. Additionally, requests that do not depend on any of
//! the same data should be able to run concurrently.
//!
//! For example, if two turtles are drawing separate lines, they should be able to draw those lines
//! at the same time. On the other hand, if a turtle is drawing a line and then a clear request
//! is sent from another thread, the turtle should be able to finish drawing that line before the
//! clear request is executed. If in the meantime another request pertaining to a different turtle
//! comes in, that request should wait until the clear request has completed.
//!
//! More precisely, we are trying to enforce that:
//! * requests that depend on an overlapping subset of app data execute in the order in which they
//!   arrive in the request queue
//! * requests that are independent of each other (no shared data dependencies), may execute
//!   concurrently without having to wait for one another
//!
//! It is tough to do this with just a `Vec<Mutex<T>>` because while tokio's Mutex does guarantee
//! FIFO order, there is no way to put something back on the front of the mutex queue in the case
//! where *all* of its data dependencies aren't ready. Also, if we were to use a Mutex, it would
//! be impossible to lock the data in between request executions. That would mean that rendering
//! would be blocked until all requests have been processed. To deal with that we would need some
//! kind of prioritized locking system that allows a lock from the renderer to override any other
//! locks from requests.
//!
//! # Access Control
//!
//! The `AccessControl` data structure below is responsible for enforcing the properties outlined
//! above. Each piece of data shared by the renderer and the requests is stored behind a separate
//! mutex. To ensure that the renderer has priority over requests when locking the data, it does
//! not use the `AccessControl` type. Instead, it is given direct access to those mutexes and is
//! able to lock them whenever it needs to. Requests on the other hand are required to use the
//! `AccessControl` type to explicitly declare the data that they need. The `AccessControl` type
//! decides when to give each request permission to begin locking the data.
//!
//! Only a single request is ever given permission to begin locking a given set of mutexes.
//! Multiple requests with disjoint sets of data may be given permission at the same time to lock
//! their respective pieces of data. In all cases, the `AccessControl` type guarantees that only up
//! to one waiter will ever be in a mutex's queue or currently holding a mutex's lock. This is
//! important because it means that the renderer is guaranteed to always get priority access to the
//! data immediately after the current set of running requests. It can lock the data almost right
//! away without waiting for all queued requests to complete.
//!
//! Some requests do not complete "instantly" and may even have long await points (e.g. for an
//! animation delay). If not handled correctly, this can block rendering as a single request could
//! hold on to a lock for too long. To avoid this, the `AccessControl` type does not actually lock
//! all of the data as soon as a request is given permission to access it. Turtle data for example
//! is provided as the mutexes holding the data, not as mutex guards which would hold the locks.
//! This allows a request that needs long await points to unlock the data before those await points
//! begin. This enables the renderer to continue while still allowing requests to maintain access
//! over a longer period of time.
//!
//! The key here is that access to the data isn't bound by the amount of time the data is locked.
//! Instead, the `AccessControl` type provides the *opportunity* to lock, and waits for the request
//! to give up that opportunity before passing it on to the next request. Requests may unlock data
//! and still re-lock it later without worrying about losing their place to another request that
//! came afterwards.
//!
//! # Example
//!
//! Example: Suppose there are N = 4 turtles and you have the following requests:
//! - Request R1 depends on turtles: 1, 2, 3
//! - Request R2 depends on turtles: 4
//! - Request R3 depends on turtles: 3, 4
//! - Request R4 depends on turtles: 1, 2, 3, 4
//! - Request R5 depends on turtles: 1
//!
//! Expected behaviour:
//! 1. R1 and R2 execute concurrently, no shared dependencies
//! 2. R3 waits on both R1 and R2
//! 3. R4 waits on R3 (and implicitly R1 and R2)
//! 4. R5 waits on R4
//!
//! Conceptually, you can imagine that there is a queue for each turtle's data. The requests can
//! be sorted into those queues like so:
//!
//! 1: R1, R4, R5
//! 2: R1, R4
//! 3: R1, R3, R4
//! 4: R2, R3, R4
//!
//! The key here is that each request is listed in the order that it was in the original queue. A
//! request cannot execute until the request before it is done. A request can't be done until it's
//! at the front of all the queues it is in.

mod data_guard;
mod data_request;
mod resources;

pub use data_guard::*;
pub use data_request::*;
pub use resources::*;

use std::sync::Arc;

use tokio::sync::oneshot;
use parking_lot::Mutex;

use super::app::{App, TurtleId};

// Note: It is important that all of the resources be protected together by a Mutex rather than
// individually on a per-resource basis. This is how we guarantee that if a request needs multiple
// resources, it is able to reserve them all at once. Without that, two requests could race to
// reserve the same resources and end up deadlocked because the requests were queued in the wrong
// order.
type SharedResources = Arc<Mutex<Resources>>;

/// Manages access to the app state, enforcing the rules around sequential consistency and
/// concurrent access
#[derive(Debug)]
pub struct AccessControl {
    app: Arc<App>,
    resources: SharedResources,
}

impl AccessControl {
    /// Creates a struct that will manage access to the given App data
    ///
    /// This struct assumes that App will only be managed here. That is, App should never be
    /// mutated without first going through this struct. Reading data externally is fine.
    pub async fn new(app: Arc<App>) -> Self {
        assert_eq!(app.turtles_len().await, 0,
            "bug: access control assumes that turtles are only added through itself");

        let resources = SharedResources::default();

        Self {app, resources}
    }

    /// Adds a new turtle to the application state
    ///
    /// This does not need any ordering protection because no call to `get()` is allowed to depend
    /// on the data for a turtle that hasn't been created yet.
    pub async fn add_turtle(&self) -> TurtleId {
        let id = self.app.add_turtle().await;

        id
    }

    /// Requests the opportunity to potentially read or modify the drawing and all turtles
    ///
    /// See `get()` for more information
    pub async fn get_all(&self, data_req_queued: oneshot::Sender<()>) -> (DrawingData, Vec<TurtleData>) {
        // Record the IDs of the turtles that are currently available. This is necessary to
        // guarantee the soundness of requesting "all" of the turtles. No request is allowed to
        // depend on turtles that haven't been created yet, so "all" must be treated as all turtles
        // that currently exist when the request was sent. Only those turtles will be accessed, even
        // if more are added while the request waits.
        let turtles: Vec<_> = self.app.turtle_ids().await.collect();

        self.get((FetchDrawing, turtles), data_req_queued).await
    }

    /// Requests the opportunity to potentially read or modify the drawing
    ///
    /// See `get()` for more information
    pub async fn get_drawing(&self, data_req_queued: oneshot::Sender<()>) -> DrawingData {
        self.get(FetchDrawing, data_req_queued).await
    }

    /// Requests the opportunity to potentially read or modify some of the
    /// application state
    ///
    /// A message will be sent through `data_req_queued` when the data requests
    /// have been queued and the next call to get() may proceed.
    ///
    /// # Deadlock Warning
    ///
    /// **Warning:** If used incorrectly, this method can deadlock the program. For example, if the
    /// same resource is requested twice, one of the requests will be fulfilled, but the other will
    /// cause a deadlock since the same resource cannot be held twice.
    pub async fn get<R: DataRequest>(
        &self,
        data_req: R,
        data_req_queued: oneshot::Sender<()>,
    ) -> <R as DataRequest>::Output {
        // Request all the necessary data

        let mut pending_request: Option<(Arc<PendingDataRequest>, _)> = None;
        let generate_data_request = || match &pending_request {
            Some((req, _)) => {
                req.add_needed_resource();
                req.clone()
            },

            None => {
                let (sender, receiver) = oneshot::channel();
                let req = Arc::new(PendingDataRequest::new(sender));
                pending_request = Some((req.clone(), receiver));
                req
            },
        };

        {
            let mut res = self.resources.lock();
            data_req.poll_resources(&mut res, generate_data_request);
        }

        // Signal that all data requests have been queued and the next call to get() can proceed.
        // This is necessary since we can only guarantee request ordering if each request gets to
        // this point before the next call to `get`. Without this, we would have a race condition
        // between all callers of get() where the order would be randomly determined based on the
        // order that the tasks calling get() are scheduled.
        //
        // Ignoring errors since this just means that whoever was waiting to find out when the
        // requests have been queued no longer needs to know.
        data_req_queued.send(()).unwrap_or(());

        // Wait for any data that is not available yet
        if let Some((_, data_ready_receiver)) = pending_request {
            data_ready_receiver.await
                .expect("bug: tasks should notify about data being available before they are dropped");
        }

        // Fetch all the data that was requested (should only be contended by renderer)

        data_req.fetch_resources(&self.app, &self.resources).await
    }
}
<|endoftext|>


<commit_before>// xfail-boot
// xfail-stage0

fn test_fn() {
  type t = fn() -> int;
  fn ten() -> int {
    ret 10;
  }
  let t res = { ten };
  check (res() == 10);
}

fn main() {
  test_fn();
}<commit_msg>Un-XFAIL expr-block-fn.rs<commit_after>// xfail-boot

fn test_fn() {
  type t = fn() -> int;
  fn ten() -> int {
    ret 10;
  }
  let t res = { ten };
  check (res() == 10);
}

fn main() {
  test_fn();
}<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{common::Author, timeout::Timeout, vote_data::VoteData};
use anyhow::{ensure, Context};
use libra_crypto::hash::CryptoHash;
use libra_types::{
    crypto_proxies::{Signature, ValidatorSigner, ValidatorVerifier},
    ledger_info::LedgerInfo,
};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};

/// Vote is the struct that is ultimately sent by the voter in response for
/// receiving a proposal.
/// Vote carries the `LedgerInfo` of a block that is going to be committed in case this vote
/// is gathers QuorumCertificate (see the detailed explanation in the comments of `LedgerInfo`).
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct Vote {
    /// The data of the vote
    vote_data: VoteData,
    /// The identity of the voter.
    author: Author,
    /// LedgerInfo of a block that is going to be committed in case this vote gathers QC.
    ledger_info: LedgerInfo,
    /// Signature of the LedgerInfo
    signature: Signature,
    /// The round signatures can be aggregated into a timeout certificate if present.
    timeout_signature: Option<Signature>,
}

impl Display for Vote {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        write!(
            f,
            "Vote: [vote data: {}, author: {}, is_timeout: {}, {}]",
            self.vote_data,
            self.author.short_str(),
            self.is_timeout(),
            self.ledger_info
        )
    }
}

impl Vote {
    /// Generates a new Vote corresponding to the "fast-vote" path without the round signatures
    /// that can be aggregated into a timeout certificate
    pub fn new(
        vote_data: VoteData,
        author: Author,
        mut ledger_info_placeholder: LedgerInfo,
        validator_signer: &ValidatorSigner,
    ) -> Self {
        ledger_info_placeholder.set_consensus_data_hash(vote_data.hash());
        let li_sig = validator_signer
            .sign_message(ledger_info_placeholder.hash())
            .expect("Failed to sign LedgerInfo");
        Self {
            vote_data,
            author,
            ledger_info: ledger_info_placeholder,
            signature: li_sig.into(),
            timeout_signature: None,
        }
    }

    /// Generates a round signature, which can then be used for aggregating a timeout certificate.
    /// Typically called for generating vote messages that are sent upon timeouts.
    pub fn add_timeout_signature(&mut self, signature: Signature) {
        if self.timeout_signature.is_some() {
            return; // round signature is already set
        }

        self.timeout_signature.replace(signature);
    }

    pub fn vote_data(&self) -> &VoteData {
        &self.vote_data
    }

    /// Return the author of the vote
    pub fn author(&self) -> Author {
        self.author
    }

    /// Return the LedgerInfo associated with this vote
    pub fn ledger_info(&self) -> &LedgerInfo {
        &self.ledger_info
    }

    /// Return the signature of the vote
    pub fn signature(&self) -> &Signature {
        &self.signature
    }

    /// Returns the hash of the data represent by a timeout proposal
    pub fn timeout(&self) -> Timeout {
        Timeout::new(
            self.vote_data().proposed().epoch(),
            self.vote_data().proposed().round(),
        )
    }

    /// Return the epoch of the vote
    pub fn epoch(&self) -> u64 {
        self.vote_data.proposed().epoch()
    }

    /// Returns the signature for the vote_data().proposed().round() that can be aggregated for
    /// TimeoutCertificate.
    pub fn timeout_signature(&self) -> Option<&Signature> {
        self.timeout_signature.as_ref()
    }

    /// The vote message is considered a timeout vote message if it carries a signature on the
    /// round, which can then be used for aggregating it to the TimeoutCertificate.
    pub fn is_timeout(&self) -> bool {
        self.timeout_signature.is_some()
    }

    /// Verifies that the consensus data hash of LedgerInfo corresponds to the vote info,
    /// and then verifies the signature.
    pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
        ensure!(
            self.ledger_info.consensus_data_hash() == self.vote_data.hash(),
            "Vote's hash mismatch with LedgerInfo"
        );
        self.signature()
            .verify(validator, self.author(), self.ledger_info.hash())
            .context("Fail to verify Vote")?;
        if let Some(timeout_signature) = &self.timeout_signature {
            timeout_signature
                .verify(validator, self.author(), self.timeout().hash())
                .context("Fail to verify Vote")?;
        }
        Ok(())
    }
}
<commit_msg>[consensus] Add better expect message<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{common::Author, timeout::Timeout, vote_data::VoteData};
use anyhow::{ensure, Context};
use libra_crypto::hash::CryptoHash;
use libra_types::{
    crypto_proxies::{Signature, ValidatorSigner, ValidatorVerifier},
    ledger_info::LedgerInfo,
};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};

/// Vote is the struct that is ultimately sent by the voter in response for
/// receiving a proposal.
/// Vote carries the `LedgerInfo` of a block that is going to be committed in case this vote
/// is gathers QuorumCertificate (see the detailed explanation in the comments of `LedgerInfo`).
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
pub struct Vote {
    /// The data of the vote
    vote_data: VoteData,
    /// The identity of the voter.
    author: Author,
    /// LedgerInfo of a block that is going to be committed in case this vote gathers QC.
    ledger_info: LedgerInfo,
    /// Signature of the LedgerInfo
    signature: Signature,
    /// The round signatures can be aggregated into a timeout certificate if present.
    timeout_signature: Option<Signature>,
}

impl Display for Vote {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        write!(
            f,
            "Vote: [vote data: {}, author: {}, is_timeout: {}, {}]",
            self.vote_data,
            self.author.short_str(),
            self.is_timeout(),
            self.ledger_info
        )
    }
}

impl Vote {
    /// Generates a new Vote corresponding to the "fast-vote" path without the round signatures
    /// that can be aggregated into a timeout certificate
    pub fn new(
        vote_data: VoteData,
        author: Author,
        mut ledger_info_placeholder: LedgerInfo,
        validator_signer: &ValidatorSigner,
    ) -> Self {
        ledger_info_placeholder.set_consensus_data_hash(vote_data.hash());
        let li_sig = validator_signer
            .sign_message(ledger_info_placeholder.hash())
            .expect("Failed to sign LedgerInfo");
        Self {
            vote_data,
            author,
            ledger_info: ledger_info_placeholder,
            signature: li_sig.into(),
            timeout_signature: None,
        }
    }

    /// Generates a round signature, which can then be used for aggregating a timeout certificate.
    /// Typically called for generating vote messages that are sent upon timeouts.
    pub fn add_timeout_signature(&mut self, signature: Signature) {
        if self.timeout_signature.is_some() {
            return; // round signature is already set
        }

        self.timeout_signature.replace(signature);
    }

    pub fn vote_data(&self) -> &VoteData {
        &self.vote_data
    }

    /// Return the author of the vote
    pub fn author(&self) -> Author {
        self.author
    }

    /// Return the LedgerInfo associated with this vote
    pub fn ledger_info(&self) -> &LedgerInfo {
        &self.ledger_info
    }

    /// Return the signature of the vote
    pub fn signature(&self) -> &Signature {
        &self.signature
    }

    /// Returns the hash of the data represent by a timeout proposal
    pub fn timeout(&self) -> Timeout {
        Timeout::new(
            self.vote_data().proposed().epoch(),
            self.vote_data().proposed().round(),
        )
    }

    /// Return the epoch of the vote
    pub fn epoch(&self) -> u64 {
        self.vote_data.proposed().epoch()
    }

    /// Returns the signature for the vote_data().proposed().round() that can be aggregated for
    /// TimeoutCertificate.
    pub fn timeout_signature(&self) -> Option<&Signature> {
        self.timeout_signature.as_ref()
    }

    /// The vote message is considered a timeout vote message if it carries a signature on the
    /// round, which can then be used for aggregating it to the TimeoutCertificate.
    pub fn is_timeout(&self) -> bool {
        self.timeout_signature.is_some()
    }

    /// Verifies that the consensus data hash of LedgerInfo corresponds to the vote info,
    /// and then verifies the signature.
    pub fn verify(&self, validator: &ValidatorVerifier) -> anyhow::Result<()> {
        ensure!(
            self.ledger_info.consensus_data_hash() == self.vote_data.hash(),
            "Vote's hash mismatch with LedgerInfo"
        );
        self.signature()
            .verify(validator, self.author(), self.ledger_info.hash())
            .context("Fail to verify Vote")?;
        if let Some(timeout_signature) = &self.timeout_signature {
            timeout_signature
                .verify(validator, self.author(), self.timeout().hash())
                .context("Fail to verify Timeout Vote")?;
        }
        Ok(())
    }
}
<|endoftext|>