Wefre on. Hey, everyone. Welcome. I donft have any handouts for you today. I actually want to finish up the implementation section of the course. I think wefll get through it today. Ifll make it a point to finish it today. Come Monday, wefre gonna start talking about multithreading and Ifll even preface that a little bit today, provided I donft run out of time. Remember that your midterm is Wednesday evening over in Hewlett 200, the largest room on this end of campus. Itfs 7:00 to 10:00 p.m. Itfs open note, open book. You can bring print outs of your programs, whatever you need. Just plan on taking the exam in the three-hour period. Itfs designed to be more than enough time for the midterm. Okay. Therefs also the technology talk right after this class right here over in Hewlett 201. So you can first visit Hewlett 200 to see what room itfs like for the exam. But in 201, therefll be a technology talk from 12:00 to 1:00. Okay. I left you last time with this example, but I got several questions about how it worked, which probably means that I rushed through it in the last five minutes of class, which is probably true. We had something like this, where I declared an int array of length four, and int i to serve as four loop index, and then Ifm just gonna go and do this. I donft care that the array hasnft been initialized. I want to go ahead and do this right here. And then just return. What you probably do remember from Wednesday is that given R memory model, that this would prompt the program to run forever. Why is that the case? Based on this local variable set, wefre dealing with this as an activation record. One, two, three, this is the array. As far as that four loop is concerned, itfs just one too small. This is the i variable. It goes through and it demotes all of these variables by four. What values were they before? We have no idea. But it will certainly take whatever values happen to reside there and demote them by four. Unfortunately, because the test is wrong, it goes up and it demotes this numerically by four, as well. Now that four isnft so much a four as it is -- it isnft so much a negative four delta as it is a negative one instruction delta, because you know that this is the Safe PC in our model. This was supposed to be pointing somewhere in the code segment to the line that called the Fu function. This is planted down there in response to that assembly code statement. Does that sit well with everybody? Okay. When you do this, you inadvertently tell the Safe PC that is wasnft pointed to this instruction, but that it should back up four bytes, which happens to be this nice round number, as far as assembly code instructions are concerned. And to stop pointing there, and to point there, instead. So when this as a function returns, it jumps back to this right here, and it executes the call, having no memory whatsoever that it called Fu like 14 or 15 assembly code instructions ago. Okay. Does that make sense to people? So this is this very well disguised form of recursion. You wonft be able to emulate this on the Solaris boxes or on the pods or the mist, because their memory model is a little bit more sophisticated than ours. They donft put the Safe PC right next to this right here. But nonetheless, that is probably the fifth example of infinite recursion wefve seen in the last two days. Let me show you another program. No infinite recursion in this one. I want to write a simple main program, int name. I donft care about the parameters. I want to do this. I want to do -- letfs just say, gdeclare and init array.h Now, Ifm not declaring any local variables whatsoever. So you should already be a little bit suspicious as to what that type of function should do. But imagine the CS 106a program in week four, learning C instead of Java. And they just donft have arrays and parameters passing down. So they have this right here. And then afterwards, they have this things called gprint array.h And that is it. Now unless therefs global variables involved, therefs no legitimate, even though we donft like globals, it still would be a legitimate way to communicate information between function calls. But suppose therefs no globals, either. The program doesnft think they need globals because they do this: void declare and init array. And they just declare an array of length 100. They declare the forward variable i. Theyfre not going to overrun the bounds this time. Theyfre gonna get it right. And theyfre gonna set array of i equal to i. So thatfs a beautiful little code, but it does very little outside of the scope of the declare and init array function. But for whatever reason, theyfve decided that they want to declare this array, locally update it to be a counting array, and then leave. Okay. Then they come back and they want to print out that array and this is not that uncommon when we taught in C. They think as long as they name the array the exact same thing, that all of a sudden therefs a relationship set up between that array and this one, as if the word array has now been reserved throughout the entire program. And they do this and then they do this for i is equal to zero. They get the forward bounds right again; thatfs not the issue. Print out percent D backslash N array of i. And they come in during office hours because therefs some part of the program beyond gprint arrayh thatfs not working properly. But then a TA looks at this or Ifll look at this and say, gOh, this is wrong right here.h And theyfre like, gNo, thatfs working fine. Itfs actually down here.h Well, it may be something thatfs wrong down here, as well, but clearly therefs something wrong going on right here. However they make the argument, gWell, itfs working.h And the answer is, it is working as far as theyfre concerned, because that manages to print out zero through 99, inclusive. You all probably have some sense as to why thatfs happening. Try explaining that to someone whofs never programmed before. This right here, built-in activation record of 104 bytes, goes down and it lays down the counting array in the top 100 of the 101 slots. Returns. It calls a function over there that has exactly the same image for its activation record. So it goes down and embraces the same 404 bytes. This is me embracing 404 bytes. Okay. And it happens to print over the footprint of this function right here. Itfs not like when this thing returned it cleared out all of those bits and said we got to scramble this so it doesnft look like a counting array. It doesnft take the time to do that. So basically, what happens is if this is the top of the activation record and this is the bottom of the activation record, SP is descendant once, this is filled up with happy information, comes back up after the first call returns, comes back to the same exact point, the happy face is still there. We print over it and it happens to be revisiting the same exact memory with the same exact activation record, so it prints everything out exactly the same. Makes sense? Now, therefs some advanced uses of this, where it really does help to do this. A lot of times, not in sequential code like wefre used to, but about 11 years ago, I had to rely on this little feature when I was writing a driver for a sound card. And you had to actually execute little snippets of code with each hardware interrupt. And so a lot of times, you had relatively little to do in one little heartbeat of an interrupt, and you had a lot to do the next one. In fact, you had so much to do that you werenft sure you were going to have time for it consistently. So a lot of times, you would prepare stuff ahead of time and put it in exactly the right space so you knew where it was the next time without having to actually generate it. Do you understand what I mean when I say that? Okay. So you almost think of this as this abusive, perverse way of dealing with a global variable. And youfre setting up parameters for code that needs to run later on. This example wouldnft exactly work that way. This isnft a great example of that, but at least it highlights the feature. This thing called gchanneling,h thatfs exactly what this thing is when really advanced C++ programmers take advantage of this type of knowledge as to how memory is laid out. As far as the problem with hand is concerned, you feel helpless, you try to explain, for instance, if youfve just put a gcall to printfh right there, that its activation record has nothing to do with these activation records. So it goes in and garbles all of the sacred data from zero to 99. And their response is, well, just donft do that. And comma is out and they think they restored program, but they really have not. Does this make sense to people? Now, I want to revisit this printf thing, actually. I donft know how many of you know the prototype for printf. You really are just learning it, basically, on the fly in context when youfre dealing with a simon four and youfre just seeing lots of printffs in the start code and you just kind of understand that therefs a one-to-one mapping between placeholders and these percent D things or percent G things and the number of additional parameters. Well, you know sample calls are things like this: printf hello. And thatfs in contrast to C out less than less of hello is string constant with an gendlh at the end. When you have something like this: percent D plus percent D equals percent D backslash N, and you want to fill it in with four and four and eight, if you wanted to do it that way, you certainly could. But the interesting thing from a programming language standpoint is that printf seems to be taking either one argument or four arguments, or really, it takes anything between one and, in theory, an infinite number of arguments. Itfs supposed to be the case that those things line up, in terms of placement and data type, with the additional parameters. What kind of prototype exists in the language to accommodate that kind of thing? Well, we always need the control string. Thatfs either the template or the verbatim string that has to be posted to the console. So the first argument to this thing is either a char star or a const char star that can polish supports const. So I call it gcontrol.h But then, therefs no data type that really has to be set in stone. There doesnft even have to be a second argument, much less a data type attached to it. I could put a string there. If this is a percent S, a float there if this is a percent G, things like that. The prototype for that is dot, dot, dot. And so forth. Whatever they want to type in. And the complier is actually quite promiscuous, and what it will accept is arguments two, three, and four, provided that dot is there. If you donft want to insert anything, itfs fine. If you want to insert 55 things, itfs great. The complier is not obligated to do any kind of type checking between this and what this evaluates to. Therefs nothing implicit in the prototype right there. Itfs just a char star, free form char star, and then whatever you want to pass in. You want to pass in structs? Great. Pointers, structs, you can do that. GCC, for quite some time, has an extension to the C spec that is implementing. Where it does try to do type checking between this and that right there, if you mismatch, itfs doing a little bit more work at compile time than itfs really obligated to do. It wants to make sure that this printf call works out. So if you were to put percent S, percent S, percent S, and put four, four, eight there, most compliers would just let you do it and run and it would just lead to really bad things. But GCC will, in fact, flag it and say, you didnft mean to do that. Okay. Does that make sense? This return type -- I mentioned this on Wednesday -- this return type is the number of placeholders that are successfully bound to. So as long as everything goes well, it would be zero for this call and three for that call. Itfs very unusual for printf to fail. Scanf, the read in equivalent of printf, can fail more often. But if, for whatever reason something goes badly, this would return negative one. Do you know how IF streams set the fail bit to true so that when you call the dot fail method inside C++, itfll basically evaluate to true and thatfs the way you break out of a file reading program? Well, youfre relying on the equivalent of printffs return value, which is called gscanfh or gf scanfh to return to negative one when itfs at the end of the file. The reason Ifm bringing this up is because, based on what we know and the way wefve adopted a memory model, I now can defend why we push parameters on the stack from right to left, why the zero f parameter is always at the bottom and the one f parameter is always above that. Letfs just consider that call right there. The prototype during compilation just says that either of these calls is legitimate, but when it actually complies that second printf there, it really does go and count parameters and figures out exactly how many bytes to decrement the stack pointer by for that particular call. So the way that the stack frame would be set up is that this would be the Safe PC thatfs set up by the call to printf. Above it would be a pointer to the string, percent D plus percent D equals percent D backslash N. And this would have a four, this would have a four, and this would have an eight. The activation record for the first call would just have this many bytes, and would have a pointer to the hello string. So the activation records, the portion above the Safe PC actually is completely influenced, not surprisingly, by the number of parameters that are pushed onto it. Now, when we actually jump to printf, and this is where the SP is left, it doesnft have any clear information about what resides above the one char star thatfs guaranteed to be there. Does that make sense to people? So really all that happens -- Ifm gonna draw this arc again, like I did before -- it knows about that much, if there were special directives in the implementation of printf that allow it to manually crawl up the stack. But a number of arguments and the interpretation of the four-byte figures that reside there, it could only figure that stuff out by really analyzing and crawling over this control string character by character. This is almost like the roadmap to what resides above it in the stack frame. Does that make sense? So the printf function really does need to get to the control string, and it reads it character by character, and every time it read a percent D -- letfs say if reads a percent D right at the front, it says, oh, the four bytes above the control string must be an integer. And then it sees another percent D along the way. It says, above that there must be some other integer. And this is how it discovers the stuff that should fill in the control string. So you understand that, otherwise, if it didnft have this right here, this would truly be a big series of question marks. Itfs still kind of is a series of question marks. If this is the wrong roadmap, if I do percent D plus percent D equals percent D and I pass in three strings, these things will be laid down as char stars. Thatfs what the caller would do. And then it would interpret them as four-byte integers. So whatever addresses happen to be stored there would be taken as unassigned integers, and it would just fill those three things in that way. Makes sense? This is consistent with the way we push parameters onto the stack, from right left, last argument first, then the second to last argument below that, etc., so that the zeroth argument is at the bottom. Imagine the scenario where the Safe PC is addressed by the stack pointer, but you have the mystery number of bytes below the control string. And that question mark region would be of height zero for the printf hello call, and of height 12 for the printf four plus four is equal to eight call. It would have no consistent, reliable way of actually going and finding the roadmap as to how to interpret the rest of the activation record. Does that sit well with everybody? So as long as you understand that, then at least you have a defense for why that dot, dot, dot -- because of the dot, dot, dot, the C spec more of less has -- I guess it doesnft have to, but it just made sense to for compliers to implement this left to right parameter pushing strategy. Because they want to support that dot, dot, dot in the language. C++ has to do the same thing, because itfs backwards compatible with C. Java just recently introduced the ellipses -- I think it was either Java 1.5 or Java 1.6, Ifm not sure -- but very recently they introduce the ellipses. And so I just know, without actually reading anything about it, that they have to push their parameters on the stack in exactly the same way. Pascal, old school, wasnft old school for me when I was in college, but itfs old school for everybody here. I didnft learn Pascal. I learned C first, but when I read about Pascal, it doesnft have this ellipses option. You have to specify the number of arguments. It happens to press the argument on in the opposite order. And it doesnft cause any problems. They had the flexibility to do it an either order because they never had to deal with this question mark region in a struct. Does that make sense? But as far as structs are concerned, you may ask -- this is a hard point to make; Ifm gonna try and do it. Struct Fu, letfs say I have an int code and I have, letfs say this. Int code, and Ifm just gonna do that right there. Itfs unusual for you to have a struct around one data type, but Ifm gonna do it anyway. And then I have struct type one, which has an int code and, letfs say, several other parameters. Maybe itfs the case that the code inside a type one struct is always supposed to be one. So just take this as a series of equipments of the example. If I have struct type two, with an int code at the front, I might require that all instances of type two actually have a two at the front. These are really esoteric examples. Ifm just making this up. I havenft done this is past quarters. But this right here is a data structure that I guarantee, whenever I give you a pointer to a struct base, the idea is that there is one of two values that sits right there. Itfs almost like itfs a little opt code in the assembly instruction, in a sense, it to figure out how to interpret what resides, or figure out what resides above the one or two in memory. You could cast that pointer to a struct base knowing that therefs gonna be at -- or maybe it is typed to be a struct base, which means that you know that therefs some kind of opt code or type code sitting there. And then based on the result here, you can either cast this arrow to be a type one star or a type two star to figure out how the rest of the information is fleshed out. A complicated example, but there are various structs -- I donft know whether youfve looked at -- I donft think Ifve exposed the code for all the networking in the URL connection stuff. If I did, you would have hated Assignment 4 even more, because you would have thought you were responsible for it. But there are lots of structs that are afforded by GCC and G++ to help manage networking, and I tried to insulate you from that. Old school networking deals with four byte representations of IP addresses. They realized about 15, 20 years ago that they were going to run out of IP addresses pretty soon, so therefs actually a six byte universal version of IP codes. Itfs standard, but itfs not really widely adopted yet. There are two different structs associated for the two different protocols. The four byte version IP, version four, therefs IP version six. The IP version four struct has been around for some 25, 30 years. Those things arenft going to go away. So when they designed the IP version six struct, they had to make sure that the first half of it had exactly the same structure as the IP four version, and then they extended it with all this extra information. Does that make sense? Do you always know that youfre getting a pointer in networking code? You always know youfre getting a pointer to one of those two structs, and you analyze the first few bytes to figure out whether or not you have an IP four struct or an IP six struct. Does that make sense? The reason Ifm mentioning this is because now it kind of gives you some sense as to why the first parameter, or the first field in a struct or a class actually has to be at the lowest address. It doesnft have to be, but thatfs just the way they did it, because they had these types of things in mind. If this and this and this were always the last thing, or the thing at the top of the struct in the activation record, it would always be at a mystery distance from the base address of the entire thing. Does that make sense? Now you could just argue that you could make the code the last thing in a struct so you could do it the other way. It just makes sense to people who designed the spec or designed the compliers. I donft think itfs part of the specification, although maybe it is, that the first field is always in offset zero, and you can exploit that knowledge to do clever things with C and C++ structs. Does that make sense? So Ifm cranking on time. So what I will do now is I will give you a little bit of a headfs up on how wefre gonna transition things. Everything so far in C and C++ has been sequential. And Ifm talking in Java itfs -- actually, not in Java necessarily, but all of the C++ and C youfve done, in all 106b and 106x, and for example, 107, has either been strictly or seemingly sequential. And you know what sequential means now, because youfre waiting for the sequence of RSS news feeds to all load over a five-minute period while you test. So you know what sequence means more than you did a week ago. I want to introduce the idea of how two functions can seemingly run at the same time. Thatfs going to be a huge win in the context of Assignment 4. Youfll all be delighted that youfre going to revisit Assignment 4 before Assignment 6. And youfre going to make it run oh so much faster by using this thing called threading. What I want to do is I want to talk about how two applications on a -- and just give you very high level stuff -- two applications can seemingly run on the same processor simultaneously, and then use ideas from that to defend how two functions within a process can seemingly run simultaneously. Ifll frame this this way. Letfs just think about one of the pods. This is the virtual address base, virtual meaning the allusion of a full address base that make has wireless running. You donft think about Make as an application, but it certainly is. Itfs designed to read in the data file that you call a Make file to figure out how to invoke GCC and G++ and the linker and purifying and all of those things, to build an instrument and executable. This has a stack segment associated with it. All the local variables of the thing that implements make go there. There is the heap. There is the code segment. While make is running, itfs probably the case that GCC, as an executable, is running several times, but wefll just talk about the snapshot or time slice where just one GCC is running. GCC is an executable. Youfre first C complier was probably written in C -- not written in C, was written in Assembly, but then it kept bootstrapping on the original compiler to build up more and more sophisticated compliers. So the C complier was written in C, Ifm sure of it. It also thinks its stack is there and its heap is there and its co-segment where all the assembly code stuff resides right there. I can tell you right now that theyfre not both all in the same place. They do not share the stack and they do not share the heap and they do not share the code. This virtual picture was in place so that make can just operate thinking it owns all of memory. And it lets the smoke and mirrors that the OS managers, to map these to real addresses and map this to real addresses and map that to real addresses. It just wants to be insulated from that. Maybe itfs the case that you have, I donft know, Firefox up and running on one of the Linux boxes, has the same picture. And then you have some other application, like Clock or whatever you have, up there and it has the same exact picture. Those are four virtual address bases that all seem to be active at the same time. Ifm gonna call this process one, Ifm gonna call this process two, call this process three, and Ifll call this process four. When I draw these little bands of segments right here, these segments is what theyfre called, theyfre all assuming that they have as much room to stretch out to make the stack as big as necessary, the heap as big as necessary, to meet the demands of the program. But on a single processor machine, there is only one address base. This is the real deal right there. Thatfs physical memory. And this has to somehow host all the memory thatfs meaning to GCC and Make and Clock and Firefox, or I should say the processes that are running according to the code thatfs stored in those executables. Does that sit well with everybody? Well, it turns out that this and that right there, they may have the same virtual address, but they canft really be the same physical address. The space has to be truly owned, or the values there in virtual space have to be truly owned by this process Ifve called number one. So what the operating system will do is it will invoke whatfs called the memory management unit to basically build a table of -- letfs say that this is address 4,000. Itfll actually build a table of process and something related to an address. And itfll actually map it to a real address somewhere in memory. Does that make sense? The idea, I think, probably makes sense to people. So that this right here, it thinks itfs storing something in address 4,000. Letfs say address 600,000 is right there. Any request to manipulate or deal with address 4,000 is somehow translated to a request to deal with the real address at address 600,000. Any type of load or store or access of this right here has to somehow be proxied or managed by this daemon process behind the scenes, this thing that just runs in the background all the time, to actually map virtual addresses to physical addresses. And it knows that this address 4,000 is the one that process one owns, so it just has this little map of information -- Ifve drawn it has a map of pairs to physical addresses -- so it knows exactly where to look on behalf of this process. It stores them in address 4,000; it updates the four bytes that resides at address 600,000. Now, it doesnft really clip these things down at the four-byte level. Normally, what will happen is itfll allocate things -- I donft mean allocate in a malik sense -- itfll just associate very large blocks in virtual address space with very equally large blocks in physical address space. So this might be some 1K or 8K block of memory. And if itfs ever used, then itfs sure to map the same size block or adopt the same size block in physical memory so that address 4,000 through, letfs say, address 8,000, would map to whatever this is through whatever this is plus 4,000. So therefs some contiguous nature going on between all the things in this virtual address base and what it maps to in physical space. Does that make sense? A lot of work. Itfs a very difficult thing to implement, certainly, the first time. Ifve never implemented one of these things. Maybe itfs even more difficult than Ifm giving the impression it is. But itfs doing all this stuff in the background, itfs threads, itfs all kinds of stuff that makes OS and systems code interesting, but difficult. So wefve solved the memory problem. In theory, you can run 40 applications. Usually itfs the case that the stack segment is never really that big. Itfs initially slotted off to be fairly small for most applications. Because unless youfre going to call Fibonacci of a billion, itfs probably not going to have a call depth greater than 50 or 60. In fact, when you have a stack called f of 50 or 60, it usually means a lot of things are happening. The distance down from main to the subhelper to the 59th power function. I donft want to say thatfs unusual -- maybe 100 is normal, maybe 200 is normal; 2 billion is not. So you know that most activation records are on the order of, letfs say that theyfre 1K. It might be the case that you set aside 64K for the virtual stack or the stack in virtual space. You have two the thirty-second different addresses. 64K is ridiculously small amount of that. Itfs like that. So it definitely has space for it. You could be more aggressive about the way you use the heap. You could allocate megs and megs of memory there. Itfs still going to be a relatively small portion of memory when youfre talking about two to the thirty-second different addresses. Makes sense? So the smoke and mirrors thatfs in place so that every single application can run at the same time and not have its address space, or what it thinks is its address space, being clobbered by other processes. Thatfs managed pretty well by the OS -- not pretty well -- ostensibly perfectly by this memory management. You can share address spaces across applications, but you have to use advanced unit directives to do that. The part that is not clear, and this is going to become more clear, hopefully, next week and the Monday after it, is how the applications seemingly run at the same time, when therefs really only one register set, one processor digesting instructions at a time. I did this the first day of class, but it totally makes sense to do it again here. Forget about Firefox and Clock, letfs just deal with Make and GCC, which is what youfve really been doing. And think about Make and GCC actually running seemingly sequentially. You look at them both running -- and my hands are sifting over the assembly code instructions. And theyfre both seemingly running at the same time. Thatfs not whatfs happening. What really happens if that Make makes a little bit of progress, and then GCC makes a little bit of progress, Make makes a little bit, and this just all happens in this interlay fashion, so fast that you donft see any one lagging over the other one. Itfs like watching two movies at the same time, where not much is happening. So you can actually follow both movies fairly well, as long as itfs clear that both of them are actually running. The argument for two hands scales perfectly well to three hands and five hands and ten hands and 50 hands, as long as the processor has the bandwidth to actually switch between all of the processes fairly quickly. That make sense? Now in a dual processor machine or a four processor machine or a multiple core machines, it can actually really run two processes and four processes at the same time, but you can always run more processes than there are processors on any sophisticated system. If somethingfs running a dishwasher, then it probably canft deal with threading, but if itfs actually running some real program, it probably is dealing with a real processor, and the OS can actually dispatch and switch between processes fairly quickly. Makes sense? The reason I bring this up is because that, as a concept, is going to translate, I think, somewhat nicely to the notion of threading. This is multiprocessing. Several processes are seemingly running at the same time, and each process has its own heap and its own stack and its own code segment, and its virtual space. Slightly different, but certainly related, is the idea that two functions in the same process, one code segment, one heap segment, technically one stack segment. Wefre curious as to whether or not itfs possible for two functions to seemingly run at the same time inside a single process. You know; youfve seen this before. Microsoft Office, like youfre typing and then while youfre typing, all of a sudden in the background, some little paperclip comes up and says, I think youfre trying to write a letter. And that happens in the background, and thatfs because something in the event handlers that actually catch your keystrokes have done enough synthesis of the string to look that it looks like a header of a letter. And so it spawns up this other function that doesnft -- itfs not really supposed to interfere with your typing, and from a computational standpoint, it doesnft. From an actual mood standpoint, it does, because you actually go down and look at it. But that is an example of a thread that is spawned off in reaction to an event, or something like that. That makes sense? iTunes, you buy an album of 13 songs -- Ifm hip; I buy music online -- and you check the actual download screen and three songs at a time are actually downloading. Not really. Itfs really doing -- and iTunes is another hand over here. And itfs pulling things in in little time slices, but it happens so fast and the time slices are so small compared to what we can detect, that it looks like all three songs are being downloaded simultaneously. Therefs one process going on there. Itfs not like it spawns off a different executable called the gpaperclip executable.h Itfs just some function to bring up that little widget. When youfre downloading music, therefs one process thatfs doing it, itfs iTunes. And internally, it has some function related to the downloading of a single song that at any one moment itfs allowing to run, seemingly, three times -- sorry, three at the same time. Does that make sense to people? So just imagine the scenario where there are two songs downloading at the same time. In that case, they both would be following the same assembly code block. They have the same recipe for downloading a song, for the most part. In that case, the stack segment itself could be subdivided into smaller substacks, where the stack frame for song one could be right there, and the stack frame for the downloading of song two is in this completely unrelated space -- not unrelated. Itfs in the same segment, but far enough away that youfre not going to have one stack run over the top of another one. Do you understand what I mean? So when the process has the processor, it also subdivided its time to switch back and forth between the two threads -- thatfs what you call these things. And so basically, it goes you have time, you have time, you have time, hope youfre downloading songs. And it keeps doing that until one or both of them ends. Does that make sense? It shares the heap. So they all share the same call to malik and they all draw their memory from the same memory pool. Therefs only one copy of the code. You only need one copy of the code. Itfs read only. Itfs fine for this stack and this stack to both be guided by the same set of assembly code instructions, as long as each one has some kind of thread identifier. If the word thread is bothering you, just think take the phrase gthread of discussionh or gdiscussion threadh and just translate it to gfunction thread.h Does that make sense? You may ask what types of scenarios actually require threading and which ones really donft require threading. Let me just go over this very simple example where you really would expect threading to be in place to model the real world situation. I will revisit this example on Monday. I have this really simple program, int main -- and therefs no threading whatsoever. I have this four loop, int num agents. When I call this num agents, Ifm actually thinking about ticket agents who answer the telephone at United or some other airline that still hasnft declared bankruptcy yet. Num agents is equal to ten, and letfs assume that the job of this program is to simulate the sale of 100 tickets for the only flight that happens to be flying anymore. You might do it this way. You might intrinsically hard code the 100 in there by calling some function gsell tickets,h where you pass in i and you pass in ten. And I donft need to tell you what sell tickets -- Ifll write code for sell tickets in a second. But the idea here is that we have to sell 100 tickets. This is the number of agents right here. That number is the same as there. In fact, letfs say that there are 150 seats on the flight, so we donft have to mix tenfs. The idea of gsell ticketsh is that itfs supposed to be in place to simulate the requirement that some ticket agent actually sells 15 tickets before his or her job is done. As long as these run in sequence, eventually youfll get to the point where you actually sell 150 tickets in this really rude but simple simulation. The problem here is that in the real world, itfs just fine for all ten of these agents to be answering telephones simultaneously. Itfs not -- I donft want to start introducing thread functions yet, but I just want to leave you with the idea that wefre going to be able to get that function right there to run in ten different threads. In other words, Ifm going to spawn off ten different threads. All of them are going to be following the same exact recipe, where each one has to sell 15 tickets, and when a particular thread exists, we just know because of the way we code it up that 15 tickets have been sold. Ifm not going to write code for this yet, but this is, I think, a fairly good analogy. Imagine a horserace or a dog race. Sequentially, if you wanted all ten dogs to get to the finish line, you could just let one go, and when it gets to the finish line, let the next one go. And just do it that way, and take 15 times as long or ten times as long as you really need to. Or you can line them all up in ten gates and lift the gates at once. And some will be faster than the other ones, but eventually, theyfre all going to get across the finish line. Like youfre basically pipelining and taking advantage of the fact that things can happen more or less at the same time. Now the difference is that they donft run like this every time, and theyfre not respecting time slices. They actually are really independent agents. But wefre going to try and simulate that idea as much as possible with this example when we introduce the thread library next time. So this is a great place to stop because Ifm at the end of a segment in the course, and to try and introduce threading in five minutes would be pretty difficult. I will see you on Monday and wefll talk more about threading then.