Multi-Thread Icon
- MT Icon allows several programs to be loaded and run under the same
invocation of the interpreter
- Such programs can communicate with each other
- MT Icon is not a concurrent programming language; although several
programs can be loaded under MT Icon, only one program is active at any
time
- transfer of control between loaded programs is done using co-expressions
- one program activates another and hence relinquishes control to the other
program
- an Icon program that runs under MT Icon starts like any other program;
a program that runs under regular Icon runs under the MT Icon without modification
- a program running under MT Icon can, however, load another MT Icon
program and start its execution by activating it as a co-expression
- in order to support the execution of several programs under the same
interpreter, MT Icon has additional functions and keywords; some standard
Icon functions also have extended capabilities under MT Icon
Threads
- as used here, the term thread means the execution state of a program
running under the Icon interpreter
- a thread consists of a set of co-expressions that share that program
state
- a single thread called the root is created when the interpreter starts
execution
- additional threads can be created dynamically as needed
- threads are created, referenced, and activated solely in terms of their
member co-expressions; threads are themselves implicit
- in MT Icon, threads execute serially with their own stack, heap, and
variables in a single address space
Loading and Activating Programs
- the function
load(s, L)
loads the icode file named s
and returns a co-expression corresponding to the invocation of main(L)
in the loaded icode file
- as a simple example, suppose a program named
example.icn
consists of
procedure main(args)
every write(!args)
return
end
mticont example
translates example.icn and produces an icode file named example
- now suppose the following program is named mttest.icn:
procedure main()
prog := load("example", ["Hi", "mom"])
@prog
write("Good-bye")
end
then
mticont mttest -x
translates and executes mttest.icn
- the program
mttest.icn
starts like any other Icon program;
but it uses the MT Icon function load()
to load the icode
file example
- the value assigned to
prog
as a result is a co-expression
for the call main(["Hi", "mom"])
- the result of activating
prog
is to call main()
in example
with a two-element list as its argument, as if
example had been run as
icont example -x Hi mom
the output produced is
Hi
mom
- at this point,
main()
in example returns; since it was
invoked by @prog
in mttest
, control returns there
mttest
then writes
Good-bye
before itself terminating, which ends the execution of MT Icon
- the argument to
main()
that is passed to a loaded program
in MT Icon can contain values of any type; they are not limited to strings,
as in command-line invocation
Communication Among Programs
- the preceding example illustrates a simple form of communication among
programs; mttest activates example much in the fashion of a procedure call
- the return from
main()
in example causes control to return
to the point of activation in mttest
- in co-expression activation, the flow of control need not be hierarchical;
furthermore, a co-expression can activate the co-expression that activated
it,
&source
- in addition, a value can be transmitted to a co-expression when it
is activated, using
x @ C
which activates C
and transmits x
to it (@C
is just an abbreviation for &null @ C
)
- this mechanism is the same whether a co-expression is being "called"
or when a "return to it" is being made
- this is illustrated by the program
recorder.icn
:
procedure main()
prog := load("textlist")
@prog # wake up!
while read() @ prog
lines := cofail(prog)
...
procedure main()
L := [ ]
while put(L, @&source)
L @ &source
end
- the program
recorder
loads textlist
and activates
it to get it underway; textlist
then activates the program
that activated it, recorder
, in a loop, putting successive
values on a list
- meanwhile,
recorder
reads lines of input, which it transmits
to textlist
- when there are no more lines of input,
recorder
activates
textlist
using cofail(prog)
, an MT Icon function
that causes failure of the activating co-expression
- this terminates the loop in
textlist
; at this point, textlist
activates recorder
to transmit the list it built, which is
assigned to lines
- of course, it's much easier to cast this functionality in terms of
a procedure in recorder and not use multiple programs and co-expressions
at all; the example here is intended only to illustrate the way that programs
can communicate under MT Icon
Data Spaces
- each program loaded under MT Icon has its own state and allocated storage
regions
- for example, a reference to &subject in a loaded program refers
to its own subject of string scanning and is not affected by - nor does
it affect - subjects of string scanning in other loaded programs
- similarly, the allocation of space for a list in one loaded program
has no affect on the storage regions of other loaded programs
- each loaded program also has its own "name space" - its own
global identifiers, keywords, and so on
- the list given as the second argument of load() is a value in the loading
program; this means that the loaded program has access to a data structure
in the loading program
- furthermore, since the list passed as the argument of main() can contain
values of any kind, it can contain, for example, other structures in the
loading program
- thus, it is possible, and in fact necessary, for loaded programs to
have access to structures in other loaded programs
- this makes it possible for one program to affect the storage regions
of another program, although normally that is not done
MT Icon Functions
- MT Icon provides several functions that allow one loaded program to
access information in another loaded program; for example,
globalnames(C)
generates the names of the global identifiers in the thread that contains
C
- the values of identifiers in another loaded program can be obtained
by using
variable(s, C)
- thus, the names and values of global identifiers in the program
prog
could be written by
every ident := globalnames(prog) do
write(
ident,
" : ",
image(variable(ident, prog))
)
- since the value of
variable(s, C)
is a variable, it is
even possible to assign to the global variables in another program as in
variable("write", prog) := 1
variable("writes", prog) := 1
which has the effect of turning off output in prog
- changing the values of variables in another program is dangerous and
should be done only in unusual circumstances
- the function
keyword(s, C)
accesses the keyword named
s in the thread that contains C
; for example,
write(keyword("subject", prog))
writes the value of &subject in prog
- for keywords that are variables,
keyword()
returns a variable;
consequently
keyword("subject", prog) := text
assigns text to &subject
in prog
- standard Icon functions that have been extended in MT Icon to allow
for the specification of a program are:
display(i, f, C)
name(x, C)
proc(x, i, C)
- additional functions specific to MT Icon include:
localnames(C)
paramnames(C)
staticnames(C)
- these functions generate the names of the local variables, parameters,
and static variables of the procedure currently active in the thread that
contains
C
Input and Output
- the function
load()
has three optional file arguments
in addition to the two arguments mentioned earlier:
load(s, L, f1, f2, f3)
- if
f1
, f2
, and f3
are given,
they become &input
, &output
, and &errout
for the loaded program
- if an argument is omitted, it defaults to the corresponding file in
the loading program, and the loaded and loading program share that file
The Relationship Among Loaded Programs
- any program can load another (including a copy of itself)
- a parent-child relationship is induced by loading
- the result is a load tree; the program originally executed under MT
Icon is the root of this tree
- for example, if
a.icn
is
procedure main()
@load("b")
...
and b.icn
is
procedure main()
load("c")
load("d")
...
then the load tree after load("d")
in b
can be depicted as:
parent(C)
produces the parent of C
but fails if C
is
the root of the load tree
- the root can be found in any loaded program by using a procedure such
as
procedure root()
prog := &main
while prog := parent(prog)
return prog
end
Code Sharing
- there are several ways that data can be shared among loaded programs
- since procedures are first-class values in Icon, code can be shared
by data sharing
- suppose, for example, that several programs that are loaded together
need to access the procedures
gcd()
, ximage()
,
and escape()
; these procedures could be included in the root
program (perhaps by linking ucode files)
- then any loaded program that needs one of these procedures can get
to it as follows:
global gcd
...
library := root()
gcd := proc("gcd", , library) |
stop("*** cannot get procedure")
...
- subsequently,
gcd()
can be used as if it were in the loaded
program; the global declaration is needed to make the procedure available
throughout the program, since it is not otherwise declared in the program