Page Numbers: Yes X: 527 Y: 10.2" First Page: 32
Margins: Top: 1.5" Bottom: 1"
Heading:
Mesa Pup Package Functional Specification
Appendix A: Stream Package
The Pup Package uses a stream interface different from the standard Alto/Mesa one. Pup’s version includes, among other things, subsequences (a generalization of mark bytes) and an attention (break) facility. Note that this appendix describes the stream interface in a general (device and implementation independent) way; see the previous section on the Byte Stream Interface for Pup specifics.
The stream package defined by the interface Stream provides a device and format independent interface for sequential access to a stream of data. In particular,
It provides a vehicle by which processes or subsystems can communicate with each other, whether they reside on the same machine or on different machines.
It permits processes or subsystems to transmit arbitrary data to or from storage media in a device-independent way.
It defines a standard way for transforming the detailed interface for a device into a uniform, high level interface which can be used by other client software.
It provides an environment for implementing simple transformations to be performed on the data as it is being transmitted.
It provides optional access to and control over the mapping of data onto the physical format of the storage or transmission medium being used.
The stream package provides several facilities, not all of which may be important to an individual client. First, there is the stream interface, the set of procedures and data types by which a client actually controls the transmission of a stream of information. Each of the operations of the stream interface takes as a parameter a Stream.Handle which identifies the particular stream being accessed. Second, the stream package defines the concepts of transducer and filter. A transducer is a software entity (e.g. module or configuration) which implements a stream connected to a specific device or medium. A filter also implements a stream, but only for the purpose of transforming, buffering, or otherwise manipulating the data before passing it on to another stream. Transducers and filters may be provided either by the system or by clients. Third, the stream package provides a standard way of concatenating a sequence of filters (usually terminated with a transducer) to form a compound stream called a pipeline. A pipeline is accessed by means of the normal stream operations, and causes a sequence of separate transformations to be applied to data flowing between the client program at one end and the physical storage (or transmission) medium at the other.
The use of pipelines permits clients to interpose new stream manipulation programs (filters and transducers) between clients (producers and consumers of data) without modifying the interfaces seen by the clients. For example, a data format conversion program can obtain its data either from a tape or from a disk, using the same stream interface, and hence the same program logic for both. Similarly, filters performing such functions as code conversion, buffering, data conversion, encryption, etc. may be inserted into a pipeline without affecting the way the client sends and receives data through the stream interface.
The stream facility transmits arbitrary data, regardless of format and without prejudice to its type or characteristics. The data may comprise a sequence of bytes, words, or arbitrary Mesa data structures. The stream facility does not presume or require the encoding of information according to any particular protocol or convention. Instead, it permits clients to define their own protocols and standards according to their own needs.
In this appendix, sections A.1, A.2, and A.3 will be of interest to all clients. Section A.4 will be of interest only to those clients wishing to control the physical record characteristics of a particular stream. Section A.5 will be of interest only to those clients wishing to implement their own filters or transducers. In addition, the clients of a particular stream type (e.g. disk, tape, etc.) will normally have to consult separate documentation regarding the details of that kind of stream.
A.1 Semantics of Streams
The stream facility supports transmission of a sequence of eight-bit bytes. This sequence may be divided into identifiable subsequences, each of which has its own subsequence type.
Stream.Byte: TYPE = [0..255];
Stream.SubSequenceType: TYPE = [0..255];
A subsequence may be null; i.e., it may be of zero length and contain no bytes but still contain the SubSequenceType information. This information allows all subsequences to be easily identified and separated from each other while shielding clients from the bothersome problems of control-codes (i.e. embedding control-codes into the stream, making them transparent, and building a parser to implement such transparency).
Additionally, an attention flag may be inserted into a stream sequence. This is neither a byte nor a SubSequenceType, but simply an indication of an extraordinary situation. Attention flags are transmitted through the stream as quickly as possible, possibly bypassing bytes and changes in SubSequenceType which were transmitted earlier but which are still in transit. This provides a simple mechanism for implementing breaks (similar to the "attention-key" of many time-sharing systems).
Streams per se have no notion of a byte number (i.e. an array index); they deal only with the sequential order of successive bytes comprising the arbitrary binary data being transmitted. The stream interface is thus unsuitable for applications requiring random access.
Streams have no intrinsic notion of the bytes passing through them being grouped into physical records. The client program can completely ignore physical record structure and is thus relieved of the burden of dealing with the associated packing and unpacking problems. If, however, it becomes necessary to control or determine the underlying physical record structure, as determined by the particular storage (or transmission) medium, the interface provides extended facilities which allow this.
All of the procedures described here are synchronous and none returns until the indicated operation is complete. That is, an input operation does not return until the data is actually available to the client, and an output operation does not return until the data has been accepted by the stream and client buffers may be reused. Note, however, that a stream component may do internal buffering and that the acceptance of data means only that the stream component itself has a correct copy and is in a position to proceed asynchronously to write or send it.
Streams are inherently full-duplex -- i. e., separate processes may be transmitting and receiving simultaneously. The stream interface does not guarantee mutual exclusion among different processes attempting to access the same stream. However, individual transducers or filters may restrict themselves to half-duplex operation and/or may implement such mutual exclusion or more elaborate forms of synchronization as are appropriate. Documentation for such filters and transducers should be consulted on a case-by-case basis for details.
A.2 Operations on Streams
The stream interface provides the following information transmission operations: GetBlock, GetByte, GetChar, GetWord, PutBlock, PutByte, PutChar, PutWord, SendNow, SetSST, SendAttention, and WaitForAttention.
A client program identifies a particular instance of the stream interface by means of a Stream.Handle.
Stream.Handle: TYPE = . . . ;
A Handle identifies an object (see :A.5.1) which embodies all of the information concerning the transfer of data to and/or from the client program via stream operations. It is passed as a parameter to each of the data transmission operations of the following sections to specify the stream to which the operations apply.
A.2.1 GetBlock and PutBlock
The principal operations for transferring blocks of data are Stream.GetBlock and Stream.PutBlock. Each of these takes a parameter specifying the block of virtual memory to or from which bytes are to be transmitted.
Stream.Block: TYPE = RECORD [
blockPointer:
LONG POINTER,
startIndex, stopIndexPlusOne: CARDINAL];
A Block describes a section of memory which will be the source and/or sink of the bytes transmitted; the section of memory described is a sequence of bytes (not necessarily word aligned). The blockPointer may be regarded as a base of a packed array of bytes; it selects a word such that a startIndex of zero would select the left byte of that word (i.e., bits 0 - 7). The selected block consists of the bytes blockPointer↑[i] for i in [startIndex..stopIndexPlusOne). Notice that a Block cannot describe more than 216-1 bytes or 215-1 words.
Note:Some of the operations of this section and the next may cause signals to be generated. If such a signal is RESUMEd, transmission continues where it left off -- i.e., any changes made by the catch phrase to the Block record and/or to the input options (see below) are ignored. If, however, such a signal is RETRY’ed, the next byte of the stream sequence is transmitted to or from the byte specified by the current value of the Block record and/or input options, either of which might have been updated by the catch phrase. In no case is the stream sequence itself "backed up." That is, bytes previously received from input are not re-received, and bytes previously transmitted on output are not withdrawn.
The primary input operation is Stream.GetBlock.
Stream.GetBlock: PROCEDURE [sH: Stream.Handle, block: Stream.Block]
RETURNS [bytesTransferred: CARDINAL, why: Stream.CompletionCode,
sst:
Stream.SubSequenceType];
Stream.CompletionCode: TYPE = {normal, endRecord, sstChange, endOfStream};
The parameter block describes the memory area into which the bytes will be placed. GetBlock does not return until the input is terminated. Its exact behavior, however, is controlled by a set of input options which may be set by the client using the following operation:
Stream.SetInputOptions: PROCEDURE [sH: Stream.Handle, options: Stream.InputOptions];
Stream.InputOptions: TYPE = RECORD [
terminateOnEndPhysicalRecord, signalLongBlock, signalShortBlock, signalSSTChange, signalEndOfStream:
BOOLEAN];
Stream.defaultInputOptions: Stream.InputOptions = [FALSE, FALSE, FALSE, FALSE, FALSE];
SetInputOptions controls exactly how GetBlock terminates and what signals it generates. Ordinarily (i.e., with the parameter options set to defaultInputOptions) the transmission will not terminate until the entire block of bytes is filled. However, under exceptional conditions described in :A.4, the transmission may terminate before the block is filled and may also result in a signal. In all cases the procedure will return the actual number of bytes transferred, a CompletionCode indicating the reason for termination, and the latest SubSequenceType encountered. The input operation may conveniently be restarted where it left off by first adding the result bytesTransferred to block.startIndex to update the record describing the block of bytes.
Two circumstances which always suspend the transmission of data before the block is filled are (a) the detection of a change in SubSequenceType and (b) the endOfStream. If the input option signalSSTChange is FALSE (the default case), then the procedure GetBlock terminates immediately and returns the number of bytes transferred, with why = sstChange, and sst set to the new value of the SubSequenceType. If the input option signalSSTChange is TRUE, then the signal
Stream.SSTChange: SIGNAL [sst: Stream.SubSequenceType, nextIndex: CARDINAL];
is generated. The parameter sst specifies the new SubSequenceType, and the parameter nextIndex specifies the byte index within the block where the first byte of the new subsequence will be placed. This signal may be resumed, and the effect is to continue the data transmission as though the change in SubSequenceType had not occurred (i.e., in the same block of bytes).
Caution:A catch phrase for this signal must not attempt any other stream operations using the same Stream.Handle, for this will corrupt the internal state information maintained for the stream.
Implementation of the end-of-stream feature is strictly transducer and filter specific, and optional. All transducers and filters need not implement this feature. Transducer and filter implementors may implement an end-of-stream mechansim using any protocol they desire. Typically, this feature would be implemented by exchanging subsequence types in some specific order. If implementors use this mechansim, then they must document the subsequence types reserved for this, so that clients do not inadvertantly use the same ones for their own protocol. When putting together a pipeline from a transducer and filters, great care needs to be taken to preserve the end-of-stream feature through all the stream components. If the input option signalEndOfStream is TRUE and the stream component detects that the end-of-stream has occured, then the signal
Stream.EndOfStream: SIGNAL [nextIndex: CARDINAL];
is generated. The parameter nextIndex specifies the byte index immediately following the last byte of the stream sequence filled into a client’s block.
Note:Stream component implementors may provide special procudure calls in order to actively cause a stream to be terminated.
The principal output operation is Stream.PutBlock.
Stream.PutBlock: PROCEDURE [sH: Stream.Handle, block: Stream.Block, endPhysicalRecord: BOOLEAN];
This operation is analogous to Stream.GetBlock. The parameter block describes the area of memory from which information is transmitted. This procedure returns only after the data has been accepted by the stream, at which time the client may reuse the block. If the client is ignoring physical record boundaries (the default case) then parameter endPhysicalRecord should be set to FALSE. Otherwise, see :A.4.
Note:Stream operations have the right to discard empty blocks, hence a PutBlock operation specifying a block of length zero may be a no-op.
A.2.2 Additional Data Transmission Operations
In addition to GetBlock and PutBlock, the following operations are provided:
Stream.GetByte: PROCEDURE [sH: Stream.Handle] RETURNS [byte: Stream.Byte];
Stream.GetChar: PROCEDURE [sH: Stream.Handle] RETURNS [char: CHARACTER];
Stream.GetWord: PROCEDURE [sH: Stream.Handle]
RETURNS
[word: Stream.Word];
Stream.Word: TYPE = [0..65535];
GetByte and GetChar operations get the next Byte or CHARACTER from the stream sequence and return it. They are equivalent to a call upon Stream.GetBlock, specifying a Block containing one byte. The GetWord operation gets the next Word from the stream sequence and returns it. It is equivalent to a call upon Stream.GetBlock, specifying a Block containing AltoDefs.BytesPerWord bytes. Input options are assumed to be signalShortBlock, signalLongBlock, and endPhysicalRecord = FALSE, and signalEndOfStream and signalSSTChange = TRUE. Thus, these operations may result in signal SSTChange or EndOfStream.
Note:When the SIGNALs SSTChange or EndOfStream are generated, nextIndex should be equal to 0. In the case of GetWord if nextIndex = 1, the caller is responsible for processing the "half-word".
Stream.PutByte: PROCEDURE [sH: Stream.Handle, byte: Stream.Byte];
Stream.PutChar: PROCEDURE [sH: Stream.Handle, char: CHARACTER];
Stream.PutWord: PROCEDURE [sH: Stream.Handle, word: Stream.Word];
The PutByte and PutChar operations transmit the next Byte or CHARACTER to the medium. They are equivalent to a call on Stream.PutBlock, specifying a Block containing one byte. The PutWord operation transmits the next Word to the medium. This procedure is equivalent to call on Stream.PutBlock, specifying a Block containing AltoDefs.BytesPerWord bytes. These output operations specify endPhysicalRecord = FALSE.
Stream.SendNow: PROCEDURE [sH: Stream.Handle];
This operation flushes the stream sequence; it guarantees that all information previously output (by means of PutBlock, PutByte, PutChar, PutWord or SetSST) will actually be transmitted to the medium (perhaps asynchronously). This procedure is equivalent to a call on Stream.PutBlock, specifying a Block containing no bytes and endPhysicalRecord = TRUE (see :A.4). Client programs which are not concerned with physical record boundaries should nevertheless call SendNow at appropriate times to ensure that the bytes and changes in SubSequenceType have actually been sent and are not buffered internally within the stream, awaiting additional output operations.
Stream.SetSST: PROCEDURE [
sH:
Stream.Handle, sst: Stream.SubSequenceType];
This operation causes all subsequent bytes to have the indicated SubSequenceType. Even if the subsequent sequence of bytes is null (i.e., a call on SetSST is immediately followed by another), the SubSequenceType change demanded by this call will still be available to the receiver of the stream sequence.
Note:SubSequenceTypes are intended to be used to delineate different kinds of information flowing over the same stream (e.g. to identify control information, indicate end-of-file, etc.) The interpretation of a SubSequenceType value is a function of the particular stream.
Note:A SetSST operation specifying a SubSequenceType identical to the previous SubSequenceType is a no-op. Otherwise, SetSST always has the side effect of completing the current physical record, as explained in :A.4.
A.2.3 Attention Flags
The following operation causes an attention flag and an associated byte of data to be transmitted via the stream facility.
Stream.SendAttention: PROCEDURE [sH: Stream.Handle, byte:Stream.Byte];
Note that neither the attention flag nor the data byte occupy a byte in the stream sequence. They are out of band signals. Note also that an attention is not necessarily transmitted in sequence, but may bypass bytes and changes in SubSequenceType which were transmitted before it.
Note:This operation may have the side effect of completing the current physical record, as explained in :A.4. A client process will typically follow the SendAttention by a change in SubSequenceType or some recognizable pattern of bytes. This permits the recipient to identify where the SendAttention occurred.
Note:byte may be used by the client protocol to transmit other information regarding this attention.
The following operation awaits the arrival of an attention flag.
Stream.WaitForAttention: PROCEDURE [sH: Stream.Handle] RETURNS [Stream.Byte];
When an attention is received on the stream sH, this procedure returns the byte of data associated with the attention. It is the responsibility of the client program to determine the appropriate action to take. If more than one attention flag has been sent, these will be queued by the stream. Each return from a call on WaitForAttention corresponds to precisely one attention sent by SendAttention.
Note:This operation is usually executed by a different process from that operating upon the stream. It returns as soon as the attention is received, whether or not all of the bytes preceding it in the stream have been transferred.
A.2.4 Timeouts
Any of the operations of this section (except SendAttention and WaitForAttention) may fail to complete within a reasonable amount of time due to external conditions. For example, a stream operation on a Pup stream may fail because the process at the other end has terminated, aborted, or ceased to pay attention. In such a case the following signal is generated:
Stream.TimeOut: SIGNAL [nextIndex: CARDINAL];
The parameter of this signal indicates the position within the block of bytes where the next byte would be placed. This signal may be resumed.
Note:If this signal is RETRY’ed all previously received data may be lost. This is because it is likely that a stream component is performing internal bufferring (transferring data from its buffer into the client’s block), and the action of RETRYing the SIGNAL may not tell the component that it must refill the client’s block. Even if the component was informed of this fact, it may have discarded data already transferred into the client’s block from its internal buffer
Caution:A catch phrase for this signal must not attempt any other stream operations using the same Stream.Handle, for this will corrupt the internal state information maintained for the stream.
A.3 Creating and Deleting Streams
There are no general operations for creating streams. The reason for this is that the components of a stream -- i.e., pipelines, transducers, and filters -- must be able to take arbitrary parameters at the time they are created. It is not possible for the system to specify a general interface for their creation without either compromising the basic typesafeness of Mesa or constraining the flexibility and power of client-provided streams. Thus, the create function is implemented on a case-by-case basis, and clients must therefore refer to documentation for individual stream components for the correct interface for this operation. In this section, the general style is illustrated by means of a hypothetical example.
For example, if a utility package implements a transducer to a particular device, it is obligated to provide a means by which other clients can create instances of that transducer, use them, and later delete them. Suppose the name of the interface module providing this function is DeviceStream. Then it would provide the following operation:
DeviceStream.Create: PROCEDURE [ --optional parameters-- ]
RETURNS [Stream.Handle, --optional other results--];
A client wishing to use the stream interface to access this device would thus call DeviceStream.Create, then use the Stream.Handle returned from it as a parameter to the stream operations of this chapter.
Similarly, a security package providing, say, an encryption facility might implement this by means of a filter for a stream. In this case, the interface might be called EncryptionFilter, and it would provide the following operation:
EncryptionFilter.Create: PROCEDURE [Stream.Handle, --optional other parameters-- ] RETURNS [Stream.Handle, --optional other results--];
The client could easily couple an instance of this filter with the transducer above. This is done by calling EncryptionFilter.Create, passing as a parameter the Stream.Handle returned from DeviceStream.Create. Then the Stream.Handle returned from EncryptionFilter.Create would be the one used in GetBlock, PutBlock, and the other operations of :A.2. The net effect would be stream components which, on input, read bytes from the device, decrypt them, and pass them to the client and which, on output, encrypt the bytes supplied by the client and write them on the device.
In general, creating a filter accepts one Stream.Handle as a parameter and returns another as its result. Thus, several filters, each implementing a simple transformation, may be concatenated together to implement a more interesting transformation on the stream. The parameter passed to each one is the result returned from the adjacent one. Such a concatenation, called a pipeline, is illustrated in Figure A.1.
<==<PupStreamsFig1.Press<
Figure A.1
This diagram illustrates how each Stream.Handle returned from a transducer or filter is passed as parameter to the next adjacent filter, and how the last one is used directly by the client. In particular, h1 is returned from the procedure which creates Transducer; it is passed to the procedure which creates Filter B, returning h2. This is passed, in turn, to the next filter, and so on, until hn is returned and passed to Filter A. Filter A is the last one in the pipeline, and its Stream.Handle, h, is returned to the client.
Figure A.2 shows the flow of data through the pipeline and the use of the various Stream.Handles as a result of a client call on Stream.GetBlock (calls on other data transmission operations are analogous).
<==<PupStreamsFig2.Press<
Figure A.2
Here, the client calls Stream.GetBlock[h, . . . ], which is transformed by the stream interface into an appropriate call on Filter A. Filter A, in turn, calls Stream.GetBlock[hn, . . . ], which is passed to the next filter in the pipeline, and so on, until eventually a call is made on Stream.GetBlock[h2, . . . ]. This is transformed into a call on Filter B, which then calls Stream.GetBlock[h1, . . . ], to invoke Transducer, which actually operates the device.
Note that the only difference between a transducer and a filter is that a transducer interfaces to some device, while a filter interfaces to another stream -- i.e., indirectly to another filter or transducer.
Note also that the client can construct a pipeline "manually," by tediously assembling the various components, instantiating each of them, and binding them together. However, a pipeline can also be presented as an integrated package, already assembled. For example, the two components described above may have been assembled into a pipeline called EncryptingDeviceStream. This pipeline might then provide the following two operations, which clients can call to create and delete an instance of this pipeline:
EncryptingDeviceStream.Create: PROCEDURE [ --optional parameters-- ]
RETURNS [Stream.Handle, --optional other results--];
EncryptingDeviceStream.Delete: PROCEDURE [Stream.Handle, --optional other parameters-- ] RETURNS [ --optional results-- ];
The client of such a stream would merely invoke these procedures to create and destroy the stream without having to bother about finding and putting together the individual components.
A.4 Control over Physical Record Characteristics
Most of the time, the client will not wish to know about how the data comprising a stream sequence is divided into physical records for recording or transmission. For some applications, however, this is of vital importance. The stream facility has been designed so that the details of the physical encoding can be ignored when desired, or completely known and controlled when that is necessary. On output, complete control of the placement of bytes in physical records can be achieved for most media. On input, complete information is available about how the bytes were arranged in physical records.
Note:These facilities to control the placement of bytes on physical records are not meant to be used as a means of transmitting information. In particular, a transducer might suppress or generate empty physical records and will necessarily partition oversize "physical" records into smaller ones. Any filter may rearrange (or completely obliterate) physical record boundaries. Documentation for the individual transducer or filter and for the individual transmission or storage medium should be consulted for full details.
The output and input cases will be treated separately. On output, bytes will be placed in turn into the same physical record until one of the following events occurs:
1) The SendNow procedure is called; it has the side effect of causing the current record to be sent. The next byte output will begin a new physical record. Thus, this is the main mechanism for controlling physical record size on output.
2) A PutBlock procedure is called with an endPhysicalRecord parameter of TRUE. After the transmission of this block of bytes, the current physical record is ended. If, at this point, the physical record is at its maximum size (see (4) below), an empty record will not be transferred.
3) A SetSST procedure has been called. The first byte of a new subsequence always begins a new record and has the new SubSequenceType. This may cause the previous record to be sent.
4) Enough bytes have been output to fill the physically maximal record. At this point the record will be written and a new record started. This maximum number is a function of the medium being written, hence documentation concerning the medium must be consulted to determine this value.
5) Some other device-dependent event, such as a timeout, occurs. In this case, a buffer may be flushed automatically. Details are documented with individual transducers.
On input, bytes will be placed in turn into the record until one of the following events occurs:
1) The end of the physical record is reached, the block of bytes described in the Block record is not exhausted, and either of the input options endPhysicalRecord or signalLongBlock is TRUE.
If signalLongBlock is TRUE, the following signal is generated:
Stream.LongBlock: SIGNAL [nextIndex: CARDINAL];
This signal indicates in nextIndex the position within the block of bytes where the next byte will be placed. If it is resumed, transmission continues as if it had not been generated.
Caution:A catch phrase for this signal must not attempt any other stream operations using the same Stream.Handle, for this will corrupt the internal state information maintained for the stream.
If endPhysicalRecord is TRUE, the input is terminated, indicating the number of bytes transferred and why = endPhysicalRecord. This applies whether or not a signal was generated.
2) The end of the physical record is reached at the same time that the block of bytes is exhausted. In this case, no signal is generated. If the input option endPhysicalRecord is TRUE, then why is set to endPhysicalRecord; otherwise, it is set to normal.
3) The block of bytes is exhausted, the end of the physical record has not been reached, and the input option signalShortBlock has the value TRUE. At this time the input is terminated (without losing the subsequent bytes of the physical record, which are still available for reading by subsequent GetBlock), and the signal Stream.ShortBlock is generated.
Stream.ShortBlock: ERROR;
This signal may not be resumed.
The easiest approach is usually to establish a Block longer than the longest expected physical record and specify input options signalLongBlock = FALSE, signalShortBlock = TRUE and endPhysicalRecord = TRUE. At this point the transmission will cease with the entire contents of the physical record in the block of bytes, and the number of bytes transmitted will be returned as the result of the GetBlock procedure. In this way a signal will be generated only under unusual circumstances.
A.5 Transducers, Filters, and Pipelines
The stream package is designed so that clients can implement their own stream components -- i.e., their own transducers, filters, and pipelines. The implementor of one of these has three different obligations to fulfill. First, he must design an interface (i.e., Mesa DEFINITIONS module) in the style described in :A.3, by which his clients create instances of that stream component. Such an interface (together with its accompanying implementation modules) is called a stream component manager. Second, he must provide a functional specification describing this interface and the detailed behavior of the stream component, including any specific signals, errors, parameters, etc., which it defines. Third, he must implement the actual component, if it is a filter or transducer. (Pipelines are assumed to be composed of previously implemented components which already have their own component managers and documentation.)
This section describes the standards, data types, and operations to be used in defining a new stream component. In :A.5.1, the precise interface which each instance of each filter or transducer must provide is specified. In :A.5.2, a typical method for implementing a filter or transducer manager is outlined.
A.5.1 Representing Filters and Transducers
At run time, a filter or transducer is represented by six procedures which execute in a common context to provide the data transmission operations of the that filter or transducer. Descriptors for these procedures are stored in a record defined by the stream package, pointed to by a Stream.Handle.
Stream.Handle: TYPE = POINTER TO Stream.Object;
Stream.Object: TYPE = RECORD [
options:
Stream.InputOptions,
get:
Stream.GetProcedure,
put:
Stream.PutProcedure,
setSST:
Stream.SetSSTProcedure,
sendAttention:
Stream.SendAttentionProcedure,
waitAttention:
Stream.WaitAttentionProcedure,
delete:
Stream.DeleteProcedure];
A client call on a stream operation is normally converted by the stream package into a call on the appropriate procedure named in the Stream.Object pointed to by the Stream.Handle parameter of that operation. Thus, it is the responsibility of the implementor of each filter and transducer to exactly satisfy the specifications of the stream package. The stream package assists in this task by utilizing the Mesa typechecking machinery and by defining the uniform interface encapsulated by Stream.Object.
In this section, the meanings of the fields of Stream.Object are enumerated.
The options field specifies the currently valid input options for the stream.
options: Stream.InputOptions,
This field is set by Stream.SetInputOptions and its current value is passed as a parameter to the get procedure described below. Implementors of filters and transducers need not be concerned with maintaining or inspecting this field.
The get field specifies the input procedure of the transducer or filter.
get: Stream.GetProcedure,
Stream.GetProcedure: TYPE = PROCEDURE [
sH:
Stream.Handle, block: Stream.Block, options: Stream.InputOptions]
RETURNS [bytesTransferred: CARDINAL,
why:
Stream.CompletionCode, sst: Stream.SubSequenceType];
This procedure is called by GetBlock, GetByte, GetChar, and GetWord. It must implement the semantics of GetBlock as described in :A.2.1 and :A.4. In particular, it must terminate according to the specifications of that section and must generate the signals SSTChange, LongBlock, ShortBlock, EndOfStream, and TimeOut (:A.2.4) as required.
Note:In a filter, the body of a GetProcedure will typically contain one or more calls on GetBlock, GetByte, GetChar, or GetWord with a Stream.Handle parameter pointing to the next stream component in the pipeline (i.e., the parameter passed at the time this filter was created). In a transducer, the body of a GetProcedure will typically have calls on input operations for the specific device being supported.
The put field specifies the output procedure provided by the filter or transducer.
put: Stream.PutProcedure,
Stream.PutProcedure: TYPE = PROCEDURE [
sH:
Stream.Handle, block: Stream.Block, endPhysicalRecord: BOOLEAN];
This procedure is called by PutBlock, PutByte, PutChar, PutWord, and SendNow. It must implement the semantics of PutBlock as described in :A.2.1 and :A.4. In particular, it must regard the parameter endPhysicalRecord = TRUE as an indication to flush any output buffers and actually initiate the physical transmission of information. It may suppress output requests specifying a block of no bytes provided that there is no previous output, change in SubSequenceType, or attention flag still waiting to be sent. This procedure may generate the signal TimeOut if necessary.
Note:In a filter, the body of a PutProcedure will typically contain one or more calls on PutBlock, PutByte, PutChar, PutWord, or SendNow with a Stream.Handle parameter pointing to the next stream component in the pipeline (i.e., the parameter passed at the time this filter was created). In a transducer, the body of a PutProcedure will typically have calls on output operations for the specific device being supported.
The setSST field specifies the procedure to change the current SubSequenceType of the output side of the filter or transducer.
setSST: Stream.SetSSTProcedure,
Stream.SetSSTProcedure: TYPE = PROCEDURE [
sH:
Stream.Handle, sst: Stream.SubSequenceType];
This procedure is called by Stream.SetSST and must conform to the semantics of that operation as described in :A.2.2. In particular, it should be a no-op if the new SubSequenceType is the same as the old one. Otherwise, it should have the effect of completing the current physical record (as if a call on Stream.SendNow had been made immediately before).
Note:A call on setSST may have the effect of changing the internal state of the stream component, or in the case of a filter, it may result in a call to SetSST to the next stream component in the pipeline, or both.
The sendAttention and waitAttention fields specify the two procedures implementing the sending of and waiting for attention flags in the transducer or filter.
sendAttention: Stream.SendAttentionProcedure,
waitAttention:
Stream.WaitAttentionProcedure,
Stream.SendAttentionProcedure: TYPE = PROCEDURE [
sH:
Stream.Handle, byte: Stream.Byte];
Stream.WaitAttentionProcedure: TYPE = PROCEDURE [sH: Stream.Handle]
RETURNS [Byte: Stream.Byte];
These two procedures will be called by Stream.SendAttention and Stream.WaitForAttention, respectively, and they must conform to the semantics of those operations as specified in :A.2.3.
Note:In a filter, it is not always necessary to implement these two procedures (or any others of this section) if all they do is call the corresponding operation in the next stream component in the pipeline. Instead, it is satisfactory to copy the procedure descriptor from one Stream.Object to the other. This has the effect of making the call pass "straight through" the filter with no overhead.
The delete field specifies a procedure to be used during deletion of a filter or transducer.
delete: Stream.DeleteProcedure,
Stream.DeleteProcedure: TYPE = PROCEDURE [sH: Stream.Handle];
This procedure provides a convenient way of deleting an instance of a transducer or filter once it is no longer needed; an example of its use is given in the next section. The parameter sH is provided for convenience and may be used for whatever purpose is useful.
A.5.2 Stream Component Managers
Implementors of stream components may create instances of them by whatever means is most appropriate to their requirements. A particular filter or transducer may, for example, consist of one module, a collection of modules, a local frame used in conjuction with the Mesa PORT facility, or some other construct. Moreover, it may exist on a given machine in only one or a limited number of copies which are regarded as "serially reusable" resources (for example, a transducer to a particular device, of which there is only one or a limited number on a machine), or it may exist in as many copies as appropriate. It is the responsibility of the stream component manager to create (or control access to) instances of that stream component, as appropriate. When access is granted, the component manager must also provide a pointer to a Stream.Object containing procedure descriptors for that component.
The typical way of implementing a component is as a single module which is instantiated at run-time by the Mesa NEW statement. Declared within this module would be the procedures of the component plus a Stream.Object containing their procedure descriptors. The component manager executes NEW to create a new instance of the stream, followed by START to initialize it (possibly passing parameters) and to obtain a pointer to its Stream.Object.
The component manager deletes instances of stream components by calling FrameDefs.UnNew or FrameDefs.SelfDestruct.
FrameDefs.UnNew (the inverse of NEW) takes a GlobalFrameHandle as its parameter. Component managers can determine the value of this parameter by calling FrameDefs.GlobalFrame, which takes a procedure descriptor of a procedure residing in the component to be deleted as a parameter, and returns a pointer to its global frame. FrameDefs.UnNew frees space occupied by the component’s global frame.
Caution:The client must ensure that there are no outstanding references to the component module being deleted -- i.e., no procedure descriptors or pointers which might be used. In addition, any process waiting for attentions (i. e., a process which has called but not returned from WaitForAttention) must be aborted and allowed to exit from the module. Failure to observe this caution will result in unpredictable effects. In particular, FrameDefs.UnNew must be called from outside the module being deleted.
FrameDefs.SelfDestruct sets the internal state of the process so that the module in which the calling procedure is declared will be unnewed after the calling procedure returns to its caller.
This operation has the effect of placing a "self-destruct" mechanism in the module which will take effect after the calling process exits from it. Thus, it is a means of deleting the stream component from within that component.
Caution:As above, the client must ensure that there are no references to the module currently in use by any process.
The typical use of FrameDefs.SelfDestruct will be from a procedure named in the delete entry of the Stream.Object. The component manager will call h.delete[h] (where h is a Stream.Handle). This procedure will perform the necessary finalization, such as flushing buffers, closing files or connections, releasing storage and resources, etc. It will then call FrameDefs.SelfDestruct and finally return to the component manager. After this return, the module representing this instance of the stream component will be deleted and space occupied by the stream’s global frame will be freed.