server/thread.rs
1//! # use server::thread::{ [ThreadPool], [Worker] }
2//!
3//! This library handles operations to build a multi-threaded system for the web-server.
4
5use std::{sync::{mpsc, Arc, Mutex}, thread};
6
7/// The ThreadPool data structure holds the underlying mechanism for processing concurrent requests.
8/// It has a sender which propagates requests through the channel and a vector of workers.
9pub struct ThreadPool {
10 /// This is an array of workers which constantly listen for requests to the server.
11 /// The number of workers defaults to 10 but can be provided with the `threads` option.
12 pub workers: Vec<Worker>,
13
14 /// This is a sender type used to propagate requests through a channel that every worker listens on.
15 pub sender: mpsc::Sender<Job>
16}
17
18
19impl ThreadPool {
20 /// Returns a struct with the type ThreaPool. This method is used to initiate a new threadpool when the server is started.
21 /// The workers/threads share ownership of a reciever which is used to accept requests.
22 ///
23 /// Args
24 ///
25 /// * `pool_size` - This represents the number of workers/threads the server is started with.
26 ///
27 /// # Examples
28 ///
29 /// ```
30 /// use server::thread::ThreadPool;
31 ///
32 /// let pool: ThreadPool = ThreadPool::new(5);
33 /// assert_eq!(pool.workers.len(), 5);
34 /// ```
35 ///
36 /// # Panics
37 ///
38 /// The associated function panics when the pool size provided is less that 1 or greater than 100.
39 ///
40 pub fn new(pool_size: usize) -> ThreadPool {
41
42 assert!(pool_size > 0 && pool_size <= 100, "Pool size must be greater than 0 and less than or equal to 100");
43 let (sender, reciever) = mpsc::channel();
44 let reciever = Arc::new(Mutex::new(reciever));
45 let mut workers = Vec::with_capacity(pool_size);
46
47 for id in 0..pool_size {
48 workers.push(Worker::new(id, Arc::clone(&reciever)));
49 }
50
51 ThreadPool { workers, sender }
52 }
53
54 /// This function executes a closure `f` which essentially sends a stream of request down the channel.
55 /// A worker in the pool recieves the request and executes it.
56 ///
57 /// Args
58 /// * `f` - A closure `handle_connection`.
59 ///
60 /// The closure is then sent down the channel as a Job.
61 ///
62 pub fn execute<F>(&self, f: F)
63 where
64 F: FnOnce() + Send + 'static
65 {
66 let job = Box::new(f);
67 match self.sender.send(job) {
68 Ok(_) => {}
69 Err(e) => {
70 println!("{}", e.to_string())
71 }
72 }
73 }
74
75 // Function signature for thread::spawn
76 /* pub fn spawn<F, T>(f: F) -> JoinHandle<T>
77 where
78 F: FnOnce() -> T + Send + 'static,
79 T: Send + 'static, {} */
80}
81
82/// This represents a thread that executes a single request at a time.
83pub struct Worker {
84 /// This is a unique Identifier for the worker
85 pub id: usize,
86
87 /// This is a thread that is used for executing requests.
88 pub thread: thread::JoinHandle<()>
89}
90
91/// Type alias `Job`.
92type Job = Box<dyn FnOnce() + Send + 'static>;
93
94impl Worker {
95 /// Function to spawn a thread that continously listens for requests and executes any it gets.
96 ///
97 /// Args
98 ///
99 /// * `receiver` - This is a Reciever type that recieves requests from the thread-pool channel.
100 ///
101 pub fn new(id: usize, reciever: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
102 let thread = thread::spawn(move ||
103 loop {
104 let job = reciever.lock().unwrap().recv().unwrap();
105
106 println!("Executing job with worker Id: {}", id);
107 job();
108 }
109 );
110
111 Worker { id, thread }
112 }
113}