Chapter 19 Threads

Blue Chapter -- Machine Translation

Summary

Ruby interface

Speaking of the actual conversation with Ruby code still not be good showing I feel the hoops. It does not have much, let me introduce ICHIOU.

 
Thread.fork ( 
     while true 
       puts' forked thread ' 
     end 
) 
while true 
   puts' main thread ' 
end 

This program will do the right "forked thread" and "main thread" is YAMAZENINATTE whinge in output.

But of course, make multiple threads of control in many ways than that. Java as synchronize do not have reserved words, Mutex , Queue , Monitor such as primitive as well as common sense Provided that the operation of the thread itself to the following API are available.

▼ thread API

Thread.pass thread running someone else to move.
Thread.kill (th) thread th to shut down.
Thread.exit end a self-conversation.
Thread.stop self suspend a thread.
Thread # join the end of the thread is to wait.
Thread # wakeup moratorium against the thread.

ruby thread

The conversation was "all move in unison," but the principle is actually a bit of時間ず It is not the one running order. Strictly speaking, multi-CPU machines in its own worker If the husband can move or two, but still more than the number of CPU thread If you still have to order動かなけれ.

That is the thread to create something for anyone to switch to let a thread I have no reason, but the method is divided into two types. And KANERUREBERUSUREDDO YUZAREBERUSUREDDO. This is neither self-の如く, Su Red kernel to make it user-level or make a difference. Kernel-level, multi-CPU to take advantage of multiple threads simultaneously move Can do.

, ruby thread what of it has to do with this is in YUZAREBERUSUREDDO There is. And (thus) At the same time動けるconversations are strictly limited by just one.

preemptive or

A little more ruby thread to talk about the feature. For another apparent thread As the "preemptive (preemptive) whether" the points raised There is.

"Topic (mechanism) is preemptive (preemptive)," he said, The thread is a thread switch to using it without having to explicitly wins The conversation switched to hand it to me. To the contrary point of view,スレッ DE can not control the timing of the switch, they said.

Meanwhile mechanism nonpreemptive threads, the thread is using it Explicit "this thread to give him the right to control it," say as long as Switch thread. Conversely standpoint, the thread is always the possibility切り替わ Find a place that is clear.

This distinction is partly because of the process, in that case it is a preemptive "great" In particular it. For example, there are bugs in some programs have fallen into an infinite loop 切り替わらなくprocess would be. That is one of the user program Go all the way to stop the entire system does, it will not meet. But I, Windows 3.1 is the basic MS-DOS, so switching process NONPURIENPU Windows 95 is KEDOMO Tib was preemptive. Thus the system is more Robust. Therefore 3.1 from Windows 95 is "great" story.

So ruby say whether the conversations and, Ruby level is preemptive, C level in the nonpreemptive. C code that is written off when the The conversation is almost certain to switch to a specific timing.

Why do I have? The conversation is certainly useful, but use It also needs to be some kind of mental attitude. That is the thread that corresponds to the number and If you do not (must be MARUCHISUREDDOSEFU). In other words C LES Bell even if the preemptive use of the library, all Cマルチスレッ DE must be aware of that.

However, the reality of the C libraries are still many non-thread-safe.せっ Struggling to extend library bishop easy to write but not required to respond to a thread The library can be used to reduce the number of those meaningless. So C level Nonpreemptive is, is ruby is a reasonable choice.

management system

ruby nonpreemptive thread is that the C level. That is a 動いたらextent of the right to voluntarily hand off the gas to run it. Let's just runningスレッ Is not run anymore and is thought to want. Who should run the right to pass ? Well before the first place, the thread is ruby in any way inside Represented by the difference and I do not know. Threads for managing Variables and data types, let us see.

▼ thread structure to manage

 
  864 typedef struct thread * rb_thread_t; 
  865 static rb_thread_t curr_thread = 0; 
  866 static rb_thread_t main_thread; 

7301 struct thread ( 
7302 struct thread * next, * prev; 

(eval.c) 

struct thread is a very big piece of reasons, so I focused only important part. So only two, but this next and prev a member name and its The type rb_thread_t because rb_thread_t is linked to two-way link list Is believed to be. And in fact, not just a list of two-way at both ends of GATSU Wants to. That is the ring. This is a major point. Static Variable main_thread and curr_thread also make the whole structure of data As in Figure 1.

(thread)
Figure 1: data structures to manage thread

main_thread (the main thread) and when you start the program existed and Of threads, or "first" thread. curr_thread is natural current thread , that is currently running thread. main_thread value will not change, the process is running, curr_thread is getting more value Things change.

This list is in a ring and the "threads" it easy to choose. Just next I TAGURE link. That's only a certain amount all threads Equality can be moved.

thread is to switch

By the way, what the thread is the first place. Or, what if it switched to thread?

This is a very difficult problem. What program, and what objects, and It's similar to what we normally appropriate "feel" about what the understanding that De Sukkiri is called into question and answer. In particular, such as What is the process and thread Thousand different to what I heard and feel at a loss.

Still, it is realistic to a degree range. All you need is a real thread Lines of context. ruby and put it in context, so far seen So, ruby_frame and ruby_scope , ruby_class had. Also ruby is BETTER NSUTAKKU on ruby_frame entity to ensure that the extended use of library I think the stack space from the machine stack program also co-Ruby NTEKISUTO as necessary. And finally, CPU registers are essential. These various elements context of a thread, which cut The change is the thread switch is not. Or Context switch (context switch) and say.

How

context switch

What remains is to switch the context of the story. ruby_scope and ruby_class is easy to change. But heap space and make sure that an honest way I do find shelter. The CPU will still manage to register. setjmp () use Writeback can be saved. Therefore, both the region rb_thread_t to Have already done that.

struct thread (part)

 
7301 struct thread ( 
7302 struct thread * next, * prev; 
7303 jmp_buf context; 

7315 struct FRAME * frame; / * ruby_frame * / 
7316 struct SCOPE * scope; / * ruby_scope * / 
7317 struct RVarmap * dyna_vars; / * ruby_dyna_vars * / 
7318 struct BLOCK * block; / * ruby_block * / 
7319 struct iter * iter; / * ruby_iter * / 
7320 struct tag * tag; / * prot_tag * / 
7321 VALUE klass; / * ruby_class * / 
7322 VALUE wrapper; / * ruby_wrapper * / 
7323 NODE * cref; / * ruby_cref * / 
7324 
7325 int flags; / * scope_vmode / rb_trap_immediate / raised * / 
7326 
7327 NODE * node; / * rb_current_node * / 
7328 
7329 int tracing; / * tracing * / 
7330 VALUE errinfo; / * $! * / 
7331 VALUE last_status; / * $? * / 
7332 VALUE last_line; / * $ _ * / 
7333 VALUE last_match; / * $ ~ * / 
7334 
7335 int safe; / * ruby_safe_level * / 

(eval.c) 

In this way ruby_frame and ruby_scope correspond to the members are heard. To register for storing jmp_buf .

Now, the problem is a stack machine. To SURIKAERU How do I do?

Mechanisms for the most docile of the stack position (tip) to specify the points To rewrite the data directly. It is common CPU in the register. Only LES Register there may be a general register In order to secure one of the cases There, but I just do. Too lazy to the point that Let's just call me a stack pointer. Once you have to change this to another region You can stack of course. But of course this is against every CPU and OS Situation does need a transplant to ensure it is very difficult.

So ruby violent means, quite a stack of machines and implements theすり替え Be. If you are not stack pointer, the stack pointer to change the points at the O's. You can fiddle with the stack directly've already GABEJIKO REKUTA seen at the left is to do just a little change. Stack also to keep in place struct thread fix.

struct thread (part)

 
7310 int stk_len; / * stack length * / 
7311 int stk_max; / * stk_ptr memory assigned * / 
7312 VALUE * stk_ptr; / * * stack copy / 
7313 VALUE * stk_pos; / * * stack position / 

(eval.c) 

explanation of how we

At least, the point is a lot to talk organized by three points.

Or to switch context,. That is the whole point of this chapter Is. The following is a passage from each of the three-point talking about using the film industry.

trigger

First, the first point, when the switch or thread. In other words, The conversation switched to what is causing it.

I / O waiting

For example IO # gets and IO # read to something called a load, I have read And appear to be time-consuming while other threads are moving it to head It should be a good addition. That is where change is required to enforce. The following is getc of C Lee Interface.

rb_getc ()

 
1185 int 
1186 rb_getc (f) 
1187 FILE * f; 
(1188 
1189 int c; 
1190 
1191 if (! READ_DATA_PENDING (f)) ( 
1192 rb_thread_wait_fd (fileno (f)); 
1193) 
1194 TRAP_BEG; 
1195 c = getc (f); 
1196 TRAP_END; 
1197 
1198 return c; 
1199) 

(io.c) 

READ_DATA_PENDING (f) buffer contents of the file, you still have to check whether Macros. The contents of the buffer should happen, zero latency, but動ける , Immediately read. Because it takes time, I would check rb_thread_wait_fd () call. This is the thread switching to indirect factors.

rb_thread_wait_fd () "indirect" and say "direct" first choice factors . What's that? rb_thread_wait_fd () in a look at.

rb_thread_wait_fd ()

 
8047 void 
8048 rb_thread_wait_fd (fd) 
8049 int fd; 
(8050 
8051 if (rb_thread_critical) return; 
8052 if (curr_thread == curr_thread-> next) return; 
8053 if (curr_thread-> status == THREAD_TO_KILL) return; 
8054 
8055 curr_thread-> status = THREAD_STOPPED; 
8056 curr_thread-> fd = fd; 
8057 curr_thread-> wait_for = WAIT_FD; 
8058 rb_thread_schedule (); 
8059) 

(eval.c) 

The last line rb_thread_schedule () somewhere. This function is "a direct Source Consideration. " ruby thread implementation of core functions, and the selection of the following thread Switching to go.

That's why this function or role of you to know what to say, if I wasスレッ De-scheduling (scheduling) have the words and know in advance From the hoops. If you're not, so I learned that you can notice from the following He said.

And in this case is simply to transfer control of other threads as well as he was stopped and the Syscall-hooking. In addition, "read until the end" with a clear policy. So the Requests rb_thread_schedule () 伝えなくto the government. That's curr_thread of Assigned to the various member per. Why stop wait_for , and the cause I use the information fd , each cart.

waiting for another thread

rb_thread_schedule () and the timing of the thread is known to switch, this time On the contrary rb_thread_schedule () thread is a switch from point to see That can be. So scan them, rb_thread_join () a Seki The number of discovered.

rb_thread_join () (part)

 
8227 static int 
8228 rb_thread_join (th, limit) 
8229 rb_thread_t th; 
8230 double limit; 
(8231 

8243 curr_thread-> status = THREAD_STOPPED; 
8244 curr_thread-> join = th; 
8245 curr_thread-> wait_for = WAIT_JOIN; 
8246 curr_thread-> delay = timeofday () + limit; 
8247 if (limit  wait_for | = WAIT_TIME; 
8248 rb_thread_schedule (); 

(eval.c) 

This function is Thread # join of the entity, Thread # join thread is the receiver's end SU Of the method to wait. Certainly, if you have other threads waiting time to move Therefore it is advised. The second reason for the switch見付かった.

waiting time

In addition rb_thread_wait_for () is also a function rb_thread_schedule () has been found Hoops. This is (Ruby's) sleep and other entities.

rb_thread_wait_for (condensed version)

 
8080 void 
8081 rb_thread_wait_for (time) 
8082 struct timeval time; 
(8083 
8084 double date; 

8124 date = timeofday () + 
                  (double) time.tv_sec + (double) time.tv_usec * 1e-6; 
8125 curr_thread-> status = THREAD_STOPPED; 
8126 curr_thread-> delay = date; 
8127 curr_thread-> wait_for = WAIT_TIME; 
8128 rb_thread_schedule (); 
8129) 

(eval.c) 

timeofday () right now is the time to return. And time to add value, date is time to run out of time shows. This means that the "Certain times you want to stop until" specified.

expired switching

Which is more than a certain level of the Ruby operation is being conducted, resulting in cutting threads Resona, which were the cause of change. This means that this level of Ruby NONPURIEN PUTIBU have not been. This is, if you continue to wrestle with calculations. One thousand threads in the program, I would forever be in the running U. So to some extent動いたらvoluntarily abandoned the right to execute someone like yourself Must be. How much does it should not stop動いたら ? Then talk about it.

setitimer

We have no tricks, I'm asking the same feeling, but also more rb_thread_schedule () to Someone called looking for him. Then見付かるThis is a strange place. It is here.

catch_timer ()

 
8574 static void 
8575 catch_timer (sig) 
8576 int sig; 
(8577 
8578 # if! Defined (POSIX_SIGNAL) & &! Defined (BSD_SIGNAL) 
8579 signal (sig, catch_timer); 
8580 # endif 
8581 if (! Rb_thread_critical) ( 
8582 if (rb_trap_immediate) ( 
8583 rb_thread_schedule (); 
8584) 
8585 else rb_thread_pending = 1; 
8586) 
8587) 

(eval.c) 

NANIYARA関係らしいthe signal, but the hell of it? This function catch_timer () using it to try to track and This area had been used.

rb_thread_start_0 () (part)

 
8620 static VALUE 
8621 rb_thread_start_0 (fn, arg, th_arg) 
8622 VALUE (* fn) (); 
8623 void * arg; 
8624 rb_thread_t th_arg; 
(8625 

8632 # if defined (HAVE_SETITIMER) 
8633 if (! Thread_init) ( 
8634 # ifdef POSIX_SIGNAL 
8635 posix_signal (SIGVTALRM, catch_timer); 
8636 # else 
8637 signal (SIGVTALRM, catch_timer); 
8638 # endif 
8639 
8640 thread_init = 1; 
8641 rb_thread_start_timer (); 
8642) 
8643 # endif 

(eval.c) 

In other words catch_timer () is SIGVTALRM HANDORARASHII signal.

Here SIGVTALRM which is or what kind of signal, it is also a problem. This is actually a setitimer to use the system calls and sent signals For both sides. Thus, just before HAVE_SETITIMER check on them. setitimer SET Interval TIMER is an abbreviation for a certain time each signal to OS system calls to send convey.

But setitimer is to call someone and say, happens to be on this list Finally, a rb_thread_start_timer () .

To summarize all of the following scenario. setitimer for a set time each CIGNA LE sent to. It catch_timer () catch. So rb_thread_schedule () calling thread to switch. Perfect.

However signals can occur at any time and it would be, but C levels also said that preemptive in the water. So catch_timer () look at the code again.

 
if (rb_trap_immediate) ( 
     rb_thread_schedule (); 
) 
else rb_thread_pending = 1; 

rb_thread_schedule () 's rb_trap_immediate only when the conditions are With. This is the point. rb_trap_immediate is the street name "signal. Immediately whether the process "and represents a lot of it is false. This is true It is a single thread, I / O, while you're gone, and only a very limited period There is. Source code is TRAP_BEG and TRAP_END surrounded by it.

Meanwhile it is false when the rb_thread_pending set so that it Let us further. The following variables are using it.

CHECK_INTS - HAVE_SETITIMER

 
   73 # if defined (HAVE_SETITIMER) & &! Defined (__BOW__) 
   74 EXTERN int rb_thread_pending; 
   75 # define CHECK_INTS do (\ 
   76 if (! Rb_prohibit_interrupt) (\ 
   77 if (rb_trap_pending) rb_trap_exec (); \ 
   78 if (rb_thread_pending & &! Rb_thread_critical) \ 
   79 rb_thread_schedule (); \ 
   80) \ 
   81) while (0) 

(rubysig.h) 

In this way CHECK_INTS in rb_thread_pending checked and rb_thread_schedule () . In other words SIGVTALRM to take the rb_thread_pending is true, then CHECK_INTS when I went to a thread It is switched.

This CHECK_INTS have so far appeared in many places. For example rb_eval () and rb_call0 () and rb_yield_0 () . CHECK_INTS on a regular To pass it and put it does not make sense and the nature and function of important Together.

tick

setitimer If you have information about this Saturday. But setitimer if there are no How? Actually, I just saw CHECK_INTS - # else 's definition is the answer .

CHECK_INTS - not HAVE_SETITIMER

 
   84 EXTERN int rb_thread_tick; 
   85 # define THREAD_TICK 500 
   86 # define CHECK_INTS do (\ 
   87 if (! Rb_prohibit_interrupt) (\ 
   88 if (rb_trap_pending) rb_trap_exec (); \ 
   89 if (! Rb_thread_critical) (\ 
   90 if (rb_thread_tick - <= 0) (\ 
   91 rb_thread_tick = THREAD_TICK; \ 
   92 rb_thread_schedule (); \ 
   93) \ 
   94) \ 
   95) \ 
   96) while (0) 

(rubysig.h) 

CHECK_INTS to go through every time rb_thread_tick is decreasing. 0 when rb_thread_schedule () . In other words THREAD_TICK (= 500) times CHECK_INTS to通ったら The conversation switched to the system.

scheduling

The second point is how to switch or thread. The decision to undertake the one hand rb_thread_schedule () .

rb_thread_schedule ()

ruby It's always important function is also big, and so on. This rb_thread_schedule () more than 220 lines. Let's isolate it thoroughly.

rb_thread_schedule () (primary)

 
7819 void 
7820 rb_thread_schedule () 
(7821 
7822 rb_thread_t next; / * OK * / 
7823 rb_thread_t th; 
7824 rb_thread_t curr; 
7825 int found = 0; 
7826 
7827 fd_set readfds; 
7828 fd_set writefds; 
7829 fd_set exceptfds; 
7830 struct timeval delay_tv, * delay_ptr; 
7831 double delay, now; / * OK * / 
7832 int n, max; 
7833 int need_select = 0; 
7834 int select_timeout = 0; 
7835 
7836 rb_thread_pending = 0; 
7837 if (curr_thread == curr_thread-> next 
7838 & & curr_thread-> status == THREAD_RUNNABLE) 
7839 return; 
7840 
7841 next = 0; 
7842 curr = curr_thread; / * starting thread * / 
7843 
7844 while (curr-> status == THREAD_KILLED) ( 
7845 curr = curr-> prev; 
7846) 

           / *…… Select and prepare a variable use…… * / 
           / *…… If necessary, a select…… * / 
           / *…… Then decided to start a thread…… * / 
           / *…… Context switch…… * / 
8045) 

(eval.c) 

(A) is only one thread is nothing if not immediately return to Has been. Therefore, since this story is make sure that there are multiple threads Possible premise.

(B) followed by variable initialization. while included initialization I think. curr is prev to辿っbecause the living ( status! = THREAD_KILLED ) Is set in the last thread. Why "first" and not say, " Curr to start next curr end up dealing with" a lot of the loop Come out.

Then the select GANANTARA, see the statement. ruby thread switch select very dependent on so The first select Let's just about prepared.

select

select is a certain file, ready to read and write Call waiting for the system. This is a prototype.

 
int select (int max, 
            fd_set * readset, fd_set * writeset, fd_set * exceptset, 
            struct timeval * timeout); 

fd_set variable you want to check fd on the set. The first argument max is "( fd_set in the fd maximum value) +1". timeout is select maximum waiting time. timeout is NULL If you wait indefinitely. timeout 0 then the value is also waiting for a second But check back soon. The return value, so when you speak.

fd_set dilate on. fd_set the following macro operations.

fd_set operations

 
fd_set set; 

FD_ZERO (& set) / * initialize * / 
FD_SET (fd, & set) / * set to add to file descriptor fd * / 
FD_ISSET (fd, & set) / * fd is true if you set * / 

fd_set is a typical sequence of bits, n the file descriptors Check if they want to stand for the n-bit (Figure 2).

(fdset)
Figure 2: fd_set

Brief select 見せようuse an example.

select example of how to use

 
# include  
# include  
# include  
# include  

int 
main (int argc, char ** argv) 
( 
     char * buf [1024]; 
     fd_set readset; 

     FD_ZERO (& readset); / * readset to initialize * / 
     FD_SET (STDIN_FILENO, & readset); / * stdin Cart to set * / 
     select (STDIN_FILENO + 1, & readset, NULL, NULL, NULL); 
     read (STDIN_FILENO, buf, 1024); / * * succeed without delay / 
     exit (0); 
) 

The code calls the system must be assumed that the success of the Error-checking is not done yet. FD_ZERO FD_SET select . Just look at the trend. select The fifth argument timeout is NULL because the This select calls are forever stdin to wait for loading. The select is It ended with the following read can not wait to read it completely . Middle print between movements in the eyes of you know better. In a little more detailed code examples include CD-ROM into it Of the \ footnote ( select code example: the accompanying CD-ROM doc / select.html about).

select prepare

, rb_thread_schedule () back to the code. The number of waiting thread is the reason for each branch, The content of the shows are short.

rb_thread_schedule () - select prepare

 
7848 again: 
           / * Select related variable initialization * / 
7849 max = -1; 
7850 FD_ZERO (& readfds); 
7851 FD_ZERO (& writefds); 
7852 FD_ZERO (& exceptfds); 
7853 delay = DELAY_INFTY; 
7854 now = -1.0; 
7855 
7856 FOREACH_THREAD_FROM (curr, th) ( 
7857 if (! Found & & th-> status <= THREAD_RUNNABLE) ( 
7858 found = 1; 
7859) 
7860 if (th-> status! = THREAD_STOPPED) continue; 
7861 if (th-> wait_for & WAIT_JOIN) ( 
                   / *…… Join waiting…… * / 
7866) 
7867 if (th-> wait_for & WAIT_FD) ( 
                   / *…… I / O waiting…… * / 
7871) 
7872 if (th-> wait_for & WAIT_SELECT) ( 
                   / *…… Select waiting…… * / 
7882) 
7883 if (th-> wait_for & WAIT_TIME) ( 
                   / *…… Waiting time…… * / 
7899) 
7900) 
7901 END_FOREACH_FROM (curr, th); 

(eval.c) 

But do not be noticeable FOREACH or how the macro. The two will then be defined.

FOREACH_THREAD_FROM

 
7360 # define FOREACH_THREAD_FROM (f, x) x = f; do (x = x-> next; 
7361 # define END_FOREACH_FROM (f, x)) while (x! = F) 

(eval.c) 

Clearly to try to expand.

 
th = curr; 
do ( 
     th = th-> next; 
     ( 
         ..... 
     ) 
) While (th! = Curr); 

Thread ring list curr haul from the last curr end of the process, When the variable th using an意味らしい. Enumeration is a little Ruby …… Reminiscent of something that is in too much imagination?

This code will return more of this strange loop subtly using select necessary Check the thread. We watched as select exception of reading, writing 待てるall the time, so suddenly, I / O is waiting and waiting time select one to integrate I think it is. Also in the preceding paragraph did not explain select of waiting. Ruby library also IO.select to have a method, C-level and rb_thread_select () that are available. We select also I have not run. fd_set do more to synthesize select at the same time You can get through.

The remaining join just waiting. This code is ICHIOU let me see.

rb_thread_schedule () - select prepare - join waiting

 
7861 if (th-> wait_for & WAIT_JOIN) ( 
7862 if (rb_thread_dead (th-> join)) ( 
7863 th-> status = THREAD_RUNNABLE; 
7864 found = 1; 
7865) 
7866) 

(eval.c) 

rb_thread_dead () meaning is obvious from the name. Arguments end of the thread Whether the decision.

select call

So far, select whether you need is identified, if necessary, the fd_set are also prepared I have. So if necessary select call. For example, you can start immediately ( THREAD_RUNNABLE ) there is a thread select is not呼ばなく. Actually we've already I / O and waiting for the end of the thread, and there is more Might be a high priority. But the case is select immediately To be back, I / O is complete only if it were in check.

rb_thread_schedule () - select

 
7904 if (need_select) ( 
7905 / * delay to convert timeval. * / 
7906 / * start immediately if there is a thread I / O check only do * / 
7907 if (found) ( 
7908 delay_tv.tv_sec = 0; 
7909 delay_tv.tv_usec = 0; 
7910 delay_ptr = & delay_tv; 
7911) 
7912 else if (delay == DELAY_INFTY) ( 
7913 delay_ptr = 0; 
7914) 
7915 else ( 
7916 delay_tv.tv_sec = delay; 
7917 delay_tv.tv_usec = (delay - (double) delay_tv.tv_sec) * 1e6; 
7918 delay_ptr = & delay_tv; 
7919) 
7920 
7921 n = select (max +1, & readfds, & writefds, & exceptfds, delay_ptr); 
7922 if (n <0) ( 
                   / *…… Signals such as the squeeze…… * / 
7944) 
7945 if (select_timeout & & n == 0) ( 
                   / *…… Timeout…… * / 
7960) 
7961 if (n> 0) ( 
                   / *…… End successfully…… * / 
7989) 
7990 / * wait some time at the end of the thread. 
7991 in order to identify the threads to loop around again * / 
7992 if (! Found & & delay! = DELAY_INFTY) 
7993 goto again; 
7994) 

(eval.c) 

The first half of the block read in the comments. delay is one of the threads are then able to start until usec So, It timeval to convert.

Is actually the second half select to bring the divergent results. This code And so long divided. If the signal to cut back in the first YARINAOSU or errors either, so the significance of the remaining two.

timeout

select timeout or if the waiting time select waiting The conversation starts it might be possible. I check the Taxed in the thread.見付かったら THREAD_RUNNABLE up.

end successfully

select successful that the I / O or ready, select is waiting For instance, either. fd_set to check the thread waiting is over Search.見付かったら THREAD_RUNNABLE up.

decision this thread

As far as considering all information of the final decision as to start a conversation. Bootable from the original source, such as waiting is over any RUNNABLE in Should be the appropriate to pick from.

rb_thread_schedule () - a decision the following thread

 
7996 FOREACH_THREAD_FROM (curr, th) ( 
7997 if (th-> status == THREAD_TO_KILL) (/ * (A) * / 
7998 next = th; 
7999 break; 
8000) 
8001 if (th-> status == THREAD_RUNNABLE & & th-> stk_ptr) ( 
8002 if (! Next | | next-> priority  priority) / * (B) * / 
8003 next = th; 
8004) 
8005) 
8006 END_FOREACH_FROM (curr, th); 

(eval.c) 

(A) is now trying to end the thread if there is a priority basis Round exit in the standings.

(B) It Big likely to find out who. However priority to consider the value SURURASHII. Ruby is also a member of this level Thread # priority Thread # priority = , Change. In particular ruby itself can be changed like that.

If at the end of next見付からなかったらthread, that is next is Set did not, what would it be? Already select of the So waiting time and I / O is waiting for one thread waiting for the end of that. It remains that there are no other threads are only waiting, and I can start Of threads, not the ends can not wait. In other words Deadlock (dead lock).

Of course, this is in addition to a deadlock happen, but generallyデッドロッ QUEUE to detect it is very difficult. In particular ruby If the Mutex such as Ruby level has to be implemented in full is almost impossible to detect.

thread switch

Then start the conversation is to be determined. I / O and select also check. After the conversations aimed at shifting control to do just that. But rb_thread_schedule () and the last thread switching codes Clause once again begin to it.

context switch

The third point is the last thread switching, Context switch (context switch). This is ruby of the most interesting conversations about it.

basic line

, rb_thread_schedule () go from the end. This section is an embarrassment, so the story spread to the condensed version.

rb_thread_schedule () (context switch)

 
if (THREAD_SAVE_CONTEXT (curr)) ( 
     return; 
) 
rb_thread_restore_context (next, RESTORE_NORMAL); 

THREAD_SAVE_CONTEXT () of the matter is Some of its contents do not know to think about the deployment.

THREAD_SAVE_CONTEXT ()

 
7619 # define THREAD_SAVE_CONTEXT (th) \ 
7620 (rb_thread_save_context (th), thread_switch (setjmp ((th) -> context))) 

7587 static int 
7588 thread_switch (n) 
7589 int n; 
(7590 
7591 switch (n) ( 
7592 case 0: 
7593 return 0; 
7594 case RESTORE_FATAL: 
7595 JUMP_TAG (TAG_FATAL); 
7596 break; 
7597 case RESTORE_INTERRUPT: 
7598 rb_interrupt (); 
7599 break; 
             / *…… Abnormal processing system to make…… * / 
7612 case RESTORE_NORMAL: 
7613 default: 
7614 break; 
7615) 
7616 return 1; 
7617) 

(eval.c) 

In other words, he deployed three combined.

 
rb_thread_save_context (curr); 
switch (setjmp (curr-> context)) ( 
   case 0: 
     break; 
   case RESTORE_FATAL: 
     .... 
   case RESTORE_INTERRUPT: 
     .... 
   / *…… Abnormal processing system…… * / 
   case RESTORE_NORMAL: 
   default: 
     return; 
) 
rb_thread_restore_context (next, RESTORE_NORMAL); 

setjmp () returned rb_thread_restore_context () both RESTORE_NORMAL are emerging that is obviously suspicious. rb_thread_restore_context () in longjmp () and the setjmp () and longjmp () is expected to respond. And from the name of the function and meaning of the imagination,

 
Save the current context of a thread 
setjmp 
Return the following thread context 
longjmp 

It's the broad outline of the flow. However, care must be taken here This is setjmp () and longjmp () pairs in this thread to complete the It is not. setjmp () in the context of their own to save Use, longjmp () in the context of this thread is used to return. - Marika following setjmp () / longjmp () the link-chain that can be (Figure 3).

(setjmploop)
Figure 3: setjmp chain of the backstitch

setjmp () / longjmp () will be able to return around the CPU, Ruby will remain in the context of the stack, stack machine. The evacuation is rb_thread_save_context () , Comeback rb_thread_restore_context () . Let's turn to look at.

rb_thread_save_context ()

First of all to save the context rb_thread_save_context () .

rb_thread_save_context () (condensed version)

 
7539 static void 
7540 rb_thread_save_context (th) 
7541 rb_thread_t th; 
(7542 
7543 VALUE * pos; 
7544 int len; 
7545 static VALUE tval; 
7546 
7547 len = ruby_stack_length (& pos); 
7548 th-> stk_len = 0; 
7549 th-> stk_pos = (rb_gc_stack_start  th-> stk_max) ( 
7552 REALLOC_N (th-> stk_ptr, VALUE, len); 
7553 th-> stk_max = len; 
7554) 
7555 th-> stk_len = len; 
7556 FLUSH_REGISTER_WINDOWS; 
7557 MEMCPY (th-> stk_ptr, th-> stk_pos, VALUE, th-> stk_len); 

           / *………… Back………… * / 
       ) 

(eval.c) 

In the second half th in ruby_scope variables such as globalまくっassigned to the single-minded. Back in the protection of fun. The rest, here is part of the machine stack載せた A whole th-> stk_ptr of trying to copy us.

First ruby_stack_length () But the argument pos address to write the end of the stack Only the length of return. This value is used to determine the extent this stack the lower end of the address side A th-> stk_ptr set. NANIYARA branch is on the stack NOBIRU NOBIRU bottom of the stack and from there (Figure 4).

(twodirection)
Figure 4: NOBIRU stack on the bottom of the stack NOBIRU

Then after the th-> stk_ptr at the memory stack can copy . th-> stk_max memory of the length of len only copy.

FLUSH_REGISTER_WINDOWS Chapter 5 of the moth - BEJIKOREKUSHON, I explained that I was better. Stack of cache memory drop macro (assembler entities). View the entire stack to call when you have to be sure.

rb_thread_restore_context ()

And finally to return to the conversation function, rb_thread_restore_context () .

rb_thread_restore_context ()

 
7635 static void 
7636 rb_thread_restore_context (th, exit) 
7637 rb_thread_t th; 
7638 int exit; 
(7639 
7640 VALUE v; 
7641 static rb_thread_t tmp; 
7642 static int ex; 
7643 static VALUE tval; 
7644 
7645 if (! Th-> stk_ptr) rb_bug ( "unsaved context"); 
7646 
7647 if (& v  th-> stk_pos) stack_extend (th, exit); 
7650) 
7651 else ( 
7652 / * * NOBIRU machine on the stack / 
7653 if (& v  stk_pos + th-> stk_len) stack_extend (th, exit); 
7654) 

           / * Global variables…… Back to return * / 

7677 tmp = th; 
7678 ex = exit; 
7679 FLUSH_REGISTER_WINDOWS; 
7680 MEMCPY (tmp-> stk_pos, tmp-> stk_ptr, VALUE, tmp-> stk_len); 
7681 
7682 tval = rb_lastline_get (); 
7683 rb_lastline_set (tmp-> last_line); 
7684 tmp-> last_line = tval; 
7685 tval = rb_backref_get (); 
7686 rb_backref_set (tmp-> last_match); 
7687 tmp-> last_match = tval; 
7688 
7689 longjmp (tmp-> context, ex); 
5914) 

(eval.c) 

Arguments th to run back to the other. The core of the second half MEMCPY () and longjmp () . MEMCPY () 近けれbe close to the end the better. From this operation longjmp () between the stack has been broken from the state.

Nevertheless, rb_lastline_set () and rb_backref_set () on. This is $_ and $~ return. These two variables are local variables, a mere thread Local and Because there is a slot in one of the local variables present in so many conversations I Be. This location is actually not to be back at it from the stack. Local variables from the slot region alloca () to ensure that.

And the base is more than good thing, but simply stack them to write back and switch At the current thread from the thread's stack is shorter in length it took, Copy the moment just running function ( rb_thread_restore_context ) The News Tack to override the frame. That is the argument th contents were broken. So I First, it should not have to have伸ばさないstack. Do you have it The first half of stack_extend () .

stack_extend ()

 
7624 static void 
7625 stack_extend (th, exit) 
7626 rb_thread_t th; 
7627 int exit; 
(7628 
7629 VALUE space [1024]; 
7630 
7631 memset (space, 0, 1); / * prevent array from optimization * / 
7632 rb_thread_restore_context (th, exit); 
7633) 

(eval.c) 

1K bytes of local variables (machine placed in the stack) to secure the stack To extend the MURIYARI. However, as might be expected, stack_extend () and return and The stack of those stretched shrivel. So then and there rb_thread_restore_context () to call back.

By the way rb_thread_restore_context () and the job is complete longjmp () call To reach out and, once呼び出したらabsolutely will not come again. Naturally stack_extend () never return calls. Thus rb_thread_restore_context () , stack_extend () after returning from treatment And everything else is no need to worry about.

problems

More than ruby thread switch implementation. There is no reason to believe are light. Mass - malloc () realloc () to the mass memcpy () and setjmp () longjmp () of NOBASU last line in order to stack the function call away. "Dead serious" ARUMAI representation of the problem. However, the OS-dependent system calls instead of calling Nor is it out the window assembler and Sparc related only registers. This If portability is certainly likely.

There are other problems as well. It is the thread's stack all the same address percent Resona picked for the stack using a pointer to the code will not move It is not possible. In fact, Tcl / TkハマっIt is wonderful to have been, Ruby's Tcl / Tk interface from the main thread is out of necessity limited access You can avoid that.

Of course chemistry between native and say hi. Only on certain native ruby thread should try not to move Not work. UNIX is a thread library is still overuse and abuse Win32, and the thread is something few moves, so caution is necessary.


The original work is Copyright © 2002 - 2004 Minero AOKI.
Translations,  additions,  and graphics by C.E. Thornton
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike2.5 License.