Parrot Virtual Machine/Multithreading and Concurrency

From Wikibooks, open books for an open world
Jump to: navigation, search

Multithreading Requirements[edit]

Multithreading is an area where interpreted languages such as Perl 5 have had some problems in the past. Perl 5, as a prime example, had a very buggy and non-robust implementation of threads that was not scalable.

In contrast to Perl 5, Perl 6 is including advanced multithreading and concurrency features in the core language. To support all these advanced features of the Perl 6 language, Parrot must provide those same features as well.

Coroutines[edit]

While not technically "multithreading", coroutines represent a simple form of concurrency that will find many interesting uses as they catch on with mainstream programmers. Coroutines are like subroutines which use a yield statement instead of a return statement. The .yield statement causes the coroutine to return a value to the caller. However, .yield does not cause the coroutine to exit, disappear, or lose it's current state. The next time the caller calls the coroutine, the coroutine will pick up where it left off the last time. Here is an example:

.sub 'MyCoroutine'
  .yield(1)
  .yield(2)
  .return(3)
.end
.sub 'MyCaller'
  $I0 = 'MyCoroutine'()
  say($I0)
  $I0 = 'MyCoroutine'()
  say($I0)
  $I0 = 'MyCoroutine'()
  say($I0)
.return

The output of the function MyCaller will be:

1
2
3

Coroutines are immediately useful in places where a persistent state needs to be maintained, but where it might be difficult to coordinate that state between multiple callers, or multiple threads. One such example is in file or database access where a coroutine can serialize accesses, and maintain persistent state information about the size of the file and locations of certain features in the file.

Coroutines are also called "continuations" in other places, or even "continuation sandwich" in others.

Coroutine Parameters[edit]

Parameters to a coroutine are only passed the first time the coroutine is called, or any time the coroutine is called after having a .return statement. Here is an example:

.sub 'MyCoroutine'
   .param int a
   .yield(a)
   .yield(a)
   .yield(a)
   .return(a)
.end

.sub 'main' :main
   $I0 = 'MyCoroutine'(1)  # the "call"
   say $I0
   $I0 = 'MyCoroutine'(2)  # "continuation"
   say $I0
   $I0 = 'MyCoroutine'(3)  # "continuation"
   say $I0
   $I0 = 'MyCoroutine'(4)  # "continuation"
   say $I0
.end

This code will print the following result, even though the parameter to the MyCoroutine function changes with each call:

1
1
1 
1

This is because only the first call to MyCoroutine is actually a function call that creates the local parameters. The other calls to MyCoroutine are simply continuations, not calls. A continuation does not create new parameter variables.

Returning and Yielding[edit]

A return call in a coroutine causes it to return a value to the caller and to destroy it's current lexical scope, as usual. A yield call, however, will return a value without destroying the current lexical scope. Yielding then will allow the coroutine to maintain it's current state and resume it's execution the next time it is called.

Continuation and Coroutine PMCs[edit]

This is where the heart of the coroutine system is located, the coroutine PMC. The coroutine PMC is an object that stores the state of the coroutine so that it can be called multiple times in a row. A continuation PMC, which is a superclass of the coroutine, is a stored interpreter state. The coroutine operates by storing a continuation when a .yield directive is called, and invoking that continuation when the coroutine is called again.

Threads[edit]

Internally, Parrot supports multiple different methods to perform threading. Luckily, all of these different methods are abstracted and the details are hidden from the HLL programmer. Parrot's concurrency system is modular, so new multithreading technologies can be added to Parrot later, and HLL programmers will be able to benefit from these changes and additions without having to make any changes to their code.

Managing Threads[edit]

The scheduler is an instance of the Scheduler PMC type. However, you don't use the scheduler PMC directly, Parrot uses it for you.

There are four basic operations that can be used to create and manage a new thread. In Parrot, threads are all abstracted in the Task PMC. To create a new thread, you first create a new Task PMC, and then you add it to the scheduler.


Task and Thread PMCs[edit]

Software Transactional Memory[edit]

Resources[edit]



Previous Parrot Virtual Machine Next
Parrot_Magic_Cookies Exception_Handling