Skip to content

Commit

Permalink
Add a basic mutex usage in L04 and minor change in L05
Browse files Browse the repository at this point in the history
  • Loading branch information
h365chen committed Jan 15, 2024
1 parent 0a9f08b commit 8490dcd
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 62 deletions.
149 changes: 95 additions & 54 deletions lectures/flipped/L04.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ We will talk about mutex, reference-counting pointers, and lifetimes.

## Reference Counted

### Mutex

```rust
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!("m = {:?}", m);
}
```

### Rc

```rust
use std::rc::Rc;

Expand All @@ -28,7 +44,7 @@ fn main() {
let v = 5;
let arc = Arc::new(Mutex::new(v)); // Is v moved? try print it out at the end

// Exercise:
// Exercise (10 minutes)
// Can you clone the `arc` and pass it to a worker thread
// Inside the worker thread, set the value to 4.

Expand All @@ -46,6 +62,39 @@ fn main() {
}
```

### Exercise: with String (if we have time)

Fix the following code so that we can modify the `s` from both threads.

```rust
use std::sync::Mutex;
use std::thread;
use std::time;

fn main() {
let s = String::from("start\n");

let mutex = Mutex::new(s);

let h = thread::spawn(|| {
for _i in 0..2 {
mutex.lock().unwrap().push_str("child thread\n");
thread::sleep(time::Duration::from_millis(1));
}
});

for _i in 0..2 {
mutex.lock().unwrap().push_str("main thread\n");
thread::sleep(time::Duration::from_millis(1));
}

h.join().expect("fail to join handle");
println!("{}", mutex.lock().unwrap());
}
```

So use `Arc` and then move a cloned mutex into the subthread would work.


## Lifetimes

Expand Down Expand Up @@ -100,7 +149,7 @@ fn main() {
}
```

### struct
### struct (advanced)

Code based on
<https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime/struct.html>
Expand Down Expand Up @@ -130,6 +179,49 @@ fn main() {
}
```

### use Drop trait

```rust
#[derive(Debug)]
struct MyInt {
x: i32,
}

impl Drop for MyInt {
fn drop(&mut self) {
println!("Dropping: {:?}", self);
}
}

fn main() {
let x = MyInt{x: 20};
let x_ref = &x;
let x = MyInt{x: 17}; // Shadowing the previous `x`
println!("x_ref={:?}, x={:?}", x_ref, x);
// let's see when the first x is dropped
}
```

### Exercise: lifetimes (advanced)

Read
<https://doc.rust-lang.org/stable/book/ch10-03-lifetime-syntax.html#lifetime-elision>
and try to expand the following functions

```rust
fn print(s: &str); // elided

fn debug(lvl: usize, s: &str); // elided

fn substr(s: &str, until: usize) -> &str; // elided

fn new(buf: &mut [u8]) -> Thing; // elided
```

Answer is proivded on
<https://doc.rust-lang.org/reference/lifetime-elision.html?highlight=lifetime#lifetime-elision-in-functions>


## Unsafe

### split a slice into two mutable slices
Expand Down Expand Up @@ -168,58 +260,7 @@ fn main() {
}
```


# In-class exercises

Fix the following code so that we can modify the `s` from both threads.

```rust
use std::sync::Mutex;
use std::thread;
use std::time;

fn main() {
let s = String::from("start\n");

let mutex = Mutex::new(s);

let h = thread::spawn(|| {
for _i in 0..2 {
mutex.lock().unwrap().push_str("child thread\n");
thread::sleep(time::Duration::from_millis(1));
}
});

for _i in 0..2 {
mutex.lock().unwrap().push_str("main thread\n");
thread::sleep(time::Duration::from_millis(1));
}

h.join().expect("fail to join handle");
println!("{}", mutex.lock().unwrap());
}
```

### lifetimes (advanced)

Read
<https://doc.rust-lang.org/stable/book/ch10-03-lifetime-syntax.html#lifetime-elision>
and try to expand the following functions

```rust
fn print(s: &str); // elided

fn debug(lvl: usize, s: &str); // elided

fn substr(s: &str, until: usize) -> &str; // elided

fn new(buf: &mut [u8]) -> Thing; // elided
```

Answer is proivded on
<https://doc.rust-lang.org/reference/lifetime-elision.html?highlight=lifetime#lifetime-elision-in-functions>

### Unsafe (advanced)
### Exercise: Unsafe (advanced)

Try to split a given i32 vector into two slices using a raw pointer. (Exercise is based on
https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#creating-a-safe-abstraction-over-unsafe-code
Expand Down
16 changes: 8 additions & 8 deletions lectures/flipped/L05.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ Blocking: M works on task 2 only after task 1 is done.
Non-blocking: M works on task 1 and task 2 simultaneously, and do summation when
whichever task has new numbers.

## Understanding async/await

In above case, do we have thread switches?
In above case, do we have multiple threads?

Let's now focus on just reading the numbers, ignoring the summation.
How about context switches? (multiple times between task 1 to task 2)

If you are writing code, how can you read them using the non-blocking approach?
Assume you are the only thread in the system. Busy polling?
## Understanding async/await

What if we have interrupts?
More like programmers provide rules to the runtime executor so that it knows how
to properly do such context switches.

## future

Expand All @@ -44,7 +42,9 @@ use futures::executor::block_on;
async fn hello_world() {
println!("hello");
}

fn main() {
// to show that async function will not be executed directly
let future = hello_world();
block_on(future);
}
Expand Down Expand Up @@ -131,7 +131,7 @@ fn main() {
A side note: [Difference between Pipelining and
Multiplex](https://stackoverflow.com/questions/34478967/what-is-the-difference-between-http-1-1-pipelining-and-http-2-multiplexing#:~:text=HTTP%2F1.1%20with%20pipelining%3A%20Each%20HTTP%20request%20over%20the,waiting%20for%20the%20previous%20response%20to%20come%20back.)

## Exercise: using futures
## Exercise: using futures (advanced)

Based on
[https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html],
Expand Down

0 comments on commit 8490dcd

Please sign in to comment.