<commit_before>//! `EventListener` class contains a set of call-back functions that will
//! be called when specific RocksDB event happens such as flush.

use error::Status;
use types::SequenceNumber;
use table_properties::{TableProperties, TablePropertiesCollection};
use options::CompressionType;
use compaction_job_stats::CompactionJobStats;

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TableFileCreationReason {
    Flush,
    Compaction,
    Recovery,
}

pub struct TableFileCreationBriefInfo<'a> {
    /// the name of the database where the file was created
    pub db_name: &'a str,
    /// the name of the column family where the file was created.
    pub cf_name: &'a str,
    /// the path to the created file.
    pub file_path: &'a str,
    /// the id of the job (which could be flush or compaction) that
    /// created the file.
    pub job_id: i32,
    /// reason of creating the table.
    pub reason: TableFileCreationReason,
}

pub struct TableFileCreationInfo<'a> {
    brief_info: TableFileCreationBriefInfo<'a>,
    /// the size of the file.
    pub file_size: u64,
    /// Detailed properties of the created file.
    pub table_properties: TableProperties<'a>,
    /// The status indicating whether the creation was successful or not.
    pub status: Status,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CompactionReason {
    Unknown,
    /// [Level] number of L0 files > level0_file_num_compaction_trigger
    LevelL0FilesNum,
    /// [Level] total size of level > MaxBytesForLevel()
    LevelMaxLevelSize,
    /// [Universal] Compacting for size amplification
    UniversalSizeAmplification,
    /// [Universal] Compacting for size ratio
    UniversalSizeRatio,
    /// [Universal] number of sorted runs > level0_file_num_compaction_trigger
    UniversalSortedRunNum,
    /// [FIFO] total size > max_table_files_size
    FIFOMaxSize,
    /// Manual compaction
    ManualCompaction,
    /// `DB::SuggestCompactRange()` marked files for compaction
    FilesMarkedForCompaction,
}

pub struct TableFileDeletionInfo<'a> {
    /// The name of the database where the file was deleted.
    pub db_name: &'a str,
    /// The path to the deleted file.
    pub file_path: &'a str,
    /// The id of the job which deleted the file.
    pub job_id: i32,
    /// The status indicating whether the deletion was successful or not.
    pub status: Status,
}

pub struct FlushJobInfo<'a> {
    /// the name of the column family
    pub cf_name: &'a str,
    /// the path to the newly created file
    pub file_path: &'a str,
    /// the id of the thread that completed this flush job.
    pub thread_id: u64,
    /// the job id, which is unique in the same thread.
    pub job_id: i32,
    /// If true, then rocksdb is currently slowing-down all writes to prevent
    /// creating too many Level 0 files as compaction seems not able to
    /// catch up the write request speed.  This indicates that there are
    /// too many files in Level 0.
    pub triggered_writes_slowdown: bool,
    /// If true, then rocksdb is currently blocking any writes to prevent
    /// creating more L0 files.  This indicates that there are too many
    /// files in level 0.  Compactions should try to compact L0 files down
    /// to lower levels as soon as possible.
    pub triggered_writes_stop: bool,
    /// The smallest sequence number in the newly created file
    pub smallest_seqno: SequenceNumber,
    /// The largest sequence number in the newly created file
    pub largest_seqno: SequenceNumber,
    /// Table properties of the table being flushed
    pub table_properties: TableProperties<'a>,
}

pub struct CompactionJobInfo<'a> {
    /// the name of the column family where the compaction happened.
    pub cf_name: &'a str,
    /// the status indicating whether the compaction was successful or not.
    pub status: Status,
    /// the id of the thread that completed this compaction job.
    pub thread_id: u64,
    /// the job id, which is unique in the same thread.
    pub job_id: i32,
    /// the smallest input level of the compaction.
    pub base_input_level: i32,
    /// the output level of the compaction.
    pub output_level: i32,
    /// the names of the compaction input files.
    pub input_files: Vec<&'a str>,

    /// the names of the compaction output files.
    pub output_files: Vec<&'a str>,
    /// Table properties for input and output tables.
    /// The map is keyed by values from input_files and output_files.
    pub table_properties: TablePropertiesCollection,

    /// Reason to run the compaction
    pub compaction_reason: CompactionReason,

    /// Compression algorithm used for output files
    pub compression: CompressionType,

    /// If non-null, this variable stores detailed information
    /// about this compaction.
    pub stats: CompactionJobStats<'a>,
}

pub struct MemTableInfo<'a> {
    /// the name of the column family to which memtable belongs
    pub cf_name: &'a str,
    /// Sequence number of the first element that was inserted
    /// into the memtable.
    pub first_seqno: SequenceNumber,
    /// Sequence number that is guaranteed to be smaller than or equal
    /// to the sequence number of any key that could be inserted into this
    /// memtable. It can then be assumed that any write with a larger(or equal)
    /// sequence number will be present in this memtable or a later memtable.
    pub earliest_seqno: SequenceNumber,
    /// Total number of entries in memtable
    pub num_entries: u64,
    /// Total number of deletes in memtable
    pub num_deletes: u64,
}

pub struct ExternalFileIngestionInfo<'a> {
    /// the name of the column family
    pub cf_name: &'a str,
    /// Path of the file outside the DB
    pub external_file_path: &'a str,
    /// Path of the file inside the DB
    pub internal_file_path: &'a str,
    /// The global sequence number assigned to keys in this file
    pub global_seqno: SequenceNumber,
    /// Table properties of the table being flushed
    pub table_properties: TableProperties<'a>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CompactionListenerValueType {
    Value,
    MergeOperand,
    Delete,
    SingleDelete,
    RangeDelete,
    Invalid,
}

// A call-back function to RocksDB which will be called when the compaction
// iterator is compacting values. It is mean to be returned from
// EventListner::GetCompactionEventListner() at the beginning of compaction
// job.
pub trait CompactionEventListener {
    fn on_compaction(
        &mut self,
        level: i32,
        key: &[u8],
        value_type: CompactionListenerValueType,
        existing_value: &[u8],
        sn: SequenceNumber,
        is_new: bool,
    );
}

/// `EventListener` class contains a set of call-back functions that will
/// be called when specific RocksDB event happens such as flush.  It can
/// be used as a building block for developing custom features such as
/// stats-collector or external compaction algorithm.
///
/// Note that call-back functions should not run for an extended period of
/// time before the function returns, otherwise RocksDB may be blocked.
/// For example, it is not suggested to do `DB::CompactFiles()` (as it may
/// run for a long while) or issue many of `DB::Put()` (as Put may be blocked
/// in certain cases) in the same thread in the `EventListener` callback.
/// However, doing `DB::CompactFiles()` and `DB::Put()` in another thread is
/// considered safe.
///
/// [Threading] All `EventListener` callback will be called using the
/// actual thread that involves in that specific event.   For example, it
/// is the RocksDB background flush thread that does the actual flush to
/// call `EventListener::OnFlushCompleted()`.
///
/// [Locking] All `EventListener` callbacks are designed to be called without
/// the current thread holding any DB mutex. This is to prevent potential
/// deadlock and performance issue when using EventListener callback
/// in a complex way. However, all `EventListener` call-back functions
/// should not run for an extended period of time before the function
/// returns, otherwise RocksDB may be blocked. For example, it is not
/// suggested to do `DB::CompactFiles()` (as it may run for a long while)
/// or issue many of `DB::Put()` (as Put may be blocked in certain cases)
/// in the same thread in the `EventListener` callback. However, doing
/// `DB::CompactFiles()` and `DB::Put()` in a thread other than the
/// EventListener callback thread is considered safe.
///
/// FIXME: how to hold DB ref and CFHandle ref
pub trait EventListener {
    /// A call-back function to RocksDB which will be called whenever a
    /// registered RocksDB flushes a file.  The default implementation is
    /// no-op.
    ///
    /// Note that the this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns.  Otherwise, RocksDB may be blocked.
    fn on_flush_completed(&mut self, db: *mut (), flush_job_info: &FlushJobInfo) {}

    /// A call-back function to RocksDB which will be called before a
    /// RocksDB starts to flush memtables.  The default implementation is
    /// no-op.
    ///
    /// Note that the this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns.  Otherwise, RocksDB may be blocked.
    fn on_flush_begin(&mut self, db: *mut (), flush_job_info: &FlushJobInfo) {}

    /// A call-back function for RocksDB which will be called whenever
    /// a SST file is deleted.  Different from OnCompactionCompleted and
    /// OnFlushCompleted, this call-back is designed for external logging
    /// service and thus only provide string parameters instead
    /// of a pointer to DB.  Applications that build logic basic based
    /// on file creations and deletions is suggested to implement
    /// OnFlushCompleted and OnCompactionCompleted.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from the
    /// returned value.
    fn on_table_file_deleted(&mut self, info: &TableFileDeletionInfo) {}

    /// A call-back function for RocksDB which will be called whenever
    /// a registered RocksDB compacts a file. The default implementation
    /// is a no-op.
    ///
    /// Note that this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns. Otherwise, RocksDB may be blocked.
    ///
    /// @param db a pointer to the rocksdb instance which just compacted
    ///   a file.
    /// @param ci a reference to a CompactionJobInfo struct. 'ci' is released
    ///  after this function is returned, and must be copied if it is needed
    ///  outside of this function.
    fn on_compaction_completed(&mut self, db: *mut (), ci: &CompactionJobInfo) {}

    /// A call-back function for RocksDB which will be called whenever
    /// a SST file is created.  Different from OnCompactionCompleted and
    /// OnFlushCompleted, this call-back is designed for external logging
    /// service and thus only provide string parameters instead
    /// of a pointer to DB.  Applications that build logic basic based
    /// on file creations and deletions is suggested to implement
    /// OnFlushCompleted and OnCompactionCompleted.
    ///
    /// Historically it will only be called if the file is successfully created.
    /// Now it will also be called on failure case. User can check info.status
    /// to see if it succeeded or not.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from these
    /// returned value.
    fn on_table_file_created(&mut self, info: &TableFileCreationInfo) {}

    /// A call-back function for RocksDB which will be called before
    /// a SST file is being created. It will follow by OnTableFileCreated after
    /// the creation finishes.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from these
    /// returned value.
    fn on_table_file_creation_started(&mut self, info: &TableFileCreationBriefInfo) {}

    /// A call-back function for RocksDB which will be called before
    /// a memtable is made immutable.
    ///
    /// Note that the this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns.  Otherwise, RocksDB may be blocked.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from these
    /// returned value.
    fn on_memtable_sealed(&mut self, info: &MemTableInfo) {}

    // A call-back function for RocksDB which will be called before
    // a column family handle is deleted.
    //
    // Note that the this function must be implemented in a way such that
    // it should not run for an extended period of time before the function
    // returns.  Otherwise, RocksDB may be blocked.
    // @param handle is a pointer to the column family handle to be deleted
    // which will become a dangling pointer after the deletion.
    // pub fn on_column_family_handle_deletion_started(&mut self, handle: *mut ()) {}

    /// A call-back function for RocksDB which will be called after an external
    /// file is ingested using IngestExternalFile.
    ///
    /// Note that the this function will run on the same thread as
    /// IngestExternalFile(), if this function is blocked, IngestExternalFile()
    /// will be blocked from finishing.
    fn on_external_file_ingested(&mut self, db: *mut (), info: &ExternalFileIngestionInfo) {}

    /// Factory method to return CompactionEventListener. If multiple listeners
    /// provides CompactionEventListner, only the first one will be used.
    fn get_compaction_event_listener(&mut self) -> Option<Box<CompactionEventListener>> {
        None
    }
}
<commit_msg>listener: now use DBRef as callback parameter<commit_after>//! `EventListener` class contains a set of call-back functions that will
//! be called when specific RocksDB event happens such as flush.

use error::Status;
use db::DBRef;
use types::SequenceNumber;
use table_properties::{TableProperties, TablePropertiesCollection};
use options::CompressionType;
use compaction_job_stats::CompactionJobStats;

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum TableFileCreationReason {
    Flush,
    Compaction,
    Recovery,
}

pub struct TableFileCreationBriefInfo<'a> {
    /// the name of the database where the file was created
    pub db_name: &'a str,
    /// the name of the column family where the file was created.
    pub cf_name: &'a str,
    /// the path to the created file.
    pub file_path: &'a str,
    /// the id of the job (which could be flush or compaction) that
    /// created the file.
    pub job_id: i32,
    /// reason of creating the table.
    pub reason: TableFileCreationReason,
}

pub struct TableFileCreationInfo<'a> {
    brief_info: TableFileCreationBriefInfo<'a>,
    /// the size of the file.
    pub file_size: u64,
    /// Detailed properties of the created file.
    pub table_properties: TableProperties<'a>,
    /// The status indicating whether the creation was successful or not.
    pub status: Status,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CompactionReason {
    Unknown,
    /// [Level] number of L0 files > level0_file_num_compaction_trigger
    LevelL0FilesNum,
    /// [Level] total size of level > MaxBytesForLevel()
    LevelMaxLevelSize,
    /// [Universal] Compacting for size amplification
    UniversalSizeAmplification,
    /// [Universal] Compacting for size ratio
    UniversalSizeRatio,
    /// [Universal] number of sorted runs > level0_file_num_compaction_trigger
    UniversalSortedRunNum,
    /// [FIFO] total size > max_table_files_size
    FIFOMaxSize,
    /// Manual compaction
    ManualCompaction,
    /// `DB::SuggestCompactRange()` marked files for compaction
    FilesMarkedForCompaction,
}

pub struct TableFileDeletionInfo<'a> {
    /// The name of the database where the file was deleted.
    pub db_name: &'a str,
    /// The path to the deleted file.
    pub file_path: &'a str,
    /// The id of the job which deleted the file.
    pub job_id: i32,
    /// The status indicating whether the deletion was successful or not.
    pub status: Status,
}

pub struct FlushJobInfo<'a> {
    /// the name of the column family
    pub cf_name: &'a str,
    /// the path to the newly created file
    pub file_path: &'a str,
    /// the id of the thread that completed this flush job.
    pub thread_id: u64,
    /// the job id, which is unique in the same thread.
    pub job_id: i32,
    /// If true, then rocksdb is currently slowing-down all writes to prevent
    /// creating too many Level 0 files as compaction seems not able to
    /// catch up the write request speed.  This indicates that there are
    /// too many files in Level 0.
    pub triggered_writes_slowdown: bool,
    /// If true, then rocksdb is currently blocking any writes to prevent
    /// creating more L0 files.  This indicates that there are too many
    /// files in level 0.  Compactions should try to compact L0 files down
    /// to lower levels as soon as possible.
    pub triggered_writes_stop: bool,
    /// The smallest sequence number in the newly created file
    pub smallest_seqno: SequenceNumber,
    /// The largest sequence number in the newly created file
    pub largest_seqno: SequenceNumber,
    /// Table properties of the table being flushed
    pub table_properties: TableProperties<'a>,
}

pub struct CompactionJobInfo<'a> {
    /// the name of the column family where the compaction happened.
    pub cf_name: &'a str,
    /// the status indicating whether the compaction was successful or not.
    pub status: Status,
    /// the id of the thread that completed this compaction job.
    pub thread_id: u64,
    /// the job id, which is unique in the same thread.
    pub job_id: i32,
    /// the smallest input level of the compaction.
    pub base_input_level: i32,
    /// the output level of the compaction.
    pub output_level: i32,
    /// the names of the compaction input files.
    pub input_files: Vec<&'a str>,

    /// the names of the compaction output files.
    pub output_files: Vec<&'a str>,
    /// Table properties for input and output tables.
    /// The map is keyed by values from input_files and output_files.
    pub table_properties: TablePropertiesCollection,

    /// Reason to run the compaction
    pub compaction_reason: CompactionReason,

    /// Compression algorithm used for output files
    pub compression: CompressionType,

    /// If non-null, this variable stores detailed information
    /// about this compaction.
    pub stats: CompactionJobStats<'a>,
}

pub struct MemTableInfo<'a> {
    /// the name of the column family to which memtable belongs
    pub cf_name: &'a str,
    /// Sequence number of the first element that was inserted
    /// into the memtable.
    pub first_seqno: SequenceNumber,
    /// Sequence number that is guaranteed to be smaller than or equal
    /// to the sequence number of any key that could be inserted into this
    /// memtable. It can then be assumed that any write with a larger(or equal)
    /// sequence number will be present in this memtable or a later memtable.
    pub earliest_seqno: SequenceNumber,
    /// Total number of entries in memtable
    pub num_entries: u64,
    /// Total number of deletes in memtable
    pub num_deletes: u64,
}

pub struct ExternalFileIngestionInfo<'a> {
    /// the name of the column family
    pub cf_name: &'a str,
    /// Path of the file outside the DB
    pub external_file_path: &'a str,
    /// Path of the file inside the DB
    pub internal_file_path: &'a str,
    /// The global sequence number assigned to keys in this file
    pub global_seqno: SequenceNumber,
    /// Table properties of the table being flushed
    pub table_properties: TableProperties<'a>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CompactionListenerValueType {
    Value,
    MergeOperand,
    Delete,
    SingleDelete,
    RangeDelete,
    Invalid,
}

// A call-back function to RocksDB which will be called when the compaction
// iterator is compacting values. It is mean to be returned from
// EventListner::GetCompactionEventListner() at the beginning of compaction
// job.
pub trait CompactionEventListener {
    fn on_compaction(
        &mut self,
        level: i32,
        key: &[u8],
        value_type: CompactionListenerValueType,
        existing_value: &[u8],
        sn: SequenceNumber,
        is_new: bool,
    );
}

/// `EventListener` class contains a set of call-back functions that will
/// be called when specific RocksDB event happens such as flush.  It can
/// be used as a building block for developing custom features such as
/// stats-collector or external compaction algorithm.
///
/// Note that call-back functions should not run for an extended period of
/// time before the function returns, otherwise RocksDB may be blocked.
/// For example, it is not suggested to do `DB::CompactFiles()` (as it may
/// run for a long while) or issue many of `DB::Put()` (as Put may be blocked
/// in certain cases) in the same thread in the `EventListener` callback.
/// However, doing `DB::CompactFiles()` and `DB::Put()` in another thread is
/// considered safe.
///
/// [Threading] All `EventListener` callback will be called using the
/// actual thread that involves in that specific event.   For example, it
/// is the RocksDB background flush thread that does the actual flush to
/// call `EventListener::OnFlushCompleted()`.
///
/// [Locking] All `EventListener` callbacks are designed to be called without
/// the current thread holding any DB mutex. This is to prevent potential
/// deadlock and performance issue when using EventListener callback
/// in a complex way. However, all `EventListener` call-back functions
/// should not run for an extended period of time before the function
/// returns, otherwise RocksDB may be blocked. For example, it is not
/// suggested to do `DB::CompactFiles()` (as it may run for a long while)
/// or issue many of `DB::Put()` (as Put may be blocked in certain cases)
/// in the same thread in the `EventListener` callback. However, doing
/// `DB::CompactFiles()` and `DB::Put()` in a thread other than the
/// EventListener callback thread is considered safe.
///
/// FIXME: how to hold DB ref and CFHandle ref
pub trait EventListener {
    /// A call-back function to RocksDB which will be called whenever a
    /// registered RocksDB flushes a file.  The default implementation is
    /// no-op.
    ///
    /// Note that the this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns.  Otherwise, RocksDB may be blocked.
    fn on_flush_completed(&mut self, db: &DBRef, flush_job_info: &FlushJobInfo) {}

    /// A call-back function to RocksDB which will be called before a
    /// RocksDB starts to flush memtables.  The default implementation is
    /// no-op.
    ///
    /// Note that the this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns.  Otherwise, RocksDB may be blocked.
    fn on_flush_begin(&mut self, db: &DBRef, flush_job_info: &FlushJobInfo) {}

    /// A call-back function for RocksDB which will be called whenever
    /// a SST file is deleted.  Different from OnCompactionCompleted and
    /// OnFlushCompleted, this call-back is designed for external logging
    /// service and thus only provide string parameters instead
    /// of a pointer to DB.  Applications that build logic basic based
    /// on file creations and deletions is suggested to implement
    /// OnFlushCompleted and OnCompactionCompleted.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from the
    /// returned value.
    fn on_table_file_deleted(&mut self, info: &TableFileDeletionInfo) {}

    /// A call-back function for RocksDB which will be called whenever
    /// a registered RocksDB compacts a file. The default implementation
    /// is a no-op.
    ///
    /// Note that this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns. Otherwise, RocksDB may be blocked.
    ///
    /// @param db a pointer to the rocksdb instance which just compacted
    ///   a file.
    /// @param ci a reference to a CompactionJobInfo struct. 'ci' is released
    ///  after this function is returned, and must be copied if it is needed
    ///  outside of this function.
    fn on_compaction_completed(&mut self, db: &DBRef, ci: &CompactionJobInfo) {}

    /// A call-back function for RocksDB which will be called whenever
    /// a SST file is created.  Different from OnCompactionCompleted and
    /// OnFlushCompleted, this call-back is designed for external logging
    /// service and thus only provide string parameters instead
    /// of a pointer to DB.  Applications that build logic basic based
    /// on file creations and deletions is suggested to implement
    /// OnFlushCompleted and OnCompactionCompleted.
    ///
    /// Historically it will only be called if the file is successfully created.
    /// Now it will also be called on failure case. User can check info.status
    /// to see if it succeeded or not.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from these
    /// returned value.
    fn on_table_file_created(&mut self, info: &TableFileCreationInfo) {}

    /// A call-back function for RocksDB which will be called before
    /// a SST file is being created. It will follow by OnTableFileCreated after
    /// the creation finishes.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from these
    /// returned value.
    fn on_table_file_creation_started(&mut self, info: &TableFileCreationBriefInfo) {}

    /// A call-back function for RocksDB which will be called before
    /// a memtable is made immutable.
    ///
    /// Note that the this function must be implemented in a way such that
    /// it should not run for an extended period of time before the function
    /// returns.  Otherwise, RocksDB may be blocked.
    ///
    /// Note that if applications would like to use the passed reference
    /// outside this function call, they should make copies from these
    /// returned value.
    fn on_memtable_sealed(&mut self, info: &MemTableInfo) {}

    // A call-back function for RocksDB which will be called before
    // a column family handle is deleted.
    //
    // Note that the this function must be implemented in a way such that
    // it should not run for an extended period of time before the function
    // returns.  Otherwise, RocksDB may be blocked.
    // @param handle is a pointer to the column family handle to be deleted
    // which will become a dangling pointer after the deletion.
    // pub fn on_column_family_handle_deletion_started(&mut self, handle: *mut ()) {}

    /// A call-back function for RocksDB which will be called after an external
    /// file is ingested using IngestExternalFile.
    ///
    /// Note that the this function will run on the same thread as
    /// IngestExternalFile(), if this function is blocked, IngestExternalFile()
    /// will be blocked from finishing.
    fn on_external_file_ingested(&mut self, db: &DBRef, info: &ExternalFileIngestionInfo) {}

    /// Factory method to return CompactionEventListener. If multiple listeners
    /// provides CompactionEventListner, only the first one will be used.
    fn get_compaction_event_listener(&mut self) -> Option<Box<CompactionEventListener>> {
        None
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis::general::StatusedTypeId;

pub fn start_comments<W: Write>(w: &mut W) -> Result<()>{
    let v = vec![
        "// 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>"
    ];
    for s in v {
        try!(writeln!(w, "{}", s));
    }

    Ok(())
}

pub fn objects_child_type<W: Write>(w: &mut W, type_name: &str, glib_name: &str) -> Result<()>{
    try!(writeln!(w, ""));
    try!(writeln!(w, "pub type {} = Object<ffi::{}>;", type_name, glib_name));

    Ok(())
}

pub fn impl_parents<W: Write>(w: &mut W, type_name: &str, parents: &Vec<StatusedTypeId>) -> Result<()>{
    try!(writeln!(w, ""));
    for stid in parents {
        //TODO: don't generate for parents without traits
        try!(writeln!(w, "unsafe impl Upcast<{}> for {} {{ }}", stid.name, type_name));
    }

    Ok(())
}

pub fn impl_static_type<W: Write>(w: &mut W, type_name: &str, glib_func_name: &str) -> Result<()>{
    try!(writeln!(w, ""));
    try!(writeln!(w, "impl types::StaticType for {} {{", type_name));
    try!(writeln!(w, "    #[inline]"));
    try!(writeln!(w, "    fn static_type() -> types::Type {{"));
    try!(writeln!(w, "        unsafe {{ from_glib(ffi::{}()) }}", glib_func_name));
    try!(writeln!(w, "    }}"));
    try!(writeln!(w, "}}"));

    Ok(())
}
<commit_msg>Made indentation by numbers<commit_after>use std::io::{Result, Write};

use analysis::general::StatusedTypeId;

pub fn start_comments<W: Write>(w: &mut W) -> Result<()>{
    let v = vec![
        "// 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>"
    ];
    for s in v {
        try!(writeln!(w, "{}", s));
    }

    Ok(())
}

pub fn objects_child_type<W: Write>(w: &mut W, type_name: &str, glib_name: &str) -> Result<()>{
    try!(writeln!(w, ""));
    try!(writeln!(w, "pub type {} = Object<ffi::{}>;", type_name, glib_name));

    Ok(())
}

pub fn impl_parents<W: Write>(w: &mut W, type_name: &str, parents: &Vec<StatusedTypeId>) -> Result<()>{
    try!(writeln!(w, ""));
    for stid in parents {
        //TODO: don't generate for parents without traits
        try!(writeln!(w, "unsafe impl Upcast<{}> for {} {{ }}", stid.name, type_name));
    }

    Ok(())
}

pub fn impl_static_type<W: Write>(w: &mut W, type_name: &str, glib_func_name: &str) -> Result<()>{
    try!(writeln!(w, ""));
    try!(writeln!(w, "impl types::StaticType for {} {{", type_name));
    try!(writeln!(w, "{}#[inline]", tabs(1)));
    try!(writeln!(w, "{}fn static_type() -> types::Type {{", tabs(1)));
    try!(writeln!(w, "{}unsafe {{ from_glib(ffi::{}()) }}", tabs(2), glib_func_name));
    try!(writeln!(w, "{}}}", tabs(1)));
    try!(writeln!(w, "}}"));

    Ok(())
}

//TODO: convert to macro with usage
//format!(indent!(5, "format:{}"), 6)
pub fn tabs(num: i32) -> String {
    (0..num).map(|_| "    ").collect::<String>()
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// 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.

#![feature(test)]
#![allow(dead_code)]

#[macro_use]
extern crate exonum;
extern crate futures;
extern crate tempdir;
extern crate test;

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use tempdir::TempDir;
    use futures::sync::mpsc;
    use test::Bencher;
    use exonum::storage::{Database, Fork, Patch, ProofMapIndex, StorageValue, RocksDB,
                          RocksDBOptions};
    use exonum::blockchain::{Blockchain, Transaction};
    use exonum::blockchain::transaction::ExecutionContext;
    use exonum::crypto::{gen_keypair, Hash, PublicKey, SecretKey};
    use exonum::messages::Message;
    use exonum::helpers::{Height, ValidatorId};
    use exonum::node::ApiSender;

    fn create_blockchain(db: Box<Database>) -> Blockchain {
        let dummy_channel = mpsc::channel(1);
        let dummy_keypair = (PublicKey::zero(), SecretKey::zero());
        Blockchain::new(
            db,
            Vec::new(),
            dummy_keypair.0,
            dummy_keypair.1,
            ApiSender::new(dummy_channel.0),
        )
    }

    fn execute_timestamping(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = create_blockchain(db);

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field data: &Hash      [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, _: &mut ExecutionContext) {}
        }

        fn prepare_txs(height: u64, count: u64) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let (pub_key, sec_key) = gen_keypair();
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(&pub_key, &i.hash(), &sec_key);
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain
                .create_patch(ValidatorId::zero(), Height(height), txs, pool)
                .1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    fn execute_cryptocurrency(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = create_blockchain(db);

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field to:   &PublicKey [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, context: &mut ExecutionContext) {
                let mut index = ProofMapIndex::new("balances_txs", context.fork());
                let from_balance = index.get(self.from()).unwrap_or(0u64);
                let to_balance = index.get(self.to()).unwrap_or(0u64);
                index.put(self.from(), from_balance - 1);
                index.put(self.to(), to_balance + 1);
            }
        }

        let mut keys = Vec::new();

        for _ in 0..10_000 {
            keys.push(gen_keypair());
        }

        fn prepare_txs(
            height: u64,
            count: u64,
            keys: &[(PublicKey, SecretKey)],
        ) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(
                    &keys[i as usize % 10_000].0,
                    &keys[(i as usize + 3_456) % 10_000].0,
                    &keys[i as usize % 10_000].1,
                );
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain
                .create_patch(ValidatorId::zero(), Height(height), txs, pool)
                .1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000, &keys);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000, &keys);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    fn create_rocksdb(tempdir: &TempDir) -> Box<Database> {
        let mut options = RocksDBOptions::default();
        options.create_if_missing(true);
        let db = Box::new(RocksDB::open(tempdir.path(), &options).unwrap());
        db as Box<Database>
    }

    #[bench]
    fn bench_execute_block_timestamping_rocksdb(b: &mut Bencher) {
        let tempdir = TempDir::new("exonum").unwrap();
        let db = create_rocksdb(&tempdir);
        execute_timestamping(db, b)
    }

    #[bench]
    fn bench_execute_block_cryptocurrency_rocksdb(b: &mut Bencher) {
        let tempdir = TempDir::new("exonum").unwrap();
        let db = create_rocksdb(&tempdir);
        execute_cryptocurrency(db, b)
    }
}
<commit_msg>Fix benchmarks<commit_after>// Copyright 2017 The Exonum Team
//
// 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.

#![feature(test)]
#![allow(dead_code)]

#[macro_use]
extern crate exonum;
extern crate futures;
extern crate tempdir;
extern crate test;

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use tempdir::TempDir;
    use futures::sync::mpsc;
    use test::Bencher;
    use exonum::storage::{Database, Fork, Patch, ProofMapIndex, StorageValue, RocksDB,
                          RocksDBOptions};
    use exonum::blockchain::{Blockchain, Transaction};
    use exonum::crypto::{gen_keypair, Hash, PublicKey, SecretKey};
    use exonum::messages::Message;
    use exonum::helpers::{Height, ValidatorId};
    use exonum::node::ApiSender;

    fn create_blockchain(db: Box<Database>) -> Blockchain {
        let dummy_channel = mpsc::channel(1);
        let dummy_keypair = (PublicKey::zero(), SecretKey::zero());
        Blockchain::new(
            db,
            Vec::new(),
            dummy_keypair.0,
            dummy_keypair.1,
            ApiSender::new(dummy_channel.0),
        )
    }

    fn execute_timestamping(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = create_blockchain(db);

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field data: &Hash      [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, _: &mut Fork) {}
        }

        fn prepare_txs(height: u64, count: u64) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let (pub_key, sec_key) = gen_keypair();
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(&pub_key, &i.hash(), &sec_key);
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain
                .create_patch(ValidatorId::zero(), Height(height), txs, pool)
                .1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    fn execute_cryptocurrency(db: Box<Database>, b: &mut Bencher) {
        let mut blockchain = create_blockchain(db);

        message! {
            struct Tx {
                const TYPE = 1;
                const ID = 0;
                const SIZE = 64;

                field from: &PublicKey [00 => 32]
                field to:   &PublicKey [32 => 64]
            }
        }

        impl Transaction for Tx {
            fn verify(&self) -> bool {
                self.verify_signature(self.from())
            }

            fn execute(&self, context: &mut ExecutionContext) {
                let mut index = ProofMapIndex::new("balances_txs", context.fork());
                let from_balance = index.get(self.from()).unwrap_or(0u64);
                let to_balance = index.get(self.to()).unwrap_or(0u64);
                index.put(self.from(), from_balance - 1);
                index.put(self.to(), to_balance + 1);
            }
        }

        let mut keys = Vec::new();

        for _ in 0..10_000 {
            keys.push(gen_keypair());
        }

        fn prepare_txs(
            height: u64,
            count: u64,
            keys: &[(PublicKey, SecretKey)],
        ) -> (Vec<Hash>, BTreeMap<Hash, Box<Transaction>>) {
            let mut txs = Vec::new();
            let mut pool = BTreeMap::new();
            for i in (height * count)..((height + 1) * count) {
                let tx = Tx::new(
                    &keys[i as usize % 10_000].0,
                    &keys[(i as usize + 3_456) % 10_000].0,
                    &keys[i as usize % 10_000].1,
                );
                let tx_hash = Transaction::hash(&tx);
                txs.push(tx_hash);
                pool.insert(tx_hash, Box::new(tx) as Box<Transaction>);
            }
            (txs, pool)
        }

        fn execute_block(
            blockchain: &Blockchain,
            height: u64,
            txs: &[Hash],
            pool: &BTreeMap<Hash, Box<Transaction>>,
        ) -> Patch {
            blockchain
                .create_patch(ValidatorId::zero(), Height(height), txs, pool)
                .1
        }

        for i in 0..100 {
            let (txs, pool) = prepare_txs(i, 1000, &keys);
            let patch = execute_block(&blockchain, i, &txs, &pool);
            blockchain.merge(patch).unwrap();
        }

        let (txs, pool) = prepare_txs(100, 1000, &keys);

        b.iter(|| execute_block(&blockchain, 100, &txs, &pool));
    }

    fn create_rocksdb(tempdir: &TempDir) -> Box<Database> {
        let mut options = RocksDBOptions::default();
        options.create_if_missing(true);
        let db = Box::new(RocksDB::open(tempdir.path(), &options).unwrap());
        db as Box<Database>
    }

    #[bench]
    fn bench_execute_block_timestamping_rocksdb(b: &mut Bencher) {
        let tempdir = TempDir::new("exonum").unwrap();
        let db = create_rocksdb(&tempdir);
        execute_timestamping(db, b)
    }

    #[bench]
    fn bench_execute_block_cryptocurrency_rocksdb(b: &mut Bencher) {
        let tempdir = TempDir::new("exonum").unwrap();
        let db = create_rocksdb(&tempdir);
        execute_cryptocurrency(db, b)
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::mem;
use std::mem::transmute;
use std::ptr;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

enum DropBehaviour {
    ShouldDrop,
    NoDrop
}

struct MaybeNonDropPointer <T> {
    raw_ptr: usize,
    _marker: PhantomData<*const T>
}

impl<T> MaybeNonDropPointer <T> {
    fn new(ptr: *mut T, drop_behaviour: DropBehaviour) -> MaybeNonDropPointer<T> {
        match drop_behaviour {
            DropBehaviour::ShouldDrop => MaybeNonDropPointer {
                raw_ptr: (ptr as usize) | 1,
                _marker: PhantomData
            },
            DropBehaviour::NoDrop => MaybeNonDropPointer {
                raw_ptr: (ptr as usize) & (!1 as usize),
                _marker: PhantomData
            }
        }
    }

    fn ptr(&self) -> *const T {
        unsafe {
            let untagged = self.raw_ptr & (!1 as usize);
            transmute(untagged)
        }
    }

    fn mut_ptr(&mut self) -> *mut T {
        unsafe {
            let untagged = self.raw_ptr & (!1 as usize);
            transmute(untagged)
        }
    }

    fn should_drop(&self) -> bool {
        (self.raw_ptr as usize) & 1 == 1
    }
}

trait Allocator {
    fn allocate<T>(cap: usize) -> *mut T;
    unsafe fn deallocate<T>(ptr: *mut T, cap: usize);
}

struct DefaultHeap {}

impl Allocator for DefaultHeap {
    fn allocate<T>(cap: usize) -> *mut T {
        let mut vec = Vec::<T>::with_capacity(cap);
        let ptr = vec.as_mut_ptr();
        mem::forget(vec);

        ptr
    }

    unsafe fn deallocate<T>(ptr: *mut T, cap: usize) {
        let _will_be_dropped = Vec::from_raw_parts(ptr, 0, cap);
    }
}

struct MaybeNonDropVec <T, A: Allocator = DefaultHeap> {
    ptr: MaybeNonDropPointer<T>,
    len: usize,
    cap: usize,
    _alloc: PhantomData
}

impl<T, A: Allocator> MaybeNonDropVec<T, A> {
    pub fn with_capacity(cap: usize) -> MaybeNonDropVec<T, A> {
        MaybeNonDropVec {
            ptr: MaybeNonDropPointer::new(A::allocate::<T>(cap), DropBehaviour::ShouldDrop),
            len: 0,
            cap: cap,
            _alloc: PhantomData
        }
    }

    pub fn from_persistent(ptr: *mut T, len: usize, cap: usize) -> MaybeNonDropVec<T, A> {
        MaybeNonDropVec {
            ptr: MaybeNonDropPointer::new(ptr, DropBehaviour::NoDrop),
            len: len,
            cap: cap,
            _alloc: PhantomData
        }
    }

    fn maybe_drop(&mut self) {
        if self.ptr.should_drop() {
            unsafe {
                ptr::drop_in_place(&mut self[..]);
                A::deallocate(self.ptr.mut_ptr(), self.cap);
            }
        }
    }

    fn double_buf(&mut self) {
        let mut vec = Vec::<T>::with_capacity(self.cap * 2);
        let new_ptr = vec.as_mut_ptr();

        unsafe {
            ptr::copy_nonoverlapping(self.ptr.ptr(), new_ptr, self.len);
        }
        self.maybe_drop();
        self.ptr = MaybeNonDropPointer::new(new_ptr, DropBehaviour::ShouldDrop);
    }

    pub fn push(&mut self, value: T) {
        if self.len == self.cap {
            self.double_buf();
        }

        unsafe {
            let end = self.as_mut_ptr().offset(self.len as isize);
            ptr::write(end, value);
            self.len += 1;
        }
    }

    pub fn pop(&mut self) -> Option<T> {
        if self.len == 0 {
            None
        } else {
            unsafe {
                self.len -= 1;
                Some(ptr::read(self.get_unchecked(self.len())))
            }
        }
    }
}

impl<T, A: Allocator> Drop for MaybeNonDropVec<T, A> {
    fn drop(&mut self) {
        self.maybe_drop();
    }
}

impl<T, A: Allocator> Deref for MaybeNonDropVec<T, A> {
    type Target = [T];

    fn deref(&self) -> &[T] {
        unsafe {
            std::slice::from_raw_parts(self.ptr.ptr(), self.len)
        }
    }
}

impl<T, A: Allocator> DerefMut for MaybeNonDropVec<T, A> {
    fn deref_mut(&mut self) -> &mut [T] {
        unsafe {
            std::slice::from_raw_parts_mut(self.ptr.mut_ptr(), self.len)
        }
    }
}

struct InlineVecPointer<T> {
    offset: u32,
    len: u16,
    cap: u16,
    _marker: PhantomData<T>
}

fn main () {

}<commit_msg>Sketched out whole actor update cycle with macros #9<commit_after>#![allow(dead_code)]
use std::mem;
use std::mem::transmute;
use std::ptr;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

enum DropBehaviour {
    ShouldDrop,
    NoDrop
}

struct MaybeNonDropPointer <T> {
    raw_ptr: usize,
    _marker: PhantomData<*const T>
}

impl<T> MaybeNonDropPointer <T> {
    fn new(ptr: *mut T, drop_behaviour: DropBehaviour) -> MaybeNonDropPointer<T> {
        MaybeNonDropPointer{
            raw_ptr: match drop_behaviour{
                DropBehaviour::ShouldDrop => (ptr as usize) | 1,
                DropBehaviour::NoDrop => (ptr as usize) & (!1 as usize)
            },
            _marker: PhantomData
        }
    }

    fn ptr(&self) -> *const T {
        unsafe {
            let untagged = self.raw_ptr & (!1 as usize);
            transmute(untagged)
        }
    }

    fn mut_ptr(&mut self) -> *mut T {
        unsafe {
            let untagged = self.raw_ptr & (!1 as usize);
            transmute(untagged)
        }
    }

    fn should_drop(&self) -> bool {
        (self.raw_ptr as usize) & 1 == 1
    }
}

trait Allocator {
    fn allocate<T>(cap: usize) -> *mut T;
    unsafe fn deallocate<T>(ptr: *mut T, cap: usize);
}

struct DefaultHeap {}

impl Allocator for DefaultHeap {
    fn allocate<T>(cap: usize) -> *mut T {
        let mut vec = Vec::<T>::with_capacity(cap);
        let ptr = vec.as_mut_ptr();
        mem::forget(vec);

        ptr
    }

    unsafe fn deallocate<T>(ptr: *mut T, cap: usize) {
        let _will_be_dropped = Vec::from_raw_parts(ptr, 0, cap);
    }
}

struct BackedVec <T, A: Allocator = DefaultHeap> {
    ptr: MaybeNonDropPointer<T>,
    len: usize,
    cap: usize,
    _alloc: PhantomData
}

impl<T, A: Allocator> BackedVec<T, A> {
    pub fn with_capacity(cap: usize) -> BackedVec<T, A> {
        BackedVec {
            ptr: MaybeNonDropPointer::new(A::allocate::<T>(cap), DropBehaviour::ShouldDrop),
            len: 0,
            cap: cap,
            _alloc: PhantomData
        }
    }

    pub fn from_backing(ptr: *mut T, len: usize, cap: usize) -> BackedVec<T, A> {
        BackedVec {
            ptr: MaybeNonDropPointer::new(ptr, DropBehaviour::NoDrop),
            len: len,
            cap: cap,
            _alloc: PhantomData
        }
    }

    fn maybe_drop(&mut self) {
        if self.ptr.should_drop() {
            unsafe {
                ptr::drop_in_place(&mut self[..]);
                A::deallocate(self.ptr.mut_ptr(), self.cap);
            }
        }
    }

    fn double_buf(&mut self) {
        let mut vec = Vec::<T>::with_capacity(self.cap * 2);
        let new_ptr = vec.as_mut_ptr();

        unsafe {
            ptr::copy_nonoverlapping(self.ptr.ptr(), new_ptr, self.len);
        }
        self.maybe_drop();
        self.ptr = MaybeNonDropPointer::new(new_ptr, DropBehaviour::ShouldDrop);
    }

    pub fn push(&mut self, value: T) {
        if self.len == self.cap {
            self.double_buf();
        }

        unsafe {
            let end = self.as_mut_ptr().offset(self.len as isize);
            ptr::write(end, value);
            self.len += 1;
        }
    }

    pub fn pop(&mut self) -> Option<T> {
        if self.len == 0 {
            None
        } else {
            unsafe {
                self.len -= 1;
                Some(ptr::read(self.get_unchecked(self.len())))
            }
        }
    }
}

impl<T, A: Allocator> Drop for BackedVec<T, A> {
    fn drop(&mut self) {
        self.maybe_drop();
    }
}

impl<T, A: Allocator> Deref for BackedVec<T, A> {
    type Target = [T];

    fn deref(&self) -> &[T] {
        unsafe {
            std::slice::from_raw_parts(self.ptr.ptr(), self.len)
        }
    }
}

impl<T, A: Allocator> DerefMut for BackedVec<T, A> {
    fn deref_mut(&mut self) -> &mut [T] {
        unsafe {
            std::slice::from_raw_parts_mut(self.ptr.mut_ptr(), self.len)
        }
    }
}

struct GrowablePointer<T> {
    offset: u32,
    len_bytes: u16,
    cap_bytes: u16,
    _marker: PhantomData<T>
}

trait GrowablePointerToValue<T> {
    fn set_from_new(&mut self, offset: usize, new: &Option<T>, old: &Self);
    unsafe fn get(ptr: *mut u8, lenu8bytes: usize, cap_bytes: usize) -> T;
}

impl<T> GrowablePointerToValue<BackedVec<T>> for GrowablePointer<BackedVec<T>> {
    fn set_from_new(&mut self, offset: usize, new: &Option<BackedVec<T>>, old: &Self) {
        self.offset = offset as u32;
        self.len_bytes = match new {&Some(ref new_vec) => (new_vec.len * mem::size_of::<T>()) as u16, &None => old.len_bytes};
        self.cap_bytes = match new {&Some(ref new_vec) => (new_vec.cap * mem::size_of::<T>()) as u16, &None => old.cap_bytes};
    }

    unsafe fn get(ptr: *mut u8, len_bytes: usize, cap_bytes: usize) -> BackedVec<T> {
        BackedVec::from_backing(mem::transmute(ptr), len_bytes / mem::size_of::<T>(), cap_bytes / mem::size_of::<T>())
    }
}

impl<T> Clone for GrowablePointer<T> {
    fn clone(&self) -> GrowablePointer<T> {
        match *self {
            GrowablePointer {
                offset: offset,
                len_bytes: len_bytes,
                cap_bytes: cap_bytes,
                _marker: _
            } => GrowablePointer {
                offset: offset,
                len_bytes: len_bytes,
                cap_bytes: cap_bytes,
                _marker: PhantomData
            }
        }
    }

}

macro_rules! actor {
    (struct $name:ident $fields:tt growables $ifields:tt $update_impl:item) => {
        actor_combined_struct!($name ; $fields ; $ifields);
        actor_update_fields_struct!(GrowablesUpdate ; $ifields);
        actor_impl!($name ; $ifields ; GrowablesUpdate);
        $update_impl
    }
}

macro_rules! actor_combined_struct {
    ($name:ident ; {$($field:ident : $ftype:ty),*} ; {$($ifield:ident: $itype:ty),*}) => {
        #[derive(Clone)]
        struct $name {
            $($field: $ftype),*,
            $($ifield: GrowablePointer<$itype>),*
        }
    }
}

macro_rules! actor_impl {
    ($name:ident ; $ifields:tt ; $update_struct:ident)  => {
        impl $name {
            actor_update_wrapper!($ifields ; $update_struct);
            actor_growables_getters!($ifields);
        }
    }
}

macro_rules! actor_update_fields_struct {
    ($name:ident ; {$($ifield:ident: $itype:ty),*}) => {
        struct $name {
            $($ifield: Option<$itype>),*
        }
    }
}

macro_rules! actor_update_wrapper {
    ($ifields:tt ; $update_struct:ident) => {
        pub fn update_and_resize<F: Fn(usize) -> (*mut u8, usize, usize), G: Fn(usize, usize, usize)>
        (&mut self, get_slot: F, move_to_slot: G) {
            let field_updates : Option<$update_struct> = self.update();
            match field_updates {
                None | Some(actor_update_fields_all_none!($update_struct ; $ifields)) => {return},
                Some(new_fields) => {
                    let mut new_self = self.clone();
                    let mut total_size_requirement = mem::size_of::<Self>();
                    let mut current_offset = mem::size_of::<Self>();

                    actor_ifields_resize_calc!($ifields ; new_fields ; self ; new_self ; total_size_requirement ; current_offset);

                    let (new_slot, new_slot_cap, new_slot_id) = get_slot(total_size_requirement);
                    unsafe {
                        let self_in_new_slot : *mut Self = mem::transmute(new_slot);
                        *self_in_new_slot = new_self.clone();
                    }

                    actor_ifields_copy!($ifields ; new_fields ; self ; new_self ; new_slot);

                    move_to_slot(self.id, new_slot_cap, new_slot_id);
                }
            }
        }
    }
}

macro_rules! actor_update_fields_all_none {
    ($struct_name:ident ; {$($ifield:ident: $vtype:ty),*}) => {
        $struct_name {
            $($ifield: None),*
        }
    }
}

macro_rules! actor_growables_getters {
    ({$($ifield:ident: $vtype:ty),*}) => {
        $(
            pub fn $ifield (&mut self) -> $vtype {
                unsafe {
                    let base_ptr : *mut u8 = transmute(self as *mut Self);
                    GrowablePointer::get(
                        base_ptr.offset(self.$ifield.offset as isize),
                        self.$ifield.len_bytes as usize,
                        self.$ifield.cap_bytes as usize
                    )
                }
            }
        )*
    }
}

macro_rules! actor_ifields_resize_calc {
    ({$($ifield:ident: $vtype:ty),*} ; $new_fields:ident ; $the_self:ident ; $new_self:ident ; $total_size_requirement:ident ; $current_offset:ident) => {
        $(
            {
                $new_self.$ifield.set_from_new($current_offset, &$new_fields.$ifield, &$the_self.$ifield);
                $total_size_requirement += $new_self.$ifield.cap_bytes as usize;
                $current_offset += $new_self.$ifield.cap_bytes as usize;
            }
        )*
    }
}

macro_rules! actor_ifields_copy {
    ({$($ifield:ident: $vtype:ty),*} ; $new_fields:ident ; $the_self:ident ; $new_self:ident ; $new_slot:ident) => {
        $(
            unsafe {
                let old_self_pointer : *const u8 = transmute($the_self as *const Self);
                let src = old_self_pointer.offset($the_self.$ifield.offset as isize);
                let dest = $new_slot.offset($new_self.$ifield.offset as isize);
                ptr::copy_nonoverlapping(src, dest, $new_self.$ifield.len_bytes as usize);
            }
        )*
    }
}

actor! {
    struct Test {
        id: usize,
        a: u32,
        b: u16
    }

    growables {
        x: BackedVec<u8>,
        y: BackedVec<u16>
    }

    impl Test {
        fn update (&mut self) -> Option<GrowablesUpdate> {
            self.a;
            Some(GrowablesUpdate{x: None, y: None})
        }
    }
}

fn main () {
    let mut t : Test;
    t.x();
}<|endoftext|>


<commit_before>use futures_core::future::{FusedFuture, Future};
use futures_core::task::{LocalWaker, Poll, Waker};
use slab::Slab;
use std::{fmt, mem, usize};
use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::sync::Mutex as StdMutex;
use std::sync::atomic::{AtomicUsize, Ordering};

/// A futures-aware mutex.
pub struct Mutex<T> {
    state: AtomicUsize,
    value: UnsafeCell<T>,
    waiters: StdMutex<Slab<Waiter>>,
}

impl<T> fmt::Debug for Mutex<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let state = self.state.load(Ordering::SeqCst);
        f.debug_struct("Mutex")
            .field("is_locked", &((state & IS_LOCKED) != 0))
            .field("has_waiters", &((state & HAS_WAITERS) != 0))
            .finish()
    }
}

enum Waiter {
    Waiting(Waker),
    Woken,
}

impl Waiter {
    fn register(&mut self, lw: &LocalWaker) {
        match self {
            Waiter::Waiting(waker) if lw.will_wake_nonlocal(waker) => {},
            _ => *self = Waiter::Waiting(lw.clone().into_waker()),
        }
    }

    fn wake(&mut self) {
        match mem::replace(self, Waiter::Woken) {
            Waiter::Waiting(waker) => waker.wake(),
            Waiter::Woken => {},
        }
    }
}

#[allow(clippy::identity_op)] // https://github.com/rust-lang/rust-clippy/issues/3445
const IS_LOCKED: usize = 1 << 0;
const HAS_WAITERS: usize = 1 << 1;

impl<T> Mutex<T> {
    /// Creates a new futures-aware mutex.
    pub fn new(t: T) -> Mutex<T> {
        Mutex {
            state: AtomicUsize::new(0),
            value: UnsafeCell::new(t),
            waiters: StdMutex::new(Slab::new()),
        }
    }

    /// Attempt to acquire the lock immediately.
    ///
    /// If the lock is currently held, this will return `None`.
    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
        let old_state = self.state.fetch_or(IS_LOCKED, Ordering::Acquire);
        if (old_state & IS_LOCKED) == 0 {
            Some(MutexGuard { mutex: self })
        } else {
            None
        }
    }

    /// Acquire the lock asynchronously.
    ///
    /// This method returns a future that will resolve once the lock has been
    /// successfully acquired.
    pub fn lock(&self) -> MutexLockFuture<'_, T> {
        MutexLockFuture {
            mutex: Some(self),
            wait_key: WAIT_KEY_NONE,
        }
    }

    fn remove_waker(&self, wait_key: usize, wake_another: bool) {
        if wait_key != WAIT_KEY_NONE {
            let mut waiters = self.waiters.lock().unwrap();
            match waiters.remove(wait_key) {
                Waiter::Waiting(_) => {},
                Waiter::Woken => {
                    // We were awoken, but then dropped before we could
                    // wake up to acquire the lock. Wake up another
                    // waiter.
                    if wake_another {
                        if let Some((_i, waiter)) = waiters.iter_mut().next() {
                            waiter.wake();
                        }
                    }
                }
            }
            if waiters.is_empty() {
                self.state.fetch_and(!HAS_WAITERS, Ordering::Relaxed); // released by mutex unlock
            }
        }
    }
}

// Sentinel for when no slot in the `Slab` has been dedicated to this object.
const WAIT_KEY_NONE: usize = usize::MAX;

/// A future which resolves when the target mutex has been successfully acquired.
pub struct MutexLockFuture<'a, T: 'a> {
    // `None` indicates that the mutex was successfully acquired.
    mutex: Option<&'a Mutex<T>>,
    wait_key: usize,
}

impl<T> fmt::Debug for MutexLockFuture<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("MutexLockFuture")
            .field("was_acquired", &self.mutex.is_none())
            .field("mutex", &self.mutex)
            .field("wait_key", &(
                    if self.wait_key == WAIT_KEY_NONE {
                        None
                    } else {
                        Some(self.wait_key)
                    }
                ))
            .finish()
    }
}

impl<T> FusedFuture for MutexLockFuture<'_, T> {
    fn is_terminated(&self) -> bool {
        self.mutex.is_none()
    }
}

impl<'a, T> Future for MutexLockFuture<'a, T> {
    type Output = MutexGuard<'a, T>;

    fn poll(mut self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
        let mutex = self.mutex.expect("polled MutexLockFuture after completion");

        if let Some(lock) = mutex.try_lock() {
            mutex.remove_waker(self.wait_key, false);
            self.mutex = None;
            return Poll::Ready(lock);
        }

        {
            let mut waiters = mutex.waiters.lock().unwrap();
            if self.wait_key == WAIT_KEY_NONE {
                self.wait_key = waiters.insert(Waiter::Waiting(lw.clone().into_waker()));
                if waiters.len() == 1 {
                    mutex.state.fetch_or(HAS_WAITERS, Ordering::Relaxed); // released by mutex unlock
                }
            } else {
                waiters[self.wait_key].register(lw);
            }
        }

        // Ensure that we haven't raced `MutexGuard::drop`'s unlock path by
        // attempting to acquire the lock again.
        if let Some(lock) = mutex.try_lock() {
            mutex.remove_waker(self.wait_key, false);
            self.mutex = None;
            return Poll::Ready(lock);
        }

        Poll::Pending
    }
}

impl<T> Drop for MutexLockFuture<'_, T> {
    fn drop(&mut self) {
        if let Some(mutex) = self.mutex {
            // This future was dropped before it acquired the mutex.
            //
            // Remove ourselves from the map, waking up another waiter if we
            // had been awoken to acquire the lock.
            mutex.remove_waker(self.wait_key, true);
        }
    }
}

/// An RAII guard returned by the `lock` and `try_lock` methods.
/// When this structure is dropped (falls out of scope), the lock will be
/// unlocked.
pub struct MutexGuard<'a, T: 'a> {
    mutex: &'a Mutex<T>,
}

impl<T: fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("MutexGuard")
            .field("value", &*self)
            .field("mutex", &self.mutex)
            .finish()
    }
}

impl<T> Drop for MutexGuard<'_, T> {
    fn drop(&mut self) {
        let old_state = self.mutex.state.fetch_and(!IS_LOCKED, Ordering::AcqRel);
        if (old_state & HAS_WAITERS) != 0 {
            let mut waiters = self.mutex.waiters.lock().unwrap();
            if let Some((_i, waiter)) = waiters.iter_mut().next() {
                waiter.wake();
            }
        }
    }
}

impl<T> Deref for MutexGuard<'_, T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.mutex.value.get() }
    }
}

impl<T> DerefMut for MutexGuard<'_, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.mutex.value.get() }
    }
}

// Mutexes can be moved freely between threads and acquired on any thread so long
// as the inner value can be safely sent between threads.
unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> Sync for Mutex<T> {}

// It's safe to switch which thread the acquire is being attempted on so long as
// `T` can be accessed on that thread.
unsafe impl<T: Send> Send for MutexLockFuture<'_, T> {}
// doesn't have any interesting `&self` methods (only Debug)
unsafe impl<T> Sync for MutexLockFuture<'_, T> {}

// Safe to send since we don't track any thread-specific details-- the inner
// lock is essentially spinlock-equivalent (attempt to flip an atomic bool)
unsafe impl<T: Send> Send for MutexGuard<'_, T> {}
unsafe impl<T: Sync> Sync for MutexGuard<'_, T> {}
<commit_msg>impl Default for Mutex<commit_after>use futures_core::future::{FusedFuture, Future};
use futures_core::task::{LocalWaker, Poll, Waker};
use slab::Slab;
use std::{fmt, mem, usize};
use std::cell::UnsafeCell;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::sync::Mutex as StdMutex;
use std::sync::atomic::{AtomicUsize, Ordering};

/// A futures-aware mutex.
pub struct Mutex<T> {
    state: AtomicUsize,
    value: UnsafeCell<T>,
    waiters: StdMutex<Slab<Waiter>>,
}

impl<T> fmt::Debug for Mutex<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let state = self.state.load(Ordering::SeqCst);
        f.debug_struct("Mutex")
            .field("is_locked", &((state & IS_LOCKED) != 0))
            .field("has_waiters", &((state & HAS_WAITERS) != 0))
            .finish()
    }
}

impl<T: Default> Default for Mutex<T> {
    fn default() -> Mutex<T> {
        Mutex::new(Default::default())
    }
}

enum Waiter {
    Waiting(Waker),
    Woken,
}

impl Waiter {
    fn register(&mut self, lw: &LocalWaker) {
        match self {
            Waiter::Waiting(waker) if lw.will_wake_nonlocal(waker) => {},
            _ => *self = Waiter::Waiting(lw.clone().into_waker()),
        }
    }

    fn wake(&mut self) {
        match mem::replace(self, Waiter::Woken) {
            Waiter::Waiting(waker) => waker.wake(),
            Waiter::Woken => {},
        }
    }
}

#[allow(clippy::identity_op)] // https://github.com/rust-lang/rust-clippy/issues/3445
const IS_LOCKED: usize = 1 << 0;
const HAS_WAITERS: usize = 1 << 1;

impl<T> Mutex<T> {
    /// Creates a new futures-aware mutex.
    pub fn new(t: T) -> Mutex<T> {
        Mutex {
            state: AtomicUsize::new(0),
            value: UnsafeCell::new(t),
            waiters: StdMutex::new(Slab::new()),
        }
    }

    /// Attempt to acquire the lock immediately.
    ///
    /// If the lock is currently held, this will return `None`.
    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
        let old_state = self.state.fetch_or(IS_LOCKED, Ordering::Acquire);
        if (old_state & IS_LOCKED) == 0 {
            Some(MutexGuard { mutex: self })
        } else {
            None
        }
    }

    /// Acquire the lock asynchronously.
    ///
    /// This method returns a future that will resolve once the lock has been
    /// successfully acquired.
    pub fn lock(&self) -> MutexLockFuture<'_, T> {
        MutexLockFuture {
            mutex: Some(self),
            wait_key: WAIT_KEY_NONE,
        }
    }

    fn remove_waker(&self, wait_key: usize, wake_another: bool) {
        if wait_key != WAIT_KEY_NONE {
            let mut waiters = self.waiters.lock().unwrap();
            match waiters.remove(wait_key) {
                Waiter::Waiting(_) => {},
                Waiter::Woken => {
                    // We were awoken, but then dropped before we could
                    // wake up to acquire the lock. Wake up another
                    // waiter.
                    if wake_another {
                        if let Some((_i, waiter)) = waiters.iter_mut().next() {
                            waiter.wake();
                        }
                    }
                }
            }
            if waiters.is_empty() {
                self.state.fetch_and(!HAS_WAITERS, Ordering::Relaxed); // released by mutex unlock
            }
        }
    }
}

// Sentinel for when no slot in the `Slab` has been dedicated to this object.
const WAIT_KEY_NONE: usize = usize::MAX;

/// A future which resolves when the target mutex has been successfully acquired.
pub struct MutexLockFuture<'a, T: 'a> {
    // `None` indicates that the mutex was successfully acquired.
    mutex: Option<&'a Mutex<T>>,
    wait_key: usize,
}

impl<T> fmt::Debug for MutexLockFuture<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("MutexLockFuture")
            .field("was_acquired", &self.mutex.is_none())
            .field("mutex", &self.mutex)
            .field("wait_key", &(
                    if self.wait_key == WAIT_KEY_NONE {
                        None
                    } else {
                        Some(self.wait_key)
                    }
                ))
            .finish()
    }
}

impl<T> FusedFuture for MutexLockFuture<'_, T> {
    fn is_terminated(&self) -> bool {
        self.mutex.is_none()
    }
}

impl<'a, T> Future for MutexLockFuture<'a, T> {
    type Output = MutexGuard<'a, T>;

    fn poll(mut self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
        let mutex = self.mutex.expect("polled MutexLockFuture after completion");

        if let Some(lock) = mutex.try_lock() {
            mutex.remove_waker(self.wait_key, false);
            self.mutex = None;
            return Poll::Ready(lock);
        }

        {
            let mut waiters = mutex.waiters.lock().unwrap();
            if self.wait_key == WAIT_KEY_NONE {
                self.wait_key = waiters.insert(Waiter::Waiting(lw.clone().into_waker()));
                if waiters.len() == 1 {
                    mutex.state.fetch_or(HAS_WAITERS, Ordering::Relaxed); // released by mutex unlock
                }
            } else {
                waiters[self.wait_key].register(lw);
            }
        }

        // Ensure that we haven't raced `MutexGuard::drop`'s unlock path by
        // attempting to acquire the lock again.
        if let Some(lock) = mutex.try_lock() {
            mutex.remove_waker(self.wait_key, false);
            self.mutex = None;
            return Poll::Ready(lock);
        }

        Poll::Pending
    }
}

impl<T> Drop for MutexLockFuture<'_, T> {
    fn drop(&mut self) {
        if let Some(mutex) = self.mutex {
            // This future was dropped before it acquired the mutex.
            //
            // Remove ourselves from the map, waking up another waiter if we
            // had been awoken to acquire the lock.
            mutex.remove_waker(self.wait_key, true);
        }
    }
}

/// An RAII guard returned by the `lock` and `try_lock` methods.
/// When this structure is dropped (falls out of scope), the lock will be
/// unlocked.
pub struct MutexGuard<'a, T: 'a> {
    mutex: &'a Mutex<T>,
}

impl<T: fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("MutexGuard")
            .field("value", &*self)
            .field("mutex", &self.mutex)
            .finish()
    }
}

impl<T> Drop for MutexGuard<'_, T> {
    fn drop(&mut self) {
        let old_state = self.mutex.state.fetch_and(!IS_LOCKED, Ordering::AcqRel);
        if (old_state & HAS_WAITERS) != 0 {
            let mut waiters = self.mutex.waiters.lock().unwrap();
            if let Some((_i, waiter)) = waiters.iter_mut().next() {
                waiter.wake();
            }
        }
    }
}

impl<T> Deref for MutexGuard<'_, T> {
    type Target = T;
    fn deref(&self) -> &T {
        unsafe { &*self.mutex.value.get() }
    }
}

impl<T> DerefMut for MutexGuard<'_, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.mutex.value.get() }
    }
}

// Mutexes can be moved freely between threads and acquired on any thread so long
// as the inner value can be safely sent between threads.
unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> Sync for Mutex<T> {}

// It's safe to switch which thread the acquire is being attempted on so long as
// `T` can be accessed on that thread.
unsafe impl<T: Send> Send for MutexLockFuture<'_, T> {}
// doesn't have any interesting `&self` methods (only Debug)
unsafe impl<T> Sync for MutexLockFuture<'_, T> {}

// Safe to send since we don't track any thread-specific details-- the inner
// lock is essentially spinlock-equivalent (attempt to flip an atomic bool)
unsafe impl<T: Send> Send for MutexGuard<'_, T> {}
unsafe impl<T: Sync> Sync for MutexGuard<'_, T> {}
<|endoftext|>


<commit_before>use std::thread;
use std::sync::mpsc;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("Hello there");
        tx.send(val).unwrap();
    });
}
<commit_msg>Receive sent message in main thread<commit_after>use std::thread;
use std::sync::mpsc;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("Hello there");
        tx.send(val).unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Got: {}", received);
}
<|endoftext|>