<commit_before>/*! Precise Garbage Collector

The precise GC exposes two functions, gc and
cleanup_stack_for_failure. The gc function is the entry point to the
garbage collector itself. The cleanup_stack_for_failure is the entry
point for GC-based cleanup.

Precise GC depends on changes to LLVM's GC which add support for
automatic rooting and addrspace-based metadata marking. Rather than
explicitly rooting pointers with LLVM's gcroot intrinsic, the GC
merely creates allocas for pointers, and allows an LLVM pass to
automatically infer roots based on the allocas present in a function
(and live at a given location). The compiler communicates the type of
the pointer to LLVM by setting the addrspace of the pointer type. The
compiler then emits a map from addrspace to tydesc, which LLVM then
uses to match pointers with their tydesc. The GC reads the metadata
table produced by LLVM, and uses it to determine which glue functions
to call to free objects on their respective heaps.

GC-based cleanup is a replacement for landing pads which relies on the
GC infrastructure to find pointers on the stack to cleanup. Whereas
the normal GC needs to walk task-local heap allocations, the cleanup
code needs to walk exchange heap allocations and stack-allocations
with destructors.

*/

import stackwalk::Word;
import libc::size_t;
import libc::uintptr_t;
import send_map::linear::LinearMap;

export Word;
export gc;
export cleanup_stack_for_failure;

// Mirrors rust_stack.h stk_seg
struct StackSegment {
    let prev: *StackSegment;
    let next: *StackSegment;
    let end: uintptr_t;
    // And other fields which we don't care about...
}

extern mod rustrt {
    fn rust_annihilate_box(ptr: *Word);

    #[rust_stack]
    fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);

    #[rust_stack]
    fn rust_gc_metadata() -> *Word;

    fn rust_get_stack_segment() -> *StackSegment;
}

unsafe fn bump<T, U>(ptr: *T, count: uint) -> *U {
    return unsafe::reinterpret_cast(&ptr::offset(ptr, count));
}

unsafe fn align_to_pointer<T>(ptr: *T) -> *T {
    let align = sys::min_align_of::<*T>();
    let ptr: uint = unsafe::reinterpret_cast(&ptr);
    let ptr = (ptr + (align - 1)) & -align;
    return unsafe::reinterpret_cast(&ptr);
}

type SafePoint = { sp_meta: *Word, fn_meta: *Word };

// Returns the safe point metadata for the given program counter, if
// any.
unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
    let module_meta = rustrt::rust_gc_metadata();
    let num_safe_points = *module_meta;
    let safe_points: *Word = bump(module_meta, 1);

    if ptr::is_null(pc) {
        return None;
    }

    // FIXME (#2997): Use binary rather than linear search.
    let mut spi = 0;
    while spi < num_safe_points {
        let sp: **Word = bump(safe_points, spi*3);
        let sp_loc = *sp;
        if sp_loc == pc {
            return Some({sp_meta: *bump(sp, 1), fn_meta: *bump(sp, 2)});
        }
        spi += 1;
    }
    return None;
}

type Visitor = fn(root: **Word, tydesc: *Word) -> bool;

// Walks the list of roots for the given safe point, and calls visitor
// on each root.
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
    let fp_bytes: *u8 = unsafe::reinterpret_cast(&fp);
    let sp_meta: *u32 = unsafe::reinterpret_cast(&sp.sp_meta);

    let num_stack_roots = *sp_meta as uint;
    let num_reg_roots = *ptr::offset(sp_meta, 1) as uint;

    let stack_roots: *u32 = bump(sp_meta, 2);
    let reg_roots: *u8 = bump(stack_roots, num_stack_roots);
    let addrspaces: *Word = align_to_pointer(bump(reg_roots, num_reg_roots));
    let tydescs: ***Word = bump(addrspaces, num_stack_roots);

    // Stack roots
    let mut sri = 0;
    while sri < num_stack_roots {
        if *ptr::offset(addrspaces, sri) >= 1 {
            let root =
                ptr::offset(fp_bytes, *ptr::offset(stack_roots, sri) as Word)
                as **Word;
            let tydescpp = ptr::offset(tydescs, sri);
            let tydesc = if ptr::is_not_null(tydescpp) &&
                ptr::is_not_null(*tydescpp) {
                **tydescpp
            } else {
                ptr::null()
            };
            if !visitor(root, tydesc) { return; }
        }
        sri += 1;
    }

    // Register roots
    let mut rri = 0;
    while rri < num_reg_roots {
        if *ptr::offset(addrspaces, num_stack_roots + rri) == 1 {
            // FIXME(#2997): Need to find callee saved registers on the stack.
        }
        rri += 1;
    }
}

// Is fp contained in segment?
unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool {
    let begin: Word = unsafe::reinterpret_cast(&segment);
    let end: Word = unsafe::reinterpret_cast(&(*segment).end);
    let frame: Word = unsafe::reinterpret_cast(&fp);

    return begin <= frame && frame <= end;
}

// Find and return the segment containing the given frame pointer. At
// stack segment boundaries, returns true for boundary, so that the
// caller can do any special handling to identify where the correct
// return address is in the stack frame.
unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment)
    -> {segment: *StackSegment, boundary: bool} {
    // Check if frame is in either current frame or previous frame.
    let in_segment = is_frame_in_segment(fp, segment);
    let in_prev_segment = ptr::is_not_null((*segment).prev) &&
        is_frame_in_segment(fp, (*segment).prev);

    // If frame is not in either segment, walk down segment list until
    // we find the segment containing this frame.
    if !in_segment && !in_prev_segment {
        let mut segment = segment;
        while ptr::is_not_null((*segment).next) &&
            is_frame_in_segment(fp, (*segment).next) {
            segment = (*segment).next;
        }
        return {segment: segment, boundary: false};
    }

    // If frame is in previous frame, then we're at a boundary.
    if !in_segment && in_prev_segment {
        return {segment: (*segment).prev, boundary: true};
    }

    // Otherwise, we're somewhere on the inside of the frame.
    return {segment: segment, boundary: false};
}

type Memory = uint;

const task_local_heap: Memory = 1;
const exchange_heap:   Memory = 2;
const stack:           Memory = 4;

const need_cleanup:    Memory = exchange_heap | stack;

// Walks stack, searching for roots of the requested type, and passes
// each root to the visitor.
unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
    let mut segment = rustrt::rust_get_stack_segment();
    let mut last_ret: *Word = ptr::null();
    // To avoid collecting memory used by the GC itself, skip stack
    // frames until past the root GC stack frame. The root GC stack
    // frame is marked by a sentinel, which is a box pointer stored on
    // the stack.
    let mut reached_sentinel = ptr::is_null(sentinel);
    for stackwalk::walk_stack |frame| {
        unsafe {
            let pc = last_ret;
            let {segment: next_segment, boundary: boundary} =
                find_segment_for_frame(frame.fp, segment);
            segment = next_segment;
            // Each stack segment is bounded by a morestack frame. The
            // morestack frame includes two return addresses, one for
            // morestack itself, at the normal offset from the frame
            // pointer, and then a second return address for the
            // function prologue (which called morestack after
            // determining that it had hit the end of the stack).
            // Since morestack itself takes two parameters, the offset
            // for this second return address is 3 greater than the
            // return address for morestack.
            let ret_offset = if boundary { 4 } else { 1 };
            last_ret = *ptr::offset(frame.fp, ret_offset) as *Word;

            if ptr::is_null(pc) {
                again;
            }

            let mut delay_reached_sentinel = reached_sentinel;
            let sp = is_safe_point(pc);
            match sp {
              Some(sp_info) => {
                for walk_safe_point(frame.fp, sp_info) |root, tydesc| {
                    // Skip roots until we see the sentinel.
                    if !reached_sentinel {
                        if root == sentinel {
                            delay_reached_sentinel = true;
                        }
                        again;
                    }

                    // Skip null pointers, which can occur when a
                    // unique pointer has already been freed.
                    if ptr::is_null(*root) {
                        again;
                    }

                    if ptr::is_null(tydesc) {
                        // Root is a generic box.
                        let refcount = **root;
                        if mem | task_local_heap != 0 && refcount != -1 {
                            if !visitor(root, tydesc) { return; }
                        } else if mem | exchange_heap != 0 && refcount == -1 {
                            if !visitor(root, tydesc) { return; }
                        }
                    } else {
                        // Root is a non-immediate.
                        if mem | stack != 0 {
                            if !visitor(root, tydesc) { return; }
                        }
                    }
                }
              }
              None => ()
            }
            reached_sentinel = delay_reached_sentinel;
        }
    }
}

fn gc() {
    unsafe {
        for walk_gc_roots(task_local_heap, ptr::null()) |_root, _tydesc| {
            // FIXME(#2997): Walk roots and mark them.
            io::stdout().write([46]); // .
        }
    }
}

type RootSet = LinearMap<*Word,()>;

fn RootSet() -> RootSet {
    LinearMap()
}

#[cfg(gc)]
fn expect_sentinel() -> bool { true }

#[cfg(nogc)]
fn expect_sentinel() -> bool { false }

// Entry point for GC-based cleanup. Walks stack looking for exchange
// heap and stack allocations requiring drop, and runs all
// destructors.
//
// This should only be called from fail, as it will drop the roots
// which are *live* on the stack, rather than dropping those that are
// dead.
fn cleanup_stack_for_failure() {
    unsafe {
        // Leave a sentinel on the stack to mark the current frame. The
        // stack walker will ignore any frames above the sentinel, thus
        // avoiding collecting any memory being used by the stack walker
        // itself.
        //
        // However, when core itself is not compiled with GC, then none of
        // the functions in core will have GC metadata, which means we
        // won't be able to find the sentinel root on the stack. In this
        // case, we can safely skip the sentinel since we won't find our
        // own stack roots on the stack anyway.
        let sentinel_box = ~0;
        let sentinel: **Word = if expect_sentinel() {
            unsafe::reinterpret_cast(&ptr::addr_of(sentinel_box))
        } else {
            ptr::null()
        };

        let mut roots = ~RootSet();
        for walk_gc_roots(need_cleanup, sentinel) |root, tydesc| {
            // Track roots to avoid double frees.
            if option::is_some(roots.find(&*root)) {
                again;
            }
            roots.insert(*root, ());

            if ptr::is_null(tydesc) {
                rustrt::rust_annihilate_box(*root);
            } else {
                rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t);
            }
        }
    }
}
<commit_msg>gc: Add early abort when GC is disabled.<commit_after>/*! Precise Garbage Collector

The precise GC exposes two functions, gc and
cleanup_stack_for_failure. The gc function is the entry point to the
garbage collector itself. The cleanup_stack_for_failure is the entry
point for GC-based cleanup.

Precise GC depends on changes to LLVM's GC which add support for
automatic rooting and addrspace-based metadata marking. Rather than
explicitly rooting pointers with LLVM's gcroot intrinsic, the GC
merely creates allocas for pointers, and allows an LLVM pass to
automatically infer roots based on the allocas present in a function
(and live at a given location). The compiler communicates the type of
the pointer to LLVM by setting the addrspace of the pointer type. The
compiler then emits a map from addrspace to tydesc, which LLVM then
uses to match pointers with their tydesc. The GC reads the metadata
table produced by LLVM, and uses it to determine which glue functions
to call to free objects on their respective heaps.

GC-based cleanup is a replacement for landing pads which relies on the
GC infrastructure to find pointers on the stack to cleanup. Whereas
the normal GC needs to walk task-local heap allocations, the cleanup
code needs to walk exchange heap allocations and stack-allocations
with destructors.

*/

import stackwalk::Word;
import libc::size_t;
import libc::uintptr_t;
import send_map::linear::LinearMap;

export Word;
export gc;
export cleanup_stack_for_failure;

// Mirrors rust_stack.h stk_seg
struct StackSegment {
    let prev: *StackSegment;
    let next: *StackSegment;
    let end: uintptr_t;
    // And other fields which we don't care about...
}

extern mod rustrt {
    fn rust_annihilate_box(ptr: *Word);

    #[rust_stack]
    fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);

    #[rust_stack]
    fn rust_gc_metadata() -> *Word;

    fn rust_get_stack_segment() -> *StackSegment;
}

unsafe fn bump<T, U>(ptr: *T, count: uint) -> *U {
    return unsafe::reinterpret_cast(&ptr::offset(ptr, count));
}

unsafe fn align_to_pointer<T>(ptr: *T) -> *T {
    let align = sys::min_align_of::<*T>();
    let ptr: uint = unsafe::reinterpret_cast(&ptr);
    let ptr = (ptr + (align - 1)) & -align;
    return unsafe::reinterpret_cast(&ptr);
}

unsafe fn get_safe_point_count() -> uint {
    let module_meta = rustrt::rust_gc_metadata();
    return *module_meta;
}

type SafePoint = { sp_meta: *Word, fn_meta: *Word };

// Returns the safe point metadata for the given program counter, if
// any.
unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
    let module_meta = rustrt::rust_gc_metadata();
    let num_safe_points = *module_meta;
    let safe_points: *Word = bump(module_meta, 1);

    if ptr::is_null(pc) {
        return None;
    }

    // FIXME (#2997): Use binary rather than linear search.
    let mut spi = 0;
    while spi < num_safe_points {
        let sp: **Word = bump(safe_points, spi*3);
        let sp_loc = *sp;
        if sp_loc == pc {
            return Some({sp_meta: *bump(sp, 1), fn_meta: *bump(sp, 2)});
        }
        spi += 1;
    }
    return None;
}

type Visitor = fn(root: **Word, tydesc: *Word) -> bool;

// Walks the list of roots for the given safe point, and calls visitor
// on each root.
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
    let fp_bytes: *u8 = unsafe::reinterpret_cast(&fp);
    let sp_meta: *u32 = unsafe::reinterpret_cast(&sp.sp_meta);

    let num_stack_roots = *sp_meta as uint;
    let num_reg_roots = *ptr::offset(sp_meta, 1) as uint;

    let stack_roots: *u32 = bump(sp_meta, 2);
    let reg_roots: *u8 = bump(stack_roots, num_stack_roots);
    let addrspaces: *Word = align_to_pointer(bump(reg_roots, num_reg_roots));
    let tydescs: ***Word = bump(addrspaces, num_stack_roots);

    // Stack roots
    let mut sri = 0;
    while sri < num_stack_roots {
        if *ptr::offset(addrspaces, sri) >= 1 {
            let root =
                ptr::offset(fp_bytes, *ptr::offset(stack_roots, sri) as Word)
                as **Word;
            let tydescpp = ptr::offset(tydescs, sri);
            let tydesc = if ptr::is_not_null(tydescpp) &&
                ptr::is_not_null(*tydescpp) {
                **tydescpp
            } else {
                ptr::null()
            };
            if !visitor(root, tydesc) { return; }
        }
        sri += 1;
    }

    // Register roots
    let mut rri = 0;
    while rri < num_reg_roots {
        if *ptr::offset(addrspaces, num_stack_roots + rri) == 1 {
            // FIXME(#2997): Need to find callee saved registers on the stack.
        }
        rri += 1;
    }
}

// Is fp contained in segment?
unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool {
    let begin: Word = unsafe::reinterpret_cast(&segment);
    let end: Word = unsafe::reinterpret_cast(&(*segment).end);
    let frame: Word = unsafe::reinterpret_cast(&fp);

    return begin <= frame && frame <= end;
}

// Find and return the segment containing the given frame pointer. At
// stack segment boundaries, returns true for boundary, so that the
// caller can do any special handling to identify where the correct
// return address is in the stack frame.
unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment)
    -> {segment: *StackSegment, boundary: bool} {
    // Check if frame is in either current frame or previous frame.
    let in_segment = is_frame_in_segment(fp, segment);
    let in_prev_segment = ptr::is_not_null((*segment).prev) &&
        is_frame_in_segment(fp, (*segment).prev);

    // If frame is not in either segment, walk down segment list until
    // we find the segment containing this frame.
    if !in_segment && !in_prev_segment {
        let mut segment = segment;
        while ptr::is_not_null((*segment).next) &&
            is_frame_in_segment(fp, (*segment).next) {
            segment = (*segment).next;
        }
        return {segment: segment, boundary: false};
    }

    // If frame is in previous frame, then we're at a boundary.
    if !in_segment && in_prev_segment {
        return {segment: (*segment).prev, boundary: true};
    }

    // Otherwise, we're somewhere on the inside of the frame.
    return {segment: segment, boundary: false};
}

type Memory = uint;

const task_local_heap: Memory = 1;
const exchange_heap:   Memory = 2;
const stack:           Memory = 4;

const need_cleanup:    Memory = exchange_heap | stack;

// Walks stack, searching for roots of the requested type, and passes
// each root to the visitor.
unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
    let mut segment = rustrt::rust_get_stack_segment();
    let mut last_ret: *Word = ptr::null();
    // To avoid collecting memory used by the GC itself, skip stack
    // frames until past the root GC stack frame. The root GC stack
    // frame is marked by a sentinel, which is a box pointer stored on
    // the stack.
    let mut reached_sentinel = ptr::is_null(sentinel);
    for stackwalk::walk_stack |frame| {
        unsafe {
            let pc = last_ret;
            let {segment: next_segment, boundary: boundary} =
                find_segment_for_frame(frame.fp, segment);
            segment = next_segment;
            // Each stack segment is bounded by a morestack frame. The
            // morestack frame includes two return addresses, one for
            // morestack itself, at the normal offset from the frame
            // pointer, and then a second return address for the
            // function prologue (which called morestack after
            // determining that it had hit the end of the stack).
            // Since morestack itself takes two parameters, the offset
            // for this second return address is 3 greater than the
            // return address for morestack.
            let ret_offset = if boundary { 4 } else { 1 };
            last_ret = *ptr::offset(frame.fp, ret_offset) as *Word;

            if ptr::is_null(pc) {
                again;
            }

            let mut delay_reached_sentinel = reached_sentinel;
            let sp = is_safe_point(pc);
            match sp {
              Some(sp_info) => {
                for walk_safe_point(frame.fp, sp_info) |root, tydesc| {
                    // Skip roots until we see the sentinel.
                    if !reached_sentinel {
                        if root == sentinel {
                            delay_reached_sentinel = true;
                        }
                        again;
                    }

                    // Skip null pointers, which can occur when a
                    // unique pointer has already been freed.
                    if ptr::is_null(*root) {
                        again;
                    }

                    if ptr::is_null(tydesc) {
                        // Root is a generic box.
                        let refcount = **root;
                        if mem | task_local_heap != 0 && refcount != -1 {
                            if !visitor(root, tydesc) { return; }
                        } else if mem | exchange_heap != 0 && refcount == -1 {
                            if !visitor(root, tydesc) { return; }
                        }
                    } else {
                        // Root is a non-immediate.
                        if mem | stack != 0 {
                            if !visitor(root, tydesc) { return; }
                        }
                    }
                }
              }
              None => ()
            }
            reached_sentinel = delay_reached_sentinel;
        }
    }
}

fn gc() {
    unsafe {
        // Abort when GC is disabled.
        if get_safe_point_count() == 0 {
            return;
        }

        for walk_gc_roots(task_local_heap, ptr::null()) |_root, _tydesc| {
            // FIXME(#2997): Walk roots and mark them.
            io::stdout().write([46]); // .
        }
    }
}

type RootSet = LinearMap<*Word,()>;

fn RootSet() -> RootSet {
    LinearMap()
}

#[cfg(gc)]
fn expect_sentinel() -> bool { true }

#[cfg(nogc)]
fn expect_sentinel() -> bool { false }

// Entry point for GC-based cleanup. Walks stack looking for exchange
// heap and stack allocations requiring drop, and runs all
// destructors.
//
// This should only be called from fail, as it will drop the roots
// which are *live* on the stack, rather than dropping those that are
// dead.
fn cleanup_stack_for_failure() {
    unsafe {
        // Abort when GC is disabled.
        if get_safe_point_count() == 0 {
            return;
        }

        // Leave a sentinel on the stack to mark the current frame. The
        // stack walker will ignore any frames above the sentinel, thus
        // avoiding collecting any memory being used by the stack walker
        // itself.
        //
        // However, when core itself is not compiled with GC, then none of
        // the functions in core will have GC metadata, which means we
        // won't be able to find the sentinel root on the stack. In this
        // case, we can safely skip the sentinel since we won't find our
        // own stack roots on the stack anyway.
        let sentinel_box = ~0;
        let sentinel: **Word = if expect_sentinel() {
            unsafe::reinterpret_cast(&ptr::addr_of(sentinel_box))
        } else {
            ptr::null()
        };

        let mut roots = ~RootSet();
        for walk_gc_roots(need_cleanup, sentinel) |root, tydesc| {
            // Track roots to avoid double frees.
            if option::is_some(roots.find(&*root)) {
                again;
            }
            roots.insert(*root, ());

            if ptr::is_null(tydesc) {
                rustrt::rust_annihilate_box(*root);
            } else {
                rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t);
            }
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(missing_doc)]
#![allow(unused_variable)]

use device::ProgramHandle;
use mesh::{Mesh, Slice};
use shade::{ParameterError, ShaderParam};
use state::DrawState;

/*-----------
Needs:
* light-weight, POD
* self-contained, safe
Variants:
1. Include ProgramInfo (pod?)
    1. Make it POD (how?)
    2. Clone instead (no pod)
2. Share ProgramInfo (no safe?)
    - context is required
    - also share the mesh
-----------*/

struct MeshLink;    //TODO

#[deriving(Clone, Show)]
pub enum BatchError {
    ErrorParameters(ParameterError),
}

pub trait Batch {
    fn get_data(&self) -> (&Mesh, Slice, &ProgramHandle, &DrawState);
    fn fill_params(&self, ::shade::ParamValues);
}

/// Heavy Batch - self-contained
pub struct HeavyBatch<L, T> {
    mesh: Mesh,
    mesh_link: MeshLink,
    pub slice: Slice,
    program: ProgramHandle,
    param: T,
    param_link: L,
    pub state: DrawState,
}

impl<L, T: ShaderParam<L>> HeavyBatch<L, T> {
    fn new(mesh: Mesh, program: ProgramHandle, param: T, state: DrawState)
           -> Result<HeavyBatch<L, T>, BatchError> {
        let slice = mesh.get_slice(::device::TriangleList);
        let link = match ShaderParam::create_link(None::<T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(ErrorParameters(e))
        };
        Ok(HeavyBatch {
            mesh: mesh,
            mesh_link: MeshLink,
            slice: slice,
            program: program,
            param: param,
            param_link: link,
            state: DrawState::new(),
        })
    }
}

impl<'a, L, T: ShaderParam<L>> Batch for &'a HeavyBatch<L, T> {
    fn get_data(&self) -> (&Mesh, Slice, &ProgramHandle, &DrawState) {
        (&self.mesh, self.slice, &self.program, &self.state)
    }

    fn fill_params(&self, values: ::shade::ParamValues) {
        self.param.fill_params(&self.param_link, values);
    }
}

type Index = u16;
struct Id<T>(Index);
struct Array<T> {
    data: Vec<T>,
    generation: u16,
}

impl<T> Array<T> {
    fn new() -> Array<T> {
        Array {
            data: Vec::new(),
            generation: 0,
        }
    }

    fn get(&self, id: Id<T>) -> &T {
        let Id(i) = id;
        &self.data[i as uint]
    }
}

impl<T: Clone + PartialEq> Array<T> {
    fn find_or_insert(&mut self, value: &T) -> Id<T> {
        let i = self.data.iter().position(|v| v == value).unwrap_or_else(|| {
            let i = self.data.len();
            self.data.push(value.clone());
            i
        });
        Id(i as Index)
    }
}

/// Light Batch - copyable and smaller
pub struct LightBatch<L, T> {
    mesh_id: Id<Mesh>,
    mesh_link: MeshLink,
    slice: Slice,
    program_id: Id<ProgramHandle>,
    param_link: L,
    state_id: Id<DrawState>,
}

/// Factory of light batches
pub struct Context {
    meshes: Array<Mesh>,
    programs: Array<ProgramHandle>,
    states: Array<DrawState>,
}

impl Context {
    pub fn new() -> Context {
        Context {
            meshes: Array::new(),
            programs: Array::new(),
            states: Array::new(),
        }
    }
}

impl Context {
    pub fn batch<L, T: ShaderParam<L>>(&mut self, mesh: &Mesh, slice: Slice,
                program: &ProgramHandle, state: &DrawState)
                -> Result<LightBatch<L, T>, BatchError> {
        let link = match ShaderParam::create_link(None::<T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(ErrorParameters(e))
        };
        Ok(LightBatch {
            mesh_id: self.meshes.find_or_insert(mesh),
            mesh_link: MeshLink,
            slice: slice,
            program_id: self.programs.find_or_insert(program),
            param_link: link,
            state_id: self.states.find_or_insert(state),
        })
    }
}

impl<'a, L, T: ShaderParam<L>> Batch for (&'a LightBatch<L, T>, &'a T, &'a Context) {
    fn get_data(&self) -> (&Mesh, Slice, &ProgramHandle, &DrawState) {
        let (b, _, ctx) = *self;
        (ctx.meshes.get(b.mesh_id),
        b.slice,
        ctx.programs.get(b.program_id),
        ctx.states.get(b.state_id))
    }

    fn fill_params(&self, values: ::shade::ParamValues) {
        let (b, data, _) = *self;
        data.fill_params(&b.param_link, values);
    }
}
<commit_msg>Added docs, cleaned up the code<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Batches are structures containing all the data required for the draw call,
//! except for the target frame. Here we define the `Batch` trait as well as
//! `LightBatch` and `HeavyBatch` implementations.

use device::ProgramHandle;
use mesh::{Mesh, Slice};
use shade::{ParameterError, ShaderParam};
use state::DrawState;

struct MeshLink; //TODO

/// An error occurring at batch creation
#[deriving(Clone, Show)]
pub enum BatchError {
    /// Error connecting shader parameters
    ErrorParameters(ParameterError),
}

/// Abstract batch trait
pub trait Batch {
    /// Obtain information about the mesh, program, and state
    fn get_data(&self) -> (&Mesh, Slice, &ProgramHandle, &DrawState);
    /// Fill shader parameter values
    fn fill_params(&self, ::shade::ParamValues);
}

/// Heavy Batch - self-contained, but has heap-allocated data
pub struct HeavyBatch<L, T> {
    mesh: Mesh,
    mesh_link: MeshLink,
    /// Mesh slice
    pub slice: Slice,
    program: ProgramHandle,
    param: T,
    param_link: L,
    /// Draw state
    pub state: DrawState,
}

impl<L, T: ShaderParam<L>> HeavyBatch<L, T> {
    /// Create a new heavy batch
    pub fn new(mesh: Mesh, program: ProgramHandle, param: T)
           -> Result<HeavyBatch<L, T>, BatchError> {
        let slice = mesh.get_slice(::device::TriangleList);
        let link = match ShaderParam::create_link(None::<T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(ErrorParameters(e))
        };
        Ok(HeavyBatch {
            mesh: mesh,
            mesh_link: MeshLink,
            slice: slice,
            program: program,
            param: param,
            param_link: link,
            state: DrawState::new(),
        })
    }
}

impl<'a, L, T: ShaderParam<L>> Batch for &'a HeavyBatch<L, T> {
    fn get_data(&self) -> (&Mesh, Slice, &ProgramHandle, &DrawState) {
        (&self.mesh, self.slice, &self.program, &self.state)
    }

    fn fill_params(&self, values: ::shade::ParamValues) {
        self.param.fill_params(&self.param_link, values);
    }
}

type Index = u16;

struct Id<T>(Index);

struct Array<T> {
    data: Vec<T>,
    //generation: u16,
}

impl<T> Array<T> {
    fn new() -> Array<T> {
        Array {
            data: Vec::new(),
            //generation: 0,
        }
    }

    fn get(&self, id: Id<T>) -> &T {
        let Id(i) = id;
        &self.data[i as uint]
    }
}

impl<T: Clone + PartialEq> Array<T> {
    fn find_or_insert(&mut self, value: &T) -> Id<T> {
        let i = self.data.iter().position(|v| v == value).unwrap_or_else(|| {
            let i = self.data.len();
            self.data.push(value.clone());
            i
        });
        Id(i as Index)
    }
}

/// Light Batch - copyable and smaller, but depends on the Context
pub struct LightBatch<L, T> {
    mesh_id: Id<Mesh>,
    mesh_link: MeshLink,
    slice: Slice,
    program_id: Id<ProgramHandle>,
    param_link: L,
    state_id: Id<DrawState>,
}

/// Factory of light batches, requires to always be used with them.
pub struct Context {
    meshes: Array<Mesh>,
    programs: Array<ProgramHandle>,
    states: Array<DrawState>,
}

impl Context {
    /// Create a new empty Context
    pub fn new() -> Context {
        Context {
            meshes: Array::new(),
            programs: Array::new(),
            states: Array::new(),
        }
    }
}

impl Context {
    /// Produce a new light batch
    pub fn batch<L, T: ShaderParam<L>>(&mut self, mesh: &Mesh, slice: Slice,
                program: &ProgramHandle, state: &DrawState)
                -> Result<LightBatch<L, T>, BatchError> {
        let link = match ShaderParam::create_link(None::<T>, program.get_info()) {
            Ok(l) => l,
            Err(e) => return Err(ErrorParameters(e))
        };
        Ok(LightBatch {
            mesh_id: self.meshes.find_or_insert(mesh),
            mesh_link: MeshLink,
            slice: slice,
            program_id: self.programs.find_or_insert(program),
            param_link: link,
            state_id: self.states.find_or_insert(state),
        })
    }
}

impl<'a, L, T: ShaderParam<L>> Batch for (&'a LightBatch<L, T>, &'a T, &'a Context) {
    fn get_data(&self) -> (&Mesh, Slice, &ProgramHandle, &DrawState) {
        let (b, _, ctx) = *self;
        (ctx.meshes.get(b.mesh_id),
        b.slice,
        ctx.programs.get(b.program_id),
        ctx.states.get(b.state_id))
    }

    fn fill_params(&self, values: ::shade::ParamValues) {
        let (b, data, _) = *self;
        data.fill_params(&b.param_link, values);
    }
}
<|endoftext|>


<commit_before>use std::f32::consts;

use na::Vector2;

use {Point2f, Vector3f};

mod distribution1d;
mod distribution2d;

pub use self::distribution1d::Distribution1D;
pub use self::distribution2d::Distribution2D;

const FRAC_PI_4: f32 = consts::FRAC_PI_2 / 2.0;

// Inline functions
pub fn uniform_sample_sphere(u: &Point2f) -> Vector3f {
    let z = 1.0 - 2.0 * u.x;
    let r = (1.0 - z * z).max(0.0).sqrt();
    let phi = 2.0 * consts::PI * u.y;

    Vector3f::new(r * phi.cos(), r * phi.sin(), z)
}

pub fn cosine_sample_hemisphere(u: &Point2f) -> Vector3f {
    let d = concentric_sample_disk(u);
    let z = (1.0 - d.x * d.x - d.y * d.y).max(0.0).sqrt();
    Vector3f::new(d.x, d.y, z)
}

pub fn concentric_sample_disk(u: &Point2f) -> Point2f {
    let u_offset = 2.0 * *u - Vector2::<f32>::new(1.0, 1.0);
    if u_offset.x == 0.0 && u_offset.y == 0.0 {
        return Point2f::new(0.0, 0.0);
    }

    let (r, theta) = if u_offset.x.abs() > u_offset.y.abs() {
        (u_offset.x, FRAC_PI_4 * (u_offset.y / u_offset.x))
    } else {
        (u_offset.y, consts::FRAC_PI_2 - FRAC_PI_4 * (u_offset.x / u_offset.y))
    };
    r * Point2f::new(theta.cos(), theta.sin())
}

#[inline]
pub fn power_heuristic(nf: u32, f_pdf: f32, ng: u32, g_pdf: f32) -> f32 {
    let f = nf as f32 * f_pdf;
    let g = ng as f32 * g_pdf;
    (f * f) / (f * f + g * g)
}

// pub fn zero_two_sequence(n: u32, scramble: (u32, u32)) -> (f32, f32) {
//     (van_der_corput(n, scramble.0), sobol(n, scramble.1))
// }
<commit_msg>Add uniform_sample_triangle<commit_after>use std::f32::consts;

use na::Vector2;

use {Point2f, Vector3f};

mod distribution1d;
mod distribution2d;

pub use self::distribution1d::Distribution1D;
pub use self::distribution2d::Distribution2D;

const FRAC_PI_4: f32 = consts::FRAC_PI_2 / 2.0;

// Inline functions
pub fn uniform_sample_sphere(u: &Point2f) -> Vector3f {
    let z = 1.0 - 2.0 * u.x;
    let r = (1.0 - z * z).max(0.0).sqrt();
    let phi = 2.0 * consts::PI * u.y;

    Vector3f::new(r * phi.cos(), r * phi.sin(), z)
}

pub fn cosine_sample_hemisphere(u: &Point2f) -> Vector3f {
    let d = concentric_sample_disk(u);
    let z = (1.0 - d.x * d.x - d.y * d.y).max(0.0).sqrt();
    Vector3f::new(d.x, d.y, z)
}

pub fn concentric_sample_disk(u: &Point2f) -> Point2f {
    let u_offset = 2.0 * *u - Vector2::<f32>::new(1.0, 1.0);
    if u_offset.x == 0.0 && u_offset.y == 0.0 {
        return Point2f::new(0.0, 0.0);
    }

    let (r, theta) = if u_offset.x.abs() > u_offset.y.abs() {
        (u_offset.x, FRAC_PI_4 * (u_offset.y / u_offset.x))
    } else {
        (
            u_offset.y,
            consts::FRAC_PI_2 - FRAC_PI_4 * (u_offset.x / u_offset.y),
        )
    };
    r * Point2f::new(theta.cos(), theta.sin())
}

pub fn uniform_sample_triangle(u: &Point2f) -> Point2f {
    let su0 = u[0].sqrt();
    Point2f::new(1.0 - su0, u[1] * su0)
}

#[inline]
pub fn power_heuristic(nf: u32, f_pdf: f32, ng: u32, g_pdf: f32) -> f32 {
    let f = nf as f32 * f_pdf;
    let g = ng as f32 * g_pdf;
    (f * f) / (f * f + g * g)
}

// pub fn zero_two_sequence(n: u32, scramble: (u32, u32)) -> (f32, f32) {
//     (van_der_corput(n, scramble.0), sobol(n, scramble.1))
// }
<|endoftext|>


<commit_before>use common;
use math::{Ray, Vector3};
use shape::GeometricObject;
use std::f64;

pub struct Sphere {
    center: Vector3,
    radius: f64,
}

impl Sphere {
    pub fn new(c: Vector3, r: f64) -> Sphere {
        Sphere {
            center: c,
            radius: r,
        }
    }

    pub fn center(&self) -> Vector3 {
        self.center
    }

    pub fn radius(&self) -> f64 {
        self.radius
    }
}

impl GeometricObject for Sphere {
    fn hit(&self, ray: &Ray) -> (bool, f64) {
        let delta = ray.origin() - self.center;
        let a = ray.direction().length_square();
        let b = 2.0 * delta * ray.direction();
        let c = delta.length_square() - self.radius.powi(2);

        let discriminant = b.powi(2) - 4.0 * a * c;

        if discriminant < 0.0 {
            return (false, f64::MAX);
        } else {
            let denominator = 2.0 * a;
            let root = discriminant.sqrt();

            let t = (-b - root) / denominator;
            if t > common::EPSILON {
                return (true, t);
            }

            let t = (-b + root) / denominator;
            if t > common::EPSILON {
                return (true, t);
            }
        }

        (false, f64::MAX)
    }
}

#[cfg(test)]
mod test {
    use super::Sphere;
    use math::vector3;
    use math::{Ray, Vector3};
    use shape::GeometricObject;
    use std::f64;

    #[test]
    fn test_hit() {
        let sphere = Sphere::new(vector3::ZERO, 1.0);

        let ray1 = Ray::new(Vector3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
        let (hit1, t1) = sphere.hit(&ray1);
        assert!(!hit1);
        assert_eq!(t1, f64::MAX);

        let ray2 = Ray::new(vector3::ZERO, Vector3::new(0.0, 1.0, 0.0));
        let (hit2, t2) = sphere.hit(&ray2);
        assert!(hit2);
        assert_eq!(t2, 1.0);
   }
}<commit_msg>add more tests<commit_after>use common;
use math::{Ray, Vector3};
use shape::GeometricObject;
use std::f64;

pub struct Sphere {
    center: Vector3,
    radius: f64,
}

impl Sphere {
    pub fn new(c: Vector3, r: f64) -> Sphere {
        Sphere {
            center: c,
            radius: r,
        }
    }

    pub fn center(&self) -> Vector3 {
        self.center
    }

    pub fn radius(&self) -> f64 {
        self.radius
    }
}

impl GeometricObject for Sphere {
    fn hit(&self, ray: &Ray) -> (bool, f64) {
        let delta = ray.origin() - self.center;
        let a = ray.direction().length_square();
        let b = 2.0 * delta * ray.direction();
        let c = delta.length_square() - self.radius.powi(2);

        let discriminant = b.powi(2) - 4.0 * a * c;

        if discriminant < 0.0 {
            return (false, f64::MAX);
        } else {
            let denominator = 2.0 * a;
            let root = discriminant.sqrt();

            let t = (-b - root) / denominator;
            if t > common::EPSILON {
                return (true, t);
            }

            let t = (-b + root) / denominator;
            if t > common::EPSILON {
                return (true, t);
            }
        }

        (false, f64::MAX)
    }
}

#[cfg(test)]
mod test {
    use super::Sphere;
    use math::vector3;
    use math::{Ray, Vector3};
    use shape::GeometricObject;
    use std::f64;

    #[test]
    fn test_hit() {
        let sphere = Sphere::new(vector3::ZERO, 1.0);

        let ray1 = Ray::new(Vector3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
        let (hit1, t1) = sphere.hit(&ray1);
        assert!(!hit1);
        assert_eq!(t1, f64::MAX);

        let ray2 = Ray::new(vector3::ZERO, Vector3::new(0.0, 1.0, 0.0));
        let (hit2, t2) = sphere.hit(&ray2);
        assert!(hit2);
        assert_eq!(t2, 1.0);

        let ray3 = Ray::new(Vector3::new(0.0, -1.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
        let (hit3, t3) = sphere.hit(&ray3);
        assert!(hit3);
        assert_eq!(t2, 1.0);
   }
}<|endoftext|>


<commit_before>use std::f64;
use std::ops::Add;
use std::ops::AddAssign;
use std::ops::Sub;
use std::ops::SubAssign;
use std::ops::Div;
use std::ops::DivAssign;
use std::ops::Mul;
use std::ops::MulAssign;
use std::ops::Neg;

#[derive(Copy, Clone)]
pub struct Vec2 {
    pub x: f64,
    pub y: f64,
}

impl Vec2 {
    pub fn normal(self) -> Vec2 {
        self / self.magnitude()
    }

    pub fn magnitude(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

impl Add for Vec2 {
    type Output = Vec2;
    fn add(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

impl AddAssign for Vec2 {
    fn add_assign(&mut self, other: Vec2) {
        self.x += other.x;
        self.y += other.y;
    }
}

impl Sub for Vec2 {
    type Output = Vec2;
    fn sub(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

impl SubAssign for Vec2 {
    fn sub_assign(&mut self, other: Vec2) {
        self.x -= other.x;
        self.y -= other.y;
    }
}

impl Div for Vec2 {
    type Output = Vec2;
    fn div(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x / other.x,
            y: self.y / other.y,
        }
    }
}

impl DivAssign for Vec2 {
    fn div_assign(&mut self, other: Vec2) {
        self.x /= other.x;
        self.y /= other.y;
    }
}

impl Div<f64> for Vec2 {
    type Output = Vec2;
    fn div(self, other: f64) -> Vec2 {
        Vec2 {
            x: self.x / other,
            y: self.y / other,
        }
    }
}

impl DivAssign<f64> for Vec2 {
    fn div_assign(&mut self, other: f64) {
        self.x /= other;
        self.y /= other;
    }
}

impl Mul for Vec2 {
    type Output = Vec2;
    fn mul(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x * other.x,
            y: self.y * other.y,
        }
    }
}

impl MulAssign for Vec2 {
    fn mul_assign(&mut self, other: Vec2) {
        self.x *= other.x;
        self.y *= other.y;
    }
}

impl Mul<f64> for Vec2 {
    type Output = Vec2;
    fn mul(self, other: f64) -> Vec2 {
        Vec2 {
            x: self.x * other,
            y: self.y * other,
        }
    }
}

impl Mul<Vec2> for f64 {
    type Output = Vec2;
    fn mul(self, other: Vec2) -> Vec2 {
        other * self
    }
}

impl MulAssign<f64> for Vec2 {
    fn mul_assign(&mut self, other: f64) {
        self.x *= other;
        self.y *= other;
    }
}

impl Neg for Vec2 {
    fn neg(self) -> Vec2 {
        Vec2 {
            x: self.x * -1.0,
            y: self.y * -1.0,
        }
    }
}
<commit_msg>Fix the build<commit_after>use std::f64;
use std::ops::Add;
use std::ops::AddAssign;
use std::ops::Sub;
use std::ops::SubAssign;
use std::ops::Div;
use std::ops::DivAssign;
use std::ops::Mul;
use std::ops::MulAssign;
use std::ops::Neg;

#[derive(Copy, Clone)]
pub struct Vec2 {
    pub x: f64,
    pub y: f64,
}

impl Vec2 {
    pub fn normal(self) -> Vec2 {
        self / self.magnitude()
    }

    pub fn magnitude(&self) -> f64 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

impl Add for Vec2 {
    type Output = Vec2;
    fn add(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

impl AddAssign for Vec2 {
    fn add_assign(&mut self, other: Vec2) {
        self.x += other.x;
        self.y += other.y;
    }
}

impl Sub for Vec2 {
    type Output = Vec2;
    fn sub(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x - other.x,
            y: self.y - other.y,
        }
    }
}

impl SubAssign for Vec2 {
    fn sub_assign(&mut self, other: Vec2) {
        self.x -= other.x;
        self.y -= other.y;
    }
}

impl Div for Vec2 {
    type Output = Vec2;
    fn div(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x / other.x,
            y: self.y / other.y,
        }
    }
}

impl DivAssign for Vec2 {
    fn div_assign(&mut self, other: Vec2) {
        self.x /= other.x;
        self.y /= other.y;
    }
}

impl Div<f64> for Vec2 {
    type Output = Vec2;
    fn div(self, other: f64) -> Vec2 {
        Vec2 {
            x: self.x / other,
            y: self.y / other,
        }
    }
}

impl DivAssign<f64> for Vec2 {
    fn div_assign(&mut self, other: f64) {
        self.x /= other;
        self.y /= other;
    }
}

impl Mul for Vec2 {
    type Output = Vec2;
    fn mul(self, other: Vec2) -> Vec2 {
        Vec2 {
            x: self.x * other.x,
            y: self.y * other.y,
        }
    }
}

impl MulAssign for Vec2 {
    fn mul_assign(&mut self, other: Vec2) {
        self.x *= other.x;
        self.y *= other.y;
    }
}

impl Mul<f64> for Vec2 {
    type Output = Vec2;
    fn mul(self, other: f64) -> Vec2 {
        Vec2 {
            x: self.x * other,
            y: self.y * other,
        }
    }
}

impl Mul<Vec2> for f64 {
    type Output = Vec2;
    fn mul(self, other: Vec2) -> Vec2 {
        other * self
    }
}

impl MulAssign<f64> for Vec2 {
    fn mul_assign(&mut self, other: f64) {
        self.x *= other;
        self.y *= other;
    }
}

impl Neg for Vec2 {
    type Output = Vec2;
    fn neg(self) -> Vec2 {
        Vec2 {
            x: self.x * -1.0,
            y: self.y * -1.0,
        }
    }
}
<|endoftext|>


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

use crate::{aws::Aws, cluster::Cluster};
use failure::prelude::format_err;
use retry::{delay::Fixed, retry};
use rusoto_core::RusotoError;
use rusoto_ecr::{
    BatchGetImageRequest, DescribeImagesRequest, Ecr, Image, ImageIdentifier, PutImageError,
    PutImageRequest,
};
use rusoto_ecs::{Ecs, UpdateServiceRequest};
use slog_scope::{info, warn};
use std::{thread, time::Duration};

#[derive(Clone)]
pub struct DeploymentManager {
    aws: Aws,
    cluster: Cluster,
}

const VALIDATOR_IMAGE_REPO: &str = "libra_e2e";
const CLIENT_IMAGE_REPO: &str = "libra_client";
const FAUCET_IMAGE_REPO: &str = "libra_faucet";
pub const SOURCE_TAG: &str = "nightly";
pub const RUNNING_TAG: &str = "cluster_test";
pub const TESTED_TAG: &str = "nightly_tested";
const UPSTREAM_PREFIX: &str = "upstream_";

impl DeploymentManager {
    pub fn new(aws: Aws, cluster: Cluster) -> Self {
        Self { aws, cluster }
    }

    pub fn latest_hash_changed(&self) -> Option<String> {
        let hash = self.image_digest_by_tag(SOURCE_TAG);
        let last_tested = self.image_digest_by_tag(TESTED_TAG);
        if hash == last_tested {
            info!("Last deployed digest matches latest digest we expect, not doing redeploy");
            return None;
        }
        Some(hash)
    }

    pub fn redeploy(&mut self, hash: String) -> failure::Result<()> {
        info!("Will deploy with digest {}", hash);
        self.tag_image(
            VALIDATOR_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: Some(hash),
                image_tag: None,
            },
            RUNNING_TAG,
        )?;
        self.update_all_services()?;
        Ok(())
    }

    pub fn update_all_services(&self) -> failure::Result<()> {
        for instance in self.cluster.instances() {
            let mut request = UpdateServiceRequest::default();
            request.cluster = Some(self.aws.workplace().clone());
            request.force_new_deployment = Some(true);
            request.service = format!(
                "{w}/{w}-validator-{hash}",
                w = self.aws.workplace(),
                hash = instance.short_hash()
            );

            self.aws
                .ecs()
                .update_service(request)
                .sync()
                .map_err(|e| format_err!("Failed to update {}: {:?}", instance, e))?;
            thread::sleep(Duration::from_millis(100));
        }
        Ok(())
    }

    fn image_digest_by_tag(&self, tag: &str) -> String {
        let mut request = DescribeImagesRequest::default();
        request.repository_name = VALIDATOR_IMAGE_REPO.into();
        let image_id = ImageIdentifier {
            image_digest: None,
            image_tag: Some(tag.to_string()),
        };
        request.image_ids = Some(vec![image_id]);
        let result = self
            .aws
            .ecr()
            .describe_images(request)
            .sync()
            .expect("Failed to find latest nightly image");
        let images = result
            .image_details
            .expect("No image_details in ECR response");
        if images.len() != 1 {
            panic!("Ecr returned {} images for libra_e2e:nightly", images.len());
        }
        let image = images.into_iter().next().unwrap();
        image.image_digest.expect("No image_digest")
    }

    pub fn get_tested_upstream_commit(&self) -> failure::Result<String> {
        let digest = self.image_digest_by_tag(TESTED_TAG);
        let prev_upstream_tag = self.get_upstream_tag(&digest)?;
        Ok(prev_upstream_tag[UPSTREAM_PREFIX.len()..].to_string())
    }

    pub fn tag_tested_image(&mut self, hash: String) -> failure::Result<String> {
        let image_id = ImageIdentifier {
            image_digest: Some(hash.clone()),
            image_tag: None,
        };
        self.tag_image(VALIDATOR_IMAGE_REPO, &image_id, TESTED_TAG)?;
        let upstream_tag = self.get_upstream_tag(&hash)?;
        self.tag_image(
            CLIENT_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: None,
                image_tag: Some(upstream_tag.clone()),
            },
            TESTED_TAG,
        )?;
        self.tag_image(
            FAUCET_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: None,
                image_tag: Some(upstream_tag.clone()),
            },
            TESTED_TAG,
        )?;

        let upstream_commit = upstream_tag[UPSTREAM_PREFIX.len()..].to_string();
        Ok(upstream_commit)
    }

    pub fn get_upstream_tag(&self, digest: &str) -> failure::Result<String> {
        let image_id = ImageIdentifier {
            image_digest: Some(digest.to_string()),
            image_tag: None,
        };
        let images = self.get_images(VALIDATOR_IMAGE_REPO, &image_id)?;
        for image in images {
            let image_id = match image.image_id {
                Some(image_id) => image_id,
                None => continue,
            };
            let tag = match image_id.image_tag {
                Some(tag) => tag,
                None => continue,
            };
            if tag.starts_with(UPSTREAM_PREFIX) {
                return Ok(tag);
            }
        }
        Err(format_err!("Failed to find upstream tag"))
    }

    fn get_images(
        &self,
        repository: &str,
        image_id: &ImageIdentifier,
    ) -> failure::Result<Vec<Image>> {
        let mut get_request = BatchGetImageRequest::default();
        get_request.repository_name = repository.to_string();
        get_request.image_ids = vec![image_id.clone()];
        // Retry upto 10 times, waiting 10 sec between retries
        let response = retry(Fixed::from_millis(10_000).take(10), || {
            self.aws
                .ecr()
                .batch_get_image(get_request.clone())
                .sync()
                .map_err(|e| {
                    warn!(
                        "Failed to get image from repository: {:?}. Retrying...",
                        get_request.repository_name
                    );
                    e
                })
        })
        .map_err(|e| {
            format_err!(
                "Failed to get image from repository: {:?} after 10 tries: {:?}",
                get_request.repository_name,
                e
            )
        })?;
        response
            .images
            .ok_or_else(|| format_err!("No images in batch_get_image response"))
    }

    fn tag_image(
        &self,
        repository: &str,
        image_id: &ImageIdentifier,
        new_tag: &str,
    ) -> failure::Result<()> {
        let images = self.get_images(repository, &image_id)?;
        let image = images
            .into_iter()
            .next()
            .ok_or_else(|| format_err!("get_images returned 0 images"))?;
        let manifest = image
            .image_manifest
            .ok_or_else(|| format_err!("no manifest in batch_get_image response"))?;
        let mut put_request = PutImageRequest::default();
        put_request.image_manifest = manifest;
        put_request.repository_name = repository.to_string();
        put_request.image_tag = Some(new_tag.to_string());
        let result = self.aws.ecr().put_image(put_request).sync();
        if let Err(e) = result {
            if let RusotoError::Service(PutImageError::ImageAlreadyExists(_)) = e {
                info!("Tag for Image already exists {}:{}", repository, new_tag);
                Ok(())
            } else {
                Err(format_err!("Failed to tag image: {:?}", e))
            }
        } else {
            Ok(())
        }
    }
}
<commit_msg>[cluster-test] Retry ECR::describe_images<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{aws::Aws, cluster::Cluster};
use failure::prelude::format_err;
use retry::{delay::Fixed, retry};
use rusoto_core::RusotoError;
use rusoto_ecr::{
    BatchGetImageRequest, DescribeImagesRequest, DescribeImagesResponse, Ecr, Image,
    ImageIdentifier, PutImageError, PutImageRequest,
};
use rusoto_ecs::{Ecs, UpdateServiceRequest};
use slog_scope::{info, warn};
use std::{thread, time::Duration};

#[derive(Clone)]
pub struct DeploymentManager {
    aws: Aws,
    cluster: Cluster,
}

const VALIDATOR_IMAGE_REPO: &str = "libra_e2e";
const CLIENT_IMAGE_REPO: &str = "libra_client";
const FAUCET_IMAGE_REPO: &str = "libra_faucet";
pub const SOURCE_TAG: &str = "nightly";
pub const RUNNING_TAG: &str = "cluster_test";
pub const TESTED_TAG: &str = "nightly_tested";
const UPSTREAM_PREFIX: &str = "upstream_";

impl DeploymentManager {
    pub fn new(aws: Aws, cluster: Cluster) -> Self {
        Self { aws, cluster }
    }

    pub fn latest_hash_changed(&self) -> Option<String> {
        let hash = self.image_digest_by_tag(SOURCE_TAG);
        let last_tested = self.image_digest_by_tag(TESTED_TAG);
        if hash == last_tested {
            info!("Last deployed digest matches latest digest we expect, not doing redeploy");
            return None;
        }
        Some(hash)
    }

    pub fn redeploy(&mut self, hash: String) -> failure::Result<()> {
        info!("Will deploy with digest {}", hash);
        self.tag_image(
            VALIDATOR_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: Some(hash),
                image_tag: None,
            },
            RUNNING_TAG,
        )?;
        self.update_all_services()?;
        Ok(())
    }

    pub fn update_all_services(&self) -> failure::Result<()> {
        for instance in self.cluster.instances() {
            let mut request = UpdateServiceRequest::default();
            request.cluster = Some(self.aws.workplace().clone());
            request.force_new_deployment = Some(true);
            request.service = format!(
                "{w}/{w}-validator-{hash}",
                w = self.aws.workplace(),
                hash = instance.short_hash()
            );

            self.aws
                .ecs()
                .update_service(request)
                .sync()
                .map_err(|e| format_err!("Failed to update {}: {:?}", instance, e))?;
            thread::sleep(Duration::from_millis(100));
        }
        Ok(())
    }

    fn image_digest_by_tag(&self, tag: &str) -> String {
        let result = self.describe_images(tag);
        let images = result
            .image_details
            .expect("No image_details in ECR response");
        if images.len() != 1 {
            panic!("Ecr returned {} images for libra_e2e:nightly", images.len());
        }
        let image = images.into_iter().next().unwrap();
        image.image_digest.expect("No image_digest")
    }

    fn describe_images(&self, tag: &str) -> DescribeImagesResponse {
        let mut retry = 0usize;
        loop {
            let mut request = DescribeImagesRequest::default();
            request.repository_name = VALIDATOR_IMAGE_REPO.into();
            let image_id = ImageIdentifier {
                image_digest: None,
                image_tag: Some(tag.to_string()),
            };
            request.image_ids = Some(vec![image_id]);
            match self.aws.ecr().describe_images(request).sync() {
                Ok(r) => return r,
                Err(e) => {
                    if retry > 10 {
                        panic!("Failed describe_images after 10 attempts");
                    } else {
                        warn!("Transient failure in describe_images: {}", e);
                        thread::sleep(Duration::from_secs(10));
                        retry += 1;
                    }
                }
            }
        }
    }

    pub fn get_tested_upstream_commit(&self) -> failure::Result<String> {
        let digest = self.image_digest_by_tag(TESTED_TAG);
        let prev_upstream_tag = self.get_upstream_tag(&digest)?;
        Ok(prev_upstream_tag[UPSTREAM_PREFIX.len()..].to_string())
    }

    pub fn tag_tested_image(&mut self, hash: String) -> failure::Result<String> {
        let image_id = ImageIdentifier {
            image_digest: Some(hash.clone()),
            image_tag: None,
        };
        self.tag_image(VALIDATOR_IMAGE_REPO, &image_id, TESTED_TAG)?;
        let upstream_tag = self.get_upstream_tag(&hash)?;
        self.tag_image(
            CLIENT_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: None,
                image_tag: Some(upstream_tag.clone()),
            },
            TESTED_TAG,
        )?;
        self.tag_image(
            FAUCET_IMAGE_REPO,
            &ImageIdentifier {
                image_digest: None,
                image_tag: Some(upstream_tag.clone()),
            },
            TESTED_TAG,
        )?;

        let upstream_commit = upstream_tag[UPSTREAM_PREFIX.len()..].to_string();
        Ok(upstream_commit)
    }

    pub fn get_upstream_tag(&self, digest: &str) -> failure::Result<String> {
        let image_id = ImageIdentifier {
            image_digest: Some(digest.to_string()),
            image_tag: None,
        };
        let images = self.get_images(VALIDATOR_IMAGE_REPO, &image_id)?;
        for image in images {
            let image_id = match image.image_id {
                Some(image_id) => image_id,
                None => continue,
            };
            let tag = match image_id.image_tag {
                Some(tag) => tag,
                None => continue,
            };
            if tag.starts_with(UPSTREAM_PREFIX) {
                return Ok(tag);
            }
        }
        Err(format_err!("Failed to find upstream tag"))
    }

    fn get_images(
        &self,
        repository: &str,
        image_id: &ImageIdentifier,
    ) -> failure::Result<Vec<Image>> {
        let mut get_request = BatchGetImageRequest::default();
        get_request.repository_name = repository.to_string();
        get_request.image_ids = vec![image_id.clone()];
        // Retry upto 10 times, waiting 10 sec between retries
        let response = retry(Fixed::from_millis(10_000).take(10), || {
            self.aws
                .ecr()
                .batch_get_image(get_request.clone())
                .sync()
                .map_err(|e| {
                    warn!(
                        "Failed to get image from repository: {:?}. Retrying...",
                        get_request.repository_name
                    );
                    e
                })
        })
        .map_err(|e| {
            format_err!(
                "Failed to get image from repository: {:?} after 10 tries: {:?}",
                get_request.repository_name,
                e
            )
        })?;
        response
            .images
            .ok_or_else(|| format_err!("No images in batch_get_image response"))
    }

    fn tag_image(
        &self,
        repository: &str,
        image_id: &ImageIdentifier,
        new_tag: &str,
    ) -> failure::Result<()> {
        let images = self.get_images(repository, &image_id)?;
        let image = images
            .into_iter()
            .next()
            .ok_or_else(|| format_err!("get_images returned 0 images"))?;
        let manifest = image
            .image_manifest
            .ok_or_else(|| format_err!("no manifest in batch_get_image response"))?;
        let mut put_request = PutImageRequest::default();
        put_request.image_manifest = manifest;
        put_request.repository_name = repository.to_string();
        put_request.image_tag = Some(new_tag.to_string());
        let result = self.aws.ecr().put_image(put_request).sync();
        if let Err(e) = result {
            if let RusotoError::Service(PutImageError::ImageAlreadyExists(_)) = e {
                info!("Tag for Image already exists {}:{}", repository, new_tag);
                Ok(())
            } else {
                Err(format_err!("Failed to tag image: {:?}", e))
            }
        } else {
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before>//! Configure the process resource limits.
use cfg_if::cfg_if;

use crate::errno::Errno;
use crate::Result;
pub use libc::rlim_t;
use std::mem;

cfg_if! {
    if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
        use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY};
    }else if #[cfg(any(
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "macos",
        target_os = "ios",
        target_os = "android",
        target_os = "dragonfly",
        all(target_os = "linux", not(target_env = "gnu"))
    ))]{
        use libc::{c_int, rlimit, RLIM_INFINITY};
    }
}

libc_enum! {
    /// The Resource enum is platform dependent. Check different platform
    /// manuals for more details. Some platform links has been provided for
    /// earier reference (non-exhaustive).
    ///
    /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
    /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)

    // linux-gnu uses u_int as resource enum, which is implemented in libc as
    // well.
    //
    // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
    // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
    #[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))]
    #[cfg_attr(any(
            target_os = "freebsd",
            target_os = "openbsd",
            target_os = "netbsd",
            target_os = "macos",
            target_os = "ios",
            target_os = "android",
            target_os = "dragonfly",
            all(target_os = "linux", not(target_env = "gnu"))
        ), repr(i32))]
    #[non_exhaustive]
    pub enum Resource {
        #[cfg(not(any(
                    target_os = "freebsd",
                    target_os = "netbsd",
                    target_os = "openbsd"
        )))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum amount (in bytes) of virtual memory the process is
        /// allowed to map.
        RLIMIT_AS,
        /// The largest size (in bytes) core(5) file that may be created.
        RLIMIT_CORE,
        /// The maximum amount of cpu time (in seconds) to be used by each
        /// process.
        RLIMIT_CPU,
        /// The maximum size (in bytes) of the data segment for a process
        RLIMIT_DATA,
        /// The largest size (in bytes) file that may be created.
        RLIMIT_FSIZE,
        /// The maximum number of open files for this process.
        RLIMIT_NOFILE,
        /// The maximum size (in bytes) of the stack segment for a process.
        RLIMIT_STACK,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum number of kqueues this user id is allowed to create.
        RLIMIT_KQUEUES,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit on the combined number of flock locks and fcntl leases that
        /// this process may establish.
        RLIMIT_LOCKS,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum size (in bytes) which a process may lock into memory
        /// using the mlock(2) system call.
        RLIMIT_MEMLOCK,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit on the number of bytes that can be allocated for POSIX
        /// message queues  for  the  real  user  ID  of  the  calling process.
        RLIMIT_MSGQUEUE,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A ceiling to which the process's nice value can be raised using
        /// setpriority or nice.
        RLIMIT_NICE,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum number of simultaneous processes for this user id.
        RLIMIT_NPROC,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum number of pseudo-terminals this user id is allowed to
        /// create.
        RLIMIT_NPTS,

        #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "openbsd", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// When there is memory pressure and swap is available, prioritize
        /// eviction of a process' resident pages beyond this amount (in bytes).
        RLIMIT_RSS,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A ceiling on the real-time priority that may be set for this process
        /// using sched_setscheduler and  sched_set‐ param.
        RLIMIT_RTPRIO,

        #[cfg(any(target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit (in microseconds) on the amount of CPU time that a process
        /// scheduled under a real-time scheduling policy may con‐ sume without
        /// making a blocking system call.
        RLIMIT_RTTIME,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit on the number of signals that may be queued for the real
        /// user ID of the  calling  process.
        RLIMIT_SIGPENDING,

        #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum size (in bytes) of socket buffer usage for this user.
        RLIMIT_SBSIZE,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum size (in bytes) of the swap space that may be reserved
        /// or used by all of this user id's processes.
        RLIMIT_SWAP,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// An alias for RLIMIT_AS.
        RLIMIT_VMEM,
    }
}

/// Get the current processes resource limits
///
/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
/// there is no limit.
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to get the limits of.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{getrlimit, Resource};
///
/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
/// println!("current soft_limit: {:?}", soft_limit);
/// println!("current hard_limit: {:?}", hard_limit);
/// ```
///
/// # References
///
/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
    let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();

    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
        }else{
            let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
        }
    }

    Errno::result(res).map(|_| {
        let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
        (Some(rlim_cur), Some(rlim_max))
    })
}

/// Set the current processes resource limits
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to set the limits of.
/// * `soft_limit`: The value that the kernel enforces for the corresponding
///   resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`.
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
///   the current hard limit for non-root users. Note: `None` input will be
///   replaced by constant `RLIM_INFINITY`.
///
/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can
/// > results `EPERM` Error. So you will need to set the number explicitly.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{setrlimit, Resource};
///
/// let soft_limit = Some(512);
/// let hard_limit = Some(1024);
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
/// ```
///
/// # References
///
/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
///
/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
pub fn setrlimit(
    resource: Resource,
    soft_limit: Option<rlim_t>,
    hard_limit: Option<rlim_t>,
) -> Result<()> {
    let new_rlim = rlimit {
        rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
        rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
    };
    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
        }else{
            let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
        }
    }

    Errno::result(res).map(drop)
}
<commit_msg>Add NetBSD configuration for supported process resources<commit_after>//! Configure the process resource limits.
use cfg_if::cfg_if;

use crate::errno::Errno;
use crate::Result;
pub use libc::rlim_t;
use std::mem;

cfg_if! {
    if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
        use libc::{__rlimit_resource_t, rlimit, RLIM_INFINITY};
    }else if #[cfg(any(
        target_os = "freebsd",
        target_os = "openbsd",
        target_os = "netbsd",
        target_os = "macos",
        target_os = "ios",
        target_os = "android",
        target_os = "dragonfly",
        all(target_os = "linux", not(target_env = "gnu"))
    ))]{
        use libc::{c_int, rlimit, RLIM_INFINITY};
    }
}

libc_enum! {
    /// Types of process resources.
    ///
    /// The Resource enum is platform dependent. Check different platform
    /// manuals for more details. Some platform links have been provided for
    /// easier reference (non-exhaustive).
    ///
    /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
    /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
    /// * [NetBSD](https://man.netbsd.org/setrlimit.2)

    // linux-gnu uses u_int as resource enum, which is implemented in libc as
    // well.
    //
    // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
    // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
    #[cfg_attr(all(target_os = "linux", target_env = "gnu"), repr(u32))]
    #[cfg_attr(any(
            target_os = "freebsd",
            target_os = "openbsd",
            target_os = "netbsd",
            target_os = "macos",
            target_os = "ios",
            target_os = "android",
            target_os = "dragonfly",
            all(target_os = "linux", not(target_env = "gnu"))
        ), repr(i32))]
    #[non_exhaustive]
    pub enum Resource {
        #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum amount (in bytes) of virtual memory the process is
        /// allowed to map.
        RLIMIT_AS,
        /// The largest size (in bytes) core(5) file that may be created.
        RLIMIT_CORE,
        /// The maximum amount of cpu time (in seconds) to be used by each
        /// process.
        RLIMIT_CPU,
        /// The maximum size (in bytes) of the data segment for a process
        RLIMIT_DATA,
        /// The largest size (in bytes) file that may be created.
        RLIMIT_FSIZE,
        /// The maximum number of open files for this process.
        RLIMIT_NOFILE,
        /// The maximum size (in bytes) of the stack segment for a process.
        RLIMIT_STACK,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum number of kqueues this user id is allowed to create.
        RLIMIT_KQUEUES,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit on the combined number of flock locks and fcntl leases that
        /// this process may establish.
        RLIMIT_LOCKS,

        #[cfg(any(
            target_os = "android",
            target_os = "freebsd",
            target_os = "openbsd",
            target_os = "linux",
            target_os = "netbsd"
        ))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum size (in bytes) which a process may lock into memory
        /// using the mlock(2) system call.
        RLIMIT_MEMLOCK,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit on the number of bytes that can be allocated for POSIX
        /// message queues  for  the  real  user  ID  of  the  calling process.
        RLIMIT_MSGQUEUE,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A ceiling to which the process's nice value can be raised using
        /// setpriority or nice.
        RLIMIT_NICE,

        #[cfg(any(
            target_os = "android",
            target_os = "freebsd",
            target_os = "netbsd",
            target_os = "openbsd",
            target_os = "linux",
        ))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum number of simultaneous processes for this user id.
        RLIMIT_NPROC,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum number of pseudo-terminals this user id is allowed to
        /// create.
        RLIMIT_NPTS,

        #[cfg(any(target_os = "android",
            target_os = "freebsd",
            target_os = "netbsd",
            target_os = "openbsd",
            target_os = "linux",
        ))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// When there is memory pressure and swap is available, prioritize
        /// eviction of a process' resident pages beyond this amount (in bytes).
        RLIMIT_RSS,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A ceiling on the real-time priority that may be set for this process
        /// using sched_setscheduler and  sched_set‐ param.
        RLIMIT_RTPRIO,

        #[cfg(any(target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit (in microseconds) on the amount of CPU time that a process
        /// scheduled under a real-time scheduling policy may con‐ sume without
        /// making a blocking system call.
        RLIMIT_RTTIME,

        #[cfg(any(target_os = "android", target_os = "linux"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// A limit on the number of signals that may be queued for the real
        /// user ID of the  calling  process.
        RLIMIT_SIGPENDING,

        #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum size (in bytes) of socket buffer usage for this user.
        RLIMIT_SBSIZE,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// The maximum size (in bytes) of the swap space that may be reserved
        /// or used by all of this user id's processes.
        RLIMIT_SWAP,

        #[cfg(target_os = "freebsd")]
        #[cfg_attr(docsrs, doc(cfg(all())))]
        /// An alias for RLIMIT_AS.
        RLIMIT_VMEM,
    }
}

/// Get the current processes resource limits
///
/// A value of `None` indicates the value equals to `RLIM_INFINITY` which means
/// there is no limit.
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to get the limits of.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{getrlimit, Resource};
///
/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
/// println!("current soft_limit: {:?}", soft_limit);
/// println!("current hard_limit: {:?}", hard_limit);
/// ```
///
/// # References
///
/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
pub fn getrlimit(resource: Resource) -> Result<(Option<rlim_t>, Option<rlim_t>)> {
    let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();

    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
        }else{
            let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
        }
    }

    Errno::result(res).map(|_| {
        let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
        (Some(rlim_cur), Some(rlim_max))
    })
}

/// Set the current processes resource limits
///
/// # Parameters
///
/// * `resource`: The [`Resource`] that we want to set the limits of.
/// * `soft_limit`: The value that the kernel enforces for the corresponding
///   resource. Note: `None` input will be replaced by constant `RLIM_INFINITY`.
/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
///   the current hard limit for non-root users. Note: `None` input will be
///   replaced by constant `RLIM_INFINITY`.
///
/// > Note: for some os (linux_gnu), setting hard_limit to `RLIM_INFINITY` can
/// > results `EPERM` Error. So you will need to set the number explicitly.
///
/// # Examples
///
/// ```
/// # use nix::sys::resource::{setrlimit, Resource};
///
/// let soft_limit = Some(512);
/// let hard_limit = Some(1024);
/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
/// ```
///
/// # References
///
/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
///
/// [`Resource`]: enum.Resource.html
///
/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
pub fn setrlimit(
    resource: Resource,
    soft_limit: Option<rlim_t>,
    hard_limit: Option<rlim_t>,
) -> Result<()> {
    let new_rlim = rlimit {
        rlim_cur: soft_limit.unwrap_or(RLIM_INFINITY),
        rlim_max: hard_limit.unwrap_or(RLIM_INFINITY),
    };
    cfg_if! {
        if #[cfg(all(target_os = "linux", target_env = "gnu"))]{
            let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
        }else{
            let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
        }
    }

    Errno::result(res).map(drop)
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

pub mod timeout {
    use ffi;

    pub fn add<T>(interval: u32, func: fn(&mut T) -> i32, data: &T) -> u32 {
        let tmp = data as *const T;
        let tmp_f = func as ffi::gpointer;

        unsafe { ffi::g_timeout_add(interval, tmp_f, tmp as ffi::gpointer) }
    }

    pub fn add_seconds<T>(interval: u32, func: fn(&mut T) -> i32, data: &T) -> u32 {
        let tmp = data as *const T;
        let tmp_f = func as ffi::gpointer;

        unsafe { ffi::g_timeout_add_seconds(interval, tmp_f, tmp as ffi::gpointer) }
    }
}<commit_msg>Add documentation<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

//! callback timeout functions

pub mod timeout {
    use ffi;

    /// Sets a function to be called at regular intervals, with the default priority, G_PRIORITY_DEFAULT.
    /// The function is called repeatedly until it returns FALSE, at which point the timeout is
    /// automatically destroyed and the function will not be called again. The first call to the
    /// function will be at the end of the first interval .
    ///
    /// Note that timeout functions may be delayed, due to the processing of other event sources. Thus
    /// they should not be relied on for precise timing. After each call to the timeout function, the
    /// time of the next timeout is recalculated based on the current time and the given interval (it
    /// does not try to 'catch up' time lost in delays).
    ///
    /// If you want to have a timer in the "seconds" range and do not care about the exact time of the
    /// first call of the timer, use the g_timeout_add_seconds() function; this function allows for more
    /// optimizations and more efficient system power usage.
    ///
    /// This internally creates a main loop source using g_timeout_source_new() and attaches it to the
    /// global GMainContext using g_source_attach(), so the callback will be invoked in whichever thread
    /// is running that main context. You can do these steps manually if you need greater control or to
    /// use a custom main context.
    ///
    /// The interval given is in terms of monotonic time, not wall clock time. See g_get_monotonic_time().
    pub fn add<T>(interval: u32, func: fn(&mut T) -> i32, data: &T) -> u32 {
        let tmp = data as *const T;
        let tmp_f = func as ffi::gpointer;

        unsafe { ffi::g_timeout_add(interval, tmp_f, tmp as ffi::gpointer) }
    }

    /// Sets a function to be called at regular intervals with the default priority, G_PRIORITY_DEFAULT.
    /// The function is called repeatedly until it returns FALSE, at which point the timeout is automatically
    /// destroyed and the function will not be called again.
    ///
    /// This internally creates a main loop source using g_timeout_source_new_seconds() and attaches it to
    /// the main loop context using g_source_attach(). You can do these steps manually if you need greater
    /// control. Also see g_timeout_add_seconds_full().
    ///
    /// Note that the first call of the timer may not be precise for timeouts of one second. If you need
    /// finer precision and have such a timeout, you may want to use g_timeout_add() instead.
    ///
    /// The interval given is in terms of monotonic time, not wall clock time. See g_get_monotonic_time().
    pub fn add_seconds<T>(interval: u32, func: fn(&mut T) -> i32, data: &T) -> u32 {
        let tmp = data as *const T;
        let tmp_f = func as ffi::gpointer;

        unsafe { ffi::g_timeout_add_seconds(interval, tmp_f, tmp as ffi::gpointer) }
    }
}
<|endoftext|>


<commit_before>use chess::logic::{Position, BoardField};
use chess::ChessGame;

pub struct System {
    // Mouse Coordinates
    mouse_x: i32,
    mouse_y: i32,
    // Selected Pieces/BoardFields
    from: Option<Position>,
    to: Option<Position>,
    // Holds Board and Players
    game: ChessGame
}

impl System {
    pub fn new() -> Self {
        System {
            mouse_x: 0,
            mouse_y: 0,
            from: None,
            to: None,
            game: ChessGame::new()
        }
    }

    pub fn mouse(&self) -> (i32, i32) {
        (self.mouse_x, self.mouse_y)
    }

    pub fn set_mouse_coordinates(&mut self, x: i32, y: i32) {
        self.mouse_x = x;
        self.mouse_y = y;
    }

    pub fn set_selected(&mut self, pos: (u8, u8)) {
        println!("What system gets: {}, {}", pos.0, pos.1);

        if self.from.is_none() {
            self.from = Some(Position::new(pos.0, pos.1));
        } else if self.to.is_none() {
            self.to = Some(Position::new(pos.0, pos.1));
        } else {
            self.from = Some(Position::new(pos.0, pos.1));
            self.to = None;
        }
    }

    pub fn check_ready_and_play(&mut self) {
        if self.from.is_some() && self.to.is_some() {
            self.execute_turn();
        }
    }

    pub fn execute_turn(&mut self) {
        if self.game.board.is_move_valid(self.from.unwrap(), self.to.unwrap()) {
            self.game.do_turn(self.from.unwrap(), self.to.unwrap());
        }
    }
}

<commit_msg>Another whitespace<commit_after>use chess::logic::{Position, BoardField};
use chess::ChessGame;

pub struct System {
    // Mouse Coordinates
    mouse_x: i32,
    mouse_y: i32,
    // Selected Pieces/BoardFields
    from: Option<Position>,
    to: Option<Position>,
    // Holds Board and Players
    game: ChessGame
}

impl System {
    pub fn new() -> Self {
        System {
            mouse_x: 0,
            mouse_y: 0,
            from: None,
            to: None,
            game: ChessGame::new()
        }
    }

    pub fn mouse(&self) -> (i32, i32) {
        (self.mouse_x, self.mouse_y)
    }

    pub fn set_mouse_coordinates(&mut self, x: i32, y: i32) {
        self.mouse_x = x;
        self.mouse_y = y;
    }

    pub fn set_selected(&mut self, pos: (u8, u8)) {
        println!("What system gets: {}, {}", pos.0, pos.1);

        if self.from.is_none() {
            self.from = Some(Position::new(pos.0, pos.1));
        } else if self.to.is_none() {
            self.to = Some(Position::new(pos.0, pos.1));
        } else {
            self.from = Some(Position::new(pos.0, pos.1));
            self.to = None;
        }
    }

    pub fn check_ready_and_play(&mut self) {
        if self.from.is_some() && self.to.is_some() {
            self.execute_turn();
        }
    }

    pub fn execute_turn(&mut self) {
        if self.game.board.is_move_valid(self.from.unwrap(), self.to.unwrap()) {
            self.game.do_turn(self.from.unwrap(), self.to.unwrap());
        }
    }
}

<|endoftext|>


<commit_before>#![allow(dead_code, non_camel_case_types)]

extern crate libc;

pub use self::utmpx::{DEFAULT_FILE,USER_PROCESS,BOOT_TIME,c_utmp};
#[cfg(target_os = "linux")]
mod utmpx {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "/var/run/utmp";

    pub const UT_LINESIZE: usize = 32;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 4;
    pub const UT_HOSTSIZE: usize = 256;

    pub const EMPTY: libc::c_short = 0;
    pub const RUN_LVL: libc::c_short = 1;
    pub const BOOT_TIME: libc::c_short = 2;
    pub const NEW_TIME: libc::c_short = 3;
    pub const OLD_TIME: libc::c_short = 4;
    pub const INIT_PROCESS: libc::c_short = 5;
    pub const LOGIN_PROCESS: libc::c_short = 6;
    pub const USER_PROCESS: libc::c_short = 7;
    pub const DEAD_PROCESS: libc::c_short = 8;
    pub const ACCOUNTING: libc::c_short = 9;

    #[repr(C)]
    pub struct c_exit_status {
        pub e_termination: libc::c_short,
        pub e_exit: libc::c_short,
    }

    #[repr(C)]
    pub struct c_utmp {
        pub ut_type: libc::c_short,
        pub ut_pid: libc::pid_t,
        pub ut_line: [libc::c_char; UT_LINESIZE],
        pub ut_id: [libc::c_char; UT_IDSIZE],

        pub ut_user: [libc::c_char; UT_NAMESIZE],
        pub ut_host: [libc::c_char; UT_HOSTSIZE],
        pub ut_exit: c_exit_status,
        pub ut_session: libc::c_long,
        pub ut_tv: libc::timeval,

        pub ut_addr_v6: [libc::int32_t; 4],
        pub __unused: [libc::c_char; 20],
    }
}

#[cfg(target_os = "macos")]
mod utmpx {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";

    pub const UT_LINESIZE: usize = 32;
    pub const UT_NAMESIZE: usize = 256;
    pub const UT_IDSIZE: usize = 4;
    pub const UT_HOSTSIZE: usize = 256;

    pub const EMPTY: libc::c_short = 0;
    pub const RUN_LVL: libc::c_short = 1;
    pub const BOOT_TIME: libc::c_short = 2;
    pub const OLD_TIME: libc::c_short = 3;
    pub const NEW_TIME: libc::c_short = 4;
    pub const INIT_PROCESS: libc::c_short = 5;
    pub const LOGIN_PROCESS: libc::c_short = 6;
    pub const USER_PROCESS: libc::c_short = 7;
    pub const DEAD_PROCESS: libc::c_short = 8;
    pub const ACCOUNTING: libc::c_short = 9;

    #[repr(C)]
    pub struct c_exit_status {
        pub e_termination: libc::c_short,
        pub e_exit: libc::c_short,
    }

    #[repr(C)]
    pub struct c_utmp {
        pub ut_user: [libc::c_char; UT_NAMESIZE],
        pub ut_id: [libc::c_char; UT_IDSIZE],
        pub ut_line: [libc::c_char; UT_LINESIZE],
        pub ut_pid: libc::pid_t,
        pub ut_type: libc::c_short,
        pub ut_tv: libc::timeval,
        pub ut_host: [libc::c_char; UT_HOSTSIZE],
        pub __unused: [libc::c_char; 16]
    }
}

#[cfg(target_os = "freebsd")]
mod utmpx {
    use super::libc;

    pub static DEFAULT_FILE : &'static str = "";

    pub const UT_LINESIZE : usize = 16;
    pub const UT_NAMESIZE : usize = 32;
    pub const UT_IDSIZE   : usize = 8;
    pub const UT_HOSTSIZE : usize = 128;

    pub const EMPTY         : libc::c_short = 0;
    pub const BOOT_TIME     : libc::c_short = 1;
    pub const OLD_TIME      : libc::c_short = 2;
    pub const NEW_TIME      : libc::c_short = 3;
    pub const USER_PROCESS  : libc::c_short = 4;
    pub const INIT_PROCESS  : libc::c_short = 5;
    pub const LOGIN_PROCESS : libc::c_short = 6;
    pub const DEAD_PROCESS  : libc::c_short = 7;
    pub const SHUTDOWN_TIME : libc::c_short = 8;

    #[repr(C)]
    pub struct c_utmp {
        pub ut_type : libc::c_short,
        pub ut_tv   : libc::timeval,
        pub ut_id   : [libc::c_char; UT_IDSIZE],
        pub ut_pid  : libc::pid_t,
        pub ut_user : [libc::c_char; UT_NAMESIZE],
        pub ut_line : [libc::c_char; UT_LINESIZE],
        pub ut_host : [libc::c_char; UT_HOSTSIZE],
        pub ut_spare : [libc::c_char; 64],
    }
}
<commit_msg>pinky: implement short format<commit_after>#![allow(dead_code, non_camel_case_types)]

extern crate libc;

pub use self::utmpx::{UT_NAMESIZE,UT_LINESIZE,DEFAULT_FILE,USER_PROCESS,BOOT_TIME,c_utmp};
#[cfg(target_os = "linux")]
mod utmpx {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "/var/run/utmp";

    pub const UT_LINESIZE: usize = 32;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 4;
    pub const UT_HOSTSIZE: usize = 256;

    pub const EMPTY: libc::c_short = 0;
    pub const RUN_LVL: libc::c_short = 1;
    pub const BOOT_TIME: libc::c_short = 2;
    pub const NEW_TIME: libc::c_short = 3;
    pub const OLD_TIME: libc::c_short = 4;
    pub const INIT_PROCESS: libc::c_short = 5;
    pub const LOGIN_PROCESS: libc::c_short = 6;
    pub const USER_PROCESS: libc::c_short = 7;
    pub const DEAD_PROCESS: libc::c_short = 8;
    pub const ACCOUNTING: libc::c_short = 9;

    #[repr(C)]
    pub struct timeval {
        pub tv_sec: libc::int32_t,
        pub tv_usec: libc::int32_t,
    }

    #[repr(C)]
    pub struct c_exit_status {
        pub e_termination: libc::c_short,
        pub e_exit: libc::c_short,
    }

    #[repr(C)]
    pub struct c_utmp {
        pub ut_type: libc::c_short,
        pub ut_pid: libc::pid_t,
        pub ut_line: [libc::c_char; UT_LINESIZE],
        pub ut_id: [libc::c_char; UT_IDSIZE],

        pub ut_user: [libc::c_char; UT_NAMESIZE],
        pub ut_host: [libc::c_char; UT_HOSTSIZE],
        pub ut_exit: c_exit_status,
        pub ut_session: libc::int32_t,
        pub ut_tv: timeval,

        pub ut_addr_v6: [libc::int32_t; 4],
        pub __unused: [libc::c_char; 20],
    }
}

#[cfg(target_os = "macos")]
mod utmpx {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";

    pub const UT_LINESIZE: usize = 32;
    pub const UT_NAMESIZE: usize = 256;
    pub const UT_IDSIZE: usize = 4;
    pub const UT_HOSTSIZE: usize = 256;

    pub const EMPTY: libc::c_short = 0;
    pub const RUN_LVL: libc::c_short = 1;
    pub const BOOT_TIME: libc::c_short = 2;
    pub const OLD_TIME: libc::c_short = 3;
    pub const NEW_TIME: libc::c_short = 4;
    pub const INIT_PROCESS: libc::c_short = 5;
    pub const LOGIN_PROCESS: libc::c_short = 6;
    pub const USER_PROCESS: libc::c_short = 7;
    pub const DEAD_PROCESS: libc::c_short = 8;
    pub const ACCOUNTING: libc::c_short = 9;

    #[repr(C)]
    pub struct c_exit_status {
        pub e_termination: libc::c_short,
        pub e_exit: libc::c_short,
    }

    #[repr(C)]
    pub struct c_utmp {
        pub ut_user: [libc::c_char; UT_NAMESIZE],
        pub ut_id: [libc::c_char; UT_IDSIZE],
        pub ut_line: [libc::c_char; UT_LINESIZE],
        pub ut_pid: libc::pid_t,
        pub ut_type: libc::c_short,
        pub ut_tv: libc::timeval,
        pub ut_host: [libc::c_char; UT_HOSTSIZE],
        pub __unused: [libc::c_char; 16]
    }
}

#[cfg(target_os = "freebsd")]
mod utmpx {
    use super::libc;

    pub static DEFAULT_FILE : &'static str = "";

    pub const UT_LINESIZE : usize = 16;
    pub const UT_NAMESIZE : usize = 32;
    pub const UT_IDSIZE   : usize = 8;
    pub const UT_HOSTSIZE : usize = 128;

    pub const EMPTY         : libc::c_short = 0;
    pub const BOOT_TIME     : libc::c_short = 1;
    pub const OLD_TIME      : libc::c_short = 2;
    pub const NEW_TIME      : libc::c_short = 3;
    pub const USER_PROCESS  : libc::c_short = 4;
    pub const INIT_PROCESS  : libc::c_short = 5;
    pub const LOGIN_PROCESS : libc::c_short = 6;
    pub const DEAD_PROCESS  : libc::c_short = 7;
    pub const SHUTDOWN_TIME : libc::c_short = 8;

    #[repr(C)]
    pub struct c_utmp {
        pub ut_type : libc::c_short,
        pub ut_tv   : libc::timeval,
        pub ut_id   : [libc::c_char; UT_IDSIZE],
        pub ut_pid  : libc::pid_t,
        pub ut_user : [libc::c_char; UT_NAMESIZE],
        pub ut_line : [libc::c_char; UT_LINESIZE],
        pub ut_host : [libc::c_char; UT_HOSTSIZE],
        pub ut_spare : [libc::c_char; 64],
    }
}
<|endoftext|>