KotlinConf 2017 - Deep Dive into Coroutines on JVM by Roman Elizarov

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hello everybody welcome back I hope you all enjoyed my my last talk where we just gave an overview of the cartoons and how we can use them and how they make your code easy and approachable and sequential and this talk will add a little bit of a deep dive into carotenes so my goal for this talk is going to be to dispel the feeling of magic you know we saw a great magic show last night and I I know this for some of you the examples that I've shown on my previous talk as also have had a feeling of magic in it you know how does it work how can you create hundred-thousand curtains on gvm so I will try my best to explain in the stuff how it works that will be my first part part and in the second part we'll just get back onto a higher level and I will show you some fun goodies and what you can do with curtains how do they change the style of your programs I don't worry if you get lost during the first part I mean it's my I will try my best to explain it I will not go into byte code I want to show like really complex stuff I will really try to explain it approachable way if you lose track somewhere don't worry I mean it's you don't have to understand exactly how curtains work in order to use them you can just use them but but I want you just to remember that there is no magic it's all just kampala transformations a regular code there's nothing magical about it it just just code so that's the key takeaway for the first part of my talk that you should be expecting so we'll start talking about this thing with this fancy name Keynesian pacing style CPS and in order to explain what CPS is will again take a look at our to a problem where we do certain steps and the code writing like this is called direct style code so what is the direct style direct style is when you have some action like request token that you want to get result from and you that you have to wait for and there is also some things that you can do after it so Africa token was requested you want to do something else is that something else is called continuation so your action and continuation an indirect style you just write it sequentially your right action your right is contagion immediately following it that is what called direct style it's supposed to it there is also continuation passing style writing this code where instead of just to write in action continuation you explicitly pass continuation as a parameter to your action so as you can see CPS is just a fancy theoretical name for callbacks so whenever somebody says how or have CPS you know they're just trying to sound smart I mean in fact is it just writing crappy code with callbacks you know that's the saltiest there's nothing more to eat just remember it next time you hear CPS CPS or anything if and so it just covers so of course if you follow call books to their extreme if you actually use them everywhere you get this nesting you get this comical we saw them so Carlton's collaborators from it they let us write a synchronous code in direct style and there's a key advantage of curtains so you don't have to be doing CPS yourself you can just write it in direct style and this is where Buicks tell is what we can understand is the code that we can simply read and immediately see what's going on so the question that you're still hammering there so how does it work so what is going on behind this and when or this suspending function and what happens is actually the cotton compiler takes functions like these that you marked with suspend keyword and behind the scenes it converts them in on GBM into functions that look similar to original ones but they get this additional parameter and this additional parameter is called conjugation and it is a callback and we all use or callbacks and appreciation passing style is the same thing and what is initiation in with cotton cartoons contingent is just an interface you know it's defined in calls in standard library and it that's that's all there is you know if you actually open there's nothing more than those things in it a plus documentation so it has a context and what is the context we'll get back to later in my talk so we'll leave it for now it also has a method to resume execution and it has method to resume with exceptions if you ever programmed with called X you know you saw something like this I mean in some code book interfaces there is unsuccess on failure methods sounds like this so call deck is just continues just a generic callback interface so again there's nothing fancy about continuations conditions is just a fancy name for a callback it just condition is a generic callbacks that any suspending function actually uses behind the scenes so behind the scenes whenever you invoke you suspend in function you're actually invoking function with a call that it just callback is implicit you don't see it in your code you code in this nice direct style and in fact you're programming with callbacks behind the scenes so let's take a closer look and how this translation happens how the code that we write indirect style is actually get translated to congestion basis now so we start with something like this direct code simple to follow it has an number of contagions inside of it first of all there is an initial continuation that's the whole execution of our suspending function there what happens when we invoke it then we have certain suspending actions we have requested and in it followed by is contagion we have created past and it followed by appreciation so we have a number of actions and conditions here so how do we convert it to initiation business style so you might think that something like this happens and indeed if you're right transform this code into CPS by hand that's what you'll get you'll just transform into callbacks and it will work in fact that is not what going on behind the scenes competitor so in cutting Kabbalah and other compilers that performed similar transformation do it in different way they do it first by identifying suspension points which correspond to actions the callback so the first label corresponds to initial continuation and others correspond to suspension points and continuation sexual beginning of the conjugations that follow them may be needed by those compiler actually transforms it to the code into this one big switch and I'm writing switch here to highlight this it's not a cult in code so I'm showing kind of Ceuta code you can actually express the directly in cotton so it's all good it's all happens when the code gets compiled into byte code but conceptually just big switch that on can jump into any any label any suspension point in the code in order to keep the state first thing that the function does is actually allocates an object to keep the current execution state the state of will store the current label where we're currently is accusing and remember that when suspending functions are compiled they acquire this additional conjugation parameter so we now show it explicitly inside of being hidden from outside so post item being suspending function when compiled gets continuation and you know every suspended invocation like you questo cannot create will has will have to accept continuations parameter and actually before we invoke any SS pending function like request token and you remember that requests are can is an asynchronous function it may suspend our execution and get result later and later we will have lost whatever state we were holding here so what what we do is we save it in this particular example the item reference that we had when the post item was invoked is not needed right now but we'll need it later when we have to create a post so this item is stored into the state option and also the state object we also store the label and the sexual not our label is the label of the next action we're going to take and then we pass our state machine as continuation to request talking so now we and remember continuation is just a callback so we invoke request all can't tell when when you finish please call us back use this object that were giving you as a callback so what happens then you know on our state machine object the resume function will get invoked at some point in the future when requester can complete when resume is invoked we provide implementation for it so when we create this object we should implement resume object and what we do we from this resume we invoke the post item function again so to get back to our code to our state machine code and in this case we actually pass reference to the state machine back to post item so it knows from what point to continue at execution where we have stopped the last time so the person in the actually does when it enters the execution it checks oh I'm past the instance of my own state machine okay that is where I should resume execution from so it should read the label from the state machine and will continue executing from the label world has left off so in this case it will start doing the next thing and then before doing the next thing to lose for all the safe state it will read the stored item it will read the result of previous operation there was the talking we have received and it will continue on with doing whatever code we have in our computation anything else until it reaches the next suspension point at the next suspension point it will again store whatever state it's needs to be stored there store the label and shoot off and that's how it works and so on so on every suspension we save the state invoke suspended function with a callback then when we get resumed we restore our state and continue so let's recap what is the difference between using state machines in convex when we code with state machines every time we suspend resume our object that would give the statement gets reused so only one object gets allocated when we invoke first item and this object to store this date is reused on every suspension we use it again again if we coat it with callbacks then every time we have to invoke a suspended function we have to create a new object for its callback so the one advantage of state machine is just few object created that's the reason actually that all the compilers that support a synchronous programming there is Ben C sharp JavaScript they actually do it through state machines because it's just more efficient we don't have to allocate new object for every for every suspending the vacations that we do the other reason is that with state machines it's easy to support complex constructs as groups you know conceptually what it looks conceptually it's I think that just jumps do does it go to the beginning from the end and when you have this label suspension points it's just easy to say oh I know that you know next time I have to go to label one again and so it's nicely translates to state machines too well if you writing this with callbacks and good luck implement in a loop I mean you can but I won't even show the code here it's it's a horrid mess that we'll hard to understand what's going on so that's it that that's how it goes behind the scenes I mean that's kind of conceptually what's going on and particular installation details may change and we're actually well curtains are evolving we're talking a little need this and this leads for this particular strategy but the concept is going to stay the same now let's talk about integration in my previous talk I've told you that if you have a future you just need to invoke away the extension on it but I haven't shown you how to implement those and I tell okay yeah you'll find elaborate to it unfortunately we have a zoo of futures on GBM literally zoo because you know the normal future that you can actually use first interest or had appeared only in Java 8 its completable future before generally there is no nothing in the standard Java libraries for a synchronous programming so the Java code systems is just loot literate with different implementation of futures there listenable futures in guava they're different every network library out there has its own callback or future interface forum which define their own futures a reactive libraries there's just literally dozens of them so chances are in your project you're using some some custom I know that every medium-sized company has its own futures library inside that's from my experience and of course if you want to use it with curtains you will need somehow write those awake functions for the Opera tickets so how do you do it so let's take a look let's take the example from my previous talk on the summer synchronous service that returns has a future in this case that's retrofit so it's returns a call we want to transform it into a suspending function in order to do this we need innovative function that will we'll wait suspend the execution until the call completes and return the result so we want the function with this kind of a signature it has to be suspended function where Marcos suspend it will suspend us it will be extension on our call that returns whatever is out there is that's what we want so how we implement it what our tools do we have it turns out that when you have a future any kind of future it's actually hides a call back inside so every kind of a synchronous future has a method to install a call back and for for example retrofit there's in queue that lets us it'll call back so actually colleagues everywhere futures are just glory for it call backs that actually just hide the fact that you can install call back from from you but the problem is we just in Valken to you you know in human being it's like adlistener set listener is having different futures like we call it a different way then you know then come then completed and I said I mean name said different all those functions they returned immediately and will invoke the call back later on but if we return immediately will have to return the result from this arrayed fashion right and we wanted to suspend so how we actually suspend our curtain in order to suspend the standard library in cotton provides a function call suspend curtain and that's exactly the building block that we're using to implement all those aways for all different kinds of futures let's take a closer look at what's at main curtain is suspect Berlin has this signature and it's a suspending function so when you invoke it you suspend but inside of it in a septa regular lumber lambda that doesn't have a sustained modifier so it's kind of a reverse separation to curtain builder so when you launch a curtain you have a regular function and you turn into suspending lambda and here you do a reverse separation you have a suspending function that has a regular function inside and this actually inspired by the operator that named call with current education in the Lisp FEM language called scheme so it basically does the same thing it will suspend execution of current code and will represent whatever follows this code as continuation object and pass it to the block of code inside of it so actually the initial per day we went called it you know suspend with current condition but then kind of looked at it you know it was to uncut lien name to have so and we ended up naming it suspend curtain which in fact represents what it does it just suspends execution of the curtain and gives you access to the continuation object so using it easier because when we suspend a curtain we now got a continuation and now we can install in Kolberg from a side of it with our curtains suspended and from inside the callback we would get invoke at some future moment today so in some future you know retrofit will say here is a response we just have to analyze the response and resume the carotene in the way we want it with the resume with the result if there is a result or we resume it with this exception if there is an error and that's all to eat I mean this is dozen lines of code in this case you need to write in order to adapt your favorite futures library to curtains or any other library that uses callbacks for this matter because remember down there it's all about callbacks every single library in its bubbles is built on callbacks I mean everything else being it futures or we need actual occurrences which is just called glorified callbacks so to make it easier we provide the number of out-of-the-box integration so in the cotton scardiest library we've already implemented those functions falls the most popular framers who implemented support for completable future for leasable fusion guava for project director types for reactive streams and you're welcome to contribute if you have if you know another popular library that we don't cover yet just going here then submit a pull request there is an integration Manuel is showing exactly how to do it what's to use example or to edit just edit the benefits you get is that as we will evolve our teens and add new features to it before the next release will be able to support your integration too and so with every update you'll get automatic update of the integration that you've contributed and when we finalize co-routines will also release updated a library with the final design and it will also out of the box contain your favorite integration so go ahead and if you're using some library that is not covered yet just just contribute the support for it so they think we've left out of this discussion is the context so we saw it yesterday and we saw today well we cursory scripted but have not when it did tell you it's funny then the original design of curtains that we had in cotton like a year ago did not have any kind of curtain context at all it just was conjugation interface there was just two functions resume resume with that's it there was no context and was no never mentioned InDesign but then we started playing with it you know writing sample applications see how we can use curtains and we stumbled upon this interesting problem and this problem concerns threads and most of the time you don't care but sometimes you do see it when I have this code and I VOC create post the question I can I am facing so what I have this continuation that processes the result the question is what thread it resumes on like where the process post gets invoked and the answer it depends depends on what it depends on exact implementation details of my create post function because if create bonds uses retrofit a synchronous call the reader feed will invoke this callback electron in one of its own thread and whatever threaded involves the callback will get forwarded as we see to resume and resume as we see is just yet passes control to our code that was transformed in state machine by compiler so whatever retrofit decides to use that's a thread we get here but what if we want to have it execute in UI thread because we want to update UI and all your framework often modern times are single threaded we can just update UI from an arbitrary thread so what we want if we want to do this we want to say please in UI thread and do this so how do we support it and when we're started thinking about this question that's where the the whole context thing had appeared in our design the way it works is that there isn't a phase called mutation receptor it's it's a part of the context and when we started design it we initially had just interceptor and then we figured out that we need to store some other curtain local things that might belong to curtain so we expanded this concept and now the context of the curtain is just a map of elements and each element has a key so the Interceptor is just one of those elements in the context as you can see from a signature it has a key object that you can use to treat the instance of interceptor from the context and what it does it's it has a function called intercept continuation which takes continuous parameter and returns context so basically equitation wrapper it lets us install custom wrappers that do with conventions whatever we want but what we do want to do we want to execute our code on the thread that we control so this is implemented in in a library by a class called dispatch continuation and it's employment it's implemented as a wrapper on top of contagion in very simple way every time its resume is invoked instead of resuming your code directly and immediately invoking your code it just submits the task to a separate threat that's it so instead so in normal code without the Interceptor invoke your resume passes control to whatever code you have in your car routine in this case invoking resume orders immediately but the task gets dispatched to corresponding thread usually that's UI thread or some thread pool so in Android you'll just you know use event loop to dispatch it and execution there so that's covers back part of the cartoons how we wait for them how we dispatch their execution on threads there is also the topic than we had in in the day before about starting curtains so we saw for example this kind of curtain builder called future we saw issues we did not see signature as every curtain builder is structured it's a regular function that returns something to us in the case for future builder of returns to complete all future and by convention we add an optional context parameter called the Builder so the invoker of this builder can specify what execution context it wants to use or just get with default it also accepts a suspending lambda inside that's kind of the defining structure of the curtain builders regular function returns something takes a suspended Appa but how do I write it what if I have my own future type not completely future not but smother company internal my might my company's future type how do I adapt it to curtain so we see so the other side right in a weight function for it but how do I write the curtain builder that returns my future the future that I want it's actually very straightforward the code starts with just creating the future we want to return and then we're using a function called start Croteam and start Croton is a jewel to suspend origin is what a standard library of cotton provides us so we can write all those curtain builders it's the most basic curtain builder out there that we would use and what parameters does it take its takes a continuation so we see our friend again here so when we start coding with we specify what to do when it completes and when it completes we have to do something else after that we have to do something that's doing something after you complete something is continuation so we specify what should I do when my curtain completes an implementing continuation is not hard you remember just three things you have to implement for continuation we have to provide the context and it's easy because we have a context right we we have a context as a parameter to our builder and we have to provide resume functions and for completable futures simple computable future in Java has completely complete exceptionally that we can just invoke to complete it again that's all it takes so integrating carotenes with any kind of future is really that trait for it it's just a page of code for any kind of future you might have so yes we have a zoo of futures but we can unify them all with suspending function just by providing integration we can have different kind of futures easily coexist in our ecosystem we can invoke functions as return or future because we can await on them and if some of our business method expects us to provide it with the future we can use a carton builder to convert whatever a synchronous code with we have into this type of a future so now let's get back to high-level stuff you see when we start writing a synchronization carotenes it starts to get it even though I've told you you know conceptual curtains are just lightweight threads so you might be tempted to start working with them just like you do with threads and we work with rest you are careful you taught that first expensive so you should not create them left and right so you create thread pools you reuse them but curtains are different which is a so lightweight they change the whole style of your programming with curtains it's okay if you have a request just create a separate curtain to handle it if you have a connection create separate or several curtains to handle it and when you start creating the amount of left and right you get into the problem my god but what if I don't need anymore if my connection closes I don't need to do this whatever I was doing or if you know something else happens user disconnects you know I need a way to cancel two more to those curtains that I that they want and will decide that the launched curtain builder returns a job in a job is exactly this kind of an object that lets you and then handle to a curtain that is you manipulate it so when I launch curtain I'll get a job back and I can join it I can wait for its completion that's one thing I can do but I can also cancel it if I don't want to wait for its completion anymore I'm not interested and it will look at what job it is will discover a bunch of things inside but the first thing we discover a local joke it's also an element of curtain context that's interesting it means then we are inside the curtain we can take it to constant context and retrieve its own job so that way we can learn what status it was it cancel it or not that is the same way we can retrieve other crouching context elements from a daisy job we can figure out what its Interceptor is or we can have our other elements in our contact because context is just a map you know and we're using the square brackets like we do with maps so we can define you know we want we can define our own country code in local variables just by defining the adding elements to carotene context using the job and being able to access it from a psychology we can do things like time outs so instead of tracking manually what our job is and when we need to cancel their is ready to use with time out primitives they just make sure that whatever happens inside gets cancelled our specified time out that makes writing typical request code super easy I just whenever I have a connection I launch code in that's okay here is it Ahmad if when it expires just cancel whatever was going inside what is constellation constellation accordion cooperative and in order to nationwide it has to be so one has to know the history how many of you actually know or heard of or actually looked at the method in Java in Java called thread not stop not many of you which is good but those who don't can actually take a look at the history and when Java 1.0 appeared first they thought it's funny but the when they implemented threads in Java they basically had this idea that they will be used like like we're trying to use courteous now it is that was the idea they will be running his first let you try to do different things which we know we cannot do because they're too heavy but that was their idea and they had they also thought but if I start the first answer why I need way to stop them so they they added this thread stop method was first stop was supposed to do it was supposed whatever code is executing is supposed to stop it and throw thread death exception at this particular moment of time and it were good for a while until they discover they did horrible Blake's any kind of code that you write because when the code expects that some data structure maintain certain invariant for example and it can throw thread this exception at any moment like it incremented Bunbury we'll forgot to increment the other rooms where this exception commands it just breaks everything so they ended up in as as far as remember soon as Joe 1.1 they've deprecated thread stop and it doesn't do anything right now it's still there deprecated but it's empty it doesn't do anything and the story tells us the fact that you cannot just just cancel something you have launched you know whatever is working there has to cooperate with cancellation it has to be Kinsella Belleek ansible at the points where it expects to be cancelable so if you have a code like while true do some computation you won't be able to cancel it because this code did not cooperate with you but if you write code like this and there is a special is active property that you can use inside proteins so to check whether I'm cancelled then it will be cancelled because as soon as the cancel request comes in you know exactly becomes false so this is canceled but canceled in predictable places you can also avoid invoke suspended functions like delay and all the suspending functions in the library that we have they're all cancelable if delay wait and you try to cancel this curtain it will just cancel aboard the they call curtain so the question you might be here how long but if I'm using standard suspending function this time but if I'm writing my own so what if I'm writing have my future and I'm writing a suspending function to wait for it how do I cancel it for this we have the library function called suspend cancelable curtain which does the same thing as curtain but makes it cancelable cancellation is not just aborting the work you also have to make sure to clean up resources up to you so in this case when we are for example a retrofit call and we will queue in a callback I mean the retrofit machine works behind the scene to make a call if the curtain that you know made it is canceled we don't want this call anymore we need to cancel - in order to support this suspend cancel balk routine actually gives you not a simple continue by the cancellable continuation and this can support contingent has a method that lets you install a handler that would be invoked on cancellation or other kinds of completion so what you doing in comic you say okay I'm a completion of this contagion when this actually means when it's cancelled for our purposes do this and this we just you know cancel whatever call we're doing so these cancel is just a method on the retrofit call that cancels right if you call so if my future is cancelable that is what I should do so my former cancel futures and interest to me on gdm almost all futures I saw are all canceled ball like this kind of common pattern adjourn so whenever if you are working with Julian Julian world completable future listen will future like all the future all calls all the futures enjoy when worlds are typically canceled there is some kind of a cancel method on them interested for example javascript world is not the case in javascript there is a promise it's not canceled there's big debate how to make it cancel but that's the just interesting piece of information so that's concludes our kind of you know technical dive and now some fun stuff so so some fun stuff for fun stuff we'll talk about other combination of letter CSP but slightly different order it's communication sequential processes and if you open Wikipedia and read what CSP is you won't get any insight at all it will service is the theory you know call process calculi blah blah blah blah blah zero practical use of reading this Wikipedia article in fact it's a style of problem you see with threads we program in a classical way when we're working with threat we typically have data that is shared between the shreds view the processes as it was called so when the fury was developed it was processes that worked on some shared mutable state but we all know painful you know nowadays that working with shared mutable did is tricky and we see something like this you know when we try to work with shared mutable state we really should not do it I mean when to really scale it it's okay we have few threads but when we start having lots of them that's that's really become a problematic so the other choice we have is to share data by communicating we don't have to Malaysia and that what is CSP is about it's communicating sequential processes me that there's no shared state for us it means it's just a style of programming where instead of heavy mutable state that we share we have a number of processes that constantly communicate between themselves passing the data back and forth and to enable this program in style that's where we need curtains because we need processes to be lightweight in order to enable this style because when you start program SSP we encounter they will always need to start some additional process to do our bidding etc etc it it's the the whole style encourages creating those processes less and right and that is what curtains let us do so they let us embrace this program is that we can still program out all the way but we don't have to anymore and you know the remaining time in order to kind of sweeten you interest to this I'll just show you a very simple example of how this works so here is an example it starts with starting curtain in the main thread so we can use the suspending function and then we create a channel a name channel and name channels are the bread and butter of CSP CSP is all about processes that use named channels makers and channels and CSP are objects that can be sent on the data in this case the channel will wear simple we'll just transfer integers and then in CSV what we do all we do is just start processes so in cotton we use lunch to start the process and it uses a trick that we haven't seen before it specifies the context of the part coaching as its own context this lets us create a child protein it is very important in CSP world because since CSP world you create so many cookies that's hard to keep track of which countries I created make sure I waited for them before I complete my own job make sure if they crash you know this the signal gets propagated to me too so this parent-child relationship solves all of those concerns for us automatically so we don't have to keep track of which Guardians will launch because we just launched them as our children and inside the coding there is just code and this code is just sequential that's where in sequential and CSP comes from because we have lots of processes they're all concurrent but they're sequential on-site their logic logic is easy to understand we can read them and normal direct style and in this code what we can send data over channel in this case we're just sending integer from zero to nine and we can close the channel you should basically sends a special close talking over this channel that's the channel's over and the other coaching we have simply reads from this channel is reading from a channel as simply as using for loophole as this channel so just loop over content section which will automatically read everything that was sent to it until it gets closed I actually do have time for demo so let me let me try to show you how this works let me see if I can flip oh yeah do you see it yeah what's oops what's going on I see it on my screen I don't see anything you on on your screen oops oops no sorry I want demo fails because I don't know how to switch to it so let me let me believe me that this code works okay because we're running out of time so I won't be spending my time trying to show how this works but it works just printing the numbers as you would expect and also because we're launching all the curtains as launching all the curtains as the children we automatically get get the effect that the code will wait until all of them complete and only return after all the numbers printed on the screen how long here is my demo okay so our next topic that we'll cover in the remaining minutes our actors adverse is another variant of CSP the difference between a crystal programming and CSP is that in CSP we use named channels and all the processes that communicate our usual and anonymous so we launch different process we wouldn't keep reference to they're all anonymous in actors which really have a similar concept because they also encourage you not to have a shared mutable state they use instead names the curtains but make channels anonymous and attach to them so an actor is actually is just a peer of a named curtain and it's in box channel and there's nothing more trade it's all it's all different ways to program both CSP and actors are different ways to program without shared mutable they state by by communicating between the curtains in our same example with actors will look like this so instead of creating a channel explicitly will use the actor that bundles curtain and it's channel together in one object that will give a name to instead of naming a channel and inside this object because it has its own channel we can just receive from this channel using for loop and print for example everything that comes in and the other protein that we start and you know it's all sequential - we'll just send instead of sending to channel it will send to the sector and that's all about it I mean the code does exactly the same it's just a different ways to approach the problem and in fact when you architecting like a complex system you'll find that sometimes you want the name Channel or sometimes you want an actor it depends on the kind of problem you're solving but if you really want your system to scale you know and you don't want to mess with shared middle say think about adopting one of those CSP styles instead think about communicating your data across the curtain boundaries instead of sharing it as some single place so he want to learn more I suggest you to read guide to cotton excursions by example it contains lots of information including some things examples I've heard here and lots of things I did not have time to cover now you're welcome to contribute both to the library and additional examples there it's all open source on github thank you very much [Applause]
Info
Channel: JetBrainsTV
Views: 51,466
Rating: undefined out of 5
Keywords: KotlinConf
Id: YrrUCSi72E8
Channel Id: undefined
Length: 45min 18sec (2718 seconds)
Published: Wed Nov 15 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.