welcome everybody this is Back to Basics r-a-i-i my name is Andre Coster I've been working in C plus plus for almost 30 years now I work for Arista networks we do very large scale switching I'd like to thank bianni for advertising my talk so eloquently during his keynote he must have noticed how many times in there he mentioned about resource management and raii uh I'm perfect update question during the talk so if you need to ask questions raise a hand I'll try to get to you today we're going to be talking about what do you mean by a resource or is what do I mean by a resource what issues crop up trying to handle Resources by ourselves in C and C plus plus introduce the whole idiom of raii the resource acquisition is initialization show you some of the examples in the standard of some classes that already exist in the library that do our AI type operations and towards the end we'll talk about the various considerations about what you want to think about when you're trying to implement your own Rai classes because almost inevitably you will probably try to manage a resource that the standard Library doesn't know about so what do I mean by a resource a resource is some sort of facility or a concept that you gain access to by some sort of acquire and release pair of operations I'm pretty sure everybody here is probably done at least one type of resource memory that's your classical new on the acquire side delete on the disposal side but in addition to just memory because REI is about resources we can also talk about things like posix files and there's your F open F close calls to open and close a file joinable threads a stood J thread deals with the create the thread during its construction and joins the thread at the end when during his destruction locking a mutex is a resource lock the mutex on one side and you want to be very sure you unlock the mutex on the other side the common problems will run up when you're trying to manage resources the first one comes up leaking your resource that's where you end up and you lose this handle that you got a hold of so you no longer have any way of talking about your resource to be able to dispose of it and depending on the resource has different implications of what happens if you keep leaking it leak lots of memory your program starts consuming more and more memory you start swapping eventually out of memory manager might come along and kill you not a good thing open too many files or forget to close them you'll run out of file descriptors in your process and then it won't let you open any more files if you like a mutex and forget to unlock it well later on you're going to want to get that mutex again somewhere whether it's in your thread or a different thread and that is a short trip to a Deadlock second issue that tends to pop up use after disposal so if you use REI well it's very hard to use the resource after disposing it because your resource object goes away you have no way of talking about it same sort of deal with a double disposal C plus doesn't turn around and Destroy objects twice not without you trying very hard at least so in my example of what comes up I'm going to talk about using the utex as an example resource let's look at a nice small slideware type function takes into mutex takes in some data source that you should have that mutex locked before using it we start off lock the mutex I have some other buffer class I'm going to read things into I read from my source into that buffer show whatever that buffer is and then return true and because this function is nice and short it's not too hard to see the problem we have here I've locked a mutex I haven't unlocked it right my required lock here but I didn't unlock the mutex so it stays locked permanently the next time someone calls this function your program comes with screeching halt next time someone tries to lock that mutex somewhere else that wants to talk about that data source comes to a screeching halt easy enough to fix I can add the unlock we're good right lock unlock but as you may notice this function I have returning a bull because it might fail expectedly that read into buffer for example I want to now test to make sure that you know if I fail to read I want to return false from my function well I've just reintroduced the problem we just solved now I'm again doing an early return from this function my mutex was forgotten to be unlocked okay I can fix this again add in my unlock we're happy again for a while what happens if reading into a buffer throws an exception here we go again you're function exits I have forgotten or forgot to unlock the mutex recipe for Deadlock okay this is still solvable add on a try catch block try catch every exception that could possibly happen unlock the mutex and since I have nothing better to do with that exception rethrow it so somebody above me can deal with it but hey at least I'm thinking ahead a little bit if display throws an exception I'm still protected thank you why don't we have the sort of leading problem with the contents of a vector right vector's got dynamic memory it allocates stuff why don't we ever leak memory from Vector because it behaves with C plus plus object lifetimes see in C plus plus our objects begin their life at a defined place and they end their life at a defined place we even have functions that get called and I'll use the term call Loosely gets called on these two places in time we have Constructors on one side destructors in the other and the language will ensure that these get called I don't have to remember to do it for at least in the beginning of this talk we're going to look at specifically objects with automatic storage duration also known as local variables and this is where the Rai idiom the core of it comes in in the purest ends of the term this is the idiom where we acquire a resource during the Constructor of an object and we dispose of the resource during its Destructor in my opinion it's somewhat of a misnomer because I think that really the destruction side of this is the really important part coupled with this idea is the concept of the ownership of a resource who actually is responsible for it when you call new new hand you back a pointer and at the same time is essentially handing you the ownership I don't want to only resource I want to hand that ownership off to the Rai class make it responsible for the resource standard library has a number of raii classes in it the first one we want to take a look at because it applies to my little sample program is stood lock guard lock guard is a standard Rai class that its job is during its construction will lock the mutex during its destruction unlock mutex that way I can't forget to do it so using this we can look back at our function that we just finished enhancing to make it bulletproof so it always releases the resource and let's modify this to actually use that Rai class to manage my resource instead of me being responsible for that resource so of course first off replace that mutex lock with declaring my locker card taking the sum mutex I have now this will acquire the mutex now that uh now that I don't need to deal with some mutex let's look at all the other places we talk about some mutex because those should be no longer required I no longer need to unlock here the next statement is I'm going to return from this function when I return from the function all of my local variables go out of scope their objects and their life that's lock guard name to lock its life ends during its Destructor it will unlock the mutex I can remove this one there's that unlock sitting there in the catch of my try catch block same deal my next operation is going to be rethrow the exception find this function is going to end local variables get destroyed my mutex gets unlocked I can remove this one here we are down at the bottom I'm about to return true this function is going to end my my local variable is going to end its life its Destructor gets called my mutex gets unlocked I don't need this one either let's look at this exception block again I've got to try I'm going to catch every exception that possibly comes out and immediately re-throw it why do I have these lines anymore I can get rid of the exception handling here too now we're down to the safe version of the function we started with using an Rai class and yet I won't ever leak that mutex now of course in a function that's what eight lines long this is easy to see what happens if this is a function much larger that doesn't fit on a slide and in three days someone else has a new early return into this function someplace that REI class keeps defending you will keep ensuring that that mutex gets unlocked this is the very important part of our AI so throughout this entire example I talked about variables with automatic storage duration this whole thing is dependent on the C plus object lifetime so however the objects start their life and end their life are applicable so it doesn't have to just be local objects I could do things such as create this worker thread as a stood J thread and I can push it into a vector so now I can have this collection of worker threads running around and now the the lifetime of that object since I moved it is now however long it lives inside that vector So eventually when I finally do remove the object out of the vector or the vector goes out of scope as the case may be that's when the thread will get joined now speaking of storage durations food for thought you could do raii with dynamic dynamic storage duration but of course that leads to the question that if you're doing a new of a stood J thread who's now responsible for that memory so of course you need to put that into an RNA that's my goal there another poster child example of our AI stood unique pointer if you're in the sharepointers talk two sessions back you learned all about stood unique pointer standard area class its job takes in a pointer and will ensure that it gets deleted when it goes out of scope I would Hazard say it's probably one of the most common Rai classes used but even Beyond unique pointer there's stood shared pointer and that deals with what happens if you have multiple people owning the resource again two sessions back you get lots of details on stair pointer other standard REI classes things such as stood unique lock we looked at lock guard which is very much begin and unique lock is a more sophisticated walk guard in that it can adopt a mutex that has already been locked by somebody it can adopt a mutex and wait to be locked you can ask it to unlock the new text during its lifetime but throughout the entire thing it's when it hits the end of its lifetime it will guarantee that the mutex gets unlocked you'll also guarantee it doesn't unlock it twice stood J thread manages joinable threads recalls we used to have stood thread and that was one of the problems of stood thread was that if you didn't join it when it went out of scope bad things happened I stood J thread fixes this and says when J thread goes out of scope it will join the thread that's REI let's read to resource stud fstream opens and closes files same idea some Rai classes may allow you to reclaim the responsibility from the object in addition you may have ways to get merely get access at the underlying object back to stood J thread for example J trick to let you do some reasonable things with threads but not everything as an example of the standard provides no way for you to do um a thread Affinity I want to tie this thread to the CPU core the standard doesn't let you do that however you it does provide a method where it can ask it for the thread handle and it gives me back whatever my platform handle is so now I can use platform tools to talk about the the raw resource but keeping in mind when you get handed one of these raw resources be very careful what you do with it particularly in the case of unique pointer and sharepointer you really don't want to get a hold of that raw pointer and hand it off to some other Rai class remember I mentioned about ownership earlier somebody should be responsible and own this thing when you hand this resource to an REI class you are handing it to the ownership you shouldn't be messing with that resource directly anymore that's the read That's the ra classes job and then as mentioning about breaking the resource out there may be some cases where you literally are trying to reclaim responsibility for that resource from the REI object and note I do also say some may do it because it's not possible for all resources my locker card you can't pull the mutex back out of the lock guard strategy sorry shared pointer you can't pull the pointer back out of the shared pointer because it doesn't make sense again lots of details the shared pointers talked two sessions ago does this solve the three problems I started with does this solve leaks caveat remember that we're generally defending against Murphy not Machiavelli if someone wants to break the stuff I'm sure someone can come with some code to break things lots of it revolved doing get the naked pointer and hand it someplace else particularly leaking pointers but when used in a reasonable manner does this prevent leaks yes because it guarantees uh it will release the resource when the object goes out of life does it solve use after disposal if your resource is now literally held by this REI class and the rii class is out of scope thus you have no way about talking about it it's hard to use it if you can't talk about it so I'd argue yes if the variable's got a scope I don't it's end in life you can't use it anymore double disposal same deal the disposal happens during the destruction of the class of the object C plus plus won't unless you're doing some really weird stuff won't destroy an object twice and if it is you've got bigger problems so yes it solves that problem too low variables don't go to scope twice REI does not solve everything lots of times if there are two resources that need that somehow need to agree with each other on something REI as the idiom is not trying to solve that problem as an example resource loops picture this you have you want to create a doubly linked list you store your next and previous nodes as shared putters because there will be multiple things pointing to the same node so I need shared resource handling so the problem is that when I take my head node and as I I'm done I can get rid of my head node so that sharep pointer goes away my first node is still pointing at my second node my second node is still pointing at my first node so in both cases they still have a resource or a reference count of one so they won't go away because there's always someone responsible for this REI as a concept isn't trying to solve this kind of problem it's focusing on the individual Mass another example dead box back to my mutex example what happens if in one thread I grab mutex a and then mutex B in a second thread I grab mutex B and then mutex a when Murphy comes along and decides to task switch at exactly the wrong time thread one grabs mutex a thread two grabs mutex B thread one tries to grab mutex B but can't because it's being held so test switches back to the other thread it tries to grab mutex a but it can't because it's already being held so now I got two threads eternally waiting for each other and again Rai as an idiom isn't trying to solve that problem that's not to say that there aren't classes that do try to solve that problem uh stood scoped lock which is in the standard Library does try to solve this problem and in that particular case you initially initialize that with both mutexes and its shared locks responsibility to make sure they're both locked in the same order but again that's a more sophisticated REI class the idiom itself doesn't consider that so what happens if you have a resource that you want to manage but the standard doesn't know how to do it if the thing you are trying to manage is already a pointer the one that likes to pop into my mind plus six files right it's a file star F open returns you pointer if it's already a pointer then you could use unique pointer unique putter to manage the pointer for you and the reason that happens is that stood unique pointer is templated on two parameters one is the type it's holding and the second one is a custom deleteer which essentially ends up being something that can be invoked being passed the pointer and will dispose of the resources and passed I prefer to use the phrase dispose rather than delete because I'm talking about resources not memory yet that custom disposal does not have to call delete and if I can put my pointer into a unique putter then I already get a lot of the REI stuff essentially for free initialized during acquisition guarantees it gets destroyed at the right time it will even prevent me from accidentally copying my REI class around because this is a unique footer it represents unique ownership there can only be one and even if you do an assignment at least temporarily there has to be two of them and that's just bad because at some point they're both going to go to scope and there's that double deletion problem so a unique pointer prevents you from doing that at all so let's assume we want to manage a file star as well as a reminder this is at least more or less what the f over F close functions look like some standard libraries add in like restrict on a few other interesting things but essentially you have your F open and your F close how do I get this into a unique pointer well let's play with a little bit of boilerplate code I can write a file closer function object so it's destruct it has the operator parens and it takes one parameter which is a file star is a const method because consts are good and F goes to the stream has been handed then I can create a using declaration to say hey I'm making this thing called A C file it is a unique putter of file and file closer and thank you Timur for showing me this next one in C plus plus 20 this gets easier because I can do a decal type on a Lambda so now I can do stood unique pointer of a file and the decal type of a Lambda which takes a file star and calls F close so I've done all my background work to get my unique pointer in place let's be nice and follow the pattern of what the standard Library does let's make a dedicated function to make a C file so I've got a new function takes the same parameters as F open does because I'm just going to turn around and call it anyway declare that local variable files to our stream it's the one actually called f open I then test to make sure that my f open succeeded and if it didn't and I'm choosing to throw an exception as it turns out this actually makes my Destructor side easier that Lambda doesn't have to consider that I might be F closing a null I just throw an exception in this case so it just can't be no it always has a valid value and I return a constructed C file so I'm just returning the Rai class itself and with this I can now do the same sorts of things that we tried to say about new and delete in well-formed call it modern C plus plus you shouldn't have to utter new and delete in the context of unique pointer with this I can do the same thing and say you should not have to ever call F open or F close you should only have to call make C file and let whatever that variable is and its life so using this example I can put this all together I have a function auto file make file of whatever file name opening it for right and now I can f printf but I do have to say file.get that's the piece where I'm getting the underlying resource and as a side note of anytime someone does a DOT get that should be a yellow flag on the play why why are we trying to peek under the covers it's not necessarily a problem but it's someplace to pay attention to during code reviews to make sure that they don't take that pointer and send it someplace else you didn't mean take copy put it in a container someplace I'll look at it later when that local variable goes out of scope uh question online all right what about the implementation around slide 35 why not use a class and put F open in the Constructor and F closing the destructor what are the advantages of a smart pointer over a dedicated class that's actually coming up in a few slides when we start talking about dealing with resources and you don't want to take the shortcut of using unique pointer where you start constructing your own class um yeah so F printf passed the file we're good shared pointer has the same facility it also has a custom disposal method so if you want to have a shared file handle you can do a similar thing here I'm not going to show the details but it's similarly not a whole lot of oil plate and you get the shared thing for free Ben then you want to go on to handle something else you want to handle your own REI glass there's some design decisions you might want to consider when writing your own class is there a valid default acquisition to your class something to the uh well sorry I'm sorry default acquisition provide a default Constructor an example of this might be I want to write an Rai class to manage the startup and the initialization and shutdown of windows winsock so my construction on my object is called wsa startup during destruction wsa shutdown because it's the default thing it's we've got everything we need it does not preclude an empty state which we'll get into next is there a valid empty state things like stood unique pointer has a valid empty state it doesn't hold a pointer it holds an outputter if your REI class has a valid empty state has something to be aware of in your Destructor that you may not be currently holding a resource write your Destructor accordingly and to the question that came in online you could provide a Constructor with its own set of parameters and use that to parameterize with a call to F open so he could do the same thing do you want to be able to adopt a resource with that file example particularly the user defined REI class file do you want to be able to create to get a file star from somewhere else and put it into this object and if you do again provide a Constructor for that also does not preclude an empty state and again may need to understand the resource released early is your object copyable does it make sense to copy your resource what does it mean to copy a file star do you really want two different file stars with the same file or do you want a shared ownership for example shared pointer you can copy those all you want because that's implicitly it's a shared ownership membership uh concept so when you copy it now there's two shared pointers pointing to the same thing three shared pointers pointing the same thing unique pointer is not as a little bit of an aside now is a good time to remind people about the rule of five as a reminder if you if you need to mention any of copy Constructor copy assignment operator move Constructor move assignment operator or Destructor it is likely you need to mention all of them and in this particulation REI I'd almost Hazard to say You must because it's almost guaranteed you are doing something special in the destructor and if you're doing something special in the destructor you're going to need to consider that in the other four so hence here if you don't want your object to be copyable declare your two copy operations as deleted similarly same question for movable should your resource be able to be moved around someplace should be able to be moved into a container if not delete your move operations be explicit of what you can and can't do with your resource it is it is possible you may even have to delete both your copy and your move operations for example scope stood scope locked you can't it is immovable it is uncopyable it's literally a scope based lock make sure the user doesn't do it however shared pointer is movable checkpoint is copyable too unique pointer movable but it's a consideration you need to have do you want to let your users get at the underlying representation because if they can get out the underlying representation you are providing a possibility that they may take that and hide that away someplace else and start opening up that possibility to your use after deletion or double uh use after deletion or double disposal so for example uh stood J thread you can get a hold of the thread handle because you might need to do operating system things with it scoped lock does not it doesn't give you a way to get at the mutex do you want to hide the underlying representation altogether don't give them away a get at it but that does mean that you now need to provide member functions of your own to gain access to whatever you want to let them do to that underlying representation the bonus here is you no longer have to do that dot get everywhere um also again feedback to the online question if you write your own file file star wrapper REI class you'd want to provide your own read and write methods and then internally they'd have access to the file star to do whatever they needed to do even if you're trying to hide it you may still want to let people get a get a hold of it anyway because they might come up with something they may want to do that you hadn't thought of but like I was mentioning earlier but if people are starting to use that underlying handle that should be a yellow flag in the play that well it drink something a little weird be careful what you do with that interesting idea is dependent resources what happens if you have an Rai class around something like a sqlite database so it's managing the opening and closing of the database all nice but now you want to start a transaction on this database now you don't you may not want to give them a start transaction call that returns a pointer to a SQL like transaction maybe you want to write your own sqlite transaction Rai class to manage it's that's a resource have it managed and your start transaction in your database class returns an instance of that raii class prevents the prevents that need that naked resource from getting out from underneath you foreign do you want to provide a way for them to get the resource out of you altogether such as stood unique Pointer's release method you get your pointer back unit pointer now holds no you may not want to let them do that the new text one for example doesn't shared pointer can't you might want to consider even marking that particular method as no discard because you're because you're explicitly asking that Aria class to relinquish its responsibility it's handing the ownership back to you you should do something with this you really don't want people to call Dot release as the only thing on the line so you get back the pointer and it drops it on the floor because that's going back to leaking an example REI class I have had to write in the past unique unlock it's actually the reverse of a scope lock I want this thing to unlock a mutex during construction and lock it during destruction I can be using this during a work queue implementation so I have the mutex locked while I was pulling something off of the queue then I want to unlock the mutex so other threads can push more things onto the queue I do my work but I want to make sure that I'm locked again before I come back and look at the queue so in this case I have a unique unlock I've templated on whatever my mutex type we want to put in there I have my explicit Constructor that takes a stood unique lock of that mutex so that way I can guarantee I'm being handed a locked mutex store the reference because I have the unique lock as a reference member variable and during my Constructor I unlock the mutex in the wonderful commented area I delete the copy and move operations because I don't want people to move my resource around and during my Destructor I re-lock the mutex which of course will block until I actually acquire the mutex which means I end up with something like this I have my view text somewhere outside of my function I have a unique lock I do something I need to do protected by mutex I create my little scope to say here's my unlocked area do whatever I need to be unlocked when it on when it locks again I might need to do more work protected by mutex again re-establish that whatever state got mutated is where I left it because it's mutex is unlocked so there's certain preconditions that no longer hold here's my plug for the C plus core guidelines REI says all sorts of good things that even the core guidelines has a number of items to say for more information on the core guidelines themselves Raynor Grimm is talking about this on Thursday um the entire R section on resource management which basically kind of kind of a short synopsis don't use new don't use delete use make unique use make share use unique pointer use sharepointer and because all these things are dependent upon the Scopes of variables that I've also listed some of the items about Scopes like es5 keep your Scopes small particularly in the case of the mutex example I've got you don't want to lock your mutex early and you don't want to hang around long after you've needed it you want your mutex to lock at the right time and unlock at the right time always initialize an object that's part of the resource acquisition is initialization part don't introduce the variable before you need to use it back to mutex example I really don't want to lock mutex to the top of my function do a bunch of other unrelated things now do the stuff that should be protected you know if I want that mutex locked as close to where I need it as I can don't declare variable until the value to initialize it with like these are good ideas for variables in general but because the resource management pieces are now tied to the over the lifetime of a variable these get additional considerations so at the at the end Rai resource acquisition is initialization Tire acquiry resource during the construction of an object or hand it a resource dispose of it during destruction thank you thank you a question I have is especially I've seen this one tried to wrap C apis uh quiet the resource and the Constructor is usually pretty straightforward but for those cases where releasing the resource can fail how do you deal with that in the instructor so essentially the question ended up what happens if releasing your resource could possibly fail particularly in the context of C apis that's just trouble all around because of the destructor and you can't stop it from happening that would imply that your only real mechanism of reporting that error would be to throw an exception from the destructor and exceptions escaping destructors while not technically illegal tends to end up being a lot of trouble particularly if it ends up having to destroy this while there's already an exception in flight and your program ends um I'm not sure REI has a good answer to that just because the resource going out kind of has the same sort of some sort of destructors in general that you can't really fail to destroy you can't really fail to release the resource and in some cases that does raise the question it wasn't mean for a resource to be failed to be released um so I'm not sure that's particularly satisfying answer but yeah I think the structure is the only viable answer um the only slightly better or less objectionable answer is writing State out to a global variable and that just makes me itchy I hate yes and I'm actually ignoring yeah sorry you mentioning that F close actually does have return value kind of along the same lines a printf has a return value who actually checks it good question um I've been ignoring it but it's a valid question yep does uh does the standard Library implementation stuff in the text uh trading systems yes um essentially it's a joint the question was does the stood mutex depend upon the operating systems of mutex and while okay I suppose my initial definitive yes is overreaching on my part I would strongly suspect yes because stood mutex is a little less has fewer constraints than say stood recursive mutex that your stood mutex might be recursive if the operating systems one happens to be but it won't guarantee it so it's not safe to assume it's recursive for example but I would expect that a stud matex almost directly maps to whatever the default mutex is on your operating system oh I will so we have a light that blinds me storage duration our thoughts for first so the question was what happens if you have a static object as an Rai class really the consideration is that the REI class is acquires when it's constructed whenever that happens to be and is released during its destruction whenever that happens to be so if we're talking about um Global static objects that would imply that it's being constructed sometime before Maine and is destroyed sometime after me and I'm being deliberately vague about exactly when that is because then you start running into what's tends to be known as the static object initialization Fiasco in that if you have static in Translation Unit A another static in Translation Unit B is indeterminate which one gets initialized first the only thing you do know is that whatever order is chosen they get undone in the opposite order so if you're acquiring a resource has some of these dependencies that could be trouble for Global Statics if we're talking about function level static uh local variables same deal first time the first time the flow of execution crosses that static local variable it gets constructed acquire a resource and then if I recall correctly it's sometime after Maine but I think it's before the global Statics it will get destroyed um on the plus side welcome in C plus plus 11 now and Beyond if that is called from multiple threads is guaranteed to only be initialized once and done so in a thread safe manner and I think it's the only just other static options that are interesting yeah are there any situations are there any situations where you wouldn't want to raii possibly uh if your release resource could fail or it could be a problem in that you can't deal with the failures not nicely anyway so that might be a case if somehow you can't map your resource lifetime to the C plus plus object lifetimes that might be another reason why you might not want to use you might not want to create an REI class but generally as a default you probably want to try finding a way to do REI precisely because the language guarantees that things will work properly it doesn't rely on the developer being excellent because of course none of us write bugs foreign doesn't happen and back it's just an extension or a uh I unfortunately did not see that talk so I don't think I can speak intelligently to that uh sorry the question was how is this different than how is this REI different than value semantics um I unfortunately didn't see that talk so I can't speak intelligently to that although somewhat related to that uh daniela's C underscore rapper I think it was called is an REI class it's just a little more sophisticated than what I showed and because you go to the sheet that encoded the acquisition and the destruction pieces in the template parameters which interesting anything else that is a type def well why use a using declaration versus a typedef okay don't quote me exactly uh but a using declaration can cover more cases that a type def can't as I recall correctly you can do a using declaration to partially specialize a template which you can't do with the typedef and then as a result for consistency sake doing all of your type aliasing with using becomes useful anything else if that's it thank you very much [Applause]
Get free YouTube transcripts with timestamps, translation, and download options.
Transcript content is sourced from YouTube's auto-generated captions or AI transcription. All video content belongs to the original creators. Terms of Service · DMCA Contact