<commit_before>extern crate serialize;

use std::io::process::Command;
use std::io::{File, TempDir};

fn main() {
    use std::io::fs;

    let (args, passthrough) = parse_arguments();

    // getting the path from the ANDROID_HOME env
    let sdk_path = std::os::env().move_iter().find(|&(ref k, _)| k.as_slice() == "ANDROID_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the ANDROID_HOME environment variable");

    // hardcoding ndk path
    let ndk_path = std::os::env().move_iter().find(|&(ref k, _)| k.as_slice() == "NDK_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the NDK_HOME environment variable");

    // hardcoding ndk path
    let standalone_path = std::os::env().move_iter().find(|&(ref k, _)| k.as_slice() == "NDK_STANDALONE")
        .map(|(_, v)| Path::new(v)).unwrap_or(Path::new("/opt/ndk_standalone"));

    // creating the build directory that will contain all the necessary files to create teh apk
    let directory = build_directory(&sdk_path, args.output.filestem_str().unwrap());

    // compiling android_native_app_glue.c
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
        .arg("-c")
        .arg("-o").arg(directory.path().join("android_native_app_glue.o"))
        .stdout(std::io::process::InheritFd(1)).stderr(std::io::process::InheritFd(2))
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling gcc to link to an executable
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .args(passthrough.as_slice())
        .arg(directory.path().join("android_native_app_glue.o"))
        .arg("-o").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .arg("-Wl,-E")
        .stdout(std::io::process::InheritFd(1))
        .stderr(std::io::process::InheritFd(2))//.cwd(directory.path())
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling elfedit to turn our executable into a shared object
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-elfedit"))
        .arg("--output-type").arg("dyn")
        .arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .stdout(std::io::process::InheritFd(1))
        .stderr(std::io::process::InheritFd(2))
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing elfedit");
        std::os::set_exit_status(1);
        return;
    }

    // calling objdump to make sure that our object has `ANativeActivity_onCreate`
    // TODO: not working
    /*{
        let mut process =
            Command::new(standalone_path.join("bin").join("arm-linux-androideabi-objdump"))
            .arg("-x").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
            .stderr(std::io::process::InheritFd(2))
            .spawn().unwrap();

        // TODO: use UFCS instead
        fn by_ref<'a, T: Reader>(r: &'a mut T) -> std::io::RefReader<'a, T> { r.by_ref() };

        let stdout = process.stdout.as_mut().unwrap();
        let mut stdout = std::io::BufferedReader::new(by_ref(stdout));

        if stdout.lines().filter_map(|l| l.ok())
            .find(|line| line.as_slice().contains("ANativeActivity_onCreate")).is_none()
        {
            println!("Error: the output file doesn't contain ANativeActivity_onCreate");
            std::os::set_exit_status(1);
            return;
        }
    }*/

    // executing ant
    if Command::new("ant").arg("debug").stdout(std::io::process::InheritFd(1))
        .stderr(std::io::process::InheritFd(2)).cwd(directory.path())
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing ant debug");
        std::os::set_exit_status(1);
        return;
    }

    // copying apk file to the requested output
    fs::copy(&directory.path().join("bin").join("rust-android-debug.apk"),
        &args.output).unwrap();
}

struct Args {
    output: Path,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_passthrough = Vec::new();

    let args = std::os::args();
    let mut args = args.move_iter().skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument")
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match arg.as_slice() {
            "-o" => {
                result_output = Some(Path::new(args.next().expect("-o must be followed by the output name")));
            },
            _ => result_passthrough.push(arg)
        };
    }
}

fn build_directory(sdk_dir: &Path, crate_name: &str) -> TempDir {
    use std::io::fs;

    let build_directory = TempDir::new("android-rs-glue-rust-to-apk")
        .ok().expect("Could not create temporary build directory");

    File::create(&build_directory.path().join("AndroidManifest.xml")).unwrap()
        .write_str(build_manifest(crate_name).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("build.xml")).unwrap()
        .write_str(build_build_xml().as_slice())
        .unwrap();

    File::create(&build_directory.path().join("local.properties")).unwrap()
        .write_str(build_local_properties(sdk_dir).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("project.properties")).unwrap()
        .write_str(build_project_properties().as_slice())
        .unwrap();

    {
        let src_path = build_directory.path().join("src").join("com").join("example")
            .join("native_activity");
        fs::mkdir_recursive(&src_path, std::io::UserRWX).unwrap();
        File::create(&src_path.join("MyNativeActivity.java")).unwrap()
            .write_str(build_java_class().as_slice())
            .unwrap();
    }

    {
        let libs_path = build_directory.path().join("libs").join("armeabi");
        fs::mkdir_recursive(&libs_path, std::io::UserRWX).unwrap();
    }

    build_directory
}

fn build_manifest(crate_name: &str) -> String {
    format!(r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <application android:label="{0}" android:hasCode="true">
        <activity android:name="com.example.native_activity.MyNativeActivity"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name)
}

fn build_build_xml() -> String {
    format!(r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#)
}

fn build_local_properties(sdk_dir: &Path) -> String {
    use std::os;
    format!(r"sdk.dir={}", os::make_absolute(sdk_dir).display())
}

fn build_project_properties() -> String {
    format!(r"target=android-18")
}

fn build_java_class() -> String {
    format!(r#"
package com.example.native_activity;

import android.app.NativeActivity;
import android.util.Log;

public class MyNativeActivity extends NativeActivity {{
  static {{
    System.loadLibrary("main");
  }}

  private static String TAG = "MyNativeActivity";

  public MyNativeActivity() {{
    super();
    Log.v(TAG, "Creating MyNativeActivity");
  }}
}}"#)
}
<commit_msg>Remove MyNativeActivity class<commit_after>extern crate serialize;

use std::io::process::Command;
use std::io::{File, TempDir};

fn main() {
    use std::io::fs;

    let (args, passthrough) = parse_arguments();

    // getting the path from the ANDROID_HOME env
    let sdk_path = std::os::env().move_iter().find(|&(ref k, _)| k.as_slice() == "ANDROID_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the ANDROID_HOME environment variable");

    // hardcoding ndk path
    let ndk_path = std::os::env().move_iter().find(|&(ref k, _)| k.as_slice() == "NDK_HOME")
        .map(|(_, v)| Path::new(v)).expect("Please set the NDK_HOME environment variable");

    // hardcoding ndk path
    let standalone_path = std::os::env().move_iter().find(|&(ref k, _)| k.as_slice() == "NDK_STANDALONE")
        .map(|(_, v)| Path::new(v)).unwrap_or(Path::new("/opt/ndk_standalone"));

    // creating the build directory that will contain all the necessary files to create teh apk
    let directory = build_directory(&sdk_path, args.output.filestem_str().unwrap());

    // compiling android_native_app_glue.c
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .arg(ndk_path.join("sources").join("android").join("native_app_glue").join("android_native_app_glue.c"))
        .arg("-c")
        .arg("-o").arg(directory.path().join("android_native_app_glue.o"))
        .stdout(std::io::process::InheritFd(1)).stderr(std::io::process::InheritFd(2))
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling gcc to link to an executable
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-gcc"))
        .args(passthrough.as_slice())
        .arg(directory.path().join("android_native_app_glue.o"))
        .arg("-o").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .arg("-Wl,-E")
        .stdout(std::io::process::InheritFd(1))
        .stderr(std::io::process::InheritFd(2))//.cwd(directory.path())
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing gcc");
        std::os::set_exit_status(1);
        return;
    }

    // calling elfedit to turn our executable into a shared object
    if Command::new(standalone_path.join("bin").join("arm-linux-androideabi-elfedit"))
        .arg("--output-type").arg("dyn")
        .arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
        .stdout(std::io::process::InheritFd(1))
        .stderr(std::io::process::InheritFd(2))
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing elfedit");
        std::os::set_exit_status(1);
        return;
    }

    // calling objdump to make sure that our object has `ANativeActivity_onCreate`
    // TODO: not working
    /*{
        let mut process =
            Command::new(standalone_path.join("bin").join("arm-linux-androideabi-objdump"))
            .arg("-x").arg(directory.path().join("libs").join("armeabi").join("libmain.so"))
            .stderr(std::io::process::InheritFd(2))
            .spawn().unwrap();

        // TODO: use UFCS instead
        fn by_ref<'a, T: Reader>(r: &'a mut T) -> std::io::RefReader<'a, T> { r.by_ref() };

        let stdout = process.stdout.as_mut().unwrap();
        let mut stdout = std::io::BufferedReader::new(by_ref(stdout));

        if stdout.lines().filter_map(|l| l.ok())
            .find(|line| line.as_slice().contains("ANativeActivity_onCreate")).is_none()
        {
            println!("Error: the output file doesn't contain ANativeActivity_onCreate");
            std::os::set_exit_status(1);
            return;
        }
    }*/

    // executing ant
    if Command::new("ant").arg("debug").stdout(std::io::process::InheritFd(1))
        .stderr(std::io::process::InheritFd(2)).cwd(directory.path())
        .status().unwrap() != std::io::process::ExitStatus(0)
    {
        println!("Error while executing ant debug");
        std::os::set_exit_status(1);
        return;
    }

    // copying apk file to the requested output
    fs::copy(&directory.path().join("bin").join("rust-android-debug.apk"),
        &args.output).unwrap();
}

struct Args {
    output: Path,
}

fn parse_arguments() -> (Args, Vec<String>) {
    let mut result_output = None;
    let mut result_passthrough = Vec::new();

    let args = std::os::args();
    let mut args = args.move_iter().skip(1);

    loop {
        let arg = match args.next() {
            None => return (
                Args {
                    output: result_output.expect("Could not find -o argument")
                },
                result_passthrough
            ),
            Some(arg) => arg
        };

        match arg.as_slice() {
            "-o" => {
                result_output = Some(Path::new(args.next().expect("-o must be followed by the output name")));
            },
            _ => result_passthrough.push(arg)
        };
    }
}

fn build_directory(sdk_dir: &Path, crate_name: &str) -> TempDir {
    use std::io::fs;

    let build_directory = TempDir::new("android-rs-glue-rust-to-apk")
        .ok().expect("Could not create temporary build directory");

    File::create(&build_directory.path().join("AndroidManifest.xml")).unwrap()
        .write_str(build_manifest(crate_name).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("build.xml")).unwrap()
        .write_str(build_build_xml().as_slice())
        .unwrap();

    File::create(&build_directory.path().join("local.properties")).unwrap()
        .write_str(build_local_properties(sdk_dir).as_slice())
        .unwrap();

    File::create(&build_directory.path().join("project.properties")).unwrap()
        .write_str(build_project_properties().as_slice())
        .unwrap();

    {
        let libs_path = build_directory.path().join("libs").join("armeabi");
        fs::mkdir_recursive(&libs_path, std::io::UserRWX).unwrap();
    }

    build_directory
}

fn build_manifest(crate_name: &str) -> String {
    format!(r#"<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">

    <uses-sdk android:minSdkVersion="9" />

    <application android:label="{0}" android:hasCode="false">
        <activity android:name="android.app.NativeActivity"
                android:label="{0}"
                android:configChanges="orientation|keyboardHidden">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<!-- END_INCLUDE(manifest) -->
"#, crate_name)
}

fn build_build_xml() -> String {
    format!(r#"<?xml version="1.0" encoding="UTF-8"?>
<project name="rust-android" default="help">
    <property file="local.properties" />
    <loadproperties srcFile="project.properties" />
    <import file="custom_rules.xml" optional="true" />
    <import file="${{sdk.dir}}/tools/ant/build.xml" />
</project>
"#)
}

fn build_local_properties(sdk_dir: &Path) -> String {
    use std::os;
    format!(r"sdk.dir={}", os::make_absolute(sdk_dir).display())
}

fn build_project_properties() -> String {
    format!(r"target=android-18")
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::trie::*;
use adapton::engine::*;
use std::rc::Rc;

mod add_dups {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(v: Vec<usize>, t: Trie<usize>) -> Trie<usize> {
        v.into_iter().fold(t, |acc, i| {
            let t = Trie::art(cell(name_of_usize(i), acc));
            let t = Trie::name(name_of_usize(i), t);
            Trie::extend(name_unit(), t, i)
        })
    }

    fn bench_naive_add_dups() {
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            naive_input = doit(v.clone(), naive_input.clone());
            let _ = SetElim::mem(&naive_input, i);
        }
    }

    fn bench_dcg_add_dups() {
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            dcg_input = doit(v.clone(), dcg_input.clone());
            let _ = SetElim::mem(&dcg_input, i);
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        // b.iter(|| bench_naive_add_dups())
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            b.iter(|| {
                naive_input = doit(v.clone(), naive_input.clone());
                SetElim::mem(&naive_input, i)
            });
            naive_input = doit(v.clone(), naive_input.clone());
            // b.iter(|| SetElim::mem(&naive_input, i));
        }
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        // b.iter(|| bench_dcg_add_dups())
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            b.iter(|| {
                dcg_input = doit(v.clone(), dcg_input.clone());
                SetElim::mem(&dcg_input, i)
            });
            dcg_input = doit(v.clone(), dcg_input.clone());
            // b.iter(|| SetElim::mem(&dcg_input, i));
        }
    }
}

mod sum_fold {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(t: Trie<usize>) -> usize {
        SetElim::fold(t, 0, Rc::new(|i, acc| i + acc))
    }

    fn push_input(i: usize, t: Trie<usize>) -> Trie<usize> {
        let t = Trie::art(cell(name_of_usize(i), t));
        let t = Trie::name(name_of_usize(i), t);
        Trie::extend(name_unit(), t, i)
    }

    fn bench_naive_fold() {
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            naive_input = push_input(i, naive_input);
            let _ = doit(naive_input.clone());
        }
    }

    fn bench_dcg_fold() {
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            dcg_input = push_input(i, dcg_input);
            let _ = doit(dcg_input.clone());
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        // b.iter(|| bench_naive_fold(b)
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            naive_input = push_input(i, naive_input);
            b.iter(|| doit(naive_input.clone()))
        }
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        // b.iter(|| bench_dcg_fold())
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            dcg_input = push_input(i, dcg_input);
            b.iter(|| doit(dcg_input.clone()))
        }
    }
}
<commit_msg>Add tree benchmarks for successful dcg speedup comparison<commit_after>#![feature(test)]
extern crate adapton;
extern crate test;
use self::test::Bencher;
use adapton::collections::{Dir2, List, ListIntro, Tree, monoid_of_tree, tree_of_list};
use adapton::trie::*;
use adapton::engine::*;
use std::rc::Rc;

mod add_dups {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(v: Vec<usize>, t: Trie<usize>) -> Trie<usize> {
        v.into_iter().fold(t, |acc, i| {
            let t = Trie::art(cell(name_of_usize(i), acc));
            let t = Trie::name(name_of_usize(i), t);
            Trie::extend(name_unit(), t, i)
        })
    }

    fn bench_naive_add_dups() {
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            naive_input = doit(v.clone(), naive_input.clone());
            let _ = SetElim::mem(&naive_input, i);
        }
    }

    fn bench_dcg_add_dups() {
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            dcg_input = doit(v.clone(), dcg_input.clone());
            let _ = SetElim::mem(&dcg_input, i);
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        // b.iter(|| bench_naive_add_dups())
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            b.iter(|| {
                naive_input = doit(v.clone(), naive_input.clone());
                SetElim::mem(&naive_input, i)
            });
            naive_input = doit(v.clone(), naive_input.clone());
            // b.iter(|| SetElim::mem(&naive_input, i));
        }
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        // b.iter(|| bench_dcg_add_dups())
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();
        let mut v = Vec::new();

        let mut ones = vec![1;50];
        let mut twos = vec![2;50];
        let mut threes = vec![3;50];
        twos.append(&mut threes);
        ones.append(&mut twos);
        for i in ones.iter() {
            v.push(*i);
            b.iter(|| {
                dcg_input = doit(v.clone(), dcg_input.clone());
                SetElim::mem(&dcg_input, i)
            });
            dcg_input = doit(v.clone(), dcg_input.clone());
            // b.iter(|| SetElim::mem(&dcg_input, i));
        }
    }
}

mod sum_fold {
    use super::*;

    // The code that we want to compare/measure under naive versus DCG engines:
    fn doit(t: Trie<usize>) -> usize {
        SetElim::fold(t, 0, Rc::new(|i, acc| i + acc))
    }

    fn push_input(i: usize, t: Trie<usize>) -> Trie<usize> {
        let t = Trie::art(cell(name_of_usize(i), t));
        let t = Trie::name(name_of_usize(i), t);
        Trie::extend(name_unit(), t, i)
    }

    fn bench_naive_fold() {
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            naive_input = push_input(i, naive_input);
            let _ = doit(naive_input.clone());
        }
    }

    fn bench_dcg_fold() {
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            dcg_input = push_input(i, dcg_input);
            let _ = doit(dcg_input.clone());
        }
    }

    #[bench]
    fn benchmark_naive_trie(b: &mut Bencher) {
        // b.iter(|| bench_naive_fold(b)
        init_naive();
        let mut naive_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            naive_input = push_input(i, naive_input);
            b.iter(|| doit(naive_input.clone()))
        }
    }

    #[bench]
    fn benchmark_dcg_trie(b: &mut Bencher) {
        // b.iter(|| bench_dcg_fold())
        init_dcg();
        let mut dcg_input: Trie<usize> = SetIntro::empty();

        for i in (1..100).into_iter() {
            dcg_input = push_input(i, dcg_input);
            b.iter(|| doit(dcg_input.clone()))
        }
    }
}

mod tree_benchmarks {
    use super::*;

    fn sum_tree(l:List<usize>) -> usize {
        let t = ns(name_of_str("tree_of_list"),
                   ||tree_of_list::<_,_,Tree<_>,_>(Dir2::Left, l));
        ns(name_of_str("monoid_of_tree"),
           || monoid_of_tree(t, 0, Rc::new(|x, y| x + y)))
    }

    fn push_list(i: usize, l: List<usize>) -> List<usize> {
        let l = List::art(cell(name_of_usize(i), l));
        let l = List::name(name_of_usize(i), l);
        List::cons(i, l)
    }

    #[bench]
    fn benchmark_naive_tree(b: &mut Bencher) {
        init_naive();
        let mut naive_input: List<usize> = List::nil();

        for i in (1..100).into_iter() {
            naive_input = push_list(i, naive_input);
            b.iter(|| sum_tree(naive_input.clone()))
        }
    }

    #[bench]
    fn benchmark_dcg_tree(b: &mut Bencher) {
        init_dcg();
        let mut dcg_input: List<usize> = List::nil();

        for i in (1..100).into_iter() {
            dcg_input = push_list(i, dcg_input);
            b.iter(|| sum_tree(dcg_input.clone()))
        }
    }
}
<|endoftext|>


<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::sys::{ffi, utils};
use crate::utils::to_cpath;
use crate::{Disk, DiskType};

use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull, CFRelease};
use core_foundation_sys::dictionary::{CFDictionaryGetValueIfPresent, CFDictionaryRef};
use core_foundation_sys::number::{kCFBooleanTrue, CFBooleanRef};
use core_foundation_sys::string as cfs;

use libc::{c_char, c_int, c_void, statfs};

use std::ffi::{OsStr, OsString};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::ptr;

fn to_path(mount_path: &[c_char]) -> Option<PathBuf> {
    let mut tmp = Vec::with_capacity(mount_path.len());
    for &c in mount_path {
        if c == 0 {
            break;
        }
        tmp.push(c as u8);
    }
    if tmp.is_empty() {
        None
    } else {
        let path = OsStr::from_bytes(&tmp);
        Some(PathBuf::from(path))
    }
}

pub(crate) fn get_disks(session: ffi::DASessionRef) -> Vec<Disk> {
    if session.is_null() {
        return Vec::new();
    }
    unsafe {
        let count = libc::getfsstat(ptr::null_mut(), 0, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        let bufsize = count * mem::size_of::<libc::statfs>() as c_int;
        let mut disks = Vec::with_capacity(count as _);
        let count = libc::getfsstat(disks.as_mut_ptr(), bufsize, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        disks.set_len(count as _);
        disks
            .into_iter()
            .filter_map(|c_disk| {
                let mount_point = to_path(&c_disk.f_mntonname)?;
                let disk = ffi::DADiskCreateFromBSDName(
                    kCFAllocatorDefault as _,
                    session,
                    c_disk.f_mntfromname.as_ptr(),
                );
                let dict = ffi::DADiskCopyDescription(disk);
                if dict.is_null() {
                    return None;
                }
                // Keeping this around in case one might want the list of the available
                // keys in "dict".
                // core_foundation_sys::base::CFShow(dict as _);
                let name = match get_str_value(dict, b"DAMediaName\0").map(OsString::from) {
                    Some(n) => n,
                    None => return None,
                };
                let removable = get_bool_value(dict, b"DAMediaRemovable\0").unwrap_or(false);
                let ejectable = get_bool_value(dict, b"DAMediaEjectable\0").unwrap_or(false);
                // This is very hackish but still better than nothing...
                let type_ = if let Some(model) = get_str_value(dict, b"DADeviceModel\0") {
                    if model.contains("SSD") {
                        DiskType::SSD
                    } else {
                        // We just assume by default that this is a HDD
                        DiskType::HDD
                    }
                } else {
                    DiskType::Unknown(-1)
                };

                CFRelease(dict as _);
                new_disk(name, mount_point, type_, removable || ejectable)
            })
            .collect::<Vec<_>>()
    }
}

unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
    dict: CFDictionaryRef,
    key: &[u8],
    callback: F,
) -> Option<T> {
    let key = ffi::CFStringCreateWithCStringNoCopy(
        ptr::null_mut(),
        key.as_ptr() as *const c_char,
        cfs::kCFStringEncodingUTF8,
        kCFAllocatorNull as _,
    );
    let mut value = std::ptr::null();
    let ret = if CFDictionaryGetValueIfPresent(dict, key as _, &mut value) != 0 {
        callback(value)
    } else {
        None
    };
    CFRelease(key as _);
    ret
}

unsafe fn get_str_value(dict: CFDictionaryRef, key: &[u8]) -> Option<String> {
    get_dict_value(dict, key, |v| {
        let v = v as cfs::CFStringRef;
        let len = cfs::CFStringGetLength(v);
        utils::cstr_to_rust_with_size(
            cfs::CFStringGetCStringPtr(v, cfs::kCFStringEncodingUTF8),
            Some(len as _),
        )
    })
}

unsafe fn get_bool_value(dict: CFDictionaryRef, key: &[u8]) -> Option<bool> {
    get_dict_value(dict, key, |v| Some(v as CFBooleanRef == kCFBooleanTrue))
}

fn new_disk(
    name: OsString,
    mount_point: PathBuf,
    type_: DiskType,
    is_removable: bool,
) -> Option<Disk> {
    let mount_point_cpath = to_cpath(&mount_point);
    let mut total_space = 0;
    let mut available_space = 0;
    let mut file_system = None;
    unsafe {
        let mut stat: statfs = mem::zeroed();
        if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
            total_space = u64::from(stat.f_bsize) * stat.f_blocks;
            available_space = u64::from(stat.f_bsize) * stat.f_bavail;
            let mut vec = Vec::with_capacity(stat.f_fstypename.len());
            for x in &stat.f_fstypename {
                if *x == 0 {
                    break;
                }
                vec.push(*x as u8);
            }
            file_system = Some(vec);
        }
        if total_space == 0 {
            return None;
        }
        Some(Disk {
            type_,
            name,
            file_system: file_system.unwrap_or_else(|| b"<Unknown>".to_vec()),
            mount_point,
            total_space,
            available_space,
            is_removable,
        })
    }
}
<commit_msg>Ignore non-root drive partitions<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::sys::{ffi, utils};
use crate::utils::to_cpath;
use crate::{Disk, DiskType};

use core_foundation_sys::base::{kCFAllocatorDefault, kCFAllocatorNull, CFRelease};
use core_foundation_sys::dictionary::{CFDictionaryGetValueIfPresent, CFDictionaryRef};
use core_foundation_sys::number::{kCFBooleanTrue, CFBooleanRef};
use core_foundation_sys::string as cfs;

use libc::{c_char, c_int, c_void, statfs};

use std::ffi::{OsStr, OsString};
use std::mem;
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
use std::ptr;

fn to_path(mount_path: &[c_char]) -> Option<PathBuf> {
    let mut tmp = Vec::with_capacity(mount_path.len());
    for &c in mount_path {
        if c == 0 {
            break;
        }
        tmp.push(c as u8);
    }
    if tmp.is_empty() {
        None
    } else {
        let path = OsStr::from_bytes(&tmp);
        Some(PathBuf::from(path))
    }
}

pub(crate) fn get_disks(session: ffi::DASessionRef) -> Vec<Disk> {
    if session.is_null() {
        return Vec::new();
    }
    unsafe {
        let count = libc::getfsstat(ptr::null_mut(), 0, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        let bufsize = count * mem::size_of::<libc::statfs>() as c_int;
        let mut disks = Vec::with_capacity(count as _);
        let count = libc::getfsstat(disks.as_mut_ptr(), bufsize, libc::MNT_NOWAIT);
        if count < 1 {
            return Vec::new();
        }
        disks.set_len(count as _);
        disks
            .into_iter()
            .filter_map(|c_disk| {
                let mount_point = to_path(&c_disk.f_mntonname)?;
                let disk = ffi::DADiskCreateFromBSDName(
                    kCFAllocatorDefault as _,
                    session,
                    c_disk.f_mntfromname.as_ptr(),
                );
                let dict = ffi::DADiskCopyDescription(disk);
                if dict.is_null() {
                    return None;
                }
                // Keeping this around in case one might want the list of the available
                // keys in "dict".
                // core_foundation_sys::base::CFShow(dict as _);
                let name = match get_str_value(dict, b"DAMediaName\0").map(OsString::from) {
                    Some(n) => n,
                    None => return None,
                };
                let removable = get_bool_value(dict, b"DAMediaRemovable\0").unwrap_or(false);
                let ejectable = get_bool_value(dict, b"DAMediaEjectable\0").unwrap_or(false);
                // This is very hackish but still better than nothing...
                let type_ = if let Some(model) = get_str_value(dict, b"DADeviceModel\0") {
                    if model.contains("SSD") {
                        DiskType::SSD
                    } else {
                        // We just assume by default that this is a HDD
                        DiskType::HDD
                    }
                } else {
                    DiskType::Unknown(-1)
                };

                CFRelease(dict as _);
                new_disk(name, mount_point, type_, removable || ejectable)
            })
            .collect::<Vec<_>>()
    }
}

unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
    dict: CFDictionaryRef,
    key: &[u8],
    callback: F,
) -> Option<T> {
    let key = ffi::CFStringCreateWithCStringNoCopy(
        ptr::null_mut(),
        key.as_ptr() as *const c_char,
        cfs::kCFStringEncodingUTF8,
        kCFAllocatorNull as _,
    );
    let mut value = std::ptr::null();
    let ret = if CFDictionaryGetValueIfPresent(dict, key as _, &mut value) != 0 {
        callback(value)
    } else {
        None
    };
    CFRelease(key as _);
    ret
}

unsafe fn get_str_value(dict: CFDictionaryRef, key: &[u8]) -> Option<String> {
    get_dict_value(dict, key, |v| {
        let v = v as cfs::CFStringRef;
        let len = cfs::CFStringGetLength(v);
        utils::cstr_to_rust_with_size(
            cfs::CFStringGetCStringPtr(v, cfs::kCFStringEncodingUTF8),
            Some(len as _),
        )
    })
}

unsafe fn get_bool_value(dict: CFDictionaryRef, key: &[u8]) -> Option<bool> {
    get_dict_value(dict, key, |v| Some(v as CFBooleanRef == kCFBooleanTrue))
}

fn new_disk(
    name: OsString,
    mount_point: PathBuf,
    type_: DiskType,
    is_removable: bool,
) -> Option<Disk> {
    let mount_point_cpath = to_cpath(&mount_point);
    let mut total_space = 0;
    let mut available_space = 0;
    let mut file_system = None;
    unsafe {
        let mut stat: statfs = mem::zeroed();
        if statfs(mount_point_cpath.as_ptr() as *const i8, &mut stat) == 0 {
            // APFS is "special" because its a snapshot-based filesystem, and modern
            // macOS devices take full advantage of this.
            //
            // By default, listing volumes with `statfs` can return both the root-level
            // "data" partition and any snapshots that exist. However, other than some flags and
            // reservered(undocumented) bytes, there is no difference between the OS boot snapshot
            // and the "data" partition.
            //
            // To avoid duplicating the number of disks (and therefore available space, etc), only return
            // a disk (which is really a partition with APFS) if it is the root of the filesystem.
            let is_root = stat.f_flags & libc::MNT_ROOTFS as u32 == 0;
            if !is_root {
                return None;
            }

            total_space = u64::from(stat.f_bsize) * stat.f_blocks;
            available_space = u64::from(stat.f_bsize) * stat.f_bavail;
            let mut vec = Vec::with_capacity(stat.f_fstypename.len());
            for x in &stat.f_fstypename {
                if *x == 0 {
                    break;
                }
                vec.push(*x as u8);
            }
            file_system = Some(vec);
        }
        if total_space == 0 {
            return None;
        }
        Some(Disk {
            type_,
            name,
            file_system: file_system.unwrap_or_else(|| b"<Unknown>".to_vec()),
            mount_point,
            total_space,
            available_space,
            is_removable,
        })
    }
}
<|endoftext|>


<commit_before>pub use self::token::Token;
pub use self::token::Category;

pub mod token;
pub mod implementations;

pub struct StateFunction(fn(&mut Lexer) -> Option<StateFunction>);

pub struct Lexer {
    data: String,
    char_count: usize,
    token_start: usize,
    token_position: usize,
    tokens: Vec<Token>,
}

pub fn new(data: &str) -> Lexer {
    Lexer{
      data: data.to_string(),
      char_count: data.chars().count(),
      token_start: 0,
      token_position: 0,
      tokens: vec![]
    }
}

impl Lexer {
    pub fn advance(&mut self) {
        if self.token_position < self.char_count-1 {
            self.token_position += 1;
        }
    }

    pub fn tokenize(&mut self, category: Category) {
        if self.token_start != self.token_position {
            let token = Token{
                lexeme: self.data.slice_chars(self.token_start, self.token_position).to_string(),
                category: category,
            };
            self.tokens.push(token);
            self.token_start = self.token_position;
        }
    }
}

mod tests {
    use super::new;
    use super::token::Token;
    use super::token::Category;

    #[test]
    fn new_initializes_correctly_with_unicode_data() {
        let lexer_data = "différent";
        let lexer = new(lexer_data);
        assert_eq!(lexer.data, lexer_data);
        assert_eq!(lexer.char_count, 9);
        assert_eq!(lexer.token_start, 0);
        assert_eq!(lexer.token_position, 0);
        assert_eq!(lexer.tokens, vec![]);
    }

    #[test]
    fn advance_increments_the_cursor_by_one() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        assert_eq!(lexer.token_position, 1);
        lexer.advance();
        assert_eq!(lexer.token_position, 2);
    }

    #[test]
    fn advance_stops_when_there_is_no_more_data() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);

        // Try to go beyond the last character.
        for _ in 0..lexer.char_count {
            lexer.advance();
        }

        assert_eq!(lexer.token_position, lexer.char_count-1);
    }

    #[test]
    fn tokenize_advances_token_start_to_cursor() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        lexer.advance();
        lexer.tokenize(Category::Text);

        assert_eq!(lexer.token_start, 2);
    }

    #[test]
    fn tokenize_creates_the_correct_token() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        lexer.advance();
        lexer.tokenize(Category::Text);

        let token = lexer.tokens.pop().unwrap();
        let expected_token = Token{ lexeme: "él".to_string(), category: Category::Text};
        assert_eq!(token, expected_token);
    }

    #[test]
    fn tokenize_does_nothing_if_range_is_empty() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.tokenize(Category::Text);

        assert_eq!(lexer.tokens.len(), 0);
        assert_eq!(lexer.token_start, 0);
        assert_eq!(lexer.token_position, 0);
    }
}
<commit_msg>Implement tokenize_next lexer method.<commit_after>pub use self::token::Token;
pub use self::token::Category;
use std::cmp::min;

pub mod token;
pub mod implementations;

pub struct StateFunction(fn(&mut Lexer) -> Option<StateFunction>);

pub struct Lexer {
    data: String,
    char_count: usize,
    token_start: usize,
    token_position: usize,
    tokens: Vec<Token>,
}

pub fn new(data: &str) -> Lexer {
    Lexer{
      data: data.to_string(),
      char_count: data.chars().count(),
      token_start: 0,
      token_position: 0,
      tokens: vec![]
    }
}

impl Lexer {
    pub fn advance(&mut self) {
        if self.token_position < self.char_count-1 {
            self.token_position += 1;
        }
    }

    pub fn tokenize(&mut self, category: Category) {
        if self.token_start != self.token_position {
            let token = Token{
                lexeme: self.data.slice_chars(self.token_start, self.token_position).to_string(),
                category: category,
            };
            self.tokens.push(token);
            self.token_start = self.token_position;
        }
    }

    pub fn tokenize_next(&mut self, amount: usize, category: Category) {
        self.tokenize(Category::Text);
        self.token_position = min(self.token_position + amount, self.char_count);
        self.tokenize(category);
    }
}

mod tests {
    use super::new;
    use super::token::Token;
    use super::token::Category;

    #[test]
    fn new_initializes_correctly_with_unicode_data() {
        let lexer_data = "différent";
        let lexer = new(lexer_data);
        assert_eq!(lexer.data, lexer_data);
        assert_eq!(lexer.char_count, 9);
        assert_eq!(lexer.token_start, 0);
        assert_eq!(lexer.token_position, 0);
        assert_eq!(lexer.tokens, vec![]);
    }

    #[test]
    fn advance_increments_the_cursor_by_one() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        assert_eq!(lexer.token_position, 1);
        lexer.advance();
        assert_eq!(lexer.token_position, 2);
    }

    #[test]
    fn advance_stops_when_there_is_no_more_data() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);

        // Try to go beyond the last character.
        for _ in 0..lexer.char_count {
            lexer.advance();
        }

        assert_eq!(lexer.token_position, lexer.char_count-1);
    }

    #[test]
    fn tokenize_advances_token_start_to_cursor() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        lexer.advance();
        lexer.tokenize(Category::Text);

        assert_eq!(lexer.token_start, 2);
    }

    #[test]
    fn tokenize_creates_the_correct_token() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        lexer.advance();
        lexer.tokenize(Category::Text);

        let token = lexer.tokens.pop().unwrap();
        let expected_token = Token{ lexeme: "él".to_string(), category: Category::Text};
        assert_eq!(token, expected_token);
    }

    #[test]
    fn tokenize_does_nothing_if_range_is_empty() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.tokenize(Category::Text);

        assert_eq!(lexer.tokens.len(), 0);
        assert_eq!(lexer.token_start, 0);
        assert_eq!(lexer.token_position, 0);
    }

    #[test]
    fn tokenize_next_tokenizes_previous_data_as_text() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        lexer.advance();
        lexer.tokenize_next(1, Category::Keyword);

        let token = lexer.tokens.remove(0);
        let expected_token = Token{ lexeme: "él".to_string(), category: Category::Text};
        assert_eq!(token, expected_token);
    }

    #[test]
    fn tokenize_next_tokenizes_next_x_chars() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        lexer.advance();
        lexer.tokenize_next(5, Category::Keyword);

        let token = lexer.tokens.pop().unwrap();
        let expected_token = Token{ lexeme: "égant".to_string(), category: Category::Keyword};
        assert_eq!(token, expected_token);
    }

    #[test]
    fn tokenize_next_takes_at_most_what_is_left() {
        let lexer_data = "élégant";
        let mut lexer = new(lexer_data);
        lexer.advance();
        lexer.advance();
        lexer.tokenize_next(15, Category::Keyword);

        let token = lexer.tokens.pop().unwrap();
        let expected_token = Token{ lexeme: "égant".to_string(), category: Category::Keyword};
        assert_eq!(token, expected_token);
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

#[cfg(any(target_os = "macos", target_os = "ios"))]
use core::ptr;
use core::{
    cell::{Cell, UnsafeCell},
    mem::MaybeUninit,
};
use libc;
use std::time::Instant;
use std::{thread, time::Duration};

// x32 Linux uses a non-standard type for tv_nsec in timespec.
// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
#[allow(non_camel_case_types)]
type tv_nsec_t = i64;
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
#[allow(non_camel_case_types)]
type tv_nsec_t = libc::c_long;

// Helper type for putting a thread to sleep until some other thread wakes it up
pub struct ThreadParker {
    should_park: Cell<bool>,
    mutex: UnsafeCell<libc::pthread_mutex_t>,
    condvar: UnsafeCell<libc::pthread_cond_t>,
    initialized: Cell<bool>,
}

impl super::ThreadParkerT for ThreadParker {
    type UnparkHandle = UnparkHandle;

    const IS_CHEAP_TO_CONSTRUCT: bool = false;

    #[inline]
    fn new() -> ThreadParker {
        ThreadParker {
            should_park: Cell::new(false),
            mutex: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER),
            condvar: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER),
            initialized: Cell::new(false),
        }
    }

    #[inline]
    unsafe fn prepare_park(&self) {
        self.should_park.set(true);
        if !self.initialized.get() {
            self.init();
            self.initialized.set(true);
        }
    }

    #[inline]
    unsafe fn timed_out(&self) -> bool {
        // We need to grab the mutex here because another thread may be
        // concurrently executing UnparkHandle::unpark, which is done without
        // holding the queue lock.
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        let should_park = self.should_park.get();
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        should_park
    }

    #[inline]
    unsafe fn park(&self) {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
            debug_assert_eq!(r, 0);
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
    }

    #[inline]
    unsafe fn park_until(&self, timeout: Instant) -> bool {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let now = Instant::now();
            if timeout <= now {
                let r = libc::pthread_mutex_unlock(self.mutex.get());
                debug_assert_eq!(r, 0);
                return false;
            }

            if let Some(ts) = timeout_to_timespec(timeout - now) {
                let r = libc::pthread_cond_timedwait(self.condvar.get(), self.mutex.get(), &ts);
                if ts.tv_sec < 0 {
                    // On some systems, negative timeouts will return EINVAL. In
                    // that case we won't sleep and will just busy loop instead,
                    // which is the best we can do.
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT || r == libc::EINVAL);
                } else {
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT);
                }
            } else {
                // Timeout calculation overflowed, just sleep indefinitely
                let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
                debug_assert_eq!(r, 0);
            }
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        true
    }

    #[inline]
    unsafe fn unpark_lock(&self) -> UnparkHandle {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);

        UnparkHandle {
            thread_parker: self,
        }
    }
}

impl ThreadParker {
    /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
    #[inline]
    unsafe fn init(&self) {}

    /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
    #[inline]
    unsafe fn init(&self) {
        let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
        let r = libc::pthread_condattr_init(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_cond_init(self.condvar.get(), attr.as_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
    }
}

impl Drop for ThreadParker {
    #[inline]
    fn drop(&mut self) {
        // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
        // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
        // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
        // this behaviour no longer occurs. The same applies to condvars.
        unsafe {
            let r = libc::pthread_mutex_destroy(self.mutex.get());
            debug_assert!(r == 0 || r == libc::EINVAL);
            let r = libc::pthread_cond_destroy(self.condvar.get());
            debug_assert!(r == 0 || r == libc::EINVAL);
        }
    }
}

pub struct UnparkHandle {
    thread_parker: *const ThreadParker,
}

impl super::UnparkHandleT for UnparkHandle {
    #[inline]
    unsafe fn unpark(self) {
        (*self.thread_parker).should_park.set(false);

        // We notify while holding the lock here to avoid races with the target
        // thread. In particular, the thread could exit after we unlock the
        // mutex, which would make the condvar access invalid memory.
        let r = libc::pthread_cond_signal((*self.thread_parker).condvar.get());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutex_unlock((*self.thread_parker).mutex.get());
        debug_assert_eq!(r, 0);
    }
}

// Returns the current time on the clock used by pthread_cond_t as a timespec.
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[inline]
fn timespec_now() -> libc::timespec {
    let mut now = MaybeUninit::<libc::timeval>::uninit();
    let r = unsafe { libc::gettimeofday(now.as_mut_ptr(), ptr::null_mut()) };
    debug_assert_eq!(r, 0);
    // SAFETY: We know `libc::gettimeofday` has initialized the value.
    let now = unsafe { now.assume_init() };
    libc::timespec {
        tv_sec: now.tv_sec,
        tv_nsec: now.tv_usec as tv_nsec_t * 1000,
    }
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
#[inline]
fn timespec_now() -> libc::timespec {
    let mut now = MaybeUninit::<libc::timespec>::uninit();
    let clock = if cfg!(target_os = "android") {
        // Android doesn't support pthread_condattr_setclock, so we need to
        // specify the timeout in CLOCK_REALTIME.
        libc::CLOCK_REALTIME
    } else {
        libc::CLOCK_MONOTONIC
    };
    let r = unsafe { libc::clock_gettime(clock, now.as_mut_ptr()) };
    debug_assert_eq!(r, 0);
    // SAFETY: We know `libc::clock_gettime` has initialized the value.
    unsafe { now.assume_init() }
}

// Converts a relative timeout into an absolute timeout in the clock used by
// pthread_cond_t.
#[inline]
fn timeout_to_timespec(timeout: Duration) -> Option<libc::timespec> {
    // Handle overflows early on
    if timeout.as_secs() > libc::time_t::max_value() as u64 {
        return None;
    }

    let now = timespec_now();
    let mut nsec = now.tv_nsec + timeout.subsec_nanos() as tv_nsec_t;
    let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t);
    if nsec >= 1_000_000_000 {
        nsec -= 1_000_000_000;
        sec = sec.and_then(|sec| sec.checked_add(1));
    }

    sec.map(|sec| libc::timespec {
        tv_nsec: nsec,
        tv_sec: sec,
    })
}

#[inline]
pub fn thread_yield() {
    thread::yield_now();
}
<commit_msg>pthread attributes are not yet supported on the esp-idf platform<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

#[cfg(any(target_os = "macos", target_os = "ios"))]
use core::ptr;
use core::{
    cell::{Cell, UnsafeCell},
    mem::MaybeUninit,
};
use libc;
use std::time::Instant;
use std::{thread, time::Duration};

// x32 Linux uses a non-standard type for tv_nsec in timespec.
// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
#[allow(non_camel_case_types)]
type tv_nsec_t = i64;
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
#[allow(non_camel_case_types)]
type tv_nsec_t = libc::c_long;

// Helper type for putting a thread to sleep until some other thread wakes it up
pub struct ThreadParker {
    should_park: Cell<bool>,
    mutex: UnsafeCell<libc::pthread_mutex_t>,
    condvar: UnsafeCell<libc::pthread_cond_t>,
    initialized: Cell<bool>,
}

impl super::ThreadParkerT for ThreadParker {
    type UnparkHandle = UnparkHandle;

    const IS_CHEAP_TO_CONSTRUCT: bool = false;

    #[inline]
    fn new() -> ThreadParker {
        ThreadParker {
            should_park: Cell::new(false),
            mutex: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER),
            condvar: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER),
            initialized: Cell::new(false),
        }
    }

    #[inline]
    unsafe fn prepare_park(&self) {
        self.should_park.set(true);
        if !self.initialized.get() {
            self.init();
            self.initialized.set(true);
        }
    }

    #[inline]
    unsafe fn timed_out(&self) -> bool {
        // We need to grab the mutex here because another thread may be
        // concurrently executing UnparkHandle::unpark, which is done without
        // holding the queue lock.
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        let should_park = self.should_park.get();
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        should_park
    }

    #[inline]
    unsafe fn park(&self) {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
            debug_assert_eq!(r, 0);
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
    }

    #[inline]
    unsafe fn park_until(&self, timeout: Instant) -> bool {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let now = Instant::now();
            if timeout <= now {
                let r = libc::pthread_mutex_unlock(self.mutex.get());
                debug_assert_eq!(r, 0);
                return false;
            }

            if let Some(ts) = timeout_to_timespec(timeout - now) {
                let r = libc::pthread_cond_timedwait(self.condvar.get(), self.mutex.get(), &ts);
                if ts.tv_sec < 0 {
                    // On some systems, negative timeouts will return EINVAL. In
                    // that case we won't sleep and will just busy loop instead,
                    // which is the best we can do.
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT || r == libc::EINVAL);
                } else {
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT);
                }
            } else {
                // Timeout calculation overflowed, just sleep indefinitely
                let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
                debug_assert_eq!(r, 0);
            }
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        true
    }

    #[inline]
    unsafe fn unpark_lock(&self) -> UnparkHandle {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);

        UnparkHandle {
            thread_parker: self,
        }
    }
}

impl ThreadParker {
    /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android", target_os = "espidf"))]
    #[inline]
    unsafe fn init(&self) {}

    /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android", target_os = "espidf")))]
    #[inline]
    unsafe fn init(&self) {
        let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
        let r = libc::pthread_condattr_init(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_cond_init(self.condvar.get(), attr.as_ptr());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
        debug_assert_eq!(r, 0);
    }
}

impl Drop for ThreadParker {
    #[inline]
    fn drop(&mut self) {
        // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
        // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
        // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
        // this behaviour no longer occurs. The same applies to condvars.
        unsafe {
            let r = libc::pthread_mutex_destroy(self.mutex.get());
            debug_assert!(r == 0 || r == libc::EINVAL);
            let r = libc::pthread_cond_destroy(self.condvar.get());
            debug_assert!(r == 0 || r == libc::EINVAL);
        }
    }
}

pub struct UnparkHandle {
    thread_parker: *const ThreadParker,
}

impl super::UnparkHandleT for UnparkHandle {
    #[inline]
    unsafe fn unpark(self) {
        (*self.thread_parker).should_park.set(false);

        // We notify while holding the lock here to avoid races with the target
        // thread. In particular, the thread could exit after we unlock the
        // mutex, which would make the condvar access invalid memory.
        let r = libc::pthread_cond_signal((*self.thread_parker).condvar.get());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutex_unlock((*self.thread_parker).mutex.get());
        debug_assert_eq!(r, 0);
    }
}

// Returns the current time on the clock used by pthread_cond_t as a timespec.
#[cfg(any(target_os = "macos", target_os = "ios"))]
#[inline]
fn timespec_now() -> libc::timespec {
    let mut now = MaybeUninit::<libc::timeval>::uninit();
    let r = unsafe { libc::gettimeofday(now.as_mut_ptr(), ptr::null_mut()) };
    debug_assert_eq!(r, 0);
    // SAFETY: We know `libc::gettimeofday` has initialized the value.
    let now = unsafe { now.assume_init() };
    libc::timespec {
        tv_sec: now.tv_sec,
        tv_nsec: now.tv_usec as tv_nsec_t * 1000,
    }
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
#[inline]
fn timespec_now() -> libc::timespec {
    let mut now = MaybeUninit::<libc::timespec>::uninit();
    let clock = if cfg!(target_os = "android") {
        // Android doesn't support pthread_condattr_setclock, so we need to
        // specify the timeout in CLOCK_REALTIME.
        libc::CLOCK_REALTIME
    } else {
        libc::CLOCK_MONOTONIC
    };
    let r = unsafe { libc::clock_gettime(clock, now.as_mut_ptr()) };
    debug_assert_eq!(r, 0);
    // SAFETY: We know `libc::clock_gettime` has initialized the value.
    unsafe { now.assume_init() }
}

// Converts a relative timeout into an absolute timeout in the clock used by
// pthread_cond_t.
#[inline]
fn timeout_to_timespec(timeout: Duration) -> Option<libc::timespec> {
    // Handle overflows early on
    if timeout.as_secs() > libc::time_t::max_value() as u64 {
        return None;
    }

    let now = timespec_now();
    let mut nsec = now.tv_nsec + timeout.subsec_nanos() as tv_nsec_t;
    let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t);
    if nsec >= 1_000_000_000 {
        nsec -= 1_000_000_000;
        sec = sec.and_then(|sec| sec.checked_add(1));
    }

    sec.map(|sec| libc::timespec {
        tv_nsec: nsec,
        tv_sec: sec,
    })
}

#[inline]
pub fn thread_yield() {
    thread::yield_now();
}
<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std::cell::{Cell, UnsafeCell};
use std::time::{Duration, Instant};
use libc;
use std::mem;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use std::ptr;

// Helper type for putting a thread to sleep until some other thread wakes it up
pub struct ThreadParker {
    should_park: Cell<bool>,
    mutex: UnsafeCell<libc::pthread_mutex_t>,
    condvar: UnsafeCell<libc::pthread_cond_t>,
    initialized: Cell<bool>,
}

impl ThreadParker {
    pub fn new() -> ThreadParker {
        ThreadParker {
            should_park: Cell::new(false),
            mutex: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER),
            condvar: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER),
            initialized: Cell::new(false),
        }
    }

    // Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
    unsafe fn init(&self) {}
    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
    unsafe fn init(&self) {
        let mut attr: libc::pthread_condattr_t = mem::uninitialized();
        let r = libc::pthread_condattr_init(&mut attr);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_cond_init(self.condvar.get(), &attr);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_destroy(&mut attr);
        debug_assert_eq!(r, 0);
    }

    // Prepares the parker. This should be called before adding it to the queue.
    pub unsafe fn prepare_park(&self) {
        self.should_park.set(true);
        if !self.initialized.get() {
            self.init();
            self.initialized.set(true);
        }
    }

    // Checks if the park timed out. This should be called while holding the
    // queue lock after park_until has returned false.
    pub unsafe fn timed_out(&self) -> bool {
        // We need to grab the mutex here because another thread may be
        // concurrently executing UnparkHandle::unpark, which is done without
        // holding the queue lock.
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        let should_park = self.should_park.get();
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        should_park
    }

    // Parks the thread until it is unparked. This should be called after it has
    // been added to the queue, after unlocking the queue.
    pub unsafe fn park(&self) {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
            debug_assert_eq!(r, 0);
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
    }

    // Parks the thread until it is unparked or the timeout is reached. This
    // should be called after it has been added to the queue, after unlocking
    // the queue. Returns true if we were unparked and false if we timed out.
    pub unsafe fn park_until(&self, timeout: Instant) -> bool {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let now = Instant::now();
            if timeout <= now {
                let r = libc::pthread_mutex_unlock(self.mutex.get());
                debug_assert_eq!(r, 0);
                return false;
            }

            if let Some(ts) = timeout_to_timespec(timeout - now) {
                let r = libc::pthread_cond_timedwait(self.condvar.get(), self.mutex.get(), &ts);
                if ts.tv_sec < 0 {
                    // On some systems, negative timeouts will return EINVAL. In
                    // that case we won't sleep and will just busy loop instead,
                    // which is the best we can do.
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT || r == libc::EINVAL);
                } else {
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT);
                }
            } else {
                // Timeout calculation overflowed, just sleep indefinitely
                let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
                debug_assert_eq!(r, 0);
            }
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        true
    }

    // Locks the parker to prevent the target thread from exiting. This is
    // necessary to ensure that thread-local ThreadData objects remain valid.
    // This should be called while holding the queue lock.
    pub unsafe fn unpark_lock(&self) -> UnparkHandle {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);

        UnparkHandle {
            thread_parker: self,
        }
    }
}

impl Drop for ThreadParker {
    fn drop(&mut self) {
        // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
        // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
        // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
        // this behaviour no longer occurs. The same applies to condvars.
        unsafe {
            let r = libc::pthread_mutex_destroy(self.mutex.get());
            if cfg!(target_os = "dragonfly") {
                debug_assert!(r == 0 || r == libc::EINVAL);
            } else {
                debug_assert_eq!(r, 0);
            }
            let r = libc::pthread_cond_destroy(self.condvar.get());
            if cfg!(target_os = "dragonfly") {
                debug_assert!(r == 0 || r == libc::EINVAL);
            } else {
                debug_assert_eq!(r, 0);
            }
        }
    }
}

// Handle for a thread that is about to be unparked. We need to mark the thread
// as unparked while holding the queue lock, but we delay the actual unparking
// until after the queue lock is released.
pub struct UnparkHandle {
    thread_parker: *const ThreadParker,
}

impl UnparkHandle {
    // Wakes up the parked thread. This should be called after the queue lock is
    // released to avoid blocking the queue for too long.
    pub unsafe fn unpark(self) {
        (*self.thread_parker).should_park.set(false);

        // We notify while holding the lock here to avoid races with the target
        // thread. In particular, the thread could exit after we unlock the
        // mutex, which would make the condvar access invalid memory.
        let r = libc::pthread_cond_signal((*self.thread_parker).condvar.get());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutex_unlock((*self.thread_parker).mutex.get());
        debug_assert_eq!(r, 0);
    }
}

// Returns the current time on the clock used by pthread_cond_t as a timespec.
#[cfg(any(target_os = "macos", target_os = "ios"))]
unsafe fn timespec_now() -> libc::timespec {
    let mut now: libc::timeval = mem::uninitialized();
    let r = libc::gettimeofday(&mut now, ptr::null_mut());
    debug_assert_eq!(r, 0);
    libc::timespec {
        tv_sec: now.tv_sec,
        tv_nsec: now.tv_usec as libc::c_long * 1000,
    }
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
unsafe fn timespec_now() -> libc::timespec {
    let mut now: libc::timespec = mem::uninitialized();
    let clock = if cfg!(target_os = "android") {
        // Android doesn't support pthread_condattr_setclock, so we need to
        // specify the timeout in CLOCK_REALTIME.
        libc::CLOCK_REALTIME
    } else {
        libc::CLOCK_MONOTONIC
    };
    let r = libc::clock_gettime(clock, &mut now);
    debug_assert_eq!(r, 0);
    now
}

// Converts a relative timeout into an absolute timeout in the clock used by
// pthread_cond_t.
unsafe fn timeout_to_timespec(timeout: Duration) -> Option<libc::timespec> {
    // Handle overflows early on
    if timeout.as_secs() > libc::time_t::max_value() as u64 {
        return None;
    }

    let now = timespec_now();
    let mut nsec = now.tv_nsec + timeout.subsec_nanos() as libc::c_long;
    let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t);
    if nsec >= 1_000_000_000 {
        nsec -= 1_000_000_000;
        sec = sec.and_then(|sec| sec.checked_add(1));
    }

    sec.map(|sec| libc::timespec {
        tv_nsec: nsec,
        tv_sec: sec,
    })
}
<commit_msg>Fix compilation error on Linux with x32 ABI<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use std::cell::{Cell, UnsafeCell};
use std::time::{Duration, Instant};
use libc;
use std::mem;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use std::ptr;

// x32 Linux uses a non-standard type for tv_nsec in timespec.
// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
#[allow(non_camel_case_types)]
type tv_nsec_t = i64;
#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
#[allow(non_camel_case_types)]
type tv_nsec_t = libc::c_long;

// Helper type for putting a thread to sleep until some other thread wakes it up
pub struct ThreadParker {
    should_park: Cell<bool>,
    mutex: UnsafeCell<libc::pthread_mutex_t>,
    condvar: UnsafeCell<libc::pthread_cond_t>,
    initialized: Cell<bool>,
}

impl ThreadParker {
    pub fn new() -> ThreadParker {
        ThreadParker {
            should_park: Cell::new(false),
            mutex: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER),
            condvar: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER),
            initialized: Cell::new(false),
        }
    }

    // Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
    #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
    unsafe fn init(&self) {}
    #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
    unsafe fn init(&self) {
        let mut attr: libc::pthread_condattr_t = mem::uninitialized();
        let r = libc::pthread_condattr_init(&mut attr);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_cond_init(self.condvar.get(), &attr);
        debug_assert_eq!(r, 0);
        let r = libc::pthread_condattr_destroy(&mut attr);
        debug_assert_eq!(r, 0);
    }

    // Prepares the parker. This should be called before adding it to the queue.
    pub unsafe fn prepare_park(&self) {
        self.should_park.set(true);
        if !self.initialized.get() {
            self.init();
            self.initialized.set(true);
        }
    }

    // Checks if the park timed out. This should be called while holding the
    // queue lock after park_until has returned false.
    pub unsafe fn timed_out(&self) -> bool {
        // We need to grab the mutex here because another thread may be
        // concurrently executing UnparkHandle::unpark, which is done without
        // holding the queue lock.
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        let should_park = self.should_park.get();
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        should_park
    }

    // Parks the thread until it is unparked. This should be called after it has
    // been added to the queue, after unlocking the queue.
    pub unsafe fn park(&self) {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
            debug_assert_eq!(r, 0);
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
    }

    // Parks the thread until it is unparked or the timeout is reached. This
    // should be called after it has been added to the queue, after unlocking
    // the queue. Returns true if we were unparked and false if we timed out.
    pub unsafe fn park_until(&self, timeout: Instant) -> bool {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);
        while self.should_park.get() {
            let now = Instant::now();
            if timeout <= now {
                let r = libc::pthread_mutex_unlock(self.mutex.get());
                debug_assert_eq!(r, 0);
                return false;
            }

            if let Some(ts) = timeout_to_timespec(timeout - now) {
                let r = libc::pthread_cond_timedwait(self.condvar.get(), self.mutex.get(), &ts);
                if ts.tv_sec < 0 {
                    // On some systems, negative timeouts will return EINVAL. In
                    // that case we won't sleep and will just busy loop instead,
                    // which is the best we can do.
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT || r == libc::EINVAL);
                } else {
                    debug_assert!(r == 0 || r == libc::ETIMEDOUT);
                }
            } else {
                // Timeout calculation overflowed, just sleep indefinitely
                let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
                debug_assert_eq!(r, 0);
            }
        }
        let r = libc::pthread_mutex_unlock(self.mutex.get());
        debug_assert_eq!(r, 0);
        true
    }

    // Locks the parker to prevent the target thread from exiting. This is
    // necessary to ensure that thread-local ThreadData objects remain valid.
    // This should be called while holding the queue lock.
    pub unsafe fn unpark_lock(&self) -> UnparkHandle {
        let r = libc::pthread_mutex_lock(self.mutex.get());
        debug_assert_eq!(r, 0);

        UnparkHandle {
            thread_parker: self,
        }
    }
}

impl Drop for ThreadParker {
    fn drop(&mut self) {
        // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
        // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
        // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
        // this behaviour no longer occurs. The same applies to condvars.
        unsafe {
            let r = libc::pthread_mutex_destroy(self.mutex.get());
            if cfg!(target_os = "dragonfly") {
                debug_assert!(r == 0 || r == libc::EINVAL);
            } else {
                debug_assert_eq!(r, 0);
            }
            let r = libc::pthread_cond_destroy(self.condvar.get());
            if cfg!(target_os = "dragonfly") {
                debug_assert!(r == 0 || r == libc::EINVAL);
            } else {
                debug_assert_eq!(r, 0);
            }
        }
    }
}

// Handle for a thread that is about to be unparked. We need to mark the thread
// as unparked while holding the queue lock, but we delay the actual unparking
// until after the queue lock is released.
pub struct UnparkHandle {
    thread_parker: *const ThreadParker,
}

impl UnparkHandle {
    // Wakes up the parked thread. This should be called after the queue lock is
    // released to avoid blocking the queue for too long.
    pub unsafe fn unpark(self) {
        (*self.thread_parker).should_park.set(false);

        // We notify while holding the lock here to avoid races with the target
        // thread. In particular, the thread could exit after we unlock the
        // mutex, which would make the condvar access invalid memory.
        let r = libc::pthread_cond_signal((*self.thread_parker).condvar.get());
        debug_assert_eq!(r, 0);
        let r = libc::pthread_mutex_unlock((*self.thread_parker).mutex.get());
        debug_assert_eq!(r, 0);
    }
}

// Returns the current time on the clock used by pthread_cond_t as a timespec.
#[cfg(any(target_os = "macos", target_os = "ios"))]
unsafe fn timespec_now() -> libc::timespec {
    let mut now: libc::timeval = mem::uninitialized();
    let r = libc::gettimeofday(&mut now, ptr::null_mut());
    debug_assert_eq!(r, 0);
    libc::timespec {
        tv_sec: now.tv_sec,
        tv_nsec: now.tv_usec as tv_nsec_t * 1000,
    }
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
unsafe fn timespec_now() -> libc::timespec {
    let mut now: libc::timespec = mem::uninitialized();
    let clock = if cfg!(target_os = "android") {
        // Android doesn't support pthread_condattr_setclock, so we need to
        // specify the timeout in CLOCK_REALTIME.
        libc::CLOCK_REALTIME
    } else {
        libc::CLOCK_MONOTONIC
    };
    let r = libc::clock_gettime(clock, &mut now);
    debug_assert_eq!(r, 0);
    now
}

// Converts a relative timeout into an absolute timeout in the clock used by
// pthread_cond_t.
unsafe fn timeout_to_timespec(timeout: Duration) -> Option<libc::timespec> {
    // Handle overflows early on
    if timeout.as_secs() > libc::time_t::max_value() as u64 {
        return None;
    }

    let now = timespec_now();
    let mut nsec = now.tv_nsec + timeout.subsec_nanos() as tv_nsec_t;
    let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t);
    if nsec >= 1_000_000_000 {
        nsec -= 1_000_000_000;
        sec = sec.and_then(|sec| sec.checked_add(1));
    }

    sec.map(|sec| libc::timespec {
        tv_nsec: nsec,
        tv_sec: sec,
    })
}
<|endoftext|>