<commit_before>//! This module specifies the type based interner for constants.
//!
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.

use rustc::ty::layout::LayoutOf;
use rustc::ty::{Ty, TyCtxt, ParamEnv, self};
use rustc::mir::interpret::{
    InterpResult, ErrorHandled,
};
use rustc::hir;
use rustc::hir::def_id::DefId;
use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet;

use syntax::ast::Mutability;
use syntax_pos::Span;

use super::{
    ValueVisitor, MemoryKind, Pointer, AllocId, MPlaceTy, InterpError, Scalar,
};
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};

struct InternVisitor<'rt, 'mir: 'rt, 'tcx: 'rt + 'mir> {
    /// previously encountered safe references
    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    param_env: ParamEnv<'tcx>,
    /// The root node of the value that we're looking at. This field is never mutated and only used
    /// for sanity assertions that will ICE when `const_qualif` screws up.
    mode: InternMode,
    /// This field stores the mutability of the value *currently* being checked.
    /// It is set to mutable when an `UnsafeCell` is encountered
    /// When recursing across a reference, we don't recurse but store the
    /// value to be checked in `ref_tracking` together with the mutability at which we are checking
    /// the value.
    /// When encountering an immutable reference, we treat everything as immutable that is behind
    /// it.
    mutability: Mutability,
    /// A list of all encountered relocations. After type-based interning, we traverse this list to
    /// also intern allocations that are only referenced by a raw pointer or inside a union.
    leftover_relocations: &'rt mut FxHashSet<AllocId>,
}

#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
    /// Mutable references must in fact be immutable due to their surrounding immutability in a
    /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
    /// that will actually be treated as mutable.
    Static,
    /// UnsafeCell is OK in the value of a constant, but not behind references in a constant
    ConstBase,
    /// `UnsafeCell` ICEs
    Const,
}

/// Signalling data structure to ensure we don't recurse
/// into the memory of other constants or statics
struct IsStaticOrFn;

impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
    /// Intern an allocation without looking at its children
    fn intern_shallow(
        &mut self,
        ptr: Pointer,
        mutability: Mutability,
    ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
        trace!(
            "InternVisitor::intern {:?} with {:?}",
            ptr, mutability,
        );
        // remove allocation
        let tcx = self.ecx.tcx;
        let memory = self.ecx.memory_mut();
        let (kind, mut alloc) = match memory.alloc_map.remove(&ptr.alloc_id) {
            Some(entry) => entry,
            None => {
                // if the pointer is dangling (neither in local nor global memory), we leave it
                // to validation to error. The `delay_span_bug` ensures that we don't forget such
                // a check in validation.
                if tcx.alloc_map.lock().get(ptr.alloc_id).is_none() {
                    tcx.sess.delay_span_bug(self.ecx.tcx.span, "tried to intern dangling pointer");
                }
                // treat dangling pointers like other statics
                // just to stop trying to recurse into them
                return Ok(Some(IsStaticOrFn));
            },
        };
        // This match is just a canary for future changes to `MemoryKind`, which most likely need
        // changes in this function.
        match kind {
            MemoryKind::Stack | MemoryKind::Vtable => {},
        }
        // Ensure llvm knows to only put this into immutable memory if the value is immutable either
        // by being behind a reference or by being part of a static or const without interior
        // mutability
        alloc.mutability = mutability;
        // link the alloc id to the actual allocation
        let alloc = tcx.intern_const_alloc(alloc);
        self.leftover_relocations.extend(alloc.relocations.iter().map(|&(_, ((), reloc))| reloc));
        tcx.alloc_map.lock().set_alloc_id_memory(ptr.alloc_id, alloc);
        Ok(None)
    }
}

impl<'rt, 'mir, 'tcx>
    ValueVisitor<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
for
    InternVisitor<'rt, 'mir, 'tcx>
{
    type V = MPlaceTy<'tcx>;

    #[inline(always)]
    fn ecx(&self) -> &CompileTimeEvalContext<'mir, 'tcx> {
        &self.ecx
    }

    fn visit_aggregate(
        &mut self,
        mplace: MPlaceTy<'tcx>,
        fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
    ) -> InterpResult<'tcx> {
        if let Some(def) = mplace.layout.ty.ty_adt_def() {
            if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
                // We are crossing over an `UnsafeCell`, we can mutate again
                let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
                assert_ne!(
                    self.mode, InternMode::Const,
                    "UnsafeCells are not allowed behind references in constants. This should have \
                    been prevented statically by const qualification. If this were allowed one \
                    would be able to change a constant at one use site and other use sites may \
                    arbitrarily decide to change, too.",
                );
                let walked = self.walk_aggregate(mplace, fields);
                self.mutability = old;
                return walked;
            }
        }
        self.walk_aggregate(mplace, fields)
    }

    fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
        // Handle Reference types, as these are the only relocations supported by const eval.
        // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
        let ty = mplace.layout.ty;
        if let ty::Ref(_, _, mutability) = ty.sty {
            let value = self.ecx.read_immediate(mplace.into())?;
            // Handle trait object vtables
            if let Ok(meta) = value.to_meta() {
                let layout = self.ecx.layout_of(ty.builtin_deref(true).unwrap().ty)?;
                if layout.is_unsized() {
                    if let ty::Dynamic(..) = self.ecx.tcx.struct_tail(layout.ty).sty {
                        if let Ok(vtable) = meta.unwrap().to_ptr() {
                            // explitly choose `Immutable` here, since vtables are immutable, even
                            // if the reference of the fat pointer is mutable
                            self.intern_shallow(vtable, Mutability::Immutable)?;
                        }
                    }
                }
            }
            let mplace = self.ecx.ref_to_mplace(value)?;
            // Check if we have encountered this pointer+layout combination before.
            // Only recurse for allocation-backed pointers.
            if let Scalar::Ptr(ptr) = mplace.ptr {
                // We do not have any `frozen` logic here, because it's essentially equivalent to
                // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
                // and we check that in `visit_aggregate`.
                // This is not an inherent limitation, but one that we know to be true, because
                // const qualification enforces it. We can lift it in the future.
                match (self.mode, mutability) {
                    // immutable references are fine everywhere
                    (_, hir::Mutability::MutImmutable) => {},
                    // all is "good and well" in the unsoundness of `static mut`

                    // mutable references are ok in `static`. Either they are treated as immutable
                    // because they are behind an immutable one, or they are behind an `UnsafeCell`
                    // and thus ok.
                    (InternMode::Static, hir::Mutability::MutMutable) => {},
                    // we statically prevent `&mut T` via `const_qualif` and double check this here
                    (InternMode::ConstBase, hir::Mutability::MutMutable) |
                    (InternMode::Const, hir::Mutability::MutMutable) =>
                        bug!("const qualif failed to prevent mutable references"),
                }
                // Compute the mutability with which we'll start visiting the allocation. This is
                // what gets changed when we encounter an `UnsafeCell`
                let mutability = match (self.mutability, mutability) {
                    // The only way a mutable reference actually works as a mutable reference is
                    // by being in a `static mut` directly or behind another mutable reference.
                    // If there's an immutable reference or we are inside a static, then our
                    // mutable reference is equivalent to an immutable one. As an example:
                    // `&&mut Foo` is semantically equivalent to `&&Foo`
                    (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
                    _ => Mutability::Immutable,
                };
                // Compute the mutability of the allocation
                let intern_mutability = intern_mutability(
                    self.ecx.tcx.tcx,
                    self.param_env,
                    mplace.layout.ty,
                    self.ecx.tcx.span,
                    mutability,
                );
                // Recursing behind references changes the intern mode for constants in order to
                // cause assertions to trigger if we encounter any `UnsafeCell`s.
                let mode = match self.mode {
                    InternMode::ConstBase => InternMode::Const,
                    other => other,
                };
                match self.intern_shallow(ptr, intern_mutability)? {
                    // No need to recurse, these are interned already and statics may have
                    // cycles, so we don't want to recurse there
                    Some(IsStaticOrFn) => {},
                    // intern everything referenced by this value. The mutability is taken from the
                    // reference. It is checked above that mutable references only happen in
                    // `static mut`
                    None => self.ref_tracking.track((mplace, mutability, mode), || ()),
                }
            }
        }
        Ok(())
    }
}

/// Figure out the mutability of the allocation.
/// Mutable if it has interior mutability *anywhere* in the type.
fn intern_mutability<'tcx>(
    tcx: TyCtxt<'tcx, 'tcx>,
    param_env: ParamEnv<'tcx>,
    ty: Ty<'tcx>,
    span: Span,
    mutability: Mutability,
) -> Mutability {
    let has_interior_mutability = !ty.is_freeze(tcx, param_env, span);
    if has_interior_mutability {
        Mutability::Mutable
    } else {
        mutability
    }
}

pub fn intern_const_alloc_recursive(
    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
    def_id: DefId,
    ret: MPlaceTy<'tcx>,
    // FIXME(oli-obk): can we scrap the param env? I think we can, the final value of a const eval
    // must always be monomorphic, right?
    param_env: ty::ParamEnv<'tcx>,
) -> InterpResult<'tcx> {
    let tcx = ecx.tcx;
    // this `mutability` is the mutability of the place, ignoring the type
    let (mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
        Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
        None => (Mutability::Immutable, InternMode::ConstBase),
        // `static mut` doesn't care about interior mutability, it's mutable anyway
        Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
    };

    // type based interning
    let mut ref_tracking = RefTracking::new((ret, mutability, base_intern_mode));
    let leftover_relocations = &mut FxHashSet::default();

    // This mutability is the combination of the place mutability and the type mutability. If either
    // is mutable, `alloc_mutability` is mutable. This exists because the entire allocation needs
    // to be mutable if it contains an `UnsafeCell` anywhere. The other `mutability` exists so that
    // the visitor does not treat everything outside the `UnsafeCell` as mutable.
    let alloc_mutability = intern_mutability(
        tcx.tcx, param_env, ret.layout.ty, tcx.span, mutability,
    );

    // start with the outermost allocation
    InternVisitor {
        ref_tracking: &mut ref_tracking,
        ecx,
        mode: base_intern_mode,
        leftover_relocations,
        param_env,
        mutability,
    }.intern_shallow(ret.ptr.to_ptr()?, alloc_mutability)?;

    while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
        let interned = InternVisitor {
            ref_tracking: &mut ref_tracking,
            ecx,
            mode,
            leftover_relocations,
            param_env,
            mutability,
        }.visit_value(mplace);
        if let Err(error) = interned {
            // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
            // to read enum discriminants in order to find references in enum variant fields.
            if let InterpError::ValidationFailure(_) = error.kind {
                let err = crate::const_eval::error_to_const_error(&ecx, error);
                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
                    Ok(mut diag) => {
                        diag.note("The rules on what exactly is undefined behavior aren't clear, \
                            so this check might be overzealous. Please open an issue on the rust \
                            compiler repository if you believe it should not be considered \
                            undefined behavior",
                        );
                        diag.emit();
                    }
                    Err(ErrorHandled::TooGeneric) |
                    Err(ErrorHandled::Reported) => {},
                }
            }
        }
    }

    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
    // pointers, ... So we can't intern them according to their type rules

    let mut todo: Vec<_> = leftover_relocations.iter().cloned().collect();
    while let Some(alloc_id) = todo.pop() {
        if let Some((_, alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
            // We can't call the `intern` method here, as its logic is tailored to safe references.
            // So we hand-roll the interning logic here again
            let alloc = tcx.intern_const_alloc(alloc);
            tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
            for &(_, ((), reloc)) in alloc.relocations.iter() {
                if leftover_relocations.insert(reloc) {
                    todo.push(reloc);
                }
            }
        } else if ecx.memory().dead_alloc_map.contains_key(&alloc_id) {
            // dangling pointer
            return err!(ValidationFailure(
                "encountered dangling pointer in final constant".into(),
            ))
        }
    }
    Ok(())
}
<commit_msg>Remove now-unnecessary lifetime<commit_after>//! This module specifies the type based interner for constants.
//!
//! After a const evaluation has computed a value, before we destroy the const evaluator's session
//! memory, we need to extract all memory allocations to the global memory pool so they stay around.

use rustc::ty::layout::LayoutOf;
use rustc::ty::{Ty, TyCtxt, ParamEnv, self};
use rustc::mir::interpret::{
    InterpResult, ErrorHandled,
};
use rustc::hir;
use rustc::hir::def_id::DefId;
use super::validity::RefTracking;
use rustc_data_structures::fx::FxHashSet;

use syntax::ast::Mutability;
use syntax_pos::Span;

use super::{
    ValueVisitor, MemoryKind, Pointer, AllocId, MPlaceTy, InterpError, Scalar,
};
use crate::const_eval::{CompileTimeInterpreter, CompileTimeEvalContext};

struct InternVisitor<'rt, 'mir: 'rt, 'tcx: 'rt + 'mir> {
    /// previously encountered safe references
    ref_tracking: &'rt mut RefTracking<(MPlaceTy<'tcx>, Mutability, InternMode)>,
    ecx: &'rt mut CompileTimeEvalContext<'mir, 'tcx>,
    param_env: ParamEnv<'tcx>,
    /// The root node of the value that we're looking at. This field is never mutated and only used
    /// for sanity assertions that will ICE when `const_qualif` screws up.
    mode: InternMode,
    /// This field stores the mutability of the value *currently* being checked.
    /// It is set to mutable when an `UnsafeCell` is encountered
    /// When recursing across a reference, we don't recurse but store the
    /// value to be checked in `ref_tracking` together with the mutability at which we are checking
    /// the value.
    /// When encountering an immutable reference, we treat everything as immutable that is behind
    /// it.
    mutability: Mutability,
    /// A list of all encountered relocations. After type-based interning, we traverse this list to
    /// also intern allocations that are only referenced by a raw pointer or inside a union.
    leftover_relocations: &'rt mut FxHashSet<AllocId>,
}

#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
    /// Mutable references must in fact be immutable due to their surrounding immutability in a
    /// `static`. In a `static mut` we start out as mutable and thus can also contain further `&mut`
    /// that will actually be treated as mutable.
    Static,
    /// UnsafeCell is OK in the value of a constant, but not behind references in a constant
    ConstBase,
    /// `UnsafeCell` ICEs
    Const,
}

/// Signalling data structure to ensure we don't recurse
/// into the memory of other constants or statics
struct IsStaticOrFn;

impl<'rt, 'mir, 'tcx> InternVisitor<'rt, 'mir, 'tcx> {
    /// Intern an allocation without looking at its children
    fn intern_shallow(
        &mut self,
        ptr: Pointer,
        mutability: Mutability,
    ) -> InterpResult<'tcx, Option<IsStaticOrFn>> {
        trace!(
            "InternVisitor::intern {:?} with {:?}",
            ptr, mutability,
        );
        // remove allocation
        let tcx = self.ecx.tcx;
        let memory = self.ecx.memory_mut();
        let (kind, mut alloc) = match memory.alloc_map.remove(&ptr.alloc_id) {
            Some(entry) => entry,
            None => {
                // if the pointer is dangling (neither in local nor global memory), we leave it
                // to validation to error. The `delay_span_bug` ensures that we don't forget such
                // a check in validation.
                if tcx.alloc_map.lock().get(ptr.alloc_id).is_none() {
                    tcx.sess.delay_span_bug(self.ecx.tcx.span, "tried to intern dangling pointer");
                }
                // treat dangling pointers like other statics
                // just to stop trying to recurse into them
                return Ok(Some(IsStaticOrFn));
            },
        };
        // This match is just a canary for future changes to `MemoryKind`, which most likely need
        // changes in this function.
        match kind {
            MemoryKind::Stack | MemoryKind::Vtable => {},
        }
        // Ensure llvm knows to only put this into immutable memory if the value is immutable either
        // by being behind a reference or by being part of a static or const without interior
        // mutability
        alloc.mutability = mutability;
        // link the alloc id to the actual allocation
        let alloc = tcx.intern_const_alloc(alloc);
        self.leftover_relocations.extend(alloc.relocations.iter().map(|&(_, ((), reloc))| reloc));
        tcx.alloc_map.lock().set_alloc_id_memory(ptr.alloc_id, alloc);
        Ok(None)
    }
}

impl<'rt, 'mir, 'tcx>
    ValueVisitor<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
for
    InternVisitor<'rt, 'mir, 'tcx>
{
    type V = MPlaceTy<'tcx>;

    #[inline(always)]
    fn ecx(&self) -> &CompileTimeEvalContext<'mir, 'tcx> {
        &self.ecx
    }

    fn visit_aggregate(
        &mut self,
        mplace: MPlaceTy<'tcx>,
        fields: impl Iterator<Item=InterpResult<'tcx, Self::V>>,
    ) -> InterpResult<'tcx> {
        if let Some(def) = mplace.layout.ty.ty_adt_def() {
            if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() {
                // We are crossing over an `UnsafeCell`, we can mutate again
                let old = std::mem::replace(&mut self.mutability, Mutability::Mutable);
                assert_ne!(
                    self.mode, InternMode::Const,
                    "UnsafeCells are not allowed behind references in constants. This should have \
                    been prevented statically by const qualification. If this were allowed one \
                    would be able to change a constant at one use site and other use sites may \
                    arbitrarily decide to change, too.",
                );
                let walked = self.walk_aggregate(mplace, fields);
                self.mutability = old;
                return walked;
            }
        }
        self.walk_aggregate(mplace, fields)
    }

    fn visit_primitive(&mut self, mplace: MPlaceTy<'tcx>) -> InterpResult<'tcx> {
        // Handle Reference types, as these are the only relocations supported by const eval.
        // Raw pointers (and boxes) are handled by the `leftover_relocations` logic.
        let ty = mplace.layout.ty;
        if let ty::Ref(_, _, mutability) = ty.sty {
            let value = self.ecx.read_immediate(mplace.into())?;
            // Handle trait object vtables
            if let Ok(meta) = value.to_meta() {
                let layout = self.ecx.layout_of(ty.builtin_deref(true).unwrap().ty)?;
                if layout.is_unsized() {
                    if let ty::Dynamic(..) = self.ecx.tcx.struct_tail(layout.ty).sty {
                        if let Ok(vtable) = meta.unwrap().to_ptr() {
                            // explitly choose `Immutable` here, since vtables are immutable, even
                            // if the reference of the fat pointer is mutable
                            self.intern_shallow(vtable, Mutability::Immutable)?;
                        }
                    }
                }
            }
            let mplace = self.ecx.ref_to_mplace(value)?;
            // Check if we have encountered this pointer+layout combination before.
            // Only recurse for allocation-backed pointers.
            if let Scalar::Ptr(ptr) = mplace.ptr {
                // We do not have any `frozen` logic here, because it's essentially equivalent to
                // the mutability except for the outermost item. Only `UnsafeCell` can "unfreeze",
                // and we check that in `visit_aggregate`.
                // This is not an inherent limitation, but one that we know to be true, because
                // const qualification enforces it. We can lift it in the future.
                match (self.mode, mutability) {
                    // immutable references are fine everywhere
                    (_, hir::Mutability::MutImmutable) => {},
                    // all is "good and well" in the unsoundness of `static mut`

                    // mutable references are ok in `static`. Either they are treated as immutable
                    // because they are behind an immutable one, or they are behind an `UnsafeCell`
                    // and thus ok.
                    (InternMode::Static, hir::Mutability::MutMutable) => {},
                    // we statically prevent `&mut T` via `const_qualif` and double check this here
                    (InternMode::ConstBase, hir::Mutability::MutMutable) |
                    (InternMode::Const, hir::Mutability::MutMutable) =>
                        bug!("const qualif failed to prevent mutable references"),
                }
                // Compute the mutability with which we'll start visiting the allocation. This is
                // what gets changed when we encounter an `UnsafeCell`
                let mutability = match (self.mutability, mutability) {
                    // The only way a mutable reference actually works as a mutable reference is
                    // by being in a `static mut` directly or behind another mutable reference.
                    // If there's an immutable reference or we are inside a static, then our
                    // mutable reference is equivalent to an immutable one. As an example:
                    // `&&mut Foo` is semantically equivalent to `&&Foo`
                    (Mutability::Mutable, hir::Mutability::MutMutable) => Mutability::Mutable,
                    _ => Mutability::Immutable,
                };
                // Compute the mutability of the allocation
                let intern_mutability = intern_mutability(
                    self.ecx.tcx.tcx,
                    self.param_env,
                    mplace.layout.ty,
                    self.ecx.tcx.span,
                    mutability,
                );
                // Recursing behind references changes the intern mode for constants in order to
                // cause assertions to trigger if we encounter any `UnsafeCell`s.
                let mode = match self.mode {
                    InternMode::ConstBase => InternMode::Const,
                    other => other,
                };
                match self.intern_shallow(ptr, intern_mutability)? {
                    // No need to recurse, these are interned already and statics may have
                    // cycles, so we don't want to recurse there
                    Some(IsStaticOrFn) => {},
                    // intern everything referenced by this value. The mutability is taken from the
                    // reference. It is checked above that mutable references only happen in
                    // `static mut`
                    None => self.ref_tracking.track((mplace, mutability, mode), || ()),
                }
            }
        }
        Ok(())
    }
}

/// Figure out the mutability of the allocation.
/// Mutable if it has interior mutability *anywhere* in the type.
fn intern_mutability<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ParamEnv<'tcx>,
    ty: Ty<'tcx>,
    span: Span,
    mutability: Mutability,
) -> Mutability {
    let has_interior_mutability = !ty.is_freeze(tcx, param_env, span);
    if has_interior_mutability {
        Mutability::Mutable
    } else {
        mutability
    }
}

pub fn intern_const_alloc_recursive(
    ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
    def_id: DefId,
    ret: MPlaceTy<'tcx>,
    // FIXME(oli-obk): can we scrap the param env? I think we can, the final value of a const eval
    // must always be monomorphic, right?
    param_env: ty::ParamEnv<'tcx>,
) -> InterpResult<'tcx> {
    let tcx = ecx.tcx;
    // this `mutability` is the mutability of the place, ignoring the type
    let (mutability, base_intern_mode) = match tcx.static_mutability(def_id) {
        Some(hir::Mutability::MutImmutable) => (Mutability::Immutable, InternMode::Static),
        None => (Mutability::Immutable, InternMode::ConstBase),
        // `static mut` doesn't care about interior mutability, it's mutable anyway
        Some(hir::Mutability::MutMutable) => (Mutability::Mutable, InternMode::Static),
    };

    // type based interning
    let mut ref_tracking = RefTracking::new((ret, mutability, base_intern_mode));
    let leftover_relocations = &mut FxHashSet::default();

    // This mutability is the combination of the place mutability and the type mutability. If either
    // is mutable, `alloc_mutability` is mutable. This exists because the entire allocation needs
    // to be mutable if it contains an `UnsafeCell` anywhere. The other `mutability` exists so that
    // the visitor does not treat everything outside the `UnsafeCell` as mutable.
    let alloc_mutability = intern_mutability(
        tcx.tcx, param_env, ret.layout.ty, tcx.span, mutability,
    );

    // start with the outermost allocation
    InternVisitor {
        ref_tracking: &mut ref_tracking,
        ecx,
        mode: base_intern_mode,
        leftover_relocations,
        param_env,
        mutability,
    }.intern_shallow(ret.ptr.to_ptr()?, alloc_mutability)?;

    while let Some(((mplace, mutability, mode), _)) = ref_tracking.todo.pop() {
        let interned = InternVisitor {
            ref_tracking: &mut ref_tracking,
            ecx,
            mode,
            leftover_relocations,
            param_env,
            mutability,
        }.visit_value(mplace);
        if let Err(error) = interned {
            // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have
            // to read enum discriminants in order to find references in enum variant fields.
            if let InterpError::ValidationFailure(_) = error.kind {
                let err = crate::const_eval::error_to_const_error(&ecx, error);
                match err.struct_error(ecx.tcx, "it is undefined behavior to use this value") {
                    Ok(mut diag) => {
                        diag.note("The rules on what exactly is undefined behavior aren't clear, \
                            so this check might be overzealous. Please open an issue on the rust \
                            compiler repository if you believe it should not be considered \
                            undefined behavior",
                        );
                        diag.emit();
                    }
                    Err(ErrorHandled::TooGeneric) |
                    Err(ErrorHandled::Reported) => {},
                }
            }
        }
    }

    // Intern the rest of the allocations as mutable. These might be inside unions, padding, raw
    // pointers, ... So we can't intern them according to their type rules

    let mut todo: Vec<_> = leftover_relocations.iter().cloned().collect();
    while let Some(alloc_id) = todo.pop() {
        if let Some((_, alloc)) = ecx.memory_mut().alloc_map.remove(&alloc_id) {
            // We can't call the `intern` method here, as its logic is tailored to safe references.
            // So we hand-roll the interning logic here again
            let alloc = tcx.intern_const_alloc(alloc);
            tcx.alloc_map.lock().set_alloc_id_memory(alloc_id, alloc);
            for &(_, ((), reloc)) in alloc.relocations.iter() {
                if leftover_relocations.insert(reloc) {
                    todo.push(reloc);
                }
            }
        } else if ecx.memory().dead_alloc_map.contains_key(&alloc_id) {
            // dangling pointer
            return err!(ValidationFailure(
                "encountered dangling pointer in final constant".into(),
            ))
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>extern crate uuid;
use coin_cbc;

use dsl::LpExprNode::*;
use dsl::*;
use solvers::{Solution, SolverTrait, Status, WithMaxSeconds, WithNbThreads};
use std::collections::HashMap;

/// Solver that calls cbc through [rust bindings](https://github.com/KardinalAI/coin_cbc)
#[derive(Debug, Clone, Default)]
pub struct NativeCbcSolver {
    name: String,
    threads: Option<u32>,
    seconds: Option<u32>,
}

impl NativeCbcSolver {
    pub fn new() -> NativeCbcSolver {
        NativeCbcSolver {
            name: "CbcNative".to_string(),
            threads: None,
            seconds: None,
        }
    }
}

impl WithMaxSeconds<NativeCbcSolver> for NativeCbcSolver {
    fn max_seconds(&self) -> Option<u32> {
        self.seconds
    }
    fn with_max_seconds(&self, seconds: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            seconds: Some(seconds),
            ..self.clone()
        }
    }
}
impl WithNbThreads<NativeCbcSolver> for NativeCbcSolver {
    fn nb_threads(&self) -> Option<u32> {
        self.threads
    }
    fn with_nb_threads(&self, threads: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            threads: Some(threads),
            ..self.clone()
        }
    }
}

fn always_literal(expr_arena: &LpExpression) -> f64 {
    match expr_arena.get_root_expr_ref() {
        &LitVal(num) => num as f64,
        _ => panic!("wrong generalization"),
    }
}

fn add_variable(m: &mut coin_cbc::Model, expr: &LpExprNode) -> coin_cbc::Col {
    match expr {
        ConsInt(LpInteger {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_integer();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsCont(LpContinuous {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_col();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsBin(_) => m.add_binary(),
        _ => panic!("Unexpected LpExpression on LpProblem.variables()!"),
    }
}

impl SolverTrait for NativeCbcSolver {
    type P = LpProblem;

    fn run<'a>(&self, problem: &'a Self::P) -> Result<Solution<'a>, String> {
        let mut m = coin_cbc::Model::default();
        // columns (variables)
        let mut cols: HashMap<String, coin_cbc::Col> = HashMap::new();
        for (name, (constraint_index, lp_expr_arena_index)) in problem.variables() {
            cols.insert(name, add_variable(&mut m, problem.constraints.get(constraint_index).unwrap().0.expr_ref_at(lp_expr_arena_index) ) );
        }
        // rows (constraints)
        for cons in problem.constraints {
            let row = m.add_row();
            let mut general = cons.generalize();
            match general.1 {
                Constraint::GreaterOrEqual => m.set_row_lower(row, always_literal(&general.2)),
                Constraint::LessOrEqual => m.set_row_upper(row, always_literal(&general.2)),
                Constraint::Equal => m.set_row_equal(row, always_literal(&general.2)),
            }
            let mut lst: Vec<_> = Vec::new();
            general.0.simplify().var_lit(general.0.get_root_index(), &mut lst, 1.0);
            lst.iter()
                .for_each(|(n, lit)| m.set_weight(row, cols[n], *lit as f64));
        };
        // objective
        if let Some(mut objective) = &problem.obj_expr_arena {
            let mut lst: Vec<_> = Vec::new();
            objective.simplify().var_lit(objective.get_root_index(), &mut lst, 1.0);
            lst.iter()
                .for_each(|(n, lit)| m.set_obj_coeff(cols[n], *lit as f64))
        }
        m.set_obj_sense(match problem.objective_type {
            LpObjective::Maximize => coin_cbc::Sense::Maximize,
            LpObjective::Minimize => coin_cbc::Sense::Minimize,
        });

        let sol = m.solve();

        Ok(Solution {
            status: match sol.raw().status() {
                coin_cbc::raw::Status::Finished => Status::Optimal,
                coin_cbc::raw::Status::Abandoned => Status::Infeasible,
                _ => Status::NotSolved,
            },
            results: cols
                .iter()
                .map(|(name, col)| (name.to_owned(), sol.col(*col) as f32))
                .collect(),
            related_problem: Some(problem),
        })
    }
}
<commit_msg>debug compilation with `--features "native_coin_cbc"`<commit_after>extern crate uuid;
use coin_cbc;

use dsl::LpExprNode::*;
use dsl::*;
use solvers::{Solution, SolverTrait, Status, WithMaxSeconds, WithNbThreads};
use std::collections::HashMap;

/// Solver that calls cbc through [rust bindings](https://github.com/KardinalAI/coin_cbc)
#[derive(Debug, Clone, Default)]
pub struct NativeCbcSolver {
    name: String,
    threads: Option<u32>,
    seconds: Option<u32>,
}

impl NativeCbcSolver {
    pub fn new() -> NativeCbcSolver {
        NativeCbcSolver {
            name: "CbcNative".to_string(),
            threads: None,
            seconds: None,
        }
    }
}

impl WithMaxSeconds<NativeCbcSolver> for NativeCbcSolver {
    fn max_seconds(&self) -> Option<u32> {
        self.seconds
    }
    fn with_max_seconds(&self, seconds: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            seconds: Some(seconds),
            ..self.clone()
        }
    }
}
impl WithNbThreads<NativeCbcSolver> for NativeCbcSolver {
    fn nb_threads(&self) -> Option<u32> {
        self.threads
    }
    fn with_nb_threads(&self, threads: u32) -> NativeCbcSolver {
        NativeCbcSolver {
            threads: Some(threads),
            ..self.clone()
        }
    }
}

fn always_literal(expr_arena: &LpExpression) -> f64 {
    match expr_arena.get_root_expr_ref() {
        &LitVal(num) => num as f64,
        _ => panic!("wrong generalization"),
    }
}

fn add_variable(m: &mut coin_cbc::Model, expr: &LpExprNode) -> coin_cbc::Col {
    match expr {
        ConsInt(LpInteger {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_integer();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsCont(LpContinuous {
            name: _,
            lower_bound,
            upper_bound,
        }) => {
            let col = m.add_col();
            if let Some(lb) = lower_bound {
                m.set_col_lower(col, *lb as f64)
            }
            if let Some(ub) = upper_bound {
                m.set_col_upper(col, *ub as f64)
            }
            col
        }
        ConsBin(_) => m.add_binary(),
        _ => panic!("Unexpected LpExpression on LpProblem.variables()!"),
    }
}

impl SolverTrait for NativeCbcSolver {
    type P = LpProblem;

    fn run<'a>(&self, problem: &'a Self::P) -> Result<Solution<'a>, String> {
        let mut m = coin_cbc::Model::default();
        // columns (variables)
        let mut cols: HashMap<String, coin_cbc::Col> = HashMap::new();
        for (name, (constraint_index, lp_expr_arena_index)) in problem.variables() {
            cols.insert(name, add_variable(&mut m, problem.constraints.get(constraint_index).unwrap().0.expr_ref_at(lp_expr_arena_index) ) );
        }
        // rows (constraints)
        for cons in problem.constraints.clone() {
            let row = m.add_row();
            let mut general = cons.generalize();
            match general.1 {
                Constraint::GreaterOrEqual => m.set_row_lower(row, always_literal(&general.2)),
                Constraint::LessOrEqual => m.set_row_upper(row, always_literal(&general.2)),
                Constraint::Equal => m.set_row_equal(row, always_literal(&general.2)),
            }
            let mut lst: Vec<_> = Vec::new();
            general.0.simplify();
            let root_index = general.0.get_root_index();
            general.0.var_lit(root_index, &mut lst, 1.0);
            lst.iter()
                .for_each(|(n, lit)| m.set_weight(row, cols[n], *lit as f64));
        };
        // objective
        if let Some(objective) = &problem.obj_expr_arena {
            let mut lst: Vec<_> = Vec::new();
            let mut cloned_objective = objective.clone();
            cloned_objective.simplify();
            let root_index = cloned_objective.get_root_index();
            cloned_objective.var_lit(root_index, &mut lst, 1.0);
            lst.iter()
                .for_each(|(n, lit)| m.set_obj_coeff(cols[n], *lit as f64))
        }
        m.set_obj_sense(match problem.objective_type {
            LpObjective::Maximize => coin_cbc::Sense::Maximize,
            LpObjective::Minimize => coin_cbc::Sense::Minimize,
        });

        let sol = m.solve();

        Ok(Solution {
            status: match sol.raw().status() {
                coin_cbc::raw::Status::Finished => Status::Optimal,
                coin_cbc::raw::Status::Abandoned => Status::Infeasible,
                _ => Status::NotSolved,
            },
            results: cols
                .iter()
                .map(|(name, col)| (name.to_owned(), sol.col(*col) as f32))
                .collect(),
            related_problem: Some(problem),
        })
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::Ordering::{Acquire, Release, Relaxed};
use std::ptr;

use mem::epoch::{self, Atomic, Owned};

/// Treiber's lock-free stack.
///
/// Usable with any number of producers and consumers.
pub struct TreiberStack<T> {
    head: Atomic<Node<T>>,
}

struct Node<T> {
    data: T,
    next: Atomic<Node<T>>,
}

impl<T> TreiberStack<T> {
    /// Crate a new, empty stack.
    pub fn new() -> TreiberStack<T> {
        TreiberStack {
            head: Atomic::null()
        }
    }

    /// Push `t` on top of the stack.
    pub fn push(&self, t: T) {
        let mut n = Owned::new(Node {
            data: t,
            next: Atomic::null()
        });
        let guard = epoch::pin();
        loop {
            let head = self.head.load(Relaxed, &guard);
            n.next.store_shared(head, Relaxed);
            match self.head.cas_and_ref(head, n, Release, &guard) {
                Ok(_) => break,
                Err(owned) => n = owned,
            }
        }
    }

    /// Attempt to pop the top element of the stack.
    ///
    /// Returns `None` if the stack is observed to be empty.
    pub fn pop(&self) -> Option<T> {
        let guard = epoch::pin();
        loop {
            match self.head.load(Acquire, &guard) {
                Some(head) => {
                    let next = head.next.load(Relaxed, &guard);
                    if self.head.cas_shared(Some(head), next, Release) {
                        unsafe {
                            guard.unlinked(head);
                            return Some(ptr::read(&(*head).data))
                        }
                    }
                }
                None => return None
            }
        }
    }
}
<commit_msg>Fix doc typo in TreiberStack<commit_after>use std::sync::atomic::Ordering::{Acquire, Release, Relaxed};
use std::ptr;

use mem::epoch::{self, Atomic, Owned};

/// Treiber's lock-free stack.
///
/// Usable with any number of producers and consumers.
pub struct TreiberStack<T> {
    head: Atomic<Node<T>>,
}

struct Node<T> {
    data: T,
    next: Atomic<Node<T>>,
}

impl<T> TreiberStack<T> {
    /// Create a new, empty stack.
    pub fn new() -> TreiberStack<T> {
        TreiberStack {
            head: Atomic::null()
        }
    }

    /// Push `t` on top of the stack.
    pub fn push(&self, t: T) {
        let mut n = Owned::new(Node {
            data: t,
            next: Atomic::null()
        });
        let guard = epoch::pin();
        loop {
            let head = self.head.load(Relaxed, &guard);
            n.next.store_shared(head, Relaxed);
            match self.head.cas_and_ref(head, n, Release, &guard) {
                Ok(_) => break,
                Err(owned) => n = owned,
            }
        }
    }

    /// Attempt to pop the top element of the stack.
    ///
    /// Returns `None` if the stack is observed to be empty.
    pub fn pop(&self) -> Option<T> {
        let guard = epoch::pin();
        loop {
            match self.head.load(Acquire, &guard) {
                Some(head) => {
                    let next = head.next.load(Relaxed, &guard);
                    if self.head.cas_shared(Some(head), next, Release) {
                        unsafe {
                            guard.unlinked(head);
                            return Some(ptr::read(&(*head).data))
                        }
                    }
                }
                None => return None
            }
        }
    }
}
<|endoftext|>


<commit_before>
#[crate_id="capnp-rpc"];
#[crate_type="bin"];

extern mod capnp;

extern mod extra;

pub mod async;

pub mod calculator_capnp;
pub mod rpc_capnp;
//pub mod rpc-twoparty_capnp;

pub mod rpc;

pub mod testing {
    use capnp;
    use calculator_capnp::Calculator;
    use rpc_capnp::{Message, Return, CapDescriptor};
    use std;

    pub fn connect<T : std::io::Writer>(out_stream : &mut T) {

        capnp::message::MessageBuilder::new_default(|message| {
                let restore = message.init_root::<Message::Builder>().init_restore();
                restore.set_question_id(0);
                restore.init_object_id().set_as_text("calculator");

                capnp::serialize::write_message(out_stream, message);
            });

        capnp::message::MessageBuilder::new_default(|message| {
                let call = message.init_root::<Message::Builder>().init_call();
                call.set_question_id(1);
                let promised_answer = call.init_target().init_promised_answer();
                promised_answer.set_question_id(0);
                call.set_interface_id(0x97983392df35cc36);
                call.set_method_id(0);
                let payload = call.init_params();
                let exp = payload.init_content().init_as_struct::<Calculator::Expression::Builder>();
                exp.set_literal(1.23456);

                capnp::serialize::write_message(out_stream, message);

            });

    }
}

pub fn main() {
    use std::io::process;

    let args = ~[std::os::args()[1].to_owned(), std::os::args()[2].to_owned()];

    let io = [process::CreatePipe(true, false), // stdin
              process::CreatePipe(false, true), // stdout
              process::InheritFd(2)];

    let config = process::ProcessConfig {
        program: "nc",
        args: args,
        env : None,
        cwd: None,
        io : io
    };
    let mut p = process::Process::new(config).unwrap();

    p.io.pop();
    let childStdOut = p.io.pop();
    let mut childStdIn = p.io.pop();

    let (port, chan) = std::comm::SharedChan::<rpc::RpcEvent>::new();

    let listener_chan = chan.clone();

    do spawn || {
        let mut r = childStdOut;

        loop {
            let message = box capnp::serialize::new_reader(
                &mut r,
                capnp::message::DEFAULT_READER_OPTIONS);

            listener_chan.send(rpc::IncomingMessage(message));
        }
    }

    do spawn || { rpc::run_loop(port); }

    testing::connect(&mut childStdIn);

    chan.send(rpc::Nothing);

    p.wait();
}
<commit_msg>update capnp-rpc<commit_after>
#[crate_id="capnp-rpc"];
#[crate_type="bin"];

extern mod capnp;

extern mod extra;

pub mod async;

pub mod calculator_capnp;
pub mod rpc_capnp;
//pub mod rpc-twoparty_capnp;

pub mod rpc;

pub mod testing {
    use capnp;
    use capnp::message::{MessageBuilder, MallocMessageBuilder};
    use calculator_capnp::Calculator;
    use rpc_capnp::{Message};
    use std;

    pub fn connect<T : std::io::Writer>(out_stream : &mut T) {

        let mut message = MallocMessageBuilder::new_default();
        let restore = message.init_root::<Message::Builder>().init_restore();
        restore.set_question_id(0);
        restore.init_object_id().set_as_text("calculator");

        capnp::serialize::write_message(out_stream, &mut message);

        let mut message = MallocMessageBuilder::new_default();
        let call = message.init_root::<Message::Builder>().init_call();
        call.set_question_id(1);
        let promised_answer = call.init_target().init_promised_answer();
        promised_answer.set_question_id(0);
        call.set_interface_id(0x97983392df35cc36);
        call.set_method_id(0);
        let payload = call.init_params();
        let exp = payload.init_content().init_as_struct::<Calculator::Expression::Builder>();
        exp.set_literal(1.23456);

        capnp::serialize::write_message(out_stream, &mut message);
    }
}

pub fn main() {
    use std::io::process;

    let args = ~[std::os::args()[1].to_owned(), std::os::args()[2].to_owned()];

    let io = [process::CreatePipe(true, false), // stdin
              process::CreatePipe(false, true), // stdout
              process::InheritFd(2)];

    let config = process::ProcessConfig {
        program: "nc",
        args: args,
        env : None,
        cwd: None,
        io : io
    };
    let mut p = process::Process::new(config).unwrap();

    p.io.pop();
    let childStdOut = p.io.pop();
    let mut childStdIn = p.io.pop();

    let (port, chan) = std::comm::SharedChan::<rpc::RpcEvent>::new();

    let listener_chan = chan.clone();

    do spawn || {
        let mut r = childStdOut;

        loop {
            let message = box capnp::serialize::new_reader(
                &mut r,
                capnp::message::DEFAULT_READER_OPTIONS);

            listener_chan.send(rpc::IncomingMessage(message));
        }
    }

    do spawn || { rpc::run_loop(port); }

    testing::connect(&mut childStdIn);

    chan.send(rpc::Nothing);

    p.wait();
}
<|endoftext|>


<commit_before>use eew::*;

pub fn format_eew_full(eew: &EEW) -> String
{
	let head = format!("[EEW: {} - {}]\n", eew.id, eew.number);
	let base = format!("source: {:?}, kind: {:?}, issued_at: {:?}, occurred_at: {:?}, status: {:?}\n",
		eew.source, eew.kind, eew.issued_at, eew.occurred_at, eew.status);

	let extended = match eew.detail {

		EEWDetail::Cancel => "".to_string(),

		EEWDetail::Full(ref detail) => {

			let global = format!("issue_pattern: {:?}, epicenter_name: {}, epicenter: {:?}, \
				depth: {:?}, magnitude: {:?}, maximum_intensity: {:?}, epicenter_accuracy: {:?}, \
				depth_accuracy: {:?}, magnitude_accuracy: {:?}, epicenter_category: {:?} \
				warning_status: {:?}, intensity_change: {:?}, change_reason: {:?}\n",
				detail.issue_pattern, detail.epicenter_name, detail.epicenter, detail.depth,
				detail.magnitude, detail.maximum_intensity, detail.epicenter_accuracy,
				detail.depth_accuracy, detail.magnitude_accuracy, detail.epicenter_category,
				detail.warning_status, detail.intensity_change, detail.change_reason);

			let areas = detail.area_info.iter().map(|area|
				format!("area_name: {}, minimum_intensity: {:?}, maximum_intensity: {:?}, \
				reached_at: {:?}, warning_status: {:?}, wave_status: {:?}\n",
				area.area_name, area.minimum_intensity, area.maximum_intensity,
				area.reached_at, area.warning_status, area.wave_status)
			).collect::<Vec<_>>().concat();

			global + &areas
		}
	};

	return head + &base + &extended;
}
<commit_msg>Translator: Fix typo<commit_after>use eew::*;

pub fn format_eew_full(eew: &EEW) -> String
{
	let head = format!("[EEW: {} - {}]\n", eew.id, eew.number);
	let base = format!("source: {:?}, kind: {:?}, issued_at: {:?}, occurred_at: {:?}, status: {:?}\n",
		eew.source, eew.kind, eew.issued_at, eew.occurred_at, eew.status);

	let extended = match eew.detail {

		EEWDetail::Cancel => "".to_string(),

		EEWDetail::Full(ref detail) => {

			let global = format!("issue_pattern: {:?}, epicenter_name: {}, epicenter: {:?}, \
				depth: {:?}, magnitude: {:?}, maximum_intensity: {:?}, epicenter_accuracy: {:?}, \
				depth_accuracy: {:?}, magnitude_accuracy: {:?}, epicenter_category: {:?}, \
				warning_status: {:?}, intensity_change: {:?}, change_reason: {:?}\n",
				detail.issue_pattern, detail.epicenter_name, detail.epicenter, detail.depth,
				detail.magnitude, detail.maximum_intensity, detail.epicenter_accuracy,
				detail.depth_accuracy, detail.magnitude_accuracy, detail.epicenter_category,
				detail.warning_status, detail.intensity_change, detail.change_reason);

			let areas = detail.area_info.iter().map(|area|
				format!("area_name: {}, minimum_intensity: {:?}, maximum_intensity: {:?}, \
				reached_at: {:?}, warning_status: {:?}, wave_status: {:?}\n",
				area.area_name, area.minimum_intensity, area.maximum_intensity,
				area.reached_at, area.warning_status, area.wave_status)
			).collect::<Vec<_>>().concat();

			global + &areas
		}
	};

	return head + &base + &extended;
}
<|endoftext|>


<commit_before>extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

#[cfg(any(feature = "profiling-listener", feature = "profiling-tracker"))]
use chrono::Utc;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{PlaybackContext, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Ready,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Stopped,
    Seeking(bool, bool), // (must_switch_to_play, must_keep_paused)
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)
const TRACKER_PERIOD: u32 = 17; //  17 ms (60 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    export_toc_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let export_toc_btn: gtk::Button = builder.get_object("export_toc-btn").unwrap();
        export_toc_btn.set_sensitive(false);

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            export_toc_btn: export_toc_btn,

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
            tracker_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.export_toc_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    fn play_pause(&mut self) {
        let context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    self.register_tracker();
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    self.remove_tracker();
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        match self.state {
            ControllerState::Stopped | ControllerState::PlayingRange(_) => (),
            _ => {
                if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                    self.info_ctrl.borrow_mut().seek(position, &self.state);
                    self.audio_ctrl.borrow_mut().seek(position, &self.state);
                }

                let (must_switch_to_play, must_keep_paused) = match self.state {
                    ControllerState::EOS | ControllerState::Ready => (true, false),
                    ControllerState::Paused => (false, true),
                    _ => (false, false),
                };
                self.state = ControllerState::Seeking(must_switch_to_play, must_keep_paused);

                self.context
                    .as_ref()
                    .expect("MainController::seek no context")
                    .seek(position, accurate);
            }
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            self.register_tracker();
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    fn hold(&mut self) {
        self.remove_tracker();
        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };

        self.play_pause_btn.set_icon_name("media-playback-start");
        self.window.set_sensitive(false);
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn restore_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.window.set_sensitive(true);
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking(must_switch_to_play, must_keep_paused) => {
                                if must_switch_to_play {
                                    this.context
                                        .as_ref()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    this.register_tracker();
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if must_keep_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();

                        let context = this.context.take().expect(
                            "MainController: InitDone but no context available",
                        );

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController::listener(InitDone) failed to lock media info")
                            .video_best
                            .is_some();

                        this.header_bar.set_subtitle(
                            Some(context.file_name.as_str()),
                        );

                        this.video_ctrl.new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);

                        this.context = Some(context);

                        this.state = ControllerState::Ready;
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                this.remove_tracker();
                                this.seek(pos_to_restore, true); // accurate
                            }
                            ControllerState::Playing => { // regular EOS
                                let position = this.get_position();
                                this.info_ctrl.borrow_mut().tick(position, true);

                                this.audio_ctrl.borrow_mut().tick();

                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tracker will be register again in case of a seek
                                this.remove_tracker();
                            }
                            _ => { // inconsistent state
                                println!("MainController::listener(eos) inconsistent state {:?}",
                                    this.state);
                                this.state = ControllerState::Paused;
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();

                        this.context = None;
                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.listener_src = None;
                this.tracker_src = None;
            }

            glib::Continue(keep_going)
        }));
    }

    fn remove_tracker(&mut self) {
        if let Some(source_id) = self.tracker_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_tracker(&mut self) {
        if self.tracker_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.tracker_src = Some(gtk::timeout_add(TRACKER_PERIOD, move || {
            #[cfg(feature = "profiling-tracker")]
            let start = Utc::now();

            let mut this = this_rc.borrow_mut();

            #[cfg(feature = "profiling-tracker")]
            let before_tick = Utc::now();

            {
                let position = this.context
                    .as_mut()
                    .expect("MainController::tracker no context while getting position")
                    .get_position();
                this.info_ctrl.borrow_mut().tick(position, false);
                this.audio_ctrl.borrow_mut().tick();
            }

            #[cfg(feature = "profiling-tracker")]
            let end = Utc::now();

            #[cfg(feature = "profiling-tracker")]
            println!(
                "tracker,{},{},{}",
                start.time().format("%H:%M:%S%.6f"),
                before_tick.time().format("%H:%M:%S%.6f"),
                end.time().format("%H:%M:%S%.6f"),
            );

            glib::Continue(this.keep_going)
        }));
    }

    fn select_media(&mut self) {
        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            self.state = ControllerState::Paused;
            self.window.set_sensitive(true);
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");
        self.export_toc_btn.set_sensitive(false);

        let (ctx_tx, ui_rx) = channel();

        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
                self.export_toc_btn.set_sensitive(true);
            }
            Err(error) => {
                eprintln!("Error opening media: {}", error);
            }
        };

        self.window.set_sensitive(true);
    }
}
<commit_msg>MainController: fix EOS handling for all states (#84)<commit_after>extern crate glib;
extern crate gstreamer as gst;
extern crate gtk;

#[cfg(any(feature = "profiling-listener", feature = "profiling-tracker"))]
use chrono::Utc;

use std::rc::Rc;
use std::cell::RefCell;

use std::path::PathBuf;

use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver};

use gtk::prelude::*;

use media::{PlaybackContext, ContextMessage};
use media::ContextMessage::*;

use super::{AudioController, ExportController, InfoController, VideoController};

#[derive(Clone, Debug, PartialEq)]
pub enum ControllerState {
    EOS,
    Ready,
    Paused,
    PendingExportToc,
    PendingSelectMedia,
    Playing,
    PlayingRange(u64),
    Stopped,
    Seeking(bool, bool), // (must_switch_to_play, must_keep_paused)
}

const LISTENER_PERIOD: u32 = 250; // 250 ms (4 Hz)
const TRACKER_PERIOD: u32 = 17; //  17 ms (60 Hz)

pub struct MainController {
    window: gtk::ApplicationWindow,
    header_bar: gtk::HeaderBar,
    play_pause_btn: gtk::ToolButton,
    export_toc_btn: gtk::Button,

    video_ctrl: VideoController,
    info_ctrl: Rc<RefCell<InfoController>>,
    audio_ctrl: Rc<RefCell<AudioController>>,
    export_ctrl: Rc<RefCell<ExportController>>,

    context: Option<PlaybackContext>,
    state: ControllerState,

    requires_async_dialog: bool, // when pipeline contains video, dialogs must wait for
    // asyncdone before opening a dialog otherwise the listener
    // may borrow the MainController while the dialog is already
    // using it leading to a borrowing conflict
    this_opt: Option<Rc<RefCell<MainController>>>,
    keep_going: bool,
    listener_src: Option<glib::SourceId>,
    tracker_src: Option<glib::SourceId>,
}

impl MainController {
    pub fn new(builder: &gtk::Builder) -> Rc<RefCell<Self>> {
        let export_toc_btn: gtk::Button = builder.get_object("export_toc-btn").unwrap();
        export_toc_btn.set_sensitive(false);

        let this = Rc::new(RefCell::new(MainController {
            window: builder.get_object("application-window").unwrap(),
            header_bar: builder.get_object("header-bar").unwrap(),
            play_pause_btn: builder.get_object("play_pause-toolbutton").unwrap(),
            export_toc_btn: export_toc_btn,

            video_ctrl: VideoController::new(builder),
            info_ctrl: InfoController::new(builder),
            audio_ctrl: AudioController::new(builder),
            export_ctrl: ExportController::new(builder),

            context: None,
            state: ControllerState::Stopped,

            requires_async_dialog: false,

            this_opt: None,
            keep_going: true,
            listener_src: None,
            tracker_src: None,
        }));

        {
            let mut this_mut = this.borrow_mut();

            let this_rc = Rc::clone(&this);
            this_mut.this_opt = Some(this_rc);

            this_mut.window.connect_delete_event(|_, _| {
                gtk::main_quit();
                Inhibit(false)
            });
            this_mut.window.set_titlebar(&this_mut.header_bar);

            let this_rc = Rc::clone(&this);
            this_mut.play_pause_btn.connect_clicked(move |_| {
                this_rc.borrow_mut().play_pause();
            });

            // TODO: add key bindings to seek by steps
            // play/pause, etc.

            let this_rc = Rc::clone(&this);
            this_mut.export_toc_btn.connect_clicked(move |_| {
                let mut this = this_rc.borrow_mut();

                if this.requires_async_dialog && this.state == ControllerState::Playing {
                    this.hold();
                    this.state = ControllerState::PendingExportToc;
                } else {
                    this.hold();
                    this.export_toc();
                }
            });

            this_mut.video_ctrl.register_callbacks(&this);
            InfoController::register_callbacks(&this_mut.info_ctrl, &this);
            AudioController::register_callbacks(&this_mut.audio_ctrl, &this);
            ExportController::register_callbacks(&this_mut.export_ctrl, &this);
        }

        let open_btn: gtk::Button = builder.get_object("open-btn").unwrap();
        let this_rc = Rc::clone(&this);
        open_btn.connect_clicked(move |_| {
            let mut this = this_rc.borrow_mut();

            if this.requires_async_dialog && this.state == ControllerState::Playing {
                this.hold();
                this.state = ControllerState::PendingSelectMedia;
            } else {
                this.hold();
                this.select_media();
            }
        });

        this
    }

    pub fn show_all(&self) {
        self.window.show_all();
    }

    pub fn get_state(&self) -> &ControllerState {
        &self.state
    }

    fn play_pause(&mut self) {
        let context = match self.context.take() {
            Some(context) => context,
            None => {
                self.select_media();
                return;
            }
        };

        if self.state != ControllerState::EOS {
            match context.get_state() {
                gst::State::Paused => {
                    self.register_tracker();
                    self.play_pause_btn.set_icon_name("media-playback-pause");
                    self.state = ControllerState::Playing;
                    context.play().unwrap();
                    self.context = Some(context);
                }
                gst::State::Playing => {
                    context.pause().unwrap();
                    self.play_pause_btn.set_icon_name("media-playback-start");
                    self.remove_tracker();
                    self.state = ControllerState::Paused;
                    self.context = Some(context);
                }
                _ => {
                    self.context = Some(context);
                    self.select_media();
                }
            };
        } else {
            // Restart the stream from the begining
            self.context = Some(context);
            self.seek(0, true); // accurate (slow)
        }
    }

    pub fn seek(&mut self, position: u64, accurate: bool) {
        match self.state {
            ControllerState::Stopped | ControllerState::PlayingRange(_) => (),
            _ => {
                if self.state == ControllerState::Playing || self.state == ControllerState::Paused {
                    self.info_ctrl.borrow_mut().seek(position, &self.state);
                    self.audio_ctrl.borrow_mut().seek(position, &self.state);
                }

                let (must_switch_to_play, must_keep_paused) = match self.state {
                    ControllerState::EOS | ControllerState::Ready => (true, false),
                    ControllerState::Paused => (false, true),
                    _ => (false, false),
                };
                self.state = ControllerState::Seeking(must_switch_to_play, must_keep_paused);

                self.context
                    .as_ref()
                    .expect("MainController::seek no context")
                    .seek(position, accurate);
            }
        }
    }

    pub fn play_range(&mut self, start: u64, end: u64, pos_to_restore: u64) {
        if self.state == ControllerState::Paused {
            self.info_ctrl.borrow_mut().start_play_range();
            self.audio_ctrl.borrow_mut().start_play_range();

            self.state = ControllerState::PlayingRange(pos_to_restore);

            self.context
                .as_ref()
                .expect("MainController::play_range no context")
                .seek_range(start, end);

            self.register_tracker();
        }
    }

    pub fn get_position(&mut self) -> u64 {
        self.context
            .as_mut()
            .expect("MainController::get_position no context")
            .get_position()
    }

    fn hold(&mut self) {
        self.remove_tracker();
        if let Some(context) = self.context.as_mut() {
            context.pause().unwrap();
        };

        self.play_pause_btn.set_icon_name("media-playback-start");
        self.window.set_sensitive(false);
    }

    fn export_toc(&mut self) {
        if let Some(mut context) = self.context.take() {
            self.info_ctrl.borrow().export_chapters(&mut context);
            self.export_ctrl.borrow_mut().open(context);
            self.state = ControllerState::Paused;
        }
    }

    pub fn restore_context(&mut self, context: PlaybackContext) {
        self.context = Some(context);
        self.state = ControllerState::Paused;
        self.window.set_sensitive(true);
    }

    fn remove_listener(&mut self) {
        if let Some(source_id) = self.listener_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_listener(&mut self, timeout: u32, ui_rx: Receiver<ContextMessage>) {
        if self.listener_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.listener_src = Some(gtk::timeout_add(timeout, move || {
            let mut keep_going = true;

            for message in ui_rx.try_iter() {
                match message {
                    AsyncDone => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PendingSelectMedia => this.select_media(),
                            ControllerState::PendingExportToc => this.export_toc(),
                            ControllerState::Seeking(must_switch_to_play, must_keep_paused) => {
                                if must_switch_to_play {
                                    this.context
                                        .as_ref()
                                        .expect("MainController::listener(AsyncDone) no context")
                                        .play()
                                        .unwrap();
                                    this.register_tracker();
                                    this.play_pause_btn.set_icon_name("media-playback-pause");
                                    this.state = ControllerState::Playing;
                                } else if must_keep_paused {
                                    this.state = ControllerState::Paused;
                                } else {
                                    this.state = ControllerState::Playing;
                                }
                            }
                            _ => (),
                        }
                    }
                    InitDone => {
                        let mut this = this_rc.borrow_mut();

                        let context = this.context.take().expect(
                            "MainController: InitDone but no context available",
                        );

                        this.requires_async_dialog = context
                            .info
                            .lock()
                            .expect("MainController::listener(InitDone) failed to lock media info")
                            .video_best
                            .is_some();

                        this.header_bar.set_subtitle(
                            Some(context.file_name.as_str()),
                        );

                        this.video_ctrl.new_media(&context);
                        this.info_ctrl.borrow_mut().new_media(&context);
                        this.audio_ctrl.borrow_mut().new_media(&context);

                        this.context = Some(context);

                        this.state = ControllerState::Ready;
                    }
                    Eos => {
                        let mut this = this_rc.borrow_mut();
                        match this.state {
                            ControllerState::PlayingRange(pos_to_restore) => {
                                // end of range => pause and seek back to pos_to_restore
                                this.context
                                    .as_ref()
                                    .expect("MainController::listener(eos) no context")
                                    .pause()
                                    .unwrap();
                                this.state = ControllerState::Paused;
                                this.remove_tracker();
                                this.seek(pos_to_restore, true); // accurate
                            }
                            _ => {
                                let position = this.get_position();
                                this.info_ctrl.borrow_mut().tick(position, true);

                                this.audio_ctrl.borrow_mut().tick();

                                #[cfg(feature = "trace-main-controller")]
                                println!("MainController::listener(eos)");

                                this.play_pause_btn.set_icon_name("media-playback-start");
                                this.state = ControllerState::EOS;

                                // The tracker will be register again in case of a seek
                                this.remove_tracker();
                            }
                        }
                    }
                    FailedToOpenMedia => {
                        eprintln!("ERROR: failed to open media");

                        let mut this = this_rc.borrow_mut();

                        this.context = None;
                        this.keep_going = false;
                        keep_going = false;
                    }
                    _ => (),
                };

                if !keep_going {
                    break;
                }
            }

            if !keep_going {
                let mut this = this_rc.borrow_mut();
                this.listener_src = None;
                this.tracker_src = None;
            }

            glib::Continue(keep_going)
        }));
    }

    fn remove_tracker(&mut self) {
        if let Some(source_id) = self.tracker_src.take() {
            glib::source_remove(source_id);
        }
    }

    fn register_tracker(&mut self) {
        if self.tracker_src.is_some() {
            return;
        }

        let this_rc = Rc::clone(self.this_opt.as_ref().unwrap());

        self.tracker_src = Some(gtk::timeout_add(TRACKER_PERIOD, move || {
            #[cfg(feature = "profiling-tracker")]
            let start = Utc::now();

            let mut this = this_rc.borrow_mut();

            #[cfg(feature = "profiling-tracker")]
            let before_tick = Utc::now();

            {
                let position = this.context
                    .as_mut()
                    .expect("MainController::tracker no context while getting position")
                    .get_position();
                this.info_ctrl.borrow_mut().tick(position, false);
                this.audio_ctrl.borrow_mut().tick();
            }

            #[cfg(feature = "profiling-tracker")]
            let end = Utc::now();

            #[cfg(feature = "profiling-tracker")]
            println!(
                "tracker,{},{},{}",
                start.time().format("%H:%M:%S%.6f"),
                before_tick.time().format("%H:%M:%S%.6f"),
                end.time().format("%H:%M:%S%.6f"),
            );

            glib::Continue(this.keep_going)
        }));
    }

    fn select_media(&mut self) {
        let file_dlg = gtk::FileChooserDialog::new(
            Some("Open a media file"),
            Some(&self.window),
            gtk::FileChooserAction::Open,
        );
        // Note: couldn't find equivalents for STOCK_OK
        file_dlg.add_button("Open", gtk::ResponseType::Ok.into());

        if file_dlg.run() == gtk::ResponseType::Ok.into() {
            if let Some(ref context) = self.context {
                context.stop();
            }
            self.open_media(file_dlg.get_filename().unwrap());
        } else {
            self.state = ControllerState::Paused;
            self.window.set_sensitive(true);
        }

        file_dlg.close();
    }

    fn open_media(&mut self, filepath: PathBuf) {
        self.remove_listener();

        self.info_ctrl.borrow_mut().cleanup();
        self.audio_ctrl.borrow_mut().cleanup();
        self.header_bar.set_subtitle("");
        self.export_toc_btn.set_sensitive(false);

        let (ctx_tx, ui_rx) = channel();

        self.keep_going = true;
        self.register_listener(LISTENER_PERIOD, ui_rx);

        let dbl_buffer_mtx = Arc::clone(&self.audio_ctrl.borrow().get_dbl_buffer_mtx());
        match PlaybackContext::new(filepath, dbl_buffer_mtx, ctx_tx) {
            Ok(context) => {
                self.context = Some(context);
                self.export_toc_btn.set_sensitive(true);
            }
            Err(error) => {
                eprintln!("Error opening media: {}", error);
            }
        };

        self.window.set_sensitive(true);
    }
}
<|endoftext|>


<commit_before>use ::PASCAL_STRING_BUF_SIZE;

pub struct PascalString {
    len: u8,
    chars_buf: [u8; PASCAL_STRING_BUF_SIZE]
}
<commit_msg>Add some documentation<commit_after>use ::PASCAL_STRING_BUF_SIZE;

pub struct PascalString {
    /// The number of bytes used in the string.
    len: u8,
    /// The internal character buffer, encoded in utf8
    chars_buf: [u8; PASCAL_STRING_BUF_SIZE]
}
<|endoftext|>


<commit_before>use conduit::Request;
use conduit_middleware::Middleware;
use pg::pool::{PostgresConnectionPool, PooledPostgresConnection};
use std::any::AnyRefExt;
use std::fmt::Show;

use db;

use std::sync::Arc;

pub struct App {
    db: PostgresConnectionPool,
}

pub struct AppMiddleware {
    app: Arc<App>
}

impl App {
    pub fn new() -> App {
        let pool = db::pool();
        db::setup(&*pool.get_connection());
        App { db: db::pool() }
    }

    pub fn db(&self) -> PooledPostgresConnection {
        self.db.get_connection()
    }
}

impl AppMiddleware {
    pub fn new(app: App) -> AppMiddleware {
        AppMiddleware { app: Arc::new(app) }
    }
}

impl Middleware for AppMiddleware {
    fn before<'a>(&self,
                  req: &'a mut Request) -> Result<&'a mut Request, Box<Show>> {
        req.mut_extensions().insert("crates.io.app", box self.app.clone());
        Ok(req)
    }
}

pub trait RequestApp<'a> {
    fn app(self) -> &'a App;
}

impl<'a> RequestApp<'a> for &'a mut Request {
    fn app(self) -> &'a App {
        &**self.extensions().find(&"crates.io.app")
               .and_then(|a| a.as_ref::<Arc<App>>())
               .expect("Missing app")
    }
}
<commit_msg>Update to master<commit_after>use conduit::Request;
use conduit_middleware::Middleware;
use pg::pool::{PostgresConnectionPool, PooledPostgresConnection};
use std::any::AnyRefExt;
use std::fmt::Show;

use db;

use std::sync::Arc;

pub struct App {
    db: PostgresConnectionPool,
}

pub struct AppMiddleware {
    app: Arc<App>
}

impl App {
    pub fn new() -> App {
        let pool = db::pool();
        db::setup(&*pool.get_connection());
        App { db: db::pool() }
    }

    pub fn db(&self) -> PooledPostgresConnection {
        self.db.get_connection()
    }
}

impl AppMiddleware {
    pub fn new(app: App) -> AppMiddleware {
        AppMiddleware { app: Arc::new(app) }
    }
}

impl Middleware for AppMiddleware {
    fn before(&self, req: &mut Request) -> Result<(), Box<Show>> {
        req.mut_extensions().insert("crates.io.app", box self.app.clone());
        Ok(())
    }
}

pub trait RequestApp<'a> {
    fn app(self) -> &'a App;
}

impl<'a> RequestApp<'a> for &'a mut Request {
    fn app(self) -> &'a App {
        &**self.extensions().find(&"crates.io.app")
               .and_then(|a| a.as_ref::<Arc<App>>())
               .expect("Missing app")
    }
}
<|endoftext|>