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}