1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! # use server::thread::{ [ThreadPool], [Worker] }
//! 
//! This library handles operations to build a multi-threaded system for the web-server.

use std::{sync::{mpsc, Arc, Mutex}, thread};

/// The ThreadPool data structure holds the underlying mechanism for processing concurrent requests.
/// It has a sender which propagates requests through the channel and a vector of workers.
pub struct ThreadPool {
    /// This is an array of workers which constantly listen for requests to the server.
    /// The number of workers defaults to 10 but can be provided with the `threads` option.
    pub workers: Vec<Worker>,

    /// This is a sender type used to propagate requests through a channel that every worker listens on.
    pub sender: mpsc::Sender<Job>
}


impl ThreadPool {
    /// Returns a struct with the type ThreaPool. This method is used to initiate a new threadpool when the server is started.
    /// The workers/threads share ownership of a reciever which is used to accept requests.
    /// 
    /// Args
    /// 
    /// * `pool_size` - This represents the number of workers/threads the server is started with.
    /// 
    /// # Examples
    /// 
    /// ```
    /// use server::thread::ThreadPool;
    /// 
    /// let pool: ThreadPool = ThreadPool::new(5);
    /// assert_eq!(pool.workers.len(), 5);
    /// ```
    /// 
    /// # Panics
    /// 
    /// The associated function panics when the pool size provided is less that 1 or greater than 100.
    /// 
    pub fn new(pool_size: usize) -> ThreadPool {

        assert!(pool_size > 0 && pool_size <= 100, "Pool size must be greater than 0 and less than or equal to 100");
        let (sender, reciever) = mpsc::channel();
        let reciever = Arc::new(Mutex::new(reciever));
        let mut workers = Vec::with_capacity(pool_size);

        for id in 0..pool_size {
            workers.push(Worker::new(id, Arc::clone(&reciever)));
        }

        ThreadPool { workers, sender }
    }

    /// This function executes a closure `f` which essentially sends a stream of request down the channel.
    /// A worker in the pool recieves the request and executes it.
    /// 
    /// Args
    /// * `f` - A closure `handle_connection`.
    /// 
    /// The closure is then sent down the channel as a Job.
    /// 
    pub fn execute<F>(&self, f: F)
        where
        F: FnOnce() + Send + 'static
    {
        let job = Box::new(f);
        match self.sender.send(job) {
            Ok(_) => {}
            Err(e) => {
                println!("{}", e.to_string())
            }
        }
    }

    // Function signature for thread::spawn
    /* pub fn spawn<F, T>(f: F) -> JoinHandle<T>
    where
        F: FnOnce() -> T + Send + 'static,
        T: Send + 'static,  {} */
}

/// This represents a thread that executes a single request at a time.
pub struct Worker {
    /// This is a unique Identifier for the worker
    pub id: usize,

    /// This is a thread that is used for executing requests.
    pub thread: thread::JoinHandle<()>
}

/// Type alias `Job`.
type Job = Box<dyn FnOnce() + Send + 'static>;

impl Worker {
    /// Function to spawn a thread that continously listens for requests and executes any it gets.
    /// 
    /// Args
    /// 
    /// * `receiver` - This is a Reciever type that recieves requests from the thread-pool channel.
    /// 
    pub fn new(id: usize, reciever: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
        let thread = thread::spawn(move || 
            loop {
                let job = reciever.lock().unwrap().recv().unwrap();
                
                println!("Executing job with worker Id: {}", id);
                job();
            }
        );

        Worker { id, thread }
    }
}