Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

📝 Knowledge Test

/
Knowledge Test Score Board
· 0%

Work through the questions below — your score updates as you go.

Keep going · grade bands mapped to DCU's honours scale

target20:00

Only your first attempt at each question counts toward this score. Reload the page to reset.

This section contains 20 interactive knowledge checks designed to test your understanding of the materials covered in Chapter 10. The questions cover OS threads, message passing via channels, shared state using Mutex and Arc, atomic operations, and the marker traits that guarantee thread safety.

Please remember that many of the quizzes have multiple correct answers, and you must select all applicable options to succeed. I have carefully balanced the lengths of the answers, so take your time to evaluate each option thoroughly.

Good luck!

Derek.


Q1
Concept Match

Match the Concurrency Fundamentals

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Concurrency
drag a definition here…
Parallelism
drag a definition here…
Process
drag a definition here…
Thread
drag a definition here…

Definition Pool

Physical simultaneity where multiple computations execute at the exact same instant.
A lightweight unit of execution that lives inside and shares memory with a process.
An isolated execution environment with private memory space and resources.
A structural property where multiple tasks make progress in overlapping time periods.
Q2
Quiz
Select 0/2

Which of the following statements accurately describe the behaviour of thread::spawn() in Rust?

It returns a JoinHandle, which can be used to wait for the thread's completion and retrieve its result.
It automatically ensures that all spawned threads are joined before the main function returns.
It creates a new OS thread and blocks the main thread until the new thread starts running.
It requires a closure that must satisfy the 'static lifetime bound if it does not use scoped threads.
Q3
Code Cloze
Rust

Spawning and Joining Threads

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1use std::thread;
2
3fn main() {
4 // Spawn a thread that returns a value
5 let handle = thread::·····(|| {
6 42
7 });
8
9 // Wait for the thread to finish and retrieve the result
10 let result = handle.·····().unwrap();
11 println!("Thread returned: {}", result);
12}

Available Snippets

wait
run
await
create
join
spawn
Q4
Quiz
Select 0/1

Why is the 'move' keyword frequently required when spawning a new thread?

To satisfy the thread-safety requirements of the operating system's kernel-level scheduler.
To prevent the main thread from accessing any variables until the spawned thread finishes.
To tell the compiler to optimise the closure body for faster concurrent execution.
To force the closure to take ownership of captured variables, ensuring they live long enough.
Q5
Concept Match

Match the Channel Components

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

mpsc
drag a definition here…
Sender
drag a definition here…
Receiver
drag a definition here…
try_recv
drag a definition here…

Definition Pool

The transmitting end of a channel; it can be cloned to allow multiple producer threads.
A non-blocking receive method that returns an error immediately if no message is waiting.
Stands for 'Multiple Producer, Single Consumer', the standard channel type in Rust.
The receiving end of a channel; it cannot be cloned, ensuring a single consumer.
Q6
Code Output
Rust

Predict the output: Channel Communication

Read the code below, then choose the terminal output it produces and click Submit.

1use std::sync::mpsc;
2use std::thread;
3
4fn main() {
5 let (tx, rx) = mpsc::channel();
6
7 thread::spawn(move || {
8 tx.send(10).unwrap();
9 tx.send(20).unwrap();
10 });
11
12 let val1 = rx.recv().unwrap();
13 let val2 = rx.recv().unwrap();
14 println!("{} {}", val1, val2);
15}
stdout
10 20
stdout
20 10
stdout
10 10
stdout
20 20
Q7
Quiz
Select 0/1

When does a loop using a Receiver as an iterator (e.g., for msg in rx { ... }) terminate?

Exactly 60 seconds after the last message was successfully transmitted through the pipe.
As soon as the buffer is empty, regardless of whether senders are still active.
When all corresponding Sender handles have been dropped and the channel buffer is empty.
Only when the developer explicitly calls an rx.close() method from the consumer thread.
Q8
Quiz
Select 0/1

In the context of Assignment 2 and responsive GUIs, why is try_recv() preferred over recv() inside the update() loop?

Because recv() is a blocking call that would freeze the GUI until a new message arrives.
Because recv() requires a mutable reference to the context, which is not available in egui.
Because try_recv() automatically handles the synchronisation of background hardware threads.
Because try_recv() is significantly faster at processing complex data structures like JSON.
Q9
Concept Match

Match the Synchronisation Primitives

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Mutex<T>
drag a definition here…
RwLock<T>
drag a definition here…
Arc<T>
drag a definition here…
MutexGuard
drag a definition here…

Definition Pool

An Atomic Reference Counted pointer that enables multiple threads to share ownership of data.
A smart pointer that provides data access and automatically releases the lock when dropped.
Enforces mutual exclusion, allowing only one thread at a time to access the protected data.
Allows multiple concurrent readers or exactly one exclusive writer; ideal for read-heavy data.
Q10
Code Cloze
Rust

Sharing a Mutex with Arc

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1use std::sync::{Arc, Mutex};
2use std::thread;
3
4fn main() {
5 // Create a shared counter
6 let counter = Arc::new(Mutex::new(0));
7 let mut handles = vec![];
8
9 for _ in 0..10 {
10 // Clone the Arc to share ownership
11 let counter_clone = Arc::·····(&counter);
12 let handle = thread::spawn(move || {
13 // Acquire the lock
14 let mut num = counter_clone.·····().unwrap();
15 *num += 1;
16 });
17 handles.push(handle);
18 }
19}

Available Snippets

read
acquire
clone
lock
new
copy
Q11
Quiz
Select 0/1

What is 'Mutex Poisoning' in Rust?

A hardware failure where the CPU fails to correctly execute an atomic compare-and-swap instruction.
An optimisation where the compiler permanently locks a mutex to prevent any further modifications.
A state where a thread panics while holding a lock, indicating the protected data may be inconsistent.
A security vulnerability where an external process injects malicious code into a shared memory region.
Q12
Quiz
Select 0/2

Why is Arc<T> used instead of Rc<T> for sharing data between threads?

Because Rc is restricted to sharing only primitive types like integers and booleans.
Because Rc uses non-atomic counters which could lead to data races on the reference count itself.
Because Arc allows the data to be stored in the program's read-only binary section.
Because Arc uses atomic operations to update the reference count, ensuring it is thread-safe.
Q13
Concept Match

Match the Advanced Sync Concepts

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

AtomicBool
drag a definition here…
Condvar
drag a definition here…
Deadlock
drag a definition here…
SeqCst
drag a definition here…

Definition Pool

A thread-safe boolean that can be loaded and stored without the overhead of a mutex lock.
The strongest memory ordering, ensuring a consistent global order of atomic operations.
Allows a thread to sleep efficiently until another thread signals that a condition has changed.
A situation where two threads are each waiting for a lock held by the other, stalling indefinitely.
Q14
Code Cloze
Rust

Using an Atomic Stop Flag

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1use std::sync::atomic::{AtomicBool, Ordering};
2
3fn main() {
4 let stop_flag = AtomicBool::new(false);
5
6 // Set the flag atomically
7 stop_flag.·····(true, Ordering::SeqCst);
8
9 // Read the flag atomically
10 if stop_flag.·····(Ordering::SeqCst) {
11 println!("Stopping...");
12 }
13}

Available Snippets

load
get
write
store
set
read
Q15
Quiz
Select 0/1

In a read-heavy workload where 100 threads read a value and only 1 thread writes it, why is RwLock<T> superior to Mutex<T>?

Because RwLock uses a more advanced hardware-level cache that is only available on multi-core ARM processors.
Because RwLock allows all 100 readers to access the data simultaneously, whereas Mutex would serialise them.
Because RwLock provides a compile-time guarantee that the writer thread will always have highest priority.
Because Mutex requires a separate memory allocation for every reader thread that attempts to acquire the lock.
Q16
Quiz
Select 0/1

Which of the following describes 'Priority Inversion'?

A situation where a high-priority thread is blocked by a low-priority thread holding a shared resource.
An optimisation where the OS scheduler prioritises background threads to save battery life.
A logic error where a thread's priority is accidentally set to a negative value by the developer.
A scenario where the main thread is forced to wait for all spawned threads to finish before exiting.
Q17
Concept Match

Match the Marker Traits

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Send
drag a definition here…
Sync
drag a definition here…
Marker Trait
drag a definition here…
Interior Mutability
drag a definition here…

Definition Pool

A trait with no methods, used purely to provide information to the compiler.
A type that can safely have its ownership transferred between thread boundaries.
Types like Mutex or Atomic that allow mutation even when accessed via an immutable reference.
A type that can safely be shared via references (&T) across multiple threads.
Q18
Quiz
Select 0/2

Why does the Rust compiler refuse to move an Rc<T> into a spawned thread?

Because Rc<T> does not implement the Send trait due to its use of non-atomic reference counting.
Because Rc<T> is too large to fit in the new thread's stack space without causing an overflow.
Because moving Rc<T> could lead to two threads updating the same counter simultaneously, causing a data race.
Because the thread::spawn function is hard-coded to only accept String and integer types.
Q19
Quiz
Select 0/1

Regarding the relationship between Send and Sync, which of the following is TRUE?

If a type is Send, it is guaranteed to be Sync, as ownership transfer is more restrictive.
The Sync trait is required for any data being sent through an mpsc channel.
A type T is Sync if and only if a reference to it (&T) is Send.
Any type that implements Sync must also explicitly implement the Copy trait.
Q20
Quiz
Select 0/2

Which of the following types are NOT Send or Sync and require 'unsafe' code or a Mutex to be used in threads? Select all that apply.

RefCell<T>, as its runtime borrow checks are not thread-safe.
AtomicU32, as it provides hardware-level synchronisation for its internal value.
String, as it is a complex heap-allocated type that requires strict management.
Raw pointers like *mut u8, as they carry no ownership or safety information.

Congratulations on completing the Chapter 10 Knowledge Test! Review any questions you missed to ensure a solid grasp of Rust’s ‘fearless concurrency’ model before moving on to networking and advanced edge patterns.