Parrot Virtual Machine/Multithreading and Concurrency

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 its 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 its 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 its current state and resume its 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