1 00:00:00,000 --> 00:00:07,800 [No Audio] 2 00:00:07,801 --> 00:00:09,966 In the last tutorial we talked about message 3 00:00:09,967 --> 00:00:11,700 passing through channels, which provides one 4 00:00:11,701 --> 00:00:14,400 way of transferring data between threads. In 5 00:00:14,401 --> 00:00:16,133 this tutorial, we will be looking at an 6 00:00:16,166 --> 00:00:18,266 alternate way of transferring data through 7 00:00:18,267 --> 00:00:20,866 shared states. The message passing is a one 8 00:00:20,867 --> 00:00:23,333 way data flow in which a sender thread passes 9 00:00:23,366 --> 00:00:25,566 a message to a receiving thread, the 10 00:00:25,567 --> 00:00:27,833 ownership is transferred from the sending 11 00:00:27,866 --> 00:00:30,166 thread to that of the receiving thread. With 12 00:00:30,167 --> 00:00:32,165 shared state concurrency on the other hand, 13 00:00:32,366 --> 00:00:34,466 we have some piece of data residing inside 14 00:00:34,467 --> 00:00:36,533 the memory that multiple threads can read and 15 00:00:36,566 --> 00:00:39,300 write to at the same time. The Rust gives 16 00:00:39,301 --> 00:00:41,833 you the flexibility to pick between these two 17 00:00:41,834 --> 00:00:43,833 different approaches. Let us look at the 18 00:00:43,834 --> 00:00:45,866 details of transferring data through shared 19 00:00:45,867 --> 00:00:48,666 states. The first thing we will talk about 20 00:00:48,667 --> 00:00:50,766 are the mutexes which is an abbreviation 21 00:00:50,767 --> 00:00:55,133 for mutual exclusion. Within the context of a, 22 00:00:55,134 --> 00:00:57,100 within the context of concurrency the term 23 00:00:57,101 --> 00:00:59,433 mutual exclusion means that you that you have 24 00:00:59,434 --> 00:01:01,966 some piece of data, and only one thread can 25 00:01:01,967 --> 00:01:04,500 access that piece of data at any given time. 26 00:01:04,733 --> 00:01:07,033 To achieve this mutexes uses a locking 27 00:01:07,034 --> 00:01:10,666 system. When a thread wants to access to a 28 00:01:10,667 --> 00:01:13,000 piece of data behind the mutex, it will 29 00:01:13,001 --> 00:01:15,333 signal that it wants access to that data, and 30 00:01:15,334 --> 00:01:19,233 acquired a lock on the mutex. The lock is, the 31 00:01:19,234 --> 00:01:21,300 lock is a type of a data structure that keeps 32 00:01:21,301 --> 00:01:23,866 track of which thread has exclusive access to 33 00:01:23,900 --> 00:01:27,033 a piece of data. Once a thread has required a 34 00:01:27,034 --> 00:01:30,033 lock on a particular piece of data, no other 35 00:01:30,034 --> 00:01:32,733 thread can access that data. Once the thread 36 00:01:32,734 --> 00:01:34,933 is done with that piece of data, it can 37 00:01:34,966 --> 00:01:38,266 unlock the data and allow other threads to 38 00:01:38,300 --> 00:01:40,333 have access to that particular data. 39 00:01:40,700 --> 00:01:42,966 Typically mutexes have a reputation for being 40 00:01:42,967 --> 00:01:45,000 hard to manage because you have to remember 41 00:01:45,033 --> 00:01:48,500 two rules. Firstly, you have to acquire a 42 00:01:48,501 --> 00:01:50,900 lock before you have access to the data. And 43 00:01:50,901 --> 00:01:53,066 secondly, you have to release that lock once 44 00:01:53,067 --> 00:01:55,500 you are done with the data, so that other 45 00:01:55,501 --> 00:01:57,866 threads can have access. And now this is 46 00:01:57,867 --> 00:02:00,000 sometimes a bit hard to always remember to 47 00:02:00,001 --> 00:02:02,400 unlock whenever you lock a mutex. This 48 00:02:02,401 --> 00:02:04,800 sometimes lead people away from using it. 49 00:02:05,133 --> 00:02:07,800 Fortunately, Rust type system and ownership 50 00:02:07,801 --> 00:02:10,199 rules guarantees that you can't get locking 51 00:02:10,200 --> 00:02:13,033 and unlocking wrong. Let's see an example and 52 00:02:13,034 --> 00:02:15,633 things will start to make sense. We will 53 00:02:15,634 --> 00:02:18,766 start with the basic working of mutex. I will 54 00:02:18,767 --> 00:02:21,133 first bring the relevant modules into scope. 55 00:02:21,134 --> 00:02:27,566 [No Audio] 56 00:02:27,567 --> 00:02:31,433 I will create a new, a new mutex that holds the integer 5. 57 00:02:31,434 --> 00:02:36,533 [No Audio] 58 00:02:36,534 --> 00:02:38,600 Just like many other types to create a new 59 00:02:38,601 --> 00:02:40,833 mutex, we will call the associated function 60 00:02:40,834 --> 00:02:43,500 new, and then pass in the data we want to 61 00:02:43,501 --> 00:02:46,066 store let's access the data in our mutex. 62 00:02:46,466 --> 00:02:48,900 First I will create an inner scope, and then I 63 00:02:48,901 --> 00:02:50,966 will create a variable called num, and it's 64 00:02:50,967 --> 00:02:52,466 going to be mutable. 65 00:02:52,467 --> 00:02:55,433 [No Audio] 66 00:02:55,434 --> 00:02:56,633 I will next set its 67 00:02:56,634 --> 00:03:01,100 value equal to m.lock. This means that we 68 00:03:01,101 --> 00:03:03,266 are acquiring a lock and then we will call 69 00:03:03,300 --> 00:03:07,700 unwrap. As explained, the lock method is used 70 00:03:07,701 --> 00:03:09,633 to acquire the lock, and this will actually 71 00:03:09,634 --> 00:03:12,100 block the current thread until it is able to 72 00:03:12,133 --> 00:03:15,533 acquire the lock. This means that if there 73 00:03:15,534 --> 00:03:17,966 are multiple threads who wants to access to 74 00:03:17,967 --> 00:03:20,433 the data by calling the lock, then all of them 75 00:03:20,434 --> 00:03:22,533 will be blocked until the lock is released. 76 00:03:23,400 --> 00:03:26,400 At any given time only one thread will 77 00:03:26,401 --> 00:03:28,000 be allowed to acquire the lock, and the 78 00:03:28,001 --> 00:03:30,166 remaining will be blocked. The call to lock 79 00:03:30,167 --> 00:03:32,166 returns a result i because if there is 80 00:03:32,167 --> 00:03:35,200 already a thread that has acquired a lock to 81 00:03:35,201 --> 00:03:37,800 that particular data, and that particular 82 00:03:37,801 --> 00:03:40,300 thread panics, then calling lock will fail. 83 00:03:40,566 --> 00:03:43,433 And in this case, we call unwrap, which is 84 00:03:43,434 --> 00:03:45,733 saying that if lock fails, then panic. 85 00:03:46,533 --> 00:03:49,233 The type system is making sure that we call lock, 86 00:03:49,234 --> 00:03:52,500 because m is not a simple integer, but it is 87 00:03:52,501 --> 00:03:54,866 rather a mutex that holds an integer so we 88 00:03:54,867 --> 00:03:57,933 can't directly manipulate that integer until 89 00:03:57,934 --> 00:04:01,066 we call the lock. After calling lock and 90 00:04:01,067 --> 00:04:03,900 unwrap, we get a mutex guard smart pointer 91 00:04:04,033 --> 00:04:06,700 whose deref trait points to the inner data of 92 00:04:06,701 --> 00:04:09,666 the mutex, which in this case is the integer 5. 93 00:04:10,533 --> 00:04:13,800 Also a MutexGuards implements the drop trait 94 00:04:14,266 --> 00:04:16,632 such that when MutexGuard goes out of 95 00:04:16,633 --> 00:04:19,132 scope, then it releases the lock to the data. 96 00:04:19,500 --> 00:04:21,366 This means releasing the lock will be done 97 00:04:21,367 --> 00:04:23,566 automatically for you, so you don't have to 98 00:04:23,567 --> 00:04:26,100 remember all the time to release the lock. We 99 00:04:26,101 --> 00:04:27,700 can now go ahead and manipulate the 100 00:04:27,701 --> 00:04:30,366 underlying data by using the dereference operator. 101 00:04:30,367 --> 00:04:34,666 [No Audio] 102 00:04:34,667 --> 00:04:36,566 Finally at the end of the main, let's 103 00:04:36,567 --> 00:04:39,033 go ahead and print m and cargo run again. 104 00:04:39,034 --> 00:04:45,033 [No Audio] 105 00:04:45,034 --> 00:04:47,200 You may note that, the variable m now stores 106 00:04:47,201 --> 00:04:49,000 the value, and now let us cover another 107 00:04:49,001 --> 00:04:51,166 example. I will comment out the code in the 108 00:04:51,167 --> 00:04:52,566 inner scope. 109 00:04:52,567 --> 00:04:56,766 [No Audio] 110 00:04:56,767 --> 00:04:58,333 I will create a variable num 111 00:04:58,334 --> 00:05:00,633 which will acquire a lock on the mutex. 112 00:05:00,634 --> 00:05:05,566 [No Audio] 113 00:05:05,567 --> 00:05:07,533 I will next create another variable which will 114 00:05:07,566 --> 00:05:09,633 acquire another lock on the mutex. 115 00:05:09,634 --> 00:05:14,666 [No Audio] 116 00:05:14,667 --> 00:05:16,833 I will next update the inner values using the 117 00:05:16,834 --> 00:05:18,900 respective variables. This code seems 118 00:05:18,901 --> 00:05:20,700 correct. Let us cargo run this. 119 00:05:22,400 --> 00:05:24,666 Seems like the program has halted. 120 00:05:24,866 --> 00:05:26,900 Let me stop it and see the code again. 121 00:05:28,300 --> 00:05:29,233 I believe you would have 122 00:05:29,234 --> 00:05:31,533 spotted the problem in this case. As pointed 123 00:05:31,534 --> 00:05:33,900 out before, when a lock is being acquired 124 00:05:33,901 --> 00:05:36,400 inside a thread than any other thread or 125 00:05:36,401 --> 00:05:38,433 within the same thread, any additional call 126 00:05:38,434 --> 00:05:40,733 to the lock will block the thread until the 127 00:05:40,734 --> 00:05:42,833 lock is released. In this case, the first 128 00:05:42,834 --> 00:05:45,200 call to the lock will acquire the lock and 129 00:05:45,201 --> 00:05:47,866 the thread will not be blocked. However, when 130 00:05:47,867 --> 00:05:50,100 the lock is again being tried for the second 131 00:05:50,101 --> 00:05:52,633 time, then it blocks the thread until the 132 00:05:52,634 --> 00:05:55,233 first block is being released. Since that can 133 00:05:55,234 --> 00:05:57,433 only happen when the locking variable goes 134 00:05:57,434 --> 00:05:59,700 out of scope, which can only happen at the 135 00:05:59,701 --> 00:06:01,400 end of the main thread which is never 136 00:06:01,401 --> 00:06:03,133 encountered therefore the thread is being 137 00:06:03,134 --> 00:06:06,666 blocked. This blocking major of mutexes leads 138 00:06:06,667 --> 00:06:09,500 to an issue that is referred to as deadlocks. 139 00:06:10,100 --> 00:06:12,300 A dead lock is a situation in which, threads 140 00:06:12,301 --> 00:06:14,766 sharing the same resource are effectively 141 00:06:14,767 --> 00:06:16,833 preventing each other from accessing the 142 00:06:16,834 --> 00:06:19,200 resource. There is anything in halting or 143 00:06:19,201 --> 00:06:23,800 blocking of both the correct way will be to 144 00:06:23,801 --> 00:06:25,933 first update the value using the variable 145 00:06:25,934 --> 00:06:31,166 num. Following this I will drop the variable 146 00:06:31,167 --> 00:06:33,800 num which will automatically unlock the mutex. 147 00:06:33,801 --> 00:06:37,800 [No Audio] 148 00:06:37,801 --> 00:06:39,533 Next I will obtain the lock using the 149 00:06:39,534 --> 00:06:41,933 variable num1. This will be followed by 150 00:06:41,934 --> 00:06:45,800 the updation of the variable num1. Finally 151 00:06:45,801 --> 00:06:50,000 we will drop the variable num1 also. This 152 00:06:50,001 --> 00:06:52,400 manual dropping may not be always 153 00:06:52,401 --> 00:06:54,566 required within the context of threads since 154 00:06:54,600 --> 00:06:57,500 when the threads and its variables are also 155 00:06:57,501 --> 00:07:00,600 being dropped. We end this tutorial here as 156 00:07:00,601 --> 00:07:02,366 there are many details to cover about the 157 00:07:02,367 --> 00:07:04,633 mutexes in the next tutorial, we will be 158 00:07:04,634 --> 00:07:06,700 covering how the mutexes can be used within the 159 00:07:06,701 --> 00:07:08,300 context of multiple threads. 160 00:07:08,733 --> 00:07:10,866 Until then happy Rust programming. 161 00:07:10,900 --> 00:07:14,966 [No Audio]