 Wow, the room is full, just the beginning. That's great. So good morning. We'll start with our first talk. Anil is coming from India, and he will introduce the jam-concurrent Ruby. He's a freelancer. He's programming in different languages, Java, Node.js, but also Ruby. He started recently. Alex here is very interested into concurrency issues, and that's why he started to dig into the concurrent Ruby jam. And I think he wants to share with you his discoveries and understanding. And so please welcome him. Hello. Good morning. So yeah, I'm going to talk about concurrent Ruby modern tools. So basically, I want to understand like, have you done like Ruby threading or anything like that? So if yes, can you raise hands? Okay. So quite a few guys. Okay. So, all right. So am I audible? Okay. Okay. So I'm Anil. I'm from India. I started freelancing since November. I'm interested in understanding like concurrency of various languages, concurrency paradigms, and I'm trying to use concurrency abstractions from these languages into the applications. So my talk is about, I'm going to give like, overview of concurrency models and their comparison, then a little bit theory about concurrency models, then what is concurrent Ruby library, then general purpose concurrency abstractions in concurrent Ruby library, third safe variables, third safe value objects, structures and collections, then third safe variables, third pools, then third synchronization classes and algorithms and each features of concurrent Ruby library. So concurrency models. So in different languages, we have different concurrency models. So these are main concurrency models that we know. First is threads and mute access. So Java, Ruby has support of threads and also has mute access. So this is very low level concurrency model that we use to write our programs concurrently. Then next is software transactional memory. This is not very common, but languages like Clojure has adopted software transactional memory. Then actors. So Erlang has been doing actors for long time and language like Elixir is written on top of Erlang is using actor model. Then we have event concurrency, which is followed by Node.js or event machine in Ruby. Then core routines, Python has core routines, which is one of the way to do concurrency and we have CSP, which is also very old style concurrency. The paper was written in 20 years back and followed by languages like Golang and we have processes and IPC. So we can run our programs as processes and still achieve concurrency. So this is table, which kind of compares different concurrency models. So we have mute access, software transactional memory, processes and IPC, CSP actors, features and promises, core routine event. So for mute access, the execution model says that scheduling is preemptive scheduling. So what is preemptive scheduling? Preemptive scheduling is if active process or task or thread can be temporarily suspended to execute more important process or task or thread. It's preemptive scheduling. Non-preemptive scheduling is if the active process or task or thread can not be suspended, it's run to completion, then it's non-preemptive scheduling. And cooperative scheduling is when currently running process voluntarily gives up executing to allow another process to run. So there are three types of scheduling. So basically threads uses mute access and it follows preemptive scheduling, then STM follows preemptive scheduling, CSP actors also follow preemptive scheduling, then we have features and promises which follows cooperative scheduling. So communication is shared memory in threads or mute access, STM again is shared memory, process and IPC shared memory, then CSP it's by message passing, actors it's also by message passing, features and promises it's also by message passing. So we have different implementations. So like first we have mute access in languages like Java, STM is implemented by Clojure, then we have like processes and IPC, we have like rescue workers. So like we create many different workers processes. So that's like processes and IPC. So let's see first kind of concurrency which is like threads. So issue with threads is that we share memory between multiple threads. So that's the evil like they say shared mutability is the root of all evil. So because of threading and shared mutability we get issues like deadlocks and disk conditions. So our deadlocks and disk conditions we have mute access and locks or synchronization techniques and this is very low level way of doing it. So there are lots of issues. So pros are no scheduling needed by a program because it's preemptive scheduling. Operating system does it for you. So in operating system like Windows or Linux when we do threads, so OS keeps switching context. So like it's a preemptive scheduling. So we don't have to worry about that. Then it's most commonly used. Cons are context switching. So operating system keeps switching the context and because of that it has to store the thread data and so that's scheduling overhead. Then there are deadlocks and disk conditions because of this and then synchronization and locking issues arise. So there is a law called Amdell's law. It's used to predict the theoretical maximum speed up for a program. So basically there is cost in creating a thread and switching context between threads. Too many threads is bad. So Amdell's law defines speed up. So if we have a program which is not, can't be paralyzed if it has sequential part. So even if you have multicore processors, still we can't achieve the speed up. So you can see if there are number of processors and that's the speed up. So even if you increase the number of processors, that program speed up doesn't go up because the parallel portion is not very high. So we have to have more parallel portion so that we can achieve better speed up. So this is not achieving any speed up because parallel portion is just 50% and it can just utilize. So speed up is not going high. So parallel portion of the program defines the speed up. So software transactional memory as per Wikipedia, so it's different than threads. So in software transactional memory, like the highlighted part verifies that other threads have not concurrently made changes. So if there are multiple threads, STM verifies that other thread has not concurrently made any changes. If it has made any change, then it doesn't write. So let's see a small code, pseudo code. So here it's STM. So thread one has read a variable and then incremented a variable and written a variable. At the same time, if thread two goes and tries to modify the variable and if it sees that thread one has incremented the variable, it doesn't like it just rolls back and it retries. So in STM, the concurrency is achieved in a different manner where it has different way of coordinating the multiple concurrent threads. Next is actor model. So it's a mathematical model of concurrent computation. The actor model treats actors as universal primitives of concurrent digital computation. Actors communicate with messages. In response to messages that it receives, actor can make local decisions. So airline follows actor model and so actor model was, the paper was published in 1973 by Carl Helmut Peter Bishop and Richard Stieger. But airline accidentally implemented actor model. So airline kind of does it. It's very old way of doing concurrency and also there was paper called CSP, Communicating Sequential Processes. It was written by Tony Ho in 1978. So what is CSP? CSP is a way of coordinating multiple processes or threads. It's a way of doing concurrency. There are processes and there are channels. So there are no threads. There is no shared memory and fixed number of processes are there and the processes communicate with channels. So languages like Go and Limbo implement CSP. So in Go we have GoRoutines which are processes and channels. Like GoRoutines talk with each other with the help of channels. So CSP is kind of, so the style, the message passing style of programming is also followed by airline. It's not, airline doesn't follow CSP but the style of message passing is done by airline and also languages like OCaml, Go, Clojure does that with channels. So pros of community creating sequential processes is that users message passing and channels heavily, alternative to locks. We don't have to do synchronization or locks. Cons is that handling very big messages. So messages are big. Instead of sharing memory where we have shared memory, now we are sharing big messages and because of lots of messages, unbounded buffers are there. So that's bad and messaging is essentially a copy of shared data. So actor model versus CSP. So in CSP send and receive may block. So in actor model only receive blocks. So also CSP synchronous, actor is message passing. CSP works on single machine, actor works on multiple machines, lacks fault tolerance in CSP. In actor model there is fault tolerance. So this is like many comparison of actor model and CSP. So let's move to Ruby threads. So Ruby has threads for doing concurrency. So we use thread.new. That's basic abstraction that we used to create multiple threads. And because of threads, there is high chance that we have deadlocks and disk conditions because there is shared memory. And Ruby has global VM lock or it's called global interpreter lock. So what is GVL? So GVL is a lock. So what happens with GVL? With GVL only one thread executes a time. Thread must request a lock. If lock is available it is acquired. If not the thread blocks and waits for the lock to become available. Ruby's runtime guarantees thread safety but it makes no guarantees about your code. So blocking or long running operations happens outside of GVL. So any blocking operations or long running applications which is which is not CPU bounded happens outside of GVL. So you can still write performance concurrent code like in Java or in Node.js. If your application is just doing IO, network IO or file IO, Ruby is still performing. It gives the same performance as the other languages. But for CPU request GVL is the issue because one thread has to get the GVL and then only that thread executes. So in Ruby, Ruby is Ruby can't utilize your all course of your machine because GVL allows only one thread to execute per code. But Ruby is fast enough for IO on heavy applications like network and file or database. So why GVL? So GVL makes developers life easier. So Mads says like he can't trim GVL because without GVL you will have race conditions because there are C extensions. It makes C extensions developer development easier. Most C libraries are not thread safe. Parts of Ruby's implementation are not thread safe. So Ruby lacks better concurrency abstractions. Java has utility lot, Java dot utility lot concurrent but Ruby doesn't have something like that. Ruby didn't have actor model or stm or better concurrency abstractions. Ruby has concurrent gem now. So there is package called Java dot utility lot concurrent in Java that has high level abstraction to do concurrency. With concurrent Ruby, we get that now. So what is concurrent Ruby? It's modern concurrency tools for Ruby. It's inspired from airline closures, Scala, Haskell, C-Sharp, Java and classic concurrency patterns. Design goals is be an unopened tool box that provides useful utilities without debating which is better and why. Stay true to the spirit of languages, powering inspiration. Keep the semantics as idiomatic Ruby as possible. Then support features that make sense in Ruby. Exit features that don't make sense in Ruby. Be small, lean and loosely coupled. Also, concurrent Ruby is supported by MRI. So concurrent Ruby works on MRI, JRuby 1.7x in 1.9 mode, then JRuby 9000, then Rubinius 2.x is also supported. So thread safety, so it has strongest thread, sub-t guarantees. It also has published memory model. So Ruby language doesn't have any published memory model, but concurrent Ruby has their own published memory model. So every abstraction in concurrent Ruby is thread safe. Similarly, all are deadlock free. Many are full lock free. Specific thread safety guarantees are documented with this abstraction. Then Ruby is a language of mutable references. So no concurrency library for Ruby can prevent the user from making the thread safety mistakes. All library can do is provide a safe abstraction which encourages safe practices. So concurrent Ruby provides more safe concurrency abstraction than any other Ruby library. So it follows the mantra of do not communicate by sharing memory, instead share memory by communicating. So only Ruby library which provides a full set of threads of mutable variable types and structures. So let's see like some concurrency abstractions in concurrent Ruby. So these are four, concurrent async, concurrent future. These are five, concurrent future, concurrent promise, concurrent scheduler task, concurrent timer task. So this is the example of concurrent async. In a model, if you include the concurrent async, in a class if you include concurrent async model, then this class becomes async, this class gets async behavior. And this turns into a simple actor. So this async and await are inspired from Erlang Gen server. So now you can call the method eco with like horn dot async dot eco. So this is non-blocking thread shapes. It returns an eyewear. Eyewear is a type of class object in a pending state. And await actually is synchronous. Waits till it gets result and it's also thread shape. So there are two ways to do call the methods. So that was one of the concurrency abstraction. Second is concurrent future. Future represents a promise to complete an action at some time in future. The action is atomic and permanent. So future, in a typical application, we need to do some tasks which happen in background, which doesn't execute in current. So we can use future. So this is one of the example, a ticker class, which fetches something from web, like a tick mark for from Yahoo final site. So this is the network call. So we can use future to do this in parallel in another thread. So by default state is pending, but after some time you get the value. So this task happens in background. So this is filled future where value is like exception. So we can see Rails snippet. So Rails is using future. So this is like action cables, client test class. So we can see this abstraction is being used by Rails framework. So next is promise. So promise is similar to futures, but it's far more robust. Can be changed in a tree structure with each promise. So it may have zero or more children. So this is example of chaining. So similar to future, it does something in background and we get result after some time, like after some delay we get result. So promise gets fulfilled after some time. So this is example of code snippet in Rails source code where they are using promises. So next is concurrent schedule task. So schedule task is it's similar to future, but we can schedule it to execute after some delay. And this is similar example, but we can pass the delay execute and then the two is nothing but the delay that we pass it so that it gets executed after some time. So this is error case where if there is an error we get schedule task with an error. So this is Rails source code using schedule task. So next is timer task. So it's a common concurrency pattern to perform task at regular intervals. Can be done with threads, but an exception can cause thread to abnormally end. But when timer task is launched it starts a thread for monitoring the execution interval. The timer task thread does not perform the task, however instead the timer task launches the task on another thread. So we can add observer class. So this is just observer class for timer class. And then when the timer gets executed with the default or set execution interval and timeout interval, we get the class executed after some time. So this is one of the Rails source example which is using timer task. So next concurrency abstraction is threads of value objects and structures and collection. So we have array, hash, concurrent map, concurrent tuple. So concurrent array and concurrent hash is a third step surplus of like one is from array and one is from hash. It locks against the object itself for every method call and ensures only one method can be reading or writing at a time. Concurrent map is hash like object, have much better performance characteristics especially under high concurrency than concurrent hash, not strictly, semantically equivalent to a Ruby hash. So this is example of concurrent hash in Rails source code. So Rails is using that in one of the active supports class. So this is concurrent map. Active supports time zone class is using concurrent map. So value objects inspired by other languages. So concurrent maybe and concurrent delay are the value objects which are inspired from other languages. So concurrent maybe is third step immutable maybe which encapsulates an optional value, maybe either contains a value or it is empty. Concurrent delay is resolution of a block yielding an immutable result. These are useful for doing expensive operations that may never be needed. So next concurrency abstractions are like structure classes developed from Ruby struct. So we have immutable struct, mutable struct and setable struct. So these are developed from Ruby struct and these are third step. So immutable struct can be set only once. Mutable struct can be set at any time during the object lifecycle and setable struct can be set at its initialization or with the setter method. But once it is set, you cannot reset it again. It will throw immutable error, concurrent immutably error. So third step variables, we have concurrent agent, concurrent atom, concurrent atomic Boolean, concurrent atomic fixedum, concurrent atomic reference. Also we have concurrent exchanger mware, thread, local wire, pware. So agent is inspired by Clojure's agent function. Agent is shared mutable variable providing independent uncoordinated synchronous change of individual values. Based use when the value will undergo frequent complex updates, suitable when the result of an update does not need to be known immediately. Agent action dispatches are made using the various same methods. So basically agent is inspired from Clojure and it does, it is a variable where we can do operations on it and it sets f. So we can get, and it is also asynchronous. So we have one function called next Fibonacci and then we created the agent variable. Then we five times call the agent variable which gets updated and after when we call await we get the value. It is asynchronous, but with await we get the result computed and with value we get value out. Similar to that there is atom. Atoms forward way to manage shared synchronous independent state. At any time the value of an atom can be synchronously unsafely changed. Suitable when the result of an update must be known immediately. So there are two ways to change the value of an atom. There is compare and set and swap. So it is inspired from Clojure and it is synchronous. Unlike agent atom we can call atom and we can immediately get value and it internally retries like if there are two threads which are using atom. If one thread like modifies one of the atom the another thread will retry and get the result out like it is synchronous. So next we have atomic thread safe variables. We have atomic Boolean, atomic fixnum, atomic reference. So reads and writes are thread safe and guaranteed to succeed. Reads and writes may block briefly, but no explicit blocking is required. So this is the real source code example using atomic fixnum. This is atomic Boolean used by rails in active support. So this next abstraction is very interesting concurrent exchanger. It is a synchronization point at which threads can pair and swap elements objects within pairs based on Java's exchanger. So if there are two threads want to exchange object they can use exchanger and the object is safely exchanged. So this is one thread this is another thread and then they can swap the objects. So this is code snippet basically the third one and third two. Then third two is calling passing bar and third one is calling foods but we can see objects exchange there. So next is threads of wires. We have mware, thread local wire and tware. So mware is a synchronous single element container taking a value from mware blocks as does putting a value into a full one like blocking Q of length one. So mware is like a blocking Q. So if you know blocking Q so it is like that and tware is a transactional variable and thread local wire is a variable where the value is different for each thread. So we can initialize tware and then for each thread we can have different values for thread local wire. Tware as I said it is a transactional variables and it can be used in bulk like if there are like multiple twares we can use it in a single transaction. So it will make sure that all twares are properly used and properly updated. So we have thread pools. So we have fixed thread pool, cashed thread pool, thread pool executor, immediate executor, serialized execution, single thread pool executor. So thread pool is nothing but a way to create multiple threads. So we reuse threads. Instead of creating threads we use thread pool. So it is high level abstraction to use threads and there is thread pool executor and then there is immediate executor. So these are examples from Rails application which are using thread pools. And there are different behaviors of each thread pool. So some immediately executes your task, some has fixed size thread pool like only four threads let's say in a pool. Then they reuse the same thread to perform the task. Then there is cashed thread pool, then single thread executor. So next set of concurrency abstraction is thread synchronization classes and algorithms. So we have countdown latch, psychic barrier, event, eyewear, retrite lock, re-entrant, retrite lock, semaphore. So countdown latch. So this is also interesting. So if there are two threads or more than two threads who wants to do like who want to synchronize to wait on each other. So we can use countdown latch. The thread will wait. So if there is one thread which waits, creates a countdown latch and set the initial value normally equal to the number of other threads. The initiating thread passes the latch to other threads, then waits for other threads by calling the wait method. When the latch counter reaches zero the waiting thread is unlocked and continues with its work. Countdown latch can be used only once its values cannot be reset. So this is example of countdown latch where we created a waiter, then this is decrementer which decrements the latch. So as we have created a countdown latch of three, we have to decrement it three times. Only when decremented decrements the latch, waiter thread gets released till it waits and stops at wait. So one of the snippet of rails is using countdown latch. So we have cyclic barrier. So two threads can, two or more threads can wait for cyclic barrier. So if they reach at first cyclic barrier they continue. So all threads stop at barrier. So a synchronization aid that allows a set of threads to wait for each other to reach a common barrier point. So this is example of cyclic barrier. So we have created a barrier then there are random slip times and I have created multiple threads. So with different slip times but all threads wait at barrier. Even the slip for any time they will wait at barrier one and then they will wait at barrier two. So we can see like three threads executed at the same time again second time they executed at the same time. So they waited at the barrier and then once the barrier reached they all started executing. So this is rail source code example which is using cyclic barrier. So my school transaction test is using it. So concurrent event. So it's old school kernel style event and again like two-thirds can use this concurrent event like once the event gets executed or the other thread will get to know and then other thread will execute till that time other thread will wait. So this is rail's source code which is using concurrent event. So we have eyewear. Eyewear is like a future that you can assign. As a future is a value that is being computed that you can wait on and eyewear is a value that is waiting to be assigned that you can wait on. Eyewear are single assignment and deterministic. So this is very simple example. You can't set eyewear multiple times. If you try to set it it will throw an error. So next is read write lock allows any number of concurrent readers but only one concurrent writer. If the write lock is taken any readers who come along will have to wait. Then we have reentrant read write lock. Any number of concurrent readers allows any number of concurrent readers but only one concurrent writer. While the write lock is taken no read locks can be obtained either. Hence the write lock can also be called an execute exclusive lock. If another thread has taken a read lock any thread which wants a write lock will block until all the readers release their locks. So we saw reentrant read write lock. So next abstraction is concurrent semaphore. Most of us know semaphore. So it's counting semaphore. It maintains a setup permits. Each aquire blocks if necessary until a permit is available and then takes it. Each release adds a permit potentially releasing a blocking aquirer. So basically we create a semaphore with some count and then threads acquire that semaphore and then if like the count is reached then until another thread releases that semaphore any other thread can't acquire the semaphore. So it keeps waiting for the semaphore. So it's another way another way to lock the threads. So rail source code is using concurrent semaphore. So this is the action cable client test example. So we saw schedule task timer task events. So these are available in concurrent Ruby gem but there is a concurrent Ruby edge library. It's part of concurrent Ruby but these features you have to require library with concurrent edge. It sort of require concurrent. If you require concurrent edge you get these edge features. These are not fully complete but some of those like promises which is almost stable is going to be merged in the concurrent stable version. But some of the abstractions you saw are going to get replaced with this edge features. So we have new promises frameworks. We have actor implementation. We have channels. We have laser register, atomic markable reference, lock free links set, lock free stack. So basically new promises framework unifies concurrent future, promise, Ivar, event, data flow, delay, timer task. So it exclusively uses the new synchronization layer to make all threads lock free. And with new promises frameworks we can combine different abstractions together and those will work and promises can branch. So we'll let's see. So this is one of the example of new concurrent promises where like we saw future, it's the new way of doing future with concurrent promises. This is error case. If you get error we get the value and reason. Then we can do chaining. Obviously we used to do chaining but with new promises we can do chaining. Then we can do branching. We can do branching and zipping. So if you have worked with JavaScript, there is JavaScript promises. So concurrent promises is having similar API but it's just not just the implementation of another promises library. It has minimum features. So error handling. So we can do error handling with new promises framework. Then we can do error handling with rescue. So if there is an error, rescue will get executed. And if there is no error, rescue doesn't get executed. So then we can use chaining. Then we have error handling. So zip is rejected if any of the zip future is. So this is doing zip. So delayed future. So we can create delays, delay tasks with new promises. Then sometimes it is needed to wait on our inner future. So we can, if you call value it blocks. So it's not good way to use dot value. So we can use flattening. So if there are promises of promises like combined promises, then we can use flat. With flat we get the value out without blocking. Then we have scheduling like schedule tasks. We can schedule like that with timing passed. Then we can use chaining as well. We can schedule and then we can chain. Then we can use time object in scheduling instead of just count. We can actually use time dot now and then pass the time plus, time dot now plus 10. Then we have concurrent actor. So promises we have. We also have concurrent actor, which is pretty stable but I didn't find much documentation. I am trying to use it for one of my side project. So Erlang has actors. So now with concurrent actor we can create actors in Ruby. And it's lightweighted running processes which run on thread pool. It's inspired by Erlang and Akka. And it's also modular. So this is example of actor. So we create actor context. So this counter is actor class and on message is with act on message. So actor has inbox and when we receive message we process that message. And then so here we can create a named actor and then we call the message to it and then we see what is the value of the actor. Next is concurrent channel. So concurrent channel as told by Petr is not very stable but we have that it replicates Go's behavior like Golang has channel. So concurrent Ruby Git repository has all the examples of tour of Go and Go by example replicated in Ruby. So it's not stable but we can use those APIs. So it can be called experimental. We have that. So hope somebody contributes and makes it stable. So we can use channels. So this is default selection. So this is a buffer channel. Then this is default selection. And next is we have laser register. So laser register is another concept section. It's a hash like collection that stores lazy evaluated values. So we saw channels, we saw actors, we saw promises. So these are new. And so concurrent Ruby library is used by SideKick, SuckerPunch and Rails. So there are many libraries which are dependent on concurrent Ruby. So just now I discussed with Petr and he says like 80% of the Ruby gems are transitive dependencies of concurrent Ruby. So there is like somewhere they depend on some gem and that it depends on concurrent Ruby. So SideKick uses concurrent Ruby, SuckerPunch uses concurrent Ruby, Rails uses concurrent Ruby and hopefully many other libraries start using it instead of like previously Rails used to use threads like used to add sleep. So that was doing a bad way of concurrency. But now with concurrent Ruby we can do better concurrency without bugs and a better way of handling concurrency issues. So concurrent Ruby is maintenance or like Petr who is here, then Jerry, then Chris, also Charles, Oliver Nutter, he is here. So his thread safe gem and atomic gem is merged back to concurrent Ruby. So I see these contributors contributing to this library, but I hope many contributors joins and we make this library better so that we will have better concurrency abstractions in Ruby. Then so use concurrent Ruby, use high level abstractions to write concurrent code, choose from different options such as actor, channel, promises, combine them. So the good part is that we can combine some of those abstractions. We can stick to one of the abstractions like but we can actually combine these abstractions in application. Also build maintainable software. So if you write threads, if you do concurrency with threads, there is a high chance you write like a bug written code. But with concurrent Ruby your code is mostly going to be mostly going to be safe thread safe. So yeah, thanks.