Limbo is the application programming language for Inferno. Syntactically similar to C, it has several features that make it simpler, safer and yet more powerful and better suited to the development of concurrent, distributed systems. The Limbo compiler generates architecture independent object code which is then interpreted by the Inferno Virtual Machine or compiled just before runtime to improve performance. This ensures that Limbo applications are completely portable across all Inferno platforms.
Life is made easier for the programmer with features such as automatic garbage collection, compile and runtime type checking and simple creation of multiple processes (threads) and communication between them. Inferno also comes with a graphical debugger, allowing the user to step into the program at any point and browse through the current state.
Limbo has the following data types:
- byte (8-bit unsigned)
- int (32-bit signed)
- big (64-bit signed)
- real (64-bit floating point)
- list
- array (with slicing)
- string
- tuple (ordered collection of types)
- channel (for inter-process communication)
- adt ("abstract data type")
- pick (discriminated union type)
- module
Sample Programs
The following sample programs illustrate a simple program which takes a number of command line parameters and displays each one on a new line with a 1 second gap in between. The second sample program uses a simple multi-threaded approach to illustrate the basic use of channels. (line numbers have been inserted for easy reference)
Program 1
1: implement Timer;
2: include "sys.m";
3: sys: Sys;
4: include "draw.m";
5: Timer: module {
6: init: fn (nil: ref Draw->Context, argv: list of string);
7: };
8: init(nil: ref Draw->Context, argv: list of string)
9: {
10: sys = load Sys Sys->PATH;
11: n := len argv;
12: sys->print("Command Line Parameters\n");
13: for (i := 0; i < n; i++) {
14: sys->sleep(1000);
15: sys->print("%d: %s\n", i, hd argv);
16: argv = tl argv;
17: }
18: }
Line 1 defines the name of the module (program) with lines 5-7 defining the interface it presents to the outside world. The init function is required in the interface for a module to be run from the Inferno shell as a normal program. This function is then defined in lines 8-18.
Lines 2 and 4 include the files that define the interfaces to the other modules that Timer requires. Line 3 sets the Sys module handle sys to be a global variable.
Line 10 loads the Sys module (which contains the sleep and print functions used on lines 14 and 15) and starts a reference to it in the global variable sys
The Inferno shell passes command lines parameters to a module via the list of string variables argv in the init function which is part of the module interface.
Line 11 sets n to the number of items in the list, argv.
Line 13 tells the system to loop n times through lines 14 to 16 which first waits for one second (the sys->sleep function takes a delay argument in milliseconds) and then prints the next parameter in the list. The program iterates through the argv list using the hd (head) and tl (tail) operators.
Program 2
In this multi-threaded version of the previous program, a timer thread is started which controls the rate at which the command line parameters are printed. This shows a channel being used to communicate between different threads.
1: implement Timer2;
2: include "sys.m";
3: sys: Sys;
4: include "draw.m";
5: Timer2: module {
6: init: fn (nil: ref Draw->Context, argv: list of string);
7: };
8: init(nil: ref Draw->Context, argv: list of string)
9: {
10: sys = load Sys Sys->PATH;
11: sync := chan of int;
12: n := len argv;
13: spawn timer(sync, n);
14: sys->print("Command Line Parameters\n");
15: for (i := 0; i < n; i++) {
16: <-sync;
17: sys->print("%d: %s\n", i, hd argv);
18: argv = tl argv;
19: }
20: }
21: timer(sync: chan of int, n: int)
22: {
23: for (i := 0; i < n; i++) {
24: sys->sleep(1000);
25: sync <-= 1;
27: }
26: }
The first 10 lines perform the same function as in the first program, to define and load required modules.
Line 11 defines a channel, sync which will be used to communicate between the different threads (processes), the channel type defining the data which may be passed on it e.g. a chan of int may be used to pass integers.
Line 13 starts the timer thread using the spawn command. From this point on, the init function and the timer function will run concurrently in separate threads.
The program now enters into a loop in the same way as the previous program. However, instead of using sys->sleep to wait for one second, it waits for a value to be sent on the sync channel. The value could be assigned to a variable using
but as the value is not used, the assignment may be omitted.
In the timer thread, the loop statement first pauses for one second (line 24) and then sends the value '1' down the sync channel (line 25). Once the thread has performed the loop n times, it will exit.
Advanced Channels
This is just a simple example of the use of channels to communicate and synchronise between different threads. Advanced features such as listening on multiple channels, passing abstract data types (including channels) across channels and using buffered channels may all be used as part of the sophisticated concurrent environment provided by Limbo.
|