Copyright (c) 1994 Carnegie Mellon University All rights reserved.
The Gwydion Project would like to thank those on the net that have contributed code patches and bug reports for Mindy:
Adam Alpern, Patrick Beard, Steve Strassman, Scott Collins, Ed Gamble, Bruno Haible, John Shen, Galen Hunt, Richard Lynch, Dan Ratner, Court Demas, Miles Bader, Kelly Murray, Nick ThompsonSpecial thanks for a major effort to Roger Critchlow for enhancements to Mindy.
Introduction
Mindy is an implementation of a language that is very much like the language
described in the Dylan(tm) Interim Reference Manual (DIRM). The name
"Mindy" is derived from "Mindy Is Not Dylan Yet", and as the name implies,
Mindy is incomplete. Mindy is incomplete for the following reasons:
Mindy was developed by the Gwydion Project at Carnegie Mellon University for our own internal use as a development tool while we work on our "real" high-performance Dylan implementation. We have decided to make Mindy available for other people who want to learn about Dylan. However, the amount of effort that we can put into maintaining Mindy is strictly limited.
Mindy will never be an industrial-strength implementation, and nobody should depend on it for real work. We will make future releases from time to time as we add new features and fix bugs, but this is strictly a sideshow for us. We would appreciate receiving bug reports (especially those accompanied by code patches) and suggestions for improvements, but we may not fix every bug reported in a timely manner, or fix it at all. Our work on development of the "real" Gwydion/Dylan must take precedence.
We hope that nobody will draw any conclusions about the performance of our future Gwydion/Dylan compiler or the performance attainable in Dylan from experience using Mindy. It's not designed to be fast.
Mindy comprises two C programs, a compiler that produces byte-codes and a byte-code interpreter. Currently, building Mindy requires Gcc, Gmake, Flex, and Bison. We do not distribute these. If you do not have GNU tools, you can ftp them from prep.ai.mit.edu:pub/gnu.
We have built and tested Mindy under MACH on the DECstation and HP-UX on HP
700's. We have built and run Mindy, but not tested it extensivly, under
OSF1 on the Alpha and Irix on the SGI.
Hello, World
Well, the first program anyone should endeavor to write in a new language
is, of course, Hello World. Type this into a file called hw.dylan:
module: dylan-user define method main (#rest ignore) puts("Hello, World.\n"); end;To compile your program invoke $INSTALL/bin/mc, for example:
% $INSTALL/bin/mc hw.dylanThis produces a file named "hw.dbc". The .dbc stands for Dylan-byte-code. To run the program, say:
% $INSTALL/bin/mindy -f hw.dbcIt should print "Hello, World." to standard output and then exit. Fun, huh?
It can be useful to load code into Mindy with no main method. Once you land
in the debugger, you can call any function manually. This provides a way to
test any library.
Multiple Files
When working with a larger program, you will probably have more than one
.dylan file. In which case, you just compile them each independently, and
then run Mindy with multiple -f switches:
% mindy -f foo.dbc -f bar.dbc -f baz.dbcMindy loads the files specified with the -f switches in the order you specify the files on the command line. This becomes important when you define your own modules (see Section Using Libraries and Modules).
If you typically load several .dbc files as part of a single program, you can combine them into one file for convenience. The mechanism for combining .dbc files is the Unix cat utility:
% cat foo.dbc bar.dbc baz.dbc > big.dbc % mindy -f big.dbc
hw.dylan:4: parse error at or before `;'As the "hw.dylan:4:" is the same format the C compiler uses, gnu-emacs's compile package can parse the error messages from mc.
Mc's error recovery isn't the best in the world. Often, it has to completely bail, telling you only about the first few errors it found. You have to fix what it reports and try again.
A hint to getting slightly tighter error recovery is to end all method and
class definitions with "end method;" or "end class;". For example, if you
forget an "end" for a statement inside a method definition, the mc parser
goes all the way to the end of the file and then reports a syntax error at
the EOF position. You don't get any more clues. If you use "end method;",
then the parser can recover at the end of the method containing the bad
syntax and report the syntax error there. This gives you a lot tighter
recovery and more information in this situation.
Runtime Errors
Much more common than syntax errors are runtime errors. And given the
simplistic model of compilation mc uses, most semantic errors are not
detected until runtime. When Mindy hits a runtime error that is not handled
via the condition system, it drops you into a debugger. From this debugger
you can look at variables, examine the stack, and invoke functions. For
example, if you had assumed that puts would be named something more
reasonable, like put-string, you would have gotten the following when you
tried to run your hello world program:
% mindy -f hw.dbc Unbound variable: put-string thread [0] D main fp 0x1003009c: invoke-debugger({<simple-error> 0x101a24c9}) mindy>Typing "help" at the "mindy>" prompt will list the various commands you can use. See the document "debug.doc" for more information.
Additions:
(var1, var2, ...) = expr1 THEN expr
var KEYED-BY key IN collectionVar is bound to each element in collection, and key is bound to the element's key value.
define method make (result-class :: limited(<class>, subclass-of(<my-class>))); let x = next-method(); do-special-logging-or-something(x); x; end method;
Deficiencies:
The Dylan-User library is the default library in which mc compiles user code. Mindy provides this library for user convenience when whipping up play code or small applications for which the programmer does not want to bother to create a library. You cannot redefine the Dylan-User library. This library contains one module, Dylan-User, and you cannot redefine this module.
The Dylan language requires every library to contain a Dylan-User module, and this module must use the Dylan module from the Dylan library regardless of any user specifications. This module provides a starting point in every library where users can begin to define modules; without an initial module in the library, you would be unable to write any code, including module definitions. Each Dylan-user module in Mindy also automatically uses the Extensions module from the Dylan library.
Other libraries are available to Mindy users. Later sections of this
document describe these libraries.
Using Libraries and Modules
To compile code into a particular library use the -l switch to mc:
% mc -lmy-lib foo.dylanIf there is no -l switch, then mc compiles the code into the Dylan-User library. When loading a .dbc file into Mindy that was compiled into a particular library, one of the following conditions must be satisified to avoid errors:
Mindy loads the Dylan library when it first sees a reference to it. A reference to the Dylan library occurs when loading a file compiled to be in the Dylan library, or when loading a file with a library definition that uses the Dylan library. Mindy loads the Dylan library by looking for the file "dylan.dbc" on MINDYPATH.
To make a single compiled file for a library which has multiple source files, compile all the files that constitute the library with the "-l" switch set to the library's name. Then cat all the resulting .dbc files together (see Section Multiple Files), making sure the file that defines the library is first. Then install the combined .dbc file in one of the directories in your MINDYPATH.
To compile code into a particular module, use the module: file header. Whenever a source file lacks a module: file header, mc issues a compiler warning and compiles the code into the Dylan-User module. Note, this is the Dylan-User module of the library specified with the "-l" switch, and if there was no "-l" switch, it is the Dylan-User module of the Dylan-User library.
When loading a .dbc file into Mindy that was compiled into a particular module, one of the following conditions must be satisfied to avoid errors:
<boolean> [Class]
This class is a subclass of <object>. There are exactly two instances of this class, #t and #f.
<byte-vector> [Class]
This class is a subclass of <vector> that can only hold integers between 0 and 255 inclusively. This class is a temporary addition to Mindy to support the requirement that the Streams library export a <byte-vector> definition. When Mindy supports limited collections, this may be defined within the Streams library.
main (#rest arguments) [Generic Function]
Has no methods, but is called by Mindy when it starts up. To make a standalone program, you define a method on main that does whatever you want it to do. Arguments is a sequence of strings. There is a string in arguments for every argument on the command line that invoked Mindy, except all "-f" switches and the argument following each "-f" switch (that is, the file to load) is missing. Remember that any module that adds a method to main must use the Extensions module from the Dylan library.
one-of [Constant]
This function is useful in type expressions. Because the union function can only take two arguments, any type that is an enumeration of three or more singleton values requires cascading calls to union. Combine that with having to wrap each value in a call to singleton, and using union starts to create a lot of parameter list bloat. For example, the expression one-of(#"foo", #"bar", #"baz") is equivalent to union(singleton(#"foo"), union(singleton(#"bar"), singleton("baz")))
type-or [Constant]
This function is useful in type expressions. Because the union function can only take two arguments, any type that is the union of three or more types requires cascading calls to union. Using type-or can be more convenient and more clear to read. For example, the expression type-or(<foo>, <bar>, <baz>, <quux>) is equivalent to union(<foo>, union(<bar>, union(<baz>, <quux>)))
The Extensions module exports the following <table> subclasses:
<equal-table> [Class]
This class is a subclass of <table> that uses the \= function to compare keys and the equal-hash function to generate hash codes. If you define your own classes and \= methods specialized on those classes, then you should define a method for the equal-hash function specialized to your classes (see the equal-hash function description).
<value-table> [Abstract Class]
This class is a subclass of <table>. Users can define subclasses of this class and provide a method for table-protocol that is specialized to their new subclass. Any subclass of <value-table> must use a hash function that never use an object's identity (that is, its location in the heap) as a means of computing a hash id. These tables are specifically designed to save overhead in testing hash states and whether the table needs to be rehashed after garbage collections. The second value of the hash-function should always be $permanent-hash-state. For example:define class <my-table> (<value-table>) end class; define method table-protocol (table :: <my-table>) values(\=, string-hash); end method;
The Extensions module exports the following functions to make it easier for users to use <equal-table>s and <value-table>s:
equal-hash (key :: <object>) [Generic Function] => (hash-id :: <integer>, hash-state)
This function returns a hash id and hash state for use with <equal-table>s. If you define your own classes and \= methods specialized on those classes, then you should define a method for the equal-hash function specialized to your classes. Specialized methods exist for <number>, <character>, <function>, <symbol>, and <collection>. The method for any other <object> returns the integer 42 and $permanent-hash-state. This function may use an object's identity (that is, its location in the heap) to produce a hash id.
collection-hash [Function] (collection :: <collection>, key-hash-function :: <function>, elt-hash-function :: <function>) => (hash-id :: <integer>, hash-state)
This function hashes every element of a collection using key-hash-function on the keys and element-hash-function on the elements. Note, though two sequences may be equal according to the \= function, sequence-hash and collection-hash may return different hash codes for the sequences.
sequence-hash [Function] (sequence :: <sequence>, elt-hash-function :: <function>) => (hash-id :: <integer>, hash-state)
This function hashes every element of sequence using elt-hash-function, merging the resulting hash codes in order. Note, though two sequences may be equal according to the \= function, sequence-hash and collection-hash may return different hash codes for the sequences.
string-hash (string :: <string>) [Function] => (hash-id :: <integer>, hash-state)
This function calls produces hash codes for strings without using the strings' identities. This function is suitable for use with <value-table>s.
value-hash (object :: <object>) [Generic Function] => (hash-id :: <integer>, hash-state)
This function produces hash codes for objects without using the objects' identities. This function is suitable for use with <value-table>s. Mindy provides methods specialized for the following types: <string> <integer> <float> <character> <symbol> singleton(#t) singleton(#f)
The Extensions module exports the following functionality for controlling the exiting of applications:
exit (#key exit-code :: <integer> = 0) [Function]
Causes the process to exit. Mindy calls this function when there is no code left to execute.
on-exit (function :: <function>) [Function]
Arranges for the exit function to call the argument function. The argument function must take no required arguments. Users may call on-exit multiple times to install more than one function for exit to call, but the order in which exit invokes the functions is undefined. Calling on-exit on the same function repeatedly, installs that function multiple times.
The Extensions module exports the following weak-pointer functionality:
<weak-pointer> [Class]
This class is a subclass of <object>. The make method for this class takes the keyword parameter object:. Instances of this class refer to the object passed to the make method as long as some other reference to the object exists. Whenever an instance of <weak-pointer> is the only reference to some object, and a garbage collection occurs, then Mindy considers the object to be garbage. When Mindy garbage collects an object referred to by a weak-pointer, then Mindy marks the weak-pointer as being broken (see the weak-pointer-object function).
weak-pointer-object (wp :: <weak-pointer>) [Function] => (object :: <object>, broken? :: <boolean>)
Returns the object referred to by the weak-pointer and whether the weak-pointer is broken. A weak-pointer is broken when it contains the only reference to some other object, and in this situation, weak-pointer-object returns the values #f and #t.
The Extensions module exports the following functionality that will go away as various official I/O modules get installed into Mindy:
format (control-string, #rest arguments) [Function]
This format adheres to the format strings described in the Dylan Interim Reference Manual with one exception. Mindy incorrectly prints instances of <condition> supplied to the %S directive.
print (thing) [Function] prin1 (thing) [Function]
Prints thing to stdout. Print follows thing with a newline. How things are printed cannot be extended, because it is done by C code.
puts (string) [Function]
Prints the contents of string, which must be a <byte-string>, to stdout.
putc (char) [Function]
Prints the character to stdout.
getc () [Function]
Read and return the next character from stdin. Returns #f at EOF.
<buffer> [Class]
This class is a subclass of <vector>. It is the built-in class in Mindy that the Stream module supports.
copy-bytes (dst :: type_or(<buffer>, <byte-vector>, <byte-string>), => dst :: type_or(<buffer, <byte-vector>, <byte-string>)
Copies count bytes from src starting at src-offset to dst starting at dst-offset. This function returns dst. This function does no bounds checking. Dst and Src may be the same (==) object; this function ensures the destination portion correctly gets the bytes that were in the source portion of the object.
The File-descriptor module exports the following functions and constants:
fd-exec (command-line :: <string>) [Function] out-fd :: union(<integer>, singleton(#f)))
This function provides a facility for running programs and scripts from within Mindy. The command-line argument should contain the name of the program and all of the command line arguments for that program. This function returns the file descriptors for the new process's standard input and output. If fd-exec is unable to start the process, then it returns #f and #f.
fd-open (path :: <byte-string>, flags :: <integer>) [Function] errno :: union(singleton(#f), <integer>))
This function calls the C open system call and returns the file descriptor and #f, if successful. If the first value is #f, then the second value is the error number. You can convert the error number to a string using the fd-error-string function.
fd-close (fd :: <integer>) [Function] errno :: union(singleton(#f), <integer>))
This function calls the C close system call and returns #t and #f, if successful. If the first value is #f, then the second value is the error number. You can convert the error number to a string using the fd-error-string function.
fd-read (fd :: <integer>, buffer :: <buffer>, offset :: <integer>, count :: <integer>) [Function] => (count :: union(singleton(#f), <integer>), errno :: union(singleton(#f), <integer>))
This function calls the C read system call and returns the number of bytes read and #f, if successful. Offset is an index into buffer that is the index at which fd-read should start writing into the buffer. All other arguments are the same as those described by the Unix man page.If the first value is #f, then the second value is the error number. You can convert the error number to a string using the fd-error-string function.
This function does no bounds checking.
fd-write (fd :: <integer>, buffer :: <buffer>, offset :: <integer>, count :: <integer>) [Function] => (count :: union(singleton(#f), <integer>), errno :: union(singleton(#f), <integer>))
This function calls the C write system call and returns the number of bytes written and #f, if successful. Offset is an index into buffer that is the index at which fd-write should start reading from the buffer. All other arguments are the same as those described by the Unix man page.If the first value is #f, then the second value is the error number. You can convert the error number to a string using the fd-error-string function.
This function does no bounds checking.
fd-input-available? (fd :: <integer>) [Function] errno :: union(singleton(#f), <integer>))
This function returns whether there is any input available on the file descriptor. The second return value is #f if fd-input-available? could determine whether input was available, but if there is an error, the second return value is the error number. You can convert the error number to a string using the fd-error-string function.
fd-sync-output (fd :: <integer>) [Function] => (win? :: <boolean>, errno :: union(singleton(#f), <integer>))
This function calls the C fsync system call and returns #t and #f, if successful. If the first value is #f, then the second value is the error number. You can convert the error number to a string using the fd-error-string function.
fd-seek (fd :: <integer>, offset :: <integer>, whence :: <integer>) [Function] => (new-pos :: union(singleton(#f), <integer>), errno :: union(singleton(#f), <integer>))
This function calls the C lseek system call and returns the new absolute position in the file and #f, if successful. If the first value is #f, then the second value is the error number. You can convert the error number to a string using the fd-error-string function.
fd-error-string (errno :: <integer>) [Function] => msg :: union(singleton(#f), <byte-string>)
This function calls the C strerror system call and returns the string that describes the given error number. If the error number is unknown, then fd-error-string return #f.
L_SET [Constant] L_INCR [Constant] L_XTND [Constant] FNDELAY [Constant] FAPPEND [Constant] FCREAT [Constant] FTRUNC [Constant] FEXCL [Constant] O_RDONLY [Constant] O_WRONLY [Constant] O_RDWR [Constant] O_NDELAY [Constant] O_APPEND [Constant] O_CREAT [Constant] O_TRUNC [Constant] O_EXCL [Constant] ENOENT [Constant] EIO [Constant] ENXIO [Constant] EACCES [Constant] EFAULT [Constant] EEXIST [Constant] ENOTDIR [Constant] EISDIR [Constant] EINVAL [Constant] ENFILE [Constant] EMFILE [Constant] ETXTBSY [Constant] ENOSPC [Constant] EROFS [Constant] EOPNOTSUPP [Constant] ELOOP [Constant] ENAMETOOLONG [Constant] EDQUOT [Constant] EBADF [Constant] EINTR [Constant] EWOULDBLOCK [Constant] EPIPE [Constant] EFBIG [Constant]
These constants are the same constants from the standard C libraries, file.h and errno.h. All the constants users need to call the functions in the File-descriptor module, or test the return values of these functions, should be exported from this module.
<thread> [Class]
This class is a subclass of <object>. Instances of this class are the handles by which programs manipulate threads.
spawn-thread (debug-name :: <byte-string>, [Function] => thread :: <thread>
Spawns a concurrent asynchronous thread and invokes init-function in that thread. The dynamic context of the thread is the same as if it were the main thread of a program at the beginning of the program's execution.
kill-thread (thread :: <thread>) [Function] => <thread>
Kills thread immediately. After calling this function, the argument thread never executes again.
<lock> [Abstract Class]
This class is a subclass of <object>. Instances of this class provide logical locks. A lock is locked when a thread successfully "grabs" a lock, and we say the thread "holds" the lock. Holding a lock in no way prohibits access to a resource. It is purely the convention of various threads to access a shared resource only after successfully grabbing a lock. If <lock> is passed to make, make returns a <spinlock>.
<spinlock> [Sealed Class]
This class is a subclass of <lock>. Instances of this class provide a single-locking model.Whenever a <spinlock> is unlocked, any thread may grab it. Whenever a <spinlock> is locked, any thread that tries to grab it will block. Whenever a <spinlock> is locked, any thread may release it.
<spinlock>s are designed to be held for a very short period of time, several machine instructions at most. Threads should only hold a <spinlock> for a very short period of time because other threads that are waiting for the lock are blocked and could be wasting CPU cycles by busy looping; that is, waiting for a <spinlock> does not necessarily use anything as heavy weight as a system call to sleep the thread waiting for the lock. If only a couple threads are sharing a resource, it may be more efficient to actually hold a <spinlock> for a moderate amount of time while performing a high-level operation, rather than use a lock to build a more heavy-weight mutual exclusion mechanism (such as a semaphore) to isolate access to the shared resource.
Unlocking a <spinlock> when it is already unlocked signals an error.
<multilock> [Sealed Class]
This class is a subclass of <lock>. Instances of this class provide a multilocking model.Whenever a <multilock> is unlocked, any thread may grab it. A thread that holds a <multilock> may grab the lock repeatedly without releasing the lock. Each grab effectively increments a counter, and each release effectively decrements a counter. A <multilock> is available to be grabbed by any thread when the counter returns to zero; therefore, a thread must release the lock for each grabbing of the lock. This behavior is useful for implementing a high-level operation that needs to isolate access to a resource while calling a few lower-level operations that lock the resource; in this way, the high-level operation effectively calls all the lower-level operations atomically with no other threads affecting the state of the resource between the calls.
Whenever a <multilock> is locked, only the thread that holds the lock may release it.
<multilock>s are designed to be held for as long as a thread requires. When other threads call the grab-lock function and block because a <multilock> is locked, the other threads are guaranteed to sleep until the lock is available.
Unlocking a <multilock> when it is already unlocked signals an error.
<semaphore> [Sealed Class]
This class is a subclass of <lock>. Instances of this class provide a single-locking model.Whenever a <semaphore> is unlocked, any thread may grab it. Whenever a <semaphore> is locked, any thread that tries to grab it will block. Whenever a <semaphore> is locked, any thread may release it.
<semaphore>s are designed to be held for as long as a thread requires. When other threads call the grab-lock function and block because a <semaphore> is locked, the other threads are guaranteed to sleep until the lock is available.
Unlocking a <semaphore> when it is already unlocked signals an error.
locked? (lock :: <lock>) [Function] => locked? :: <boolean>
Returns whether the lock is held by any thread.
grab-lock (lock :: <lock>) [Generic Function] => meaningless :: singleton(#f)
Returns after successfully grabbing the lock. If the lock is not immediately available, this function waits for the lock to become available.
grab-lock (lock :: <spinlock>) [G.F. Method] => meaningless :: singleton(#f)
Returns after successfully grabbing the lock. This method can only grab lock when it is unlocked. When the lock is held, this method may busy-loop until the lock is unlocked.
grab-lock (lock :: <semaphore>) [G.F. Method] => meaningless :: singleton(#f)
Returns after successfully grabbing the lock. This method can only grab lock when it is unlocked. When the lock is held, this method puts the calling thread to sleep until until the lock is available.
grab-lock (lock :: <multilock>) [G.F. Method] => meaningless :: singleton(#f)
Returns after successfully grabbing the lock. A single thread may successfully call this method repeatedly, but the thread must call release-lock once for each call to grab-lock. If the thread calls release-lock fewer times than grab-lock, the lock remains locked, and any threads waiting for the lock will continue to wait. When a thread that does not hold the lock calls this method, the method puts the calling thread to sleep until the lock is available.
release-lock (lock :: <lock>) [Generic Function] => meaningless :: singleton(#f)
Releases the lock. If lock is unlocked, this function signals an error.
release-lock (lock :: union(<spinlock>, <semaphore>) [G.F. Method] => meaningless :: singleton(#f)
Releases the lock. If lock is unlocked, this function signals an error. Any thread may unlock a <spinlock> or <semaphore>, regardless of whether it is the thread that successfully grabbed the lock.
release-lock (lock :: <multilock>) [G.F. Method] => meaningless :: singleton(#f)
Releases the lock. If lock is unlocked, this function signals an error. Only the thread that holds lock may call this function, and if another thread tries to release the lock, this method signals an error. When this function returns, lock may still be locked. A thread that has repeatedly grabbed a <multilock> must call release-lock once for each call to grab-lock.
<event> [Class]
This class is a subclass of <object>. Threads use events to block without busy looping and to communicate to other threads that they should wake up.
wait-for-event (event :: <event>, lock :: <lock>) [Generic Function] => meaningless :: singleton(#f)
Releases the lock and puts the calling thread to sleep until some other thread signals event. After this function returns, the lock is unheld, and the calling thread must try to grab the lock before accessing any shared resources. Due to implementation details, this function may return even when the lock is unavailable, or the event has not truly occurred; because of this, programs need to loop over wait-for-event and grab-lock, testing that the event actually occurred. Methods exist for both <spinlock>s and <semaphore>s.
signal-event (event :: <event>) [Function] => meaningless :: singleton(#f)
Signals that the event occurred, indicating that Mindy should wake up a thread that is waiting on this event.
broadcast-event(<event>) [Function] => meaningless :: singleton(#f)
Signals that the event occurred and causes Mindy to wake up every thread that is waiting on this event.
// This example shows two routines, get-queue and release-queue. Code // that accesses the queue should call get-queue before doing so and call // release-queue when done. Any code failing to isolate access to the // queue in this way has undefined behavior and is incorrectly written. // // This variable is #t if and only if the queue is generally available. // define variable queue-available? = #t; // This constant holds an event object used to signal when the queue // becomes generally available again. // define constant queue-available = make(<event>); // This constant holds a lock object used to isolate access to // queue-available? for testing and setting purposes. // define constant queue-lock = make(<lock>); // When this function returns, the caller has exclusive access to the // queue. If necessary, this function waits for the queue to become // available, but it does not busy loop. This function returns #f as // a meaningless return value. // define method get-queue () grab-lock(queue-lock); while (~ queue-available?) wait-for-event(queue-available, queue-lock); grab-lock(queue-lock) end; queue-available? := #f; lock-release(queue-lock); #f; end; // This function releases the queue and signals that it is released so // that someone waiting on the queue will be woken up. This function // returns #f as a meaningless return value. // define method release-queue () grab-lock(queue-lock); queue-available? := #t release-lock(queue-lock); signal-event(queue-available); #f; end;The following example shows how to use a lock to isolate queue access in a different way than the previous example:
// This constant holds an event object used to signal when an element // exists in the queue. // define constant something-available = make(<event>); // This constant holds a lock that is held whenever a thread is accessing // queue. // define constant lock = make(<lock>); // This constant holds a queue object. // define constant queue = make(<deque>); // This function returns an element from queue. If no element is // immediately available, then this function blocks until it can return // an element. This function assumes only one or two other threads are // ever waiting for the queue, and it assumes pop is a fast high-level // operation. // define method get-something() grab-lock(lock); while (empty?(queue))) wait-for-event(something-available, lock); grab-lock(lock); end; let result = pop(queue); lock-release(lock); result; end; // This function adds thing to queue. It assumes only one or two other // threads are ever waiting for the queue, and it assumes push is a fast // high-level operation. // define method put-something(thing) grab-lock(lock); push(queue, thing); release-lock(lock); signal-event(something-available); end;
$INSTALL/doc/streams.{ps,txt}The Streams library exports two modules, Streams and Standard-io. The Streams module exports all identifiers from the streams specification. The Streams module also exports <fd-stream>:
<fd-stream> [Class]
This class is a subclass of <stream>. These streams are based on C file descriptors, and they do not adhere to the Random Access Protocol described in the Gwydion streams specification. The make method accepts the following keywords: direction: This keyword is optional and defaults to #"input". When supplied, it must be either #"input" or #"output". fd: This keyword is required and should be an open file-descriptor. size: This keyword is optional and is the size of the buffer. See the streams specification for details.
The Standard-io module exports the following:
*standard-input* [Constant] *standard-output* [Constant] *standard-error* [Constant]
These have the following values respectively:make(<fd-stream>, fd: 0) make(<fd-stream>, fd: 1, direction: #"output") make(<fd-stream>, fd: 2, direction: #"output")
Rest arguments in Mindy are <sequence>s. You cannot use any functions on the rest argument that assumes the collection is an instance of any class more specific than <sequence>; for example, you cannot use the head or tail functions because they operate on instances of <pair>.