0

I am trying to implement a merge sort using processes but I have a problem using the waitpid function:

extern crate nix;
extern crate rand;

use nix::sys::wait::WaitStatus;
use rand::Rng;
use std::io;
use std::process::exit;
use std::thread::sleep;
use std::time::{Duration, Instant};

use nix::sys::wait::waitpid;
use nix::unistd::Pid;
use nix::unistd::{fork, getpid, getppid, ForkResult};

static mut process_count: i32 = 0;
static mut thread_count: i32 = 0;

fn generate_array(len: usize) -> Vec<f64> {
    let mut my_vector: Vec<f64> = Vec::new();
    for _ in 0..len {
        my_vector.push(rand::thread_rng().gen_range(0.0, 100.0)); // 0 - 99.99999
    }
    return my_vector;
}

fn get_array_size_from_user() -> usize {
    let mut n = String::new();
    io::stdin()
        .read_line(&mut n)
        .expect("failed to read input.");
    let n: usize = n.trim().parse().expect("invalid input");

    return n;
}

fn display_array(array: &mut Vec<f64>) {
    println!("{:?}", array);
    println!();
}

fn clear_screen() {
    print!("{}[2J", 27 as char);
    //print!("\x1B[2J"); // 2nd option
}

pub fn mergeSort(a: &mut Vec<f64>, low: usize, high: usize) {
    let middle = (low + high) / 2;
    let mut len = (high - low + 1);

    if (len <= 1) {
        return;
    }

    let lpid = fork();

    match lpid {
        Ok(ForkResult::Child) => {
            println!("Left Process Running ");
            mergeSort(a, low, middle);
            exit(0);
        }
        Ok(ForkResult::Parent { child }) => {
            let rpid = fork();

            match rpid {
                Ok(ForkResult::Child) => {
                    println!("Right Process Running ");
                    mergeSort(a, middle + 1, high);
                    exit(0);
                }
                Ok(ForkResult::Parent { child }) => {}
                Err(err) => {
                    panic!("Right process not created: {}", err);
                }
            };
        }

        Err(err) => {
            panic!("Left process not created {}", err);
        }
    };

    //waitpid(lpid, None);
    //waitpid(rpid, None);

    // Merge the sorted subarrays
    merge(a, low, middle, high);
}

fn merge(a: &mut Vec<f64>, low: usize, m: usize, high: usize) {
    println!("x");
    let mut left = a[low..m + 1].to_vec();
    let mut right = a[m + 1..high + 1].to_vec();

    println!("left: {:?}", left);
    println!("right: {:?}", right);

    left.reverse();
    right.reverse();

    for k in low..high + 1 {
        if left.is_empty() {
            a[k] = right.pop().unwrap();
            continue;
        }
        if right.is_empty() {
            a[k] = left.pop().unwrap();
            continue;
        }
        if right.last() < left.last() {
            a[k] = right.pop().unwrap();
        } else {
            a[k] = left.pop().unwrap();
        }
    }

    println!("array: {:?}", a);
}

unsafe fn display_process_thread_counts() {
    unsafe {
        println!("process count:");
        println!("{}", process_count);
        println!("thread count:");
        println!("{}", thread_count);
    }
}

unsafe fn process_count_plus_plus() {
    process_count += 1;
}

unsafe fn thread_count_plus_plus() {
    thread_count += 1;
}

fn print_time(start: Instant, end: Instant) {
    println!("TIME:");
    println!("{:?}", end.checked_duration_since(start));
}

fn main() {
    println!("ENTER SIZE OF ARRAY \n");
    let array_size = get_array_size_from_user();

    let mut generated_array = generate_array(array_size);

    clear_screen();

    println!("GENERATED ARRAY: \n");
    display_array(&mut generated_array);

    // SORTING
    let start = Instant::now();
    mergeSort(&mut generated_array, 0, array_size - 1);
    let end = Instant::now();

    // RESULT

    //unsafe{
    //    process_count_plus_plus();
    //    thread_count_plus_plus();
    //}

    println!("SORTED ARRAY: \n");
    display_array(&mut generated_array);

    print_time(start, end);

    unsafe {
        display_process_thread_counts();
    }
}

I get these results without using waitpid for the vector [3, 70, 97, 74]:

  1. array before comparison: [3, 70, 97, 74]

    comparison: [97], [74]

    array after comparison: [3, 70, 74, 97]

  2. array before comparison: [3, 70, 97, 74]

    comparison: [3], [70]

    array after comparison: [3, 70, 97, 74]

  3. array before comparison: [3, 70, 97, 74]

    comparison: [3, 70], [97, 74] (should be [74, 97])

    array after comparison: [3, 70, 97, 74]

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Mateusz Piwowarski
  • 559
  • 1
  • 6
  • 15
  • *but I have a problem using the waitpid function* — why do you ascribe the problem to `waitpid`? – Shepmaster Dec 12 '19 at 01:54
  • 1
    I haven't studied your code so if you're doing a `mmap` or something, forgive me -- but it looks like you're expecting different processes to share memory, and processes don't (generally) work that way. See [What is the difference between a process and a thread?](https://stackoverflow.com/questions/200469/what-is-the-difference-between-a-process-and-a-thread) – trent Dec 12 '19 at 04:07

1 Answers1

2

This has nothing to do with waitpid and everything to do with fork. When you fork a process, the OS creates a copy of your data and the child operates on this copy 1. When the child exits, its memory is discarded. The parent never sees the changes made by the child.

If you need the parent to see the changes made by the child, you should do one of the following:

  • Easiest and fastest is to use threads instead of processes. Threads share memory, so the parent and children all use the same memory. In Rust, the borrow checker ensures that parent and children behave correctly when accessing the same piece of memory.
  • Use mmap or something equivalent to share memory between the parent and children processes. Note however that it will be very difficult to ensure memory safety while the processes all try to access the same memory concurrently.
  • Use some kind of Inter-Process Communication (IPC) mechanism to send the result back from the children to the parent. This is easier than mmap since there is no risk of collision during memory accesses but in your case, given the amount of data that will need to be sent, this will be the slowest.

1 Actually, it uses Copy-On-Write, so data that is simply read is shared, but anything that either the parent or child writes will be copied and the other will not see the result of the write.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jmb
  • 18,893
  • 2
  • 28
  • 55