How are you? It's my hands, Jim I'm doing all right, man. We're at the sports equinox only game of the year where there's every sport playing So that's pretty cool the sports equinox all four leagues only day of the year they play tonight MLS actually is a Hockey That's what the soccer fans will tell you Now you don't even need the music let's go to the sports Jimmy, it's the world series and Art imitates life this world series Jim my Arizona diamond back see in the pool Wednesday They're just doing the same thing they did in 2001 same exact thing the dead bird is very freaky to me That's crazy gallon hit a bird. We covered it on weekly dumb. Obviously But that gallon baseballed a bird Taking after is wow diamond backs was what team was Randy playing for when he did it 2001 diamond backs Yep, the diamond backs kill birds In 2001 where any Johnson hit a bird where some people's news a lot of people don't know what we're about to say the two times a Pitcher has hit a bird midair It's been an Arizona diamond backs pitcher and they go to the world series that year the world series dates are the exact same and The all-star game was in Seattle one time in high school I found out about how Kennedy had an assistant named Lincoln and Lincoln had an assistant named Kennedy and blah blah blah and I told my history teacher He was like yeah, you could find stuff like that for anything and just shit down my throat But I like the bird one. That's crazy. It's only happened twice on camera What was this almost breakdown you're telling me about from the motherland this almost a breakdown is awesome. This guy is Windsurfing kayaking definitely not kayaking definitely not his windsurfing. Is that what it's called?  Are they calling him the jaw? He got hit by a whale Jake. He's got his GoPro on him the whale comes and body slams him and Then tugged on his rope his leash to his thing which is supposed to never break It's designed to never break and was dragging him to the depths of the ocean and then it broke and he got freed He comes up and his first words are 100% accurate by Jason the jaw brain Number one reaction you could have yeah I do like that you say, you know the ropes aren't supposed to break unless a whale's talking unless a whale's tugging on your rope The video is crazy the whale It's like college. Did you see they have two angles? How crazy is it? Who is filming the wide angle? That made the whole thing suss? Unfortunately, is that just by the way? Here's another camera shot. What? When you go wide like that you realize they just cross paths at the wrong time Yeah, that whale saying the same whoa I just ran into a human he said the quote I liked was I saw the head come I saw the head come out then I saw the rest of the body come out Then I saw the whole thing above me and about that state. I got to be honest I thought I was in trouble get in a play-by-play It's every note there. Yeah Tell me about the others the more sports Jimmy and more sports college football season still underway in your Air Force Academy Ranked they're playing Colorado state Unfortunately Air Force was fighting the people of Colorado as well because we've got a penalty for fans thrown snowballs On the field and isn't this brought to us by Draft kinks. Oh This reminds me of Zach. Um, I initially Australian Zach That looks like a dog wiener his foreskin joke.  Yeah, like that. He knew the he knew the reference Wait, don't move yet. I think you're gonna like my angle Okay, I'll send that through Okay, so first call you can't throw a bunch of snowballs at players Kind of soft in general on the type of snow playing football, right depends on the type of football You're looking for home field advantage Jim you jumped To the militaristic side of this why wouldn't you have your own side throw some more?  Yeah, and I don't bleep that and I don't yeah Okay, yeah, okay. Yeah Get hot in this room not as hot as this not sports the not sports is Troubling a tortoise was found three years ago Someone was close about losing a tortoise turns out it is the same tortoise Jake in 2020 these people posted Hey our 60-pound tortoise got out and there's a picture of the dog Who I was like oh that dog sad that dog sad that the tortoise exists not that it got lost The tortoise has been living on its own for three and a half years And now it's back to captivity with these owners who didn't even give it a name You know this kind of reeks of they named it something inappropriate and they didn't want to share because you have to name You didn't have a tortoise and not name it. Yep. Yep. So I don't know. I feel bad for the tortoise It was living life on its own and now it's back with the dog who clearly hates it a lot of dangerous Assumptions that dog hates that turtle. I mean come on. We play a lot of geogessor with Zach those a keys That's two a keys And yeah, if you showed the picture of the dog the tortoise the tires in the corner of that house you'd say Florida mm-hmm It's the employee of the week. It's the employee of the week. It's the employee of the week Who do you want to give it to last week talking baseball hit a 100,000 subscribers? So that's an accomplishment. You want to give it to Trevor I kind of like that though butter him up a little bit before we give him the work Congrats to Trevor, please. Yeah, and shaved his face for the first time. So he's feeling a bit Oh, yeah, he kind of needs it. So he needs it. Yeah trying to be Paul road for Halloween too tall Yeah, that could be Paul run ends with the okay. Go music video on the treadmill. Zack get copyrighted.  Sometimes they're just out enough rocks You want us to do this is the best way to do it Where'd it go? Oh That one got up you want us to keep doing this as a decent one as a decent one Maybe you got to come around it. No, definitely not. Oh backhand it backhand it Don't use that one. Zack wait. Zack. Maybe use this. No, it's a backhand float situation.


The Global Flood Awareness System, the LOEFAS, is a system that uses distributed hydrological simulation and numerical ensemble weather predictions with global coverage to provide an overview on upcoming floods in large world river basins and can accurately detect hazardous events with a forecast horizon of up to one month. This article was authored by L. Alfury, P. Burek, Idutra, and others.


So students, this is the continuation of the previous lecture. In that lecture, we were looking at storing the data about a football match season. And there was a log table also there. Now the manager of that record keeping soon realizes that there are certain issues with that record keeping mechanism. And in this lecture, we are going to talk about those issues and how those issues can be resolved. So this is the module coverage for this module, removing data redundancy. Redundancy is that the data which is getting repeated and that data doesn't changes. So breaking the data into multiple tables, so we have a table of more or less static data and we have a table of changing data. I will go through the details and I will also show you a picture also. Then when we have this static data table and we have that dynamic data table, we need to link them together. That is what the relational model is all about. And then of course, briefly talk about in this module and in subsequent modules, a lot of discussion and examples of the MySQL SQL. MySQL is the database and SQL is structured query language. So let's go ahead with this module inefficient table structure. Why? Because I give you a number of examples. For example, when a player is playing and he performs good or bad, the nickname of that player can change. So when the nickname changes, then this table in which the log of the matches was maintained, that change has to be reflected everywhere. That's a big issue. It's a performance issue also. Then of course, when a player enters his data in that table, there are a number of things which are constant. Constant means that they don't change. For example, the phone number of the player, the address of the player and other related things which are fixed, right? Of course, the date at which the match was played is changes. But the problem over here is there is a lot of redundancy, there's a lot of repetition. And not only the repetition is there, but when the change has to be made, that repetition decreases the performance. It affects the performance. It unnecessarily increases the size of the table in which that constant data is being repeated again, again, and again. So what we do, some update in the multiple records, so this problem was handled by Dr. Codd. So he came up with the different rules of normalization, which we've already covered in the prior modules. What the Dr. Codd's rules covered was that we ensure that there is no loss of data. We ensure that the relationships are there among the different data elements. And we can query all the data to get the answers which we are looking for. So what is the solution over here? This is the solution over here is that move, we separate the static data and we have that dynamic data also. We have that dynamic data also and we have this static data and we have this dynamic data and we link them together. So collectively we are going to use this, this relationship among the tables, which is the basis of the relational model to get our answers from this database. So let me get into more detail, into more concrete. So we have this table of our players. This is the static data I was mentioning and this is the match log. This is the dynamic data I was mentioning. Now all the keys over here, they are unique. So this is the primary key over here. Is it not null? This is not null. I'll talk more about it. And over here the keys are repeating. For example, this key is repeating over here. This is repeating over here. And of course, this is repeating over here. This is, so this is not, this is the foreign key. This is not the primary key, right? Now this is a one-to-many relationship we have. This player of ID 6 and he is playing the matches on this date and on this date also. We have over here is that one-to-many relationship. So we are able to link multiple tables, which is the static data and the dynamic data, and you see there is less repetition. There is much less repetition over here. This is the beauty of this approach. Of course, this is normalized. So now we have to query this. How do we query this? We use SQL, the structured query language, and which is the standard for the relational model. It means that if you have a relational model, then it's going to support SQL, right? And using this SQL, you can create, you can update, you can delete, okay? And of course, there are different dialects of the SQL across different databases. So more or less it is going to be the same. Some differences might be there. Basic concepts are similar. So of course, we'll be discussing more of this interesting stuff in the next modules. So that's all I have for this module.


All right, another exciting day of operating systems. So today, we will continue talking about locking, because we had lots of fun yesterday talking about Centiphores. Today, we're going to see another option to using Centiphores, and I guess also what you'll be doing in Lab 3. So a little preview for that. So first, I will talk about monitors because they're built into some languages with object-oriented programming. Locks are kind of covered some a little bit. You have to do a lock and unlock. Make sure you always lock and unlock, and then you have the critical section. So when you have object-oriented programming, someone thought it would be a brilliant idea to make it much, much easier. So in Java, you can kind of mark a method as monitored with the synchronized keyword, and then let the compiler handle locking for you. And it will create a lock for you on a per object basis. So basically, it's one mutex per object, and it's created for you.  So in Java, everyone's seen Java before, right? Ish? No? OK, well, just in case you see Java, it's kind of like C++, so you can have a class with methods. And one of the keywords you can put on a method is a synchronized keyword. And basically, that will just let the compiler do some transformations for you. Or basically, this object will have a new field called monitor, which is basically a mutex, and it will always lock and unlock it. So no matter what, any methods you have marked synchronized, you only have one thread that can access it at a time. And therefore, for an object, you won't have any data races. So that's just one thing you might encounter if you happen to program in Java or other object-oriented languages. I don't, C++ does not have this. But that's just an aside. Today, the main thing we'll be talking about are condition variables. And they're kind of behave like centerfors. And you can think of them as a kind of custom queue that you can create and then put threads on at will, essentially, when you're waiting for a condition. So they have the same kind of interfaces as we're familiar with from mutexes. They have an int. They have a destroy. And then the difference is that they have a signal, a broadcast, a wait, and a time wait. So wait will put itself, add itself to the queue, and it also takes a mutex. So it needs to be paired with a mutex always. And you're essentially waiting on some condition and we'll see some example, but whenever you change that condition, you have to have the mutex as well. So we'll see what that looks like an example. And then time wait is just the non-blocking version. So if you want it to time out so that you are guaranteed it always wakes up at some point, you can use the time wait function. And then the difference between signal and broadcast is signal will wake up one thread from the queue and then broadcast will wake up every thread that is currently on the queue. And that's the only difference between them. So you can think of wait as adding the currently running thread to the queue and it needs to be woken up somehow either through a signal or broadcast. And through this course, I think you only have to implement signal and we'll probably only ever use signal and not broadcast. So like I said, they must be paired with a mutex. So any call to wait must already hold the mutex. So it must be locked at the time you call wait. And for signal and broadcast, you don't have to hold the lock. There's like some way you can ensure order if you have the lock, but it's kind of outside the scope of this course. So we'll just, for signal and broadcast, you may or may not hold the lock and it doesn't really matter. So why you might think of it as wait needs to add itself to a queue safely so it doesn't have any data races. It would need the argument to be able to unlock that mutex before it puts it to sleep so another thread can go ahead and run and hopefully set that condition to true so that it can wake up and actually do something. So that wait function is atomic so it will atomically unlock the lock and then put itself in the queue and then at some point whenever it wakes up again it will hold the lock. So it will hold the lock before the call and it will hold the lock after the call and the idea of it is you don't have to worry about the implementation details and it's atomic. So you don't have to worry about data races. And then another caveat is that one mutex can protect multiple condition variables that we'll kind of see as an example. But again, we'll only consider calls to wait and signal. We won't really use time wait for now and we won't use broadcast. So this is how we could modify our producer consumer example. So we could have two queues, one queue that is essentially waiting for a filled slot and another queue that is waiting for an empty slot. And then our condition instead of having two center fours that are keeping track of how many empty slots there are and how many full slots there are we can just have one variable that keeps track of how many slots are filled which may or may not be easier to understand. So how you would use this is you would in the producer, you would lock a mutex because we're going to be modifying that end filled variable so we want it to not have any data races. So first thing we would check our condition. So the producer thread should not produce anything if all the slots are filled. So if the number of filled slots equals N which would be the buffer size in this case then it should wait. It should put itself in this has empty queue which is all the threads that are waiting for at least one empty slot. And then it has to give it the mutex. Yeah, so the condition variable you can think of as just representing a queue. Yeah, it's an easier way to think about it. So the condition in this case there's what I should probably hide myself get myself out of the way. So the one condition is this condition where there's the number of every slot is filled and the other condition is there are no filled slots all the way in the consumer thread. So that's kind of my condition I'll have two queues. One queue are threads waiting for something that threads are waiting for at least one empty slot. And then another queue on the producer's or on the consumer side, whoops, that is not the right window. Another thread on the consumer side that is waiting for at least one filled slot. So let's first look at the producer code. So the condition here is while the number of filled slots is equal to essentially the buffer size I should wait because I can't do anything. So it would add itself to the queue relinquish the lock and then wait for a corresponding signal to wake it up. So we'll see that if it doesn't get put to sleep it would just fill a slot like it's supposed to and then increase the number of filled slots and then it could signal, you know, it has filled. Then it would unlock the mutex. Yeah. So the wait is waiting for someone to call signal on it. So in this case, so assume the queue is filled already. So the producer would hit this while condition, check this condition is true. So it would wait and add itself to a has empty queue. So there's, you know, one thread, one producer thread waiting in that queue and wait atomically adds itself to the queue and gives up the mutex. So a consumer thread could run here, grab the lock, check the condition. In this case, the number of filled slots would not be zero because they're all full. So it could empty a slot, decrease the number of filled slots and then it would signal. So after it's signal, it just unblocks it. So it can run at any time. You don't know it essentially will try and get the lock again and then it unlocks. So then at this point, the producer thread is now active and it may grab the lock and then return from P thread wait with the lock. And because it's in a while loop, it would check the condition again. So because the consumer thread decremented the number of filled slots, you know, that condition is not true anymore. So it's false, so I can run and then I would fill up my slot and then increase and filled back up to the buffer size. And then I could signal just in case anything's waiting for a filled slot. Yep. Yeah, while you're holding a mutex, there's only one active thread at the time. So at the weight? Yeah. Yeah, so as part of the weight atomically, it will add itself to the queue and give up the lock. So you can think of weight as having, you know, it's atomic, but it's doing two things. It's going to unlock the lock so something else can go ahead and hopefully signal it and it will add itself to the queue of threads and do that atomically. Yeah, so it does those two things. It unlocks the lock, adds itself to the queue and then it's blocked. It's in that queue. So it's going to be blocked and wait for another thread to signal it to wake it up. And then when it's woken up, it'll try and grab the lock again. Yeah, another thread could grab the lock instead, which is why it's in a while loop, too, because another thread might come and take the lock before it and, you know, the condition, something might wake it up, be like, oh, the condition, we'll see an example of it, too. But the while loop is important. Yeah, yeah. So when you call weight, the thread will add itself to this empty queue. This has empty queue and it will only run again if something calls signal on has empty, which in this case would be the consumer thread. And yeah, the question back there. Yeah, so the question is, why do I need the while if I'm blocking that point? And we'll see an example. So we'll make it a bit simpler than this. Yeah, so the signal, if it's fair, would wake up whatever's at the front. It could be first in, first out. But I think in POSIX, if you read the standards, I think there's actually a defined order. Yeah. Yeah, so in this case, there's two queues. There's essentially a condition that's waiting for a filled slot, at least one. And then another condition that's waiting for a empty slot. Yeah, signal will only wake up one thread. So we'll look at the full producer consumer example, but first let's argue about some simpler examples. So let's read this real quick and then someone tell me something very bad that could happen here. So we have two threads. Again, we can't ensure any ordering between the threads. So in thread one, it's gonna lock a mutex. Check a condition, so if that condition's false, it will put itself to sleep and put itself in that condition's queue and then unlock the mutex and then thread two will can set the condition equal to true and then it would just signal a thread that would be waiting on that condition. And initially, the condition is false. Yeah, so let's think about this for like another 30 seconds and then we'll see 20 seconds for other people. Okay, go for it. Sorry, am I unlocking and unlocking there? Okay, so thread one would run first, grab the lock, right? So you're saying thread one first executes, grabs the mutex and then calls wait? Yeah. Okay, so it would call wait and then put itself in the queue atomically and give up the mutex. Okay, and then so it's waiting there. The only thing that can happen is thread two can execute and it would set the condition equal to true and then wake up that thread, right? Signal would just wake it up. So when it returns back here, it would re-grab the lock because no one has it and then it would check the condition. Now the condition is true. So it would fall out of the while loop and just unlock the mutex, everything's good. So this is okay, it woke up when the condition changed, right? Yeah, all right, was that what you were gonna say? Okay, well here, let's go through that quickly. So bad thing that could happen is so thread one starts executing, right? I guess we don't have the example up, but first thing it would do would be to grab the lock. So it would lock the mutex and now it's locked. It could check the condition. So it would be a read of condition and in this case it returns false. So let's look back at the code. So thread one, check the condition, it's false. So the next thing it's about to execute is wait. But we could unfortunately have a context switch right after it checks the condition and then thread two could come in, then it could write condition equals true. And then the next thing it would do is signal condition variable, right?  And now it's stuck, right? So yeah, you're allowed to call signal if there's nothing in the queue, it just won't do anything. It just won't wake up any threads. Yeah, like this, like. Yeah, like say it's a word problem. Yeah. And then it blocks itself, it gets off the lock, then thread two can run, it sends the signal, which puts the locked thread back into the queue. Yeah, okay, so. And where does it run from though? Right after the wait call. Oh, it starts right after the wait call. Yeah, so it would pick itself back up. So ideally what would have happened, so this is a case where we essentially have a lost wake up where we had a signal that should have woken up a thread but didn't wake up a thread. So ideally instead of this, what should have happened is right after thread one checks the condition, it should have waited and then it would have atomically added itself to the queue. So our condition queue here, con queue, would have thread one on it. And then if thread two executed and set the condition equal to true and then signaled, it would essentially thread two would be eligible to run again, it would try and get the lock and whenever it gets that mutex, it will just pick up right where it left off. So right after the wait call. So it would get signaled, it would grab the lock again, so it would return from wait with the lock, check the condition again, now it's true. So it would go here, it could do whatever it needed to do and then unlock the lock. Yeah, which one? So this? So this lock and unlock call is only in thread one. Yeah, because remember this is like preemptive scheduling so you can get context switched at any time. So how I would fix this is I would put a lock in thread two. Yeah, so if I wanted to fix this, where should I put locks in thread two? Yeah, I could put them right around this, right? Wouldn't that work? Okay, so let's go back because, so instead of, here I'll write the code down too. So in thread one, we have essentially lock mutex, then while not condition wait on some condition variable on some condition variable with the mutex and then unlock. So in thread two, one of the proposed solutions is to put a lock mutex and then set condition equals to true. And then we could have a signal here, it doesn't really matter, and then a unlock, right? Because my problem was when I essentially had this happen, right? So that was my problem, this context switch right before the wait after I read it. So now if I have, if I have this lock here, is that possible anymore? Yeah, because right now if I read the condition in the while loop and then I get context to switch to thread two, the first thing it actually has to do now is called lock, whoops, that is thick, called lock on the mutex and it'll get stuck there, right? Because it's already locked. So it can't execute anything else at that point, right? Everyone agree with us, or me, or the whole class, whoever, right? And now at some point it's gonna have to get context, at least between these two threads, the only thread that can make progress is whenever a context switch is back over to thread one. Yeah, yeah, because thread one at this point has the lock and hasn't unlocked it.  So the calls unlocked are locked? No. So how not? No, so wait, wait, before you call it, the lock is locked and when you return from it, the lock is also locked. So that's part of the guarantee you get as part of wait. Yeah, so wait has essentially two parts. The first part, atomically, it's going to unlock the lock and add itself to the queue and then whenever it's signaled again to return, it's going to try and grab the lock again. And it's only going to return from wait whenever it successfully grabs the lock. So it will own the lock before the call and own the lock after the call, which makes it a hell of a lot easier to program than that. If it's unlocked and if it's unlocked when it returns, then it's a mess and it is really, really painful. Yeah. Yeah, so let's keep carrying on with this example. So now we had thread one executed, locked the mutex, it read the condition and it got interrupted right before it called wait, which we saw was bad, right? But now when thread two starts running again, its first call would be to lock the mutex, but it's already locked, so it can't progress, right? So the only thing that can happen to have our program make any progress is context switch over back to thread two, or sorry, thread one, and then thread one would call, oops, thick, would call wait on that condition and use that mutex. So at this point, thread one can't execute anymore. So our condition queue, if we want to think of it that way, we'll have thread one on it. And right after, as part of the wait, it is now unlocked and added itself to the queue, so the mutex is now unlocked and thread one can't run anymore. So the only option to make progress again is to go switch back to thread two, which was still waiting at that locked mutex, but now that mutex is unlocked, so it can pass go. So now at this point, it would pass go, change the condition over to true, right? And then it could signal, conned, and then right now there's only thread one in the queue, so now it is unblocked. So now it's eligible to get scheduled again, right? Then at this point, even if it gets scheduled again right after this call, essentially internally it's gonna try and lock the mutex and it's still locked so it wouldn't be able to return at this point, right? Yeah, it's unblocked so it could be scheduled, but it has to grab the lock again before it can return from the wait. Yeah, that's just how it works. Yeah, yeah, yes. You'll figure that out in lab three because you have to implement one. That's something that the thread one upon exiting the wait call. Yeah, yeah. But also to answer your question, because it's part of the wait, we hold the mutex going into it, we could add ourselves to the queue and not have any data races, right? We have the lock. So as long as the only operations to the queue are with the lock, you're good, right? Yeah, yeah, so the question is, why do I have a while loop if I know and we'll see that in the next slide. So, okay, so yeah, at this point, it is now unblocked, it could run, but internally it has to call lock, so even if it gets scheduled, it will try and call lock, thread two still has the lock. So eventually thread two will unlock the mutex and then thread one would finally, whoops, return from wait, the mutex locked, right? Yeah, so we did signal and then unlock, right? You can do either. So internally it's not going to return from wait until it can grab the lock. So you could signal before the unlock or signal after the lock, they both work. If you don't, so if we change the order like here, no. No, whenever I signal, the only thing it does is it changes thread one from being blocked and it can't be scheduled at all to being unblocked and it can be scheduled. So if thread one got scheduled right after the signal, so thread one got scheduled here, it has to lock the lock to return from the wait. So it would try and grab the lock, it can't get the lock and it would just try over and over again, doesn't matter. The only time it can make progress is whenever we have the unlock, right? Yeah, signal only unblocks one thread in that queue. So, oops, I should put thread. So thread one, as soon as thread two unlocks it, it's allowed to continue from the wait with the lock held. So it would go check the condition again, right? So it would read the condition and now it's true, which is good, which is what we expect and then we could do something cool and then it would unlock itself. Good with that? Okay, so then this is the question, can we change the while to an if, right? So if I know the condition's gonna change anyways and it's gonna be true, then why do I recheck it again? And the answer is you probably will likely have code that looks more like this, where you have multiple threads changing the condition. So say we have thread two changing the condition to true and then thread three changing the condition to false. Well, actually I could remove the signal here. Yeah, so sorry, I could actually remove the signal call here. But anyways, if I have something that looks more like this, this thread would put itself to sleep whenever the condition is false. But what might happen is that this thread comes through, changes the condition to true, wakes it up so that thread's eligible to run, but it gets unlucky and then this thread happens to run before the one that gets woken up and then the condition changes back to false again. So this one would wake up and now the condition is false. So it shouldn't execute any, so it shouldn't pass that condition anymore, right? So if I didn't have a while loop, it would just assume that condition is true when it might not be. So does that make sense? There's also a iffy case where under some implementations weight can just return whenever the condition, without even being signaled, weight just might kind of return and have the lock anyways. And the only way to do that, it's better just to put a while loop in front of it at all times because some weird things can happen. Under some implementations on some systems, this is like super, don't need to know, but just to be safe on some systems, weight will just return with the lock even though that it was never signaled. Why? I don't know to make your life difficult, I guess. This is why programming these things is kind of a pain. Yeah, yeah, yeah. You could broadcast too and wake them all up or have other threads that just are using that lock anyways or any combination of things. Yeah. No wake up or something like that? No, so it would get woken up from the true, but it would check it again and then the condition's not true, so it should wait again, right? Okay, so it needs to eventually come out. Yeah, your program's gonna not make progress if there's some thread waiting for that to be set to true and then nothing ever sets it to true, right, and that's just your program thing. It's not condition's variable's fault, it's your fault now. Yeah. Oh no, you're good. So they kind of serve a similar purpose to center force. You can think of center force kind of as a special case of condition variables because I could just make the condition a value and then wait if it's zero and then have anything that increments it, essentially signal something that should wake up, but you could also implement it in the reverse way, so you could use center force instead of condition variables, but it gets really messy. And then, but for complex conditions, generally, condition variables, even though they're equivalent, are much, much easier to read and much, much easier to argue about because for the most part, for threading now, we just have to argue. We can't actually, running it does not necessarily mean it works properly, right? So now we can talk about practical matters. So locking granularity is kind of the extent of your locks. You need locks to prevent data races, that's just a given, but if you lock large sections of your program, you're gonna have problems where you essentially make it the whole thing serial, right? And then one thing you could do is divide locks into smaller sections. So for example, one thing you could do is try and paralyze your hash table implementation. Wouldn't that, anyone wanna do that? That sounds fun. Uh-oh. No, this is for fun. So how many of you use probing for your hash table implementation? Yeah, so anyone that use probing, that's gonna be really hard to paralyze, right? Because if there's any collisions, you don't know how far you should go and what you should lock. While if you use just a list for each individual element, you could just have a lock for every individual list and so that if two threads come in and wanna access two individual lists, that's cool. They can do that, they can do it in parallel. And you could have one lock for individual list and then that's it, that's your whole implementation and it's really easy to paralyze. While if you have linear probing, it's gonna be just a giant pain. So yeah, those are some things you have to think about because now your implementation kind of dictates how easy it is to paralyze it. So some things you have to consider about locks is the overhead, of course. If you have more locks, locking and unlocking isn't free, they might be fighting over it, right? So contention is just how often that lock is locked for. So if every thread is fighting for the same lock, they're gonna be essentially in serial, but if you have eight locks and generally only one or two threads fight for a lock at a time, it will be mostly parallel, which is good. And then we'll see dead locks. So some locking overheads, locks, of course, have to allocate some memory. So if you have a million locks that is going to take up a lot of unnecessary memory, it takes some time to create and destroy them, of course, and it, of course, takes time to acquire and release the locks. So the more locks you have, the worse this is gonna be, which kind of goes against our goal of, if you wanna divide it into the small sections possible, well, you also kind of don't wanna go overboard because then the cost of the locks themselves gets significant and then you're kind of screwed. So now we get to talk about dead locks. So the more locks you have to, the more you have to worry about dead locks. So the conditions for dead locking are one, you have to have some type of mutual exclusion, which for mutexes, of course that's the case. You have to have another condition called hold and wait. So you have to have at least two locks. So you have to hold the lock and then try to get another lock. Then the third condition is you have no preemption, which in this case is preemption for the locks, like I can take your lock away from you and make it unlocked and make you have to try again. So no preemption just means you can't take locks away and then circular wait where I'm waiting for a lock held by another process or thread. So here's a simple deadlock example. You have two processors or two threads or two processes trying to get two locks. So in the thread one code, it gets lock one, gets lock two. And then in thread two, it gets lock two and then gets lock one. Well, an easy deadlock is first thread one executes, grabs lock one, and then we context switch over to thread two, it gets lock two. And now the next thing that thread one wants to do is get lock two, which is held by thread two. And then if thread two gets scheduled, it tries to get lock one, which is held by thread two. So we are kind of at an impasse now, right? So no matter what thread gets scheduled, we now can't make any progress and that is a deadlock. Everyone see a deadlock? All right, you will probably get a deadlock and it will drive you crazy. So one way to prevent deadlocks is you can ensure ordering between them. So if you acquire locks in the exact same order, you can't get a deadlock, right? So if you lock, always lock lock one and then always lock lock two, then you won't have a deadlock because one implies the other. So I will only get lock two if I already have lock one, right? Yeah, so you could have code that just, so this could be protecting two variables and lock two could be protecting a whole different variable, right? There are cases where you would actually want this or yeah, essentially for protecting two different values that you might wanna change in parallel, that's in under some conditions, but might affect each other. So a good example for this one is like, if this was a bank account, right? There might be a lock for someone's balance and a lock for another person's balance. So if I need to change someone's balance, right? It needs to, I can't have a data race, otherwise the accounting won't work. There won't be a net zero at the end of it. So you might have a lock for each individual bank account. So in this case, right? If you have lock one, then you get context switch to another thread. It would try and get lock one. It can't get lock one, so you're always going to get lock two in this case. The other way to prevent deadlocks is by using a try lock. So now we get to actually see where this comes into play. So if you don't know the, if you can't ensure ordering on the locks, you could have try locks. So the idea here is that I could try to grab lock one and then, or sorry, I could grab lock one and then as soon as I have lock one, I try and grab lock two and then if I don't get it, which means it returns zero. So zero means I didn't get the lock. I would give up the lock I already have and then I would essentially wait, do something and then try and grab lock one again. So then I would try it again. So the only way out of this while loop is if I have both the locks, but if I have lock one and I can't get lock two, I'll give it up. So does that make sense? So this can't deadlock under any scenario, yep. So try lock if it returns one means it's locked. So try lock will try and lock the lock and tell you if it did. Try lock locks lock two. Yeah, if it returns like one, it means it locked it. So in the case where I actually get it, I would lock lock one and then I would try lock lock two and if I get it, this condition wouldn't be true. So at the end of the while loop, I have both locks, right? And if I didn't get it, I would unlock lock one, wait a bit, get lock one again and then try. If I didn't get it, I'd do the same thing again. If I do get it, I'd jump out and then right before the protected code, I always have both locks. Yeah. So the thinking behind that is just to break the deadlock. So during this wait, if someone else was trying to do the same thing, like so if we had this situation before where someone had lock two and was trying to get lock one, I would have, instead of us being deadlocked, waiting on each other's locks, if I have lock one, I just give up lock one if I don't get the other one. So that thread could actually run and make progress now, right? Any questions? Yeah. Yeah, for the wait, you could just put a yield there. That's probably a good thing to put there. All right, so I guess let's see. So also in the examples if you want, there is the condition variable version of that producer consumer. So this is kind of what it would look like, right? Kind of on the slide just so you have it. Curious. Hands up if you'd like the center four implementation better if that was more clear. So here is the center four example. Remember, it looked kind of like this. So this was the center four. So do we like center four? It was like ish 30. All right, who likes condition variables better? Eh, like 10%. Okay, so half the class is undivided. So in this case, condition variables, in my opinion, are a little clearer because you don't have to worry about the initial value being correct. But yeah, it's up to you. Whatever you can argue about better and make a more convincing case for is probably what you should use. Okay, so let's wrap it up. So today we explored some more advanced locking before we saw mutual exclusion and then we saw one way to ensure order using center fours and now we have another way using condition variables. Right, remember center fours were some atomic value that can be used for signaling. Condition variables are often clearer for more complex conditions. Locking granularity matters. You'll kind of find out in lab three, maybe in this case, I'm actually, yeah, but you're actually implementing condition variables so maybe you won't figure that out. But you could paralyze your hash table and figure it out. That would be lots of fun. And then of course, now you have to prevent data races and then as part of that, you might be looking to paralyze as much as you can, adding more locks, making it more complicated and now suddenly the dead locks are going to become more relevant and you also have to prevent those. So you have a lot of factors to consider when you're trying to make something run in parallel or concurrently. Yeah. So there's just a whole bunch of different tools just depending on the job. Cause even for locks, remember there's mutexes, there's read write locks, there's spin locks and they all have their own benefits in whatever situation they're good for and you'll kind of get used to it. All right. Well, just remember, I'm pulling for you. We're all in this together.


The National Emergency Management Agency has distributed food items to over 650 IDP's in Adama State ahead of any federal celebrations following the directive of President Mohamed Buhari. Plastic correspondents, Ibrahim Bakr, said that under Yuba Head of Operations and in charge of Adama and Tarab State, who conducted the distribution urged the households to make good use of the items. That said, the food supply is part of the monthly support which NEMA typically undertakes to assist affected persons of different circumstances. Throughout every month, I do assist the Internet displays people in the camps. And in the camps, we have the three basic camps we operate here in Adama State. We have the Fufure camps, which has about 305 households. Then we have Maikoi. We also have about 239 households. Then San Teresa, which has only 91 households. And in all these camps, the basic things we give them is food items, which comprises of rice, beans, maize, seasoning cubes, tomatoes, bags of salt, and vegetable oil. The basic challenge that I think we have now is on health ground. We have particularly the initial wish. The one that provides the basic health services in the camps, they have pulled out since last year. And as a result of that, the IDPs seek medical attention from outside the camps. Please do subscribe to our YouTube channel and don't forget to hit the notification button so you get notified about fresh news updates.


The new Raspberry Pi 4B has been received with open arms by the computer press. It performs like an entry level desktop computer, so all your lovers want to know how much better it is for musically production. The processor of the Raspberry Pi 4B now has a quad core ARM Cortex A72 CPU running at 1.5 GHz. Although it runs at only slightly higher clock frequency, it has three times the performance of the Raspberry Pi 3B+. Add to that the LPDDR4 RAM instead of the LPDDR2 RAM found in earlier models and you'll understand why it does number crunching at far higher speed. There also is an option now to boot from USB and since Raspberry Pi 4B now also has USB 3.0 further speed improvements can be made. For video lovers the Raspberry Pi 4B now supports 4K video of two micro HDMI ports. Not a good thing about the 4B is the gigabit network adapter. Previous models were limited to 100 Mbit ethernet that had to share throughput with USB. There are three versions of the 4B, the only difference B in the amount of RAM that can vary from 1 to 4 GB. A short note on the power supply. Don't use smart power supplies like the ones that come with Apple MacBooks. Due to a design flaw in the Pi only dumb power supplies will work. That clearly is a hefty performance improvement on all sides. The question is what it will do for audio. Let me first make clear that using any Raspberry Pi for audio without the addition of a special sound card will work but the analog output sounds horrible while using the USB to feed a USB DAC also leads to very poor sound quality due to the polluted USB signal. You should use a sound card in the shape of a headboard. I've tested the Raspberry Pi 4B against the Raspberry Pi 3B+, using an Aloe Boss 1.2 DAC board and the Aloe DigiOne Signature Spidive Board. The bus was powered using the SBooster BOTW PNPE Co. In its turn the bus sent the power to the Pi. The DigiOne Signature was used with two power supplies, the same SBooster plus a cheap switching mode power supply. Both were connected to the DigiOne's specific power inputs. For a review of the bus and the DigiOne Signature see the links below this video in Youtube. I started with installing Rupee XL, the new version of Rupee that not only makes the Pi a rune endpoint, it also makes it an Airplay and DLNA renderer. A short review will follow suit. I also installed Vol. 2 to test a more processor intense program. More on both programs in their reviews. The links below this video in Youtube. In general the Raspberry Pi 4B sounds somewhat less open, has slightly less sibilance control while the stereo image was narrower. Only when playing the Bosdijk with Volumio, the low end was slightly better defined but everything else stays the same. You might wonder why a better computer produces a lower sound quality. I have no means of measuring this but in general faster processes generate more electric and electromagnetic noise for a number of reasons. The headboards are mounted on a Pi at about 2.5 cm distance. The LO bus does some filtering. The DigiOne Signature has fully galvanic separation and even then there is the influence from the Pi to the headboard. It is to be expected that simpler DAC boards even suffer more. I have not tested these though. Now let me be somewhat more precise about the sound penalty for using the Raspberry Pi 4B. If you own a stereo, like my sub 1000 euro setup 3, I wouldn't lose any sleep over the difference between the Pi 3B Plus and the Pi 4B. If your stereo is more like my 4000 euro setup 2 or better, I would definitely go for the Pi 3B Plus for as long as it is available. I am in the privileged position to be able to look inside very good sounding dedicated streamers and they almost always have relatively lightweight CPUs. And I have often hear designers saying that USB 2 is better for DACs than USB 3. It's not really strange. Normal 44.1 kHz 16 bit audio has a data throughput of 1.41 Mbps and even 192 kHz 24 bit signals only do 8.46 Mbps. USB 2 has a data signaling rate of 480 Mbps. Even if the transport overhead is subtracted here, there is ample bandwidth for 2 channel audio. The same goes for Ethernet. A 100 Mbit network is more than fast enough for audio, provided the network is not used for other high bandwidth activities like watching video and backing up large volumes while listening to audio. If there is, you might consider separating your audio network from the rest. A simple way is to connect your audio related equipment to one switch and patch that to the rest of the network. Connecting a hard disk directly to your audio gear, like a USB drive directly to the Raspberry Pi is no bad idea either. The KISS principle is still valid.  That's it for this week. There will be another video next Friday, as always at 5 pm central european time. If you don't want to miss that, subscribe to this channel or follow me on the social media so you'll be warned when new videos are out. If you liked this video, give it a thumbs up. Many thanks to all that support this channel financially. It keeps me independent and thus trustworthy. If you also feel like supporting my work, the links are in the comments below this video on YouTube. I am Hans Beekhuyzen, thank you for watching and see you in the next show or on theHVproject.com. And whatever you do, enjoy the music.


My candidate for the first state dinner would be President Mohammedou Buhari of Nigeria. That's an unusual choice, but Nigeria is an absolutely critical country in Africa, critical on everything from economic growth, tech innovation and counter-terrorism. And President Buhari is democratically elected. He's a Muslim president who succeeded a Christian president. The symbolism is very important. The country and the continent are critically important, and that would be my pick. Asking any author about a book the president should read immediately makes me think mine, of course. But what I would actually say is the next president should read Jamie Marisotys' America Needs Talent. It is a short book that essentially explains why talent and the cultivation and recruiting and nurturing of talent is essential to the United States for just about every issue we face. My advice for the next president would be to check in regularly with Bryan Stevenson. Bryan Stevenson runs the Equal Justice Initiative seeking equal justice for all races and faiths. He has his finger on the pulse of many of the most disadvantaged and discriminated against Americans, and he will help pierce the Beltway bubble.