CSC/ECE 517 Spring 2015/ch1a 14 RI: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 73: Line 73:
Now, we can stop the rails server and run it again, but this time in the production environment (where we enabled threadsafe configuration option): rails s -e production
Now, we can stop the rails server and run it again, but this time in the production environment (where we enabled threadsafe configuration option): rails s -e production


Once we run the 5 curl request command as in previous scenario (<pre>repeat 5 (curl http://localhost:3000/foo/bar &)</pre>), we will see the "foobar" strings being outputted nearly simultaneously. Since there is no 1s delay between the "foobar" outputs, which is a part of the bar method definition, it becomes clear that all 5 requests are being processed synchronously, hence proving the functionality of multithreading.
Once we run the 5 curl request command as in previous scenario:
<pre>repeat 5 (curl http://localhost:3000/foo/bar &)</pre>  
we will see the "foobar" strings being outputted nearly simultaneously. Since there is no 1s delay between the "foobar" outputs, which is a part of the bar method definition, it becomes clear that all 5 requests are being processed synchronously, hence proving the functionality of multithreading.


=== 2. Memory access ===  
=== 2. Memory access ===  

Revision as of 04:39, 7 February 2015

Thread Safety

A multi-threaded program presents the risk of race conditions, which are situations in which multiple threads rely on the same system state. This is a dangerous situation because if multiple threads access the same state simultaneously, the state could become corrupted. Thread safety avoids race conditions by guaranteeing that multiple threads can run safely, concurrently.

Background

A thread is a small set of instructions that can be handled independently by the scheduler. Multithreading is the concept of allowing multiple threads within the execution of a single process, which allows those threads to share resources. Multithreading is a useful and powerful construct within web-server environments, such as rails. For example, multiple threads could be used to handle requests from multiple users simultaneously, while also handling long running tasks in the background. As a result of multithreading, users have a better experience, because tasks that would pause execution in a single threaded application, can be allotted their own thread, and the user is never interrupted. Although having multiple threads is powerful, it is equally dangerous, due to race conditions. Race conditions occur when multiple threads are accessing the same state, most specifically the same data, and thus the output depends on the correct order of operations, but the threads execute out of order, causing the data to be corrupted. Thus, thread safety ensures that when multiple threads alter shared components, they do so in a guaranteed safe manner, therefore there will be no race conditions within a thread safe application.

Race Condition Example

The following is example of code that, if included in a non thread safe context, could lead to race conditions:

1: def raceCondition (param, check)
2:   if param.eql?(check)
3:      param += 2
4:   end
5: end

Line 1 is simply defining the method and taking in two parameters: param and check. Line 2 then checks to see if param is equal to check, if so in Line 3 2 is added to param. This method introduces a possible race condition because of the read, then update nature. If multiple threads were running simultaneously and in between Thread A running Line 2 and Line 3, Thread B changes the value of param, then param will not receive the anticipated value (check + 2) at Line 3 of Thread A. In a critical environment, a race condition like this could have serious negative effects on a production database, which is why having thread safe applications is of utmost importance.

History

Multithreading is not a new concept to Rails. Previous versions of Rails were capable of supporting multithreading in the applications very effectively. In the previous versions of Rails, once an application template was created, a programmer could find a method named "threadsafe!" in the configurations and enable it for multithreaded applications. This option used to be located in the production.rb file located on the generic path config/environments/production.rb. Therefore, this option for multithreading was automatically disabled (commented out in the configuration file) in the previous versions of Rails and the programmers always had to manually enable it to make their application thread-safe.

Manually Enabling Multithreading

In order to manually enable multitreading, in versions of Ralis prior to 4.0, the following source code of threadsafe! methodsimply needed to be un-commented out:

1: def threadsafe!
2:   @preload_frameworks = true
3:   @cache_classes      = true
4:   @dependency_loading = false
5:   @allow_concurrency  = true
6:   self
7: end

We can see that threadsafe method consists of only 4 options. The first option @preload_frameworks describes how the application is loaded and ensure that the entire framework is preloaded instead of relying on auto-loading behavior which is not thread-safe. The second option @cache_classes tells Rails not to automatically reload classes when they are modified so to not waste resources. The third option @dependency_loading prevents Rails from loading code when missing constants are discovered, which is not thread safe. The last option is set to true and it tells Rails to not use Rack::Lock middleware, which ensures that only one thread executes at a time (synchronization). The Rack::Lock middleware is how previous versions of Rails guaranteed thread safety, due to synchronization.

Examples threadsafe use

Now that Rails 4.0 transitioned away from the use of threadsafe! method, it is still beneficial to look back at the earlier versions of Rails and learn how threadsafe method was used in practice. This is beneficial because it gives an insight into the functionality of multithreading to the students that are new to the concept of multithreading. Since students learn the best by observing and studying practical examples of learning concepts, two examples of threadsafe use is explained in the next two subsections.

1. Speed of execution

Whether the threadsafe configuration option has been enabled or not can easily be established by simply observing the execution flow of a program. If there is a noticeable lag between the instructions that should be executed simultaneously, we will have a clue that multithreading has not been achieved in the program. To further explain this approach, we provide a practical example.

We can enable threadsafe configuration option in production mode and then create a controller with a name FooController:

1: class FooController < ApplicationController
2:   def bar
3:     sleep 1
4:     render text: "foobar\n"
5:   end
6: end

We can now start the rails server in development mode (note that threadsafe configuration option has been enabled only in the production mode). Then, in a separate tab, we can create a curl request to the foo's application bar action, repeat it 5 times and use ampersand (&) to fork the processes and have them all triggered simultaneously:

repeat 5 (curl http://localhost:3000/foo/bar &)

The result: We will get 5 "foobar" strings printed to the screen, but they will be printed one at a time with 1 second delay between each (as a result of "sleep 1" instruction in the bar method definition). Therefore, the 5 requests are being processed separately even though they were all made at the same time. This tells us that the processes are not executed simultaneously and that multithreading is not implemented.

Now, we can stop the rails server and run it again, but this time in the production environment (where we enabled threadsafe configuration option): rails s -e production

Once we run the 5 curl request command as in previous scenario:

repeat 5 (curl http://localhost:3000/foo/bar &)

we will see the "foobar" strings being outputted nearly simultaneously. Since there is no 1s delay between the "foobar" outputs, which is a part of the bar method definition, it becomes clear that all 5 requests are being processed synchronously, hence proving the functionality of multithreading.

2. Memory access

Still not convinced that enabling the threadsafe configuration option effectively introduces multithreading into a rails application? Lets prove this point again, but on a slightly different approach. In this example, we show the presence of multithreading by observing the state of a memory location and the value it holds during the program execution. Lets edit our FooController from the previous example:

1:  class FooController < ApplicationController
2:    @@counter = 0
3: 
4:    def bar
5:      counter = @@counter
6:      sleep 1
7:      counter += 1
8:      @@counter = counter
9:      render text: "#{@@counter}\n"
10:   end
11: end

In this modified FooController, we initialized a class variable counter to 0 and with each bar method execution, this variable is read and stored in a local counter variable. It is then incremented and the new value is stored back in the class variable counter. Finally, at the end of bar method, the value of class variable counter is displayed.

Again, we will first start the server in the development mode and observe the behavior of this controller in the single-threaded environment. Once we run the 5 curl requests as in the previous example (repeat 5 (curl http://localhost:3000/foo/bar &)), the result will be

1
2
3
4
5

printed with 1 second delay in between. Now, we will stop the rails server and run it again, but this time in the production environment, where we enabled the threadsafe configuration option. This time, when we run the 5 curl requests, the result will be:

1
1
1
1
1

and all the lines will be printed nearly simultaneously. In this scenario, all the requests are being processed concurently and there is no time for the counter to be incremented before the next request is processed. This behavior points out a very important symptom in multithreading programs that has been mentioned at the beginning of this article: The developpers have to be very careful and pay attention to the flow of a multithreaded system in order to avoid the corruption of data. For example, in a case where numerous threads are accessing and modifying the same variable concurrently, problems can arise if one thread attempts to read a variable at the same time as the other thread is changing it. This would result in a garbage value being returned by the reading thread. To solve this problem, a mutex can be used to lock certain variable and have a single thread hold the key to it at a time. This concept will further be explained in one of the future wiki pages.

Rack::Lock

Within Rails there is middleware called Rack, which essentially provides a uniform way for Ruby and Rails to work with web servers and frameworks such as Apache, Thin, etc. Within Rack there is an environment property rack.multithread. The Rack::Lock piece of Rack disables the rack.multithread flag, which means that the application will be allowed to serve multiple threads. When Rack::Lock is enabled within Rails (when threadsafe! is disabled), the application is only allowed one thread at a time. This serialization does ensure thread safety, but it does so at the expense of run time, because if only one thread can execute at a time, then the other threads must wait for every thread before it to finish. This defeats the purpose of a threaded webserver, which is a necessity in today's Web.

Rails 4.0

Beginning with Rails version 4.0 multithreading has been automatically enabled and supported by the initial configuration. The threadsafe method has been removed from the configuration file and it cannot be found in the production.rb file anymore. This enables Rails 4.0 to automatically provides thread safety to all of its applications. The authors of this wiki page believe that the main reason for this approach has been to make learning rails more beginner-friendly and eliminate corruption of Rails applications written by inexperienced programmers.

References

http://www.sitepoint.com/config-threadsafe/

http://en.wikipedia.org/wiki/Thread_(computing)

http://en.wikipedia.org/wiki/Race_condition

http://en.wikipedia.org/wiki/Thread_safety

http://weblog.rubyonrails.org/2013/6/25/Rails-4-0-final/

http://guides.rubyonrails.org/configuring.html

http://tenderlovemaking.com/2012/06/18/removing-config-threadsafe.html

http://railscasts.com/episodes/365-thread-safety

http://guides.rubyonrails.org/rails_on_rack.html