*start* 00370 00024 US Date: 16 Sept. 1981 4:05 pm PDT (Wednesday) From: kolling.PA Subject: FS note To: taft, mbrown cc: kolling FS may wish to complain if its client hands it more than one request for the same property in Read/WriteProperties. AC's interface is being changed so that it can't be fed duplicate lists in the same operation, so it will not see this. *start* 00631 00024 US Date: 16 Sept. 1981 6:29 pm PDT (Wednesday) From: Taft.PA Subject: Alpine Grapevine To: Birrell cc: Schroeder, MBrown, Kolling, Taft I have snatched copies of the Grapevine interfaces that were developed for the Walnut project by Robert Nix, and I have stored them on [Ivy]<Alpine>Defs>Grapevine>*. I had to recompile the defs files because the Rope interface has changed since they were last recompiled. Also in the same subdirectory is an updated (Cedar-style) version of my speculative GVAuthenticate interface. Grapevine.df brings over all these Grapevine interfaces, including GVAuthenticate. Ed *start* 01991 00024 US Date: 17 Sept. 1981 5:21 pm PDT (Thursday) From: Nelson.PA Subject: Alpine interfaces and RPC To: Taft, MBrown, Kolling cc: Birrell, Boggs, Nelson I read through your very good design documents, and have these RPC-related comments. The names of the three stub modules (and files, with .mesa) are (for example): AlpineAccessRpcControl (the binding interface) AlpineAccessRpcClientImpl (imports RPC ; exports AlpineAccess, AlpineAccessRpcControl) AlpineAccessRpcServerImpl (imports RPC, AlpineAccess; exports AlpineAccessRpcControl) The latter two are bound into your configurations along with RpcRuntime. It is quite likely that the encryption-related definitions will move to a new, public, non-Rpc-specific interface(s). The intent is that the RPC interface be strictly private to Lupine and the stubs. Plus, the encryption stuff will probably have a broad community. (In light of this change, XXRpcControl may change to XXRpcBinding; not clear yet; probably not.) Your msg was fortunately timed in that I was preparing a marshaling memo at the same time. Your use of Ropes and REF ANY caused some changes in my thinking. I now propose to marshal ropes (perhaps expensively), but there's no chance for REF ANY. However, a variant record for properties might do if you are willing to map between REFs and variants at remote boundaries. VALUE and RESULT must appear in the type names, as the upcoming memo describes. It is not difficult to handle address-containing types at levels>1. Diplomat does this. But there is a specification problem (which Diplomat handles poorly): is the parameter flavor always VALUE, always RESULT, inherited, or what? Programmers using pointers to get VAR parameters want dereferencing; those using pointers as handles do not. Lupine can probably get standard idioms right 98% of the time, but what about other cases? The marshaling memo will explore the last three issues, and more, further. Bruce *start* 02806 00024 US Date: 18 Sept. 1981 3:58 pm PDT (Friday) From: Taft.PA Subject: FYI: multi-pack logical volumes To: MBrown, Kolling As I was saying...... --------------------------- Date: 18 Sept. 1981 3:08 pm PDT (Friday) From: PSmith.ES Subject: IFS Questions To: Taft.pa cc: PSmith Ed, I have two questions that I would like to ask your advice on: ..... 2.) Second question: We have been unsuccessful in "Brownieing" from [Ivy]<Dover>Clover>Spruce.fonts due to transcient errors. The Spruce.fonts file is 13726 pages in total. We were sucessful in FTP'ing the file Spruce.fonts from up north to a T-80. This T-80 is not a file server, so in order to get the Spruce.fonts file onto our IFS, ISIS we tried to FTP the file Spruce.fonts from the T-80 to Isis. We were not successful we encountered the error message: "disk full" I checked the Dover directory on Isis and it had 14000 available pages, the spruce.fonts file is 13726 pages in length. My question, why couldn't we successfully FTP from the T-80 to the IFS? As a side note we were successful in FTPing the file Spruce.fonts from the T-80 to our printers T-80. Have you ever encountered this kind of a problem with FTP in the past? Thank-you for your time, Patty Smith 823-9272 --------------------------- Date: 18 Sept. 1981 3:53 pm PDT (Friday) From: Taft.PA Subject: Re: IFS Questions In-reply-to: Your message of 18 Sept. 1981 3:08 pm PDT (Friday) To: PSmith.ES cc: Taft ...... 2. The file system is divided into multiple "logical units" for software reasons; there are two logical units (about 65,000 pages each) on each T-300 disk. Each individual file has to be entirely contained on a single logical unit. Consequently, you can't store a file larger than the amount of free space on the emptiest logical unit. (The Statistics command shows the amount of free space on each logical unit.) This causes problems when you are trying to store a very large file on a large IFS that is nearly full, since the remaining free space will tend to be more-or-less evenly divided between the logical units, and so any individual unit will have a relatively small amount of space. The way we deal with Spruce.fonts is to store new copies on top of the existing one, rather than creating a new version as is ordinarily done. That is, if you already have a <Dover>Clover>Spruce.fonts!1, then store on top of that exact file rather than creating <Dover>Clover>Spruce.fonts!2. If you don't already have a Spruce.fonts of approximately the right size, then I'm afraid you are out of luck for now. Next time you add another T-300 pack to the system, you should quickly store Spruce.fonts so as to "reserve" the necessary space all on one logical unit. Ed ------------------------------------------------------------ *start* 02780 00024 US Date: 18 Sept. 1981 5:50 pm PDT (Friday) From: Taft.PA Subject: Internal use of FileStore interfaces To: MBrown, Kolling cc: Taft I've developed the following model of how internal clients (such as the AccessControl implementation) should make use of the public FileStore interfaces. Please tell me if you see anything wrong with it, as it's fairly fundamental. 1. Internal clients will bind directly to the public interfaces. That is, the public interfaces won't be a veneer over some corresponding local interfaces; rather, internal clients such as AccessControl will directly call AlpineFile, etc. This is NOT the same as saying that internal calls to the public interfaces will use RPC. However, it DOES mean that internal and remote calls through the FileStore interfaces will be indistinguishable from the FileStore implementation's point of view. In particular, it means that most internal calls through FileStore must pass a ConnectionID just the same as a remote client. 2. When an internal client is performing work on behalf of some external client, the internal client will pass the ConnectionID corresponding to that external client when calling the public FileStore interfaces. 3. When an internal client is performing work on its own behalf (e.g., AccessControl operating on the owner data base file), it will pass a special ConnectionID which corresponds to that internal client. This ConnectionID is created at initialization time and remains constant thereafter. The consequence of (2) on our present system organization is that we must use a ConnectionID in many places where we now use a ClientName (or ClientID, as we used to call it). For example, all the AccessControl procedures that are passed a ClientName to identify the calling client, such as AddOwner and CheckAccessFile, should instead be passed a ConnectionID. This is so that the implementation can in turn pass the same ConnectionID to FileStore when performing file operations on behalf of the client rather than on its own behalf. When the actual ClientName (RName) is required, it is possible to obtain it from the ConnectionID. I intend to provide a ConnectionMap that efficiently maps from ConnectionID to ClientName; it will be maintained by FileStore using information provided by AlpineAccess.Login and Logout. If we adopt this approach, the use of ConnectionIDs within Alpine is likely to become quite widespread, and to pervade to regions of the system that wouldn't otherwise think in terms of RPC, connections, etc. So I propose to rename ConnectionID to be ClientHandle. A ClientHandle represents an authenticated client on behalf of which some operation is to be performed; that client may be either external or internal to FileStore. Ed *start* 02711 00024 US Date: 20 Sept. 1981 9:44 am PDT (Sunday) From: Taft.PA Subject: RPC type safety To: Nelson, Birrell, Boggs cc: MBrown, Kolling, Taft I'm now convinced that the RPC conversation encryption mechanisms will guarantee the authenticity of clients. However, if we aren't careful, an authenticated client can still bring the server to its knees by violating the type system. I assume the RPC design group has considered this problem. Here are my thoughts. The Cedar safe language ensures only that address-containing types will not be confused with each other or with random bit patterns. From Cedar's point of view, confusing two non-AC types doesn't endanger the Cedar runtime system, so Cedar doesn't enforce type-correctness in such a case. Since AC types are not passed across remote interfaces (they are always dereferenced and passed by value), I conclude that the Cedar safe language does not buy us any guarantees of type safety. (This is probably just as well, since such guarantees would be hard to enforce remotely in any event.) The first problem is that a client may use LOOPHOLEs to pass arbitrary garbage as arguments to a procedure call. There's nothing RPC can do about this. This means that implementations must be somewhat cautious about the type-correctness of their arguments. For example, a value of an enumerated type or subrange may be out of bounds; an array may be larger than was declared in the type; etc. It may be worth attempting to draw up an exhaustive list of things that the server must check for. [Note to Alpine implementors: whatever checking is required will be performed by FileStore, right at the level of the exported interface. Implementations of internal interfaces may assume type-correctness of their arguments. Also, internal calls to a FileStore, either local or remote, may be considered type-safe: RPC guarantees that the remote FileStore is authentic, and FileStores are assumed to be cooperative rather than hostile.] The second problem is that a client may cause runtime errors in the server. The only one I can think of at the moment is a stack error caused by a mismatch in the number or size of arguments to a procedure. This can happen if, at remote binding time, the client lies to RPC about what interface it is importing. I'm hoping that RPC can make some guarantees about this, since it's at a level that is invisible to the implementation. At least, any runtime errors that occur should occur within the RPC machinery (not within the implementation) and either be reflected back to the client or be reported to the implementation in a controlled way. Are there any other issues or problems of this sort? Ed *start* 00871 00024 US Date: 20 Sept. 1981 10:34 am PDT (Sunday) From: Taft.PA Subject: ConversationID generation To: Birrell, Nelson, Boggs cc: MBrown, Kolling, Taft We are using RPC ConversationIDs to identify the client of an interface. There will be situations in which a client is local rather than remote and binds directly to the interface rather than using RPC. For uniformity, we will need a ConversationID to identify that client as well, even though RPC is never involved in the conversation. Therefore, I would like to be able to ask RPC to generate a unique ConnectionID for my own use, not associated with a conversation key or with an actual RPC conversation. I am adding a procedure GenerateConversationID to my speculative version of the RPC interface, [Ivy]<Alpine>Defs>RPC.mesa. If there are any difficulties with this, please let me know. Ed *start* 01121 00024 US Date: 21 Sept. 1981 9:55 am PDT (Monday) From: Nelson.PA Subject: Re: ConversationID generation To: Taft.PA cc: Birrell, MBrown, Kolling, Nelson I am slightly confused because you used "ConnectionID" once in your message. If what you want is a way to generate conversationIDs directly, I believe you will have no problem. The Authenticate (or somesuch) interface will have the procedure you want (remember, Andrew is going to separate all this security stuff from RPC.mesa). I, in fact, believe that you will always generate these IDs, just using our procedure so that they have a nice form for us. Once in your hands, you pass them back to us as necessary. Also, Andrew proposes that the ConversationID argument be restricted to be the first one in any argument list. In the absence of dissention, Lupine will implement this. FInally, using ConversationIDs to identify the clients of an interface seems like an excellent thing to do, along the lines of my embedded-handle idea. Of course, of a superior binding scheme appears, this decision might stand some reevaluation. Bruce *start* 02749 00024 US Date: 21 Sept. 1981 10:39 am PDT (Monday) From: Nelson.PA Subject: Re: RPC type safety To: Taft.PA cc: Nelson, Birrell, Boggs, MBrown, Kolling Type Safety. While your memo enumerates some real problems, my first thought was that nearly all the problems are also present in the local case--LOOPHOLES, bounds checks, and so forth. Even if clients program in the safe language, can't they declare themselves to be CHECKED or somesuch and then get away with whatever they want? Thus the problems are with general type safety, not RPC type safety in particular. Runtime Errors. I agree that runtime errors can occur in the RPC system. Stack errors, however, will not happen because Lupine generates source text that performs standard procedure calls. Any misbehavior will be detected when the stubs are compiled. What can happen, however, is that the marshaling code receives parameter records of the wrong size (because of misbinding, protocol errors, client malice). This is Lupine's version of stack error. Right now, I believe most of these errors will happen in the client stub as the (for instance) bogus descriptor is marshaled for the outward trip. All these specific details aside, in a short msg to Andrew last week I outlined the three kinds of runtime ERRORs Lupine is likely to detect and cause the stubs to raise: LupineBindingError: eg, calling Export on a client, Import on server. LupineRuntimeError: eg, ENDCASE => LupineRuntimeError. LupineMarshalError: as above; perhaps this could flush one call only. At the time I thought that these were unrecoverable and would land in the debugger (of the raising host only). Perhaps a better scheme is that the RpcRuntime catches them (server only) and calls a special procedure you register at Export time. This gives you control over restarting, booting, or whatever. Hmm...I guess a backstop normally does this, and somewhat better. Interface Consistency. One prolific source of errors will be the "version" field of an InterfaceName. Mesa's conservative approach to interface consistency causes headaches, but it also provides an ironclad guarantee. Diplomat's philosophy was to extend this uniformly: Diplomat would not bind two parties unless their compiled-in Mesa versionstampes agreed at runtime. We know this is not suitable for large, evolving systems, and thus RPC clients get to call their own shots with "version." Many will erringly shoot themselves down. Lupine's contributing arrows will be the RuntimeError (undefined procedure called) and the MarshalError (wrong procedure called). Finally, I believe that other problems of this flavor will arise. But I usually don't find them until I'm writing the code. Bruce *start* 12257 00024 US Date: 21 Sept. 1981 1:36 pm PDT (Monday) From: Nelson.PA Subject: A Preliminary Position on Lupine's Parameter Marshaling To: MBrown, Taft, Kolling To: Satterthwaite, Rovner, Levin, Swinehart, Atkinson cc: Birrell, Nelson RPC friends: Lupine (pronounced loo-pin) is Cedar's RPC stub translator. To its human users and RPC-using client programs, Lupine closely resembles Diplomat. Internally, however, Lupine generates stubs which [1] contain inline marshaling code and [2] call a special RPC runtime system that provides secure (authenticated, encrypted) RPC packet transport and a Grapevine-oriented remote binding scheme. This Liaison-derived design, which eliminates high-level intermediaries such as Courier, is motivated by a strong performance goal. Client- and server-level syntactic and semantic transparency is not greatly sacrificed. Progress Report I'm happy to report that Lupine, which is far from complete, has successfully generated the simple XxxRPCControl modules for a number of Xxx test interfaces. Some included Cedar-specific types. This represents significant progress because all the underlying code runs, and using Cedar/Tajo at that. Marshaling Now, to the heart of this missive. Lupine's basic symbol table interface is now at a point where I can begin writing the higher-level marshalling interface. (To "marshal" parameters is to move them back and forth between RPC packets and parameter records. The high-density connotation of "packing" is often wrong, and "serialize" already has a poplular definition among the transaction folks.) Type-specific Marshalings Here are the Cedar types and how Lupine proposes to marshal them when they occur as parameters. It is the address-containing (AC) types that cause difficulties. Basic types--UNSPECIFIED, CHARACTER, BOOLEAN, REAL, INTEGER, ..., subranges, and enumerations--are no problem. They are sent intact, as you would expect. ARRAYs are no problem. The elements are marshaled and sent. ARRAY [0..0) OF ElementType--an old computed sequence idiom--will issue a warning. Doing PACKED arrays appears reasonable; we'll see. (Computing the size before Mesa6 provided SIZE[ElementType, #elements] was hard). DESCRIPTORS are okay; the underlying arrays are marshaled and sent. Nonvariant RECORDs are no problem as long as the component types are marshalable. NonCOMPUTED variant RECORDs and SEQUENCEs are okay. PACKED sequences should be fine, as discussed above. Computed variant records, including OVERLAID, are illegal if they have address-containing types. Computed sequences are always illegal, although this is not strictly necessary for sequences with bounds types of small cardinality. Where possible, sequences should have a reasonable upperbound specified. Needlessly using a bounds (index) type with a large cardinality--eg, NAT or CARDINAL, as in most string types--can cause the slow multipacket machinery to be unnecessarily engaged. Again, sequences must have a marshalable element type. POINTERs and REFs with statically marshalable nonrecursive referents are okay. The nonrecursive stipulation implies that Lupine will not infer the existence of and marshal user-defined lists, trees, or graphs (see LIST, below). POINTER (TO UNSPECIFIED) is treated as a handle; i.e., just the uninterpreted pointer is sent. REF ANY is outright illegal; handling it requires a (costly) runtime marshaler, and I expect writing the static one to take most of my time. This is a task for someone else down the road. RELATIVE pointers and descriptors are not interpreted and are treated like the basic types, with a warning. Alternatively, they could be illegal. STRING and TEXT (REF TEXT) are no problem. For strings and (ref) text I propose to always send the characters, not the pointers. ROPEs are no problem, although they might be slow in the non-STRING-equivalent case. Lupine will recognize type Rope.Ref. LONG versions of the above are okay. Opaque TYPEs are handled as follows. Assume the declaration T: TYPE. For the parameter p: REF --or (LONG) POINTER TO-- T, the pointer p will be sent. Alternatively, if T's length is known, send p↑ instead of p. For a parameter t: T, the entire object t will be sent iff the length is known (error otherwise). LISTs will be handled because of their utility. They will be fairly expensive because of the CONSes. Order will be preserved. Of course, if the target of a list is unmarshalable, the list will be too. For example, LIST OF TEXT is okay, but LIST OF REF ANY is not. ATOMs are questionable. Marshaling atoms as unique print names is no problem; property lists--for example, LIST OF RECORD [name: ATOM, prop: REF ANY]--are out of the question. PROCEDUREs (and probably SIGNALs and ERRORs, for what it's worth) are permitted as arguments as well as interface items. Client use of callback routines must adhere to the stack discipline for now. I propose to outlaw transfer-type results until Cedar has closures. The implementation of transfer parameters will have low priority. ZONEs are either illegal or treated as ZoneRep handles. I believe handle is best (see interMDS, below). VAR, VALUE, RESULT, and HANDLE. VAR, VALUE, and RESULT are supported through the typename-driven mechanism derived earlier in the summer. In the absence of aliasing, VAR is identical to specifing both VALUE and RESULT. For example: VALUEBuffer: TYPE = LONG DESCRIPTOR FOR READONLY ARRAY OF WORD; RESULTBuffer: TYPE = LONG DESCRIPTOR FOR ARRAY OF WORD; VALUERESULTBuffer: TYPE = LONG DESCRIPTOR FOR ARRAY OF WORD; VARBuffer: --same as VALUERESULTBuffer-- TYPE = LONG DESCRIPTOR FOR ARRAY OF WORD; GetPages: PROC [pages: Pages, buffer: RESULTBuffer]; PutPages: PROC [pages: Pages, buffer: VALUEBuffer]; XorBuffer: TYPE = VARBuffer; XorPages: PROC [pages: Pages, buffer: XorBuffer]; Simple example: Page: TYPE = ARRAY [0..Disk.PageSize) OF WORD; pageBuffers: ARRAY [0..*) OF Page; GetPages[ pages: [start, stop], buffer: DESCRIPTOR[@pageBuffers, (stop-start)*SIZE[Page] ]; The underlying scheme is that VAR, VALUE, or RESULT can appear as uppercase prefixes in the type names (possibly via some type definitions) of top-level arguments. These three attributes can be applied only to REFs, POINTERs, LISTs, STRINGS, and DESCRIPTORs (or equivalent type definitions). Default parameter attributes and consistency There is uncertainty about the proper default treatment of address-containing (AC) types. My own preference is for value handling: List, string, and descriptor arguments are copied over but not back; results (i.e., return parameters) of these same types are copied back. For arguments, I realize this is different semantics from Mesa, which gives VAR. I could easily be talked into a VAR default. I just try to never do business that way myself. [Aside: For heap (nonGC) items such as string and descriptor bodies, there is a deallocation problem. A previously used solution is that the server stub frees heap arguments before returning (it allocated them too); the client programmer frees heap results himself (the client stub having allocated them). Of course, Cedar programmers are expected to use REF types and VALUE-RESULT to avoid these problems entirely.] For ropes, only value handling makes sense. For refs and pointers, however, the problem is harder. My personal goal is to make Lupine do what is wanted 98% of the time. Andrew, however, is lobbying for absolute consistency. For example, I think his position is that if REF OpaqueType is treated as a handle, all pointers (including STRING and REF TEXT) should be treated as handles. Programmers wanting dereferencing must always say (for example) VALUESTRING. Another example of this consistency question arises in the suggested alternative handling of length-known opaque types (above). I'm not a language designer and won't decide this question. What I do know is that [1] I prefer REF TEXT and STRING to send the characters by default (and perhaps to bring them back); [2] if any AC type has nonhandle treatment, then a way to force handle treatment is needed for completeness. In this case, I propose adding HANDLE to VAR, VALUE, and RESULT. Your comments are solicited. Observe that for string, ref text, list, and descriptor types, default value(-result) treatment usually makes sense; it is the general ref and pointer cases that are hard. For example, consider the following as parameters of a remote call: s: REF System.NetworkAddress; -- Length known opaque type. c: LONG POINTER TO CARDINAL; r: REF FullName --RECORD [volume, directory, file: LONG STRING]--; As a user programmer, I would want Lupine to send s↑ and r↑ because their REFs are an underhanded statement of VALUE. On the other hand, I would not expect Lupine to figure out c. Fortunately, it's unlikely that a sensible designer would include any of these types in a real remote interface. But that doesn't stop me from wanting to consider the possibilities while there's still an even chance of doing something about them. Remote Interface Considerations Interface transfers. Lupine permits remote interfaces to contain resumable SIGNALs as well as ERRORs and PROCEDUREs. PROCESSes, PROGRAMs, and PORTs are illegal; so is POINTER TO FRAME (which is actually an interface variable, below). Interface variables. Interface variables are, of course, meaningless across machine boundaries and therefore illegal in RPC interfaces. Lupine gives a translation-time warning and proceeds. Notice the implications for inline procedures. Inline procedures. Inline procedures are ignored by Lupine. This means that they expand at compile time in remote clients just as in local clients. This is the only treatment possible without changing the client's interface. (Why would you want to change a remote interface? Assume that interface Defs has P: PROC = INLINE {Defs.A[...]; Defs.B[...]; Defs.C[...]}, where A, B, and C are not inlines. When P is expanded in the client, a call of P results in three remote calls to the server. If P were not an inline, only one remote call would occur.) InterMDS "Remote" Calls There is a Lupine-translation-time way to specify that all calls through a remote interface are to be only interMDS calls. This option results in much faster marshaling at the cost of some generality. The RPC binder will check that interMDS stubs are in fact bound in the same machine. (Furthermore, encryption is less useful now, although authentication is still needed.) Of course, regular stubs can always make (slower) interMDS calls as well. InterMDS parameter records will be sent as a block, with no marshaling. Lupine will probably issue a warning in this case if any short strings, pointers, or descriptors are contained in any of the parameters' (sub)types. Some normally forbidden types are legal in the interMDS case, for instance, computed variant records and zones (but not MDSZone). On the other hand, procedure parameters do not work unless the marshaler continues to marshal them as a special case. I haven't thought enough about interMDS calls to decide all of this yet. Do interface variables make sense for interMDS calls? I think not. Roy, you've pondered interMDS the most, what's reasonable in these situations? [Aside: it's possible to make the interMDS test at binding time rather than translation time. The cost would be some extra marshaling overhead for both kinds of calls. I haven't thought about it much. Is there any interest in this possibility? It gets more complicated if we allow for the eventual possibility of multiple MDSes (specifically, both local and remote MDSes) exporting items to the same remote interface.] Conclusion There is a lot of work to do here. Your comments on what you find crucial, important, unnecessary, and ridiculous are requested. It will help me set priorities. None of this planning is set in concrete, and I expect a lot to change once Lupine has real clients. Please bring any oversights or hidden problems to my attention. Sample interfaces are graciously accepted for testing. Thanks, Bruce *start* 02044 00024 US Date: 21 Sept. 1981 1:57 pm PDT (Monday) From: Kolling.PA Subject: FS7, etc. To: taft, mbrown cc: kolling 1. Is there any reason to logout AC's conversation id at the end of each transaction or can I keep it around ad infinitum? 2. Any suggestions for how AC can know the password it needs to establish itself as a privileged client without having this password sitting around where someone evil can get at it? The best I could think of was having it passed across by FS at system initialization time, which means it still sits in some command file. 3. The volumeId in Open isn't totally superfluous (sp?) as it is used by AC, even though Pilot ignores it. Thought: what if a client creates a file on volume foo, then two weeks later opens it but saying it is on volume bar. Is there any check that the client is telling the truth? If not, his/her space allocation may come out of the wrong volume group. No harm I suppose, but..... Similarly the file access stuff that uses the owner database (associated with the volume group) would be wrong. 4. Ref your comment on closing a file. We may want to put this in as an option to free our internal structures. Juniper/ftp sent the system west when clients with truly enormous directories tried to touch all their files; it was "fixed" by forcing a checkpoint, which since it was ftp didn't matter, but we obviously can't do that in the general case. 5. No way can an owner write its space quota. tsk, tsk. 6. A discussion about access lists again? I don't believe it.....: I was thinking that the owner lists mean as follows for owner properties (we won't even discuss their effect on file properties): owner create who can create files owner modify who can modify the owner access lists I will change over to your way, which I think is: owner create who can create files owner modify who can modify the owner create list (Of course, an asserted AW can do anything). N.b., I let anyone in either owner access list read the owner entry. Karen *start* 00460 00024 US Date: 21 Sept. 1981 2:35 pm PDT (Monday) From: Kolling.PA Subject: wheels To: taft, mbrown cc: kolling I think I prefer the assertion of AW state to stay in AC rather than in the ClientMap, so that all the privilege control stays together. (We can have a call AC.IsAssertedAlpineWheel[conversationID, transID] if we need it) AC could handle the special system conversationID by a check and always treat it as an asserted AW. Karen *start* 00167 00024 US Date: 21 Sept. 1981 3:18 pm PDT (Monday) From: kolling.PA Subject: wheels again To: taft, mbrown cc: kolling Yes, keep it in the client map. *start* 00408 00024 US Date: 21 Sept. 1981 3:24 pm PDT (Monday) From: kolling.PA Subject: wheels again again To: taft, mbrown cc: kolling Is the ClientMap going to field AssertAlpineWheel completely itself? I.e., will it know the AW list name and call "something" to do IsMember? or will it call AC.IsAlpineWheel? If the former, the clientmap has to know the name of the server-specific AW list. Karen *start* 01288 00024 US Date: 21 Sept. 1981 3:54 pm PDT (Monday) From: Taft.PA Subject: Re: FS7, etc. In-reply-to: Kolling's message of 21 Sept. 1981 1:57 pm PDT (Monday) To: Kolling cc: taft, mbrown 1. ConversationIDs can be kept around forever. 3. When we open an existing file, Pilot's File.GetAttributes will tell us what volume it's actually on, and that's the information we'll use for AccessControl operations involving that file. That's why it's ok to ignore the client's volume argument. For consistency with the future, we might have Open fail if the volume passed by the client differs from the volume where the file actually resides (rather than just ignoring the volume argument). This would force clients to remember VolumeIDs in addition to FileIDs and FileStoreRNames; I'm uncertain whether this is really a good idea. 4. We can cope with giant enumerations either by providing an explicit Close operation or by implementing the OpenFileMap in such a way that it "can't" overflow. The explicit Close does seem easier. 5, 6. Oh, sigh. Do it whatever way you want. We can always change it later if we don't like it. I thought having the owner modify list control changes to the space allocation was perfectly reasonable; but your way is equally reasonable. Ed *start* 00378 00024 US Date: 21 Sept. 1981 3:59 pm PDT (Monday) From: kolling.PA Subject: Re: FS7, etc. In-reply-to: Taft's message of 21 Sept. 1981 3:54 pm PDT (Monday) To: Taft cc: Kolling, mbrown But, if the owners can set their own space quotas why bother to have quotas at all? I can't munge in and set my Ivy quota, can I? (If I can, my space problems are over.....) *start* 00535 00024 US Date: 21 Sept. 1981 4:00 pm PDT (Monday) From: Taft.PA Subject: Re: wheels again again In-reply-to: kolling's message of 21 Sept. 1981 3:24 pm PDT (Monday) To: kolling cc: taft, mbrown ClientMap.EnableAlpineWheel just remembers the bit it was passed. I think we've agreed that AlpineAccess.AssertAlpineWheel will just call AccessControl.AssertAlpineWheel; AccessControl will do the access check and put the answer into the ClientMap itself. Thus the wheel RName needs to be known only by AccessControl. Ed *start* 00884 00024 US Date: 21 Sept. 1981 4:31 pm PDT (Monday) From: Taft.PA Subject: Re: FS7, etc. In-reply-to: kolling's message of 21 Sept. 1981 3:59 pm PDT (Monday) To: kolling cc: Taft, mbrown The mode of usage I had in mind was that the owner modify list would include the owner's administrator, not the owner him/herself, and that the administrator would be able to modify the owner's create list and space quota. This would permit us to have administrators who aren't wheels. Your model, which is equally valid, is that the owner modify list includes the owner him/herself, and that the owner is permitted to change only the create list. Administration is entirely separate, and requires that all administrators be wheels. I'm perfectly happy to adopt your model (if indeed I've described it correctly), and will change the FileStore documentation appropriately. Ed *start* 00414 00024 US Date: 21 Sept. 1981 4:47 pm PDT (Monday) From: kolling.PA Subject: Re: FS7, etc. In-reply-to: Taft's message of 21 Sept. 1981 4:31 pm PDT (Monday) To: Taft cc: kolling, mbrown I liked your idea of the administrator not being a true wheel, until I realized that then there was no way for the administrator to do AddOwner. I think I'll leave it the old way (modify can twitch both lists). *start* 00322 00024 US Date: 21 Sept. 1981 4:51 pm PDT (Monday) From: kolling.PA Subject: Re: FS7, etc. In-reply-to: Taft's message of 21 Sept. 1981 4:31 pm PDT (Monday) To: Taft cc: kolling, mbrown Sigh. I've changed my mind half way. Let's let people in the modify list only change the create list, not both lists. *start* 00759 00024 US Date: 24 Sept. 1981 5:30 pm PDT (Thursday) From: Taft.PA Subject: Re: deadlock error In-reply-to: kolling's message of 24 Sept. 1981 3:52 pm PDT (Thursday) To: kolling cc: mbrown, taft It feels like an abstraction failure to me. In any event, unwinding the error should leave the abstraction in a good state. If you're asking what AccessControl should do if it gets a LockFailed error from calling AlpineFile, I'd say that you should clean up your state and raise LockFailed yourself with the same argument. (Alternatively, you could REJECT the error and let it pass through to your caller, who would certainly have to catch it; this violates the Levin signalling guidelines, but I don't see any harm from that in this case.) Ed *start* 00591 00024 US Date: 25 Sept. 1981 3:29 pm PDT (Friday) From: kolling.PA Subject: what I currently believe about Locks To: mbrown, taft cc: kolling AC wants to explicitly lock both individual file pages and an entire file; these locks should be indistinguishable from the locks that AlpineFile or whatever will get for the same entities. I also expect to be able to release the read locks on the individual file pages. AC also wants to both read and write lock "strange objects" corresponding to a VolumeGroup. This makes life simple during (un)registering volumeGroups, etc. *start* 01213 00024 US Date: 25 Sept. 1981 3:49 pm PDT (Friday) From: Taft.PA Subject: Re: what I currently believe about Locks In-reply-to: kolling's message of 25 Sept. 1981 3:29 pm PDT (Friday) To: kolling cc: mbrown, taft File locks are explicitly set by an argument to AlpineFile.Open, and may be upgraded by AlpineFile.SetLockOption. Page locks are implicitly set by AlpineFile.Read/WritePages operations on the individual pages. We don't provide an explicit page lock operation, and I'm doubtful whether we need to (why bother locking a page if you aren't going to at least read it?) Note that you can set an update or write lock during a ReadPages operation if you want. AlpineFile does not presently export an explicit "unlock" operation; locks are released implicitly when a transaction terminates. I suppose there's no harm in exporting a procedure to release read locks, since by doing so a client can screw only itself (the same cannot be said about update and write locks, however). Unless Mark objects, I will add this to the interface. For locking non-file objects, you are welcome to deal directly with the lock manager itself via the Lock interface specified in LockDesign0.bravo. Ed *start* 00478 00024 US Date: 25 Sept. 1981 4:01 pm PDT (Friday) From: Kolling.PA Subject: Re: what I currently believe about Locks In-reply-to: Taft's message of 25 Sept. 1981 3:49 pm PDT (Friday) To: Taft cc: kolling, mbrown 1. "why bother locking a page if you aren't going to at least read it": because that page may be in my buffer cache, and if I don't modify it, I won't ever do IO. 2. I'm using locks for queueing, that's why I need to get them before the actual IO. *start* 00620 00024 US Date: 4 Sept. 1981 4:12 pm PDT (Friday) From: kolling.PA Subject: Here's Mark's solution for CheckAccessFile. To: Taft cc: mbrown, kolling FS.OpenFile makes an OpenFileID at the beginning of OpenFile. FS calls AC.CheckAccessFile[openFileID blah blah]. AC calls FileProperties.something to read the access list it wants[openFileID]. FP calls into FS to read the leader page, which makes the locking and looking in the log happen right. I'll go ahead with the extra stuff for the access lists as you suggest, unless Mark says differently. I think this resolves all the questions raised? Karen *start* 00480 00024 US Date: 8-Oct-81 17:15:07 PDT (Thursday) From: Kolling.PA Subject: Locking strange things To: mbrown Reply-To: Kolling cc: Kolling I want to lock "volume groups" to allow registering them when the system is running. I assume that the entity would be the volumeGroupID and the subentity is some constant. How do I guarantee that the LockIDs I use don't get confused with LockIDs for other things? By instantiating a lock manager of my very own? Karen *start* 00373 00024 US Date: 8-Oct-81 17:30:04 PDT (Thursday) From: MBrown.PA Subject: Re: Locking strange things In-reply-to: Your message of 8-Oct-81 17:15:07 PDT (Thursday) To: Kolling cc: MBrown A VolumeGroupID should be globally unique, so there is not any possibility that it will conflict with a FileID or anything else that might go into that field of a LockID. *start* 00595 00024 US Date: 13 Oct. 1981 1:08 pm PDT (Tuesday) From: Taft.PA Subject: Re: exported types In-reply-to: MBrown's message of 13-Oct-81 12:53:12 PDT (Tuesday) To: MBrown cc: Taft, Kolling I agree with you that hiding representations is pointless in internal interfaces. Unfortunately, the case at hand, OpenFileHandle, IS a public type, and I believe making it opaque is appropriate in this case. Perhaps we should have a meeting to discuss interface design conventions. You two are far more experienced in designing Mesa interfaces than I am, so I might learn something. Ed *start* 00874 00024 US Date: 13-Oct-81 12:53:12 PDT (Tuesday) From: MBrown.PA Subject: Re: exported types In-reply-to: Taft's message of 13 Oct. 1981 8:50 am PDT (Tuesday) To: Taft cc: Kolling, MBrown Another possibility is to make OpenFileHandle concrete in the interface. My experience with exported types is that they are a big pain, largely due to the poor debugger support but also because the semantics aren't quite right. So my attitude is that it may be worth the effort to make the types in public interfaces opaque, but it is often not worth the effort for internal interfaces. Karen, for the purposes of getting your stuff to compile, you should feel free to make edits to the agreed-upon interfaces, keeping your private versions on a separate subdirectory or something. At least, that is what I've been doing. We can merge in the changes later. --mark *start* 02222 00024 US Date: 15 Oct. 1981 9:38 am PDT (Thursday) From: Swinehart.PA Subject: File properties To: Kolling, MBrown, Taft cc: Swinehart I think the tradeoffs you've made in the Alpine design are just right. This system should really do the trick for us. The one decision I was puzzled about is not a big deal, but I wondered about the reasons -- the decision to allow only a fixed number of file properties. Although as far as I know I'm the only person ever to use them regularly, I have found the Alto system's willingness to support an arbitrary number of arbitrary (theoretically, registered) leader page properties very valuable. It allows me to attach system-specific version information, comments, checksums, etc., to files whose format I have no control over (e.g., to .RUN files produced by BLDR, or .BCD or .BOOT files produced by the compiler, linker, or whatever -- or just to Tioga-edited text files.) With directory systems to be provided outside the scope of the system, the ability to retrofit properties that could assist indexing and directory management becomes even more important. Much of that could be provided elsewhere, but some of it might be associated with high-level scavenging, and that would have to go with the file. (Just remembered: Eric Schmidt used this capability to store full path names of Bringover/Smodel'ed files in local file leader pages, with very nice results.) The obvious response is that if only one or two people used these features they weren't very important. I think few people used them because the shared file systems and FTP were not willing to preserve them. I could have done a very nice Spruce version release management system by annotating the released object files themselves. As it was, I got some limited benefit from the local capabilities alone. I guess it depends on where you're going to store the properties and how much room you have for them, but I'd vote for keeping something like the Alto/Pilot leader page notion -- maybe even going as far as the fixed number of fixed-sized properties followed by a jungle of variable-length ones. It's a way to impose a small amount of orthogonal semantics on a file, after the fact. *start* 02327 00024 US Date: 15-Oct-81 10:19:14 PDT (Thursday) From: MBrown.PA Subject: Re: File properties In-reply-to: Swinehart's message of 15 Oct. 1981 9:38 am PDT (Thursday) To: Swinehart cc: Kolling, MBrown, Taft Thanks for your comments. I'm can't say precisely how Karen and Ed feel about this, but here is my rationale about file properties: 1) I feel that it is very important to keep the per-file fixed overhead low. I don't want to pay for features that I never use. Alpine is intended to have a nicer file system layered on it, and this system is likely to have its own ideas about most of the built-in features of files. At the same time, I feel that Pilot files are too primitive (no properties beyond type, temporary, and immutable.) The obvious compromise at this time is a single leader page per file. In our current design, this page is pretty full (the access lists, the name of the file's owner, and the file string name are the main consumers of space.) The current design is VERY simple: all items, even variable length ones, are found at fixed locations in the leader page. The Pilot redesign includes the ability to extend a file at both ends (as at SAIL, with negative page numbers.) This would make it possible for some files to have more leader pages than others. Extra leader pages could be used to store optional file properties with more complex formats. 2) I like the view that file properties are logically part of a database of information, keyed by file ID. So why do we use leader pages at all? The motivation seems to derive primarily from reliability considerations: the information in a leader page is so precious that losing it implies that the associated file is worthless. Hence storing them in close proximity makes it likely that they survive or fail together. In a system like Alpine, which takes other precautions to ensure reliability, it is not clear that this argument carries much weight. The secondary motivation is performance: if some information (such as acess control lists) must be consulted on every file access, then it makes sense to cluster it with the file. I have a feeling that you'd be happy with a good database system to store your extra file properties. The sooner Alpine is finished, the sooner we'll have that database system. --mark *start* 01703 00024 US Date: 12 Oct. 1981 7:08 pm PDT (Monday) From: Nelson.PA Subject: Supporting multiple versions of remote interfaces To: Birrell cc: Taft, MBrown, Kolling I assume that RPC clients are allowed to export the same interface type from the same instance several times as long as the VersionRanges are not identical. For example, AlpineWorker [5,9] AlpineWorker [3,6] and AlpineWorker [1,2] might all be exported from the same server, and the binder's job is to match importers with a suitable exporter. I suggest that it may also be advisable to include Lupine's protocol version in this matching algorithm. For example, assume that parameter marshaling changes (incompatibly) for some performance reason. I can imagine RPC clients generating new stubs to get the increased speed for most of their users, but not being able to force some users to update (for the same reasons that they have multiple versions of their own interfaces). In this case, the client's interface type has not changed, but the underlying protocol has. If the protocol version is included with the client's version, all works well: AlpineWorker [5,9] [1,2] AlpineWorker [5,9] [3,3] The client exports both interfaces by calling the Control modules for each (which were generated by the two versions of Lupine). It is possible, as well, to put this problem in the client's hands by making him increase his VersionRange whenever Lupine changes. This is not too difficult, but may be undesireable. The purpose of this note is to [1] inquire about nonidentical version range exports, [2] point out the Lupine protocol twist, [3] see if any of you think this is something to worry about. Bruce *start* 01435 00024 US Date: 14 Oct. 1981 10:59 am PDT (Wednesday) From: Nelson.PA Subject: Change to RPC.mesa To: Birrell, Taft cc: MBrown, Kolling, Nelson Ed's CedarRpc uses a record to hold the parameter zones, and I think this idea is good enough to adopt outright. To make it work properly, tho, the RECORD must be defined in one public place, and RPC is the most natural. I suggest that the following two defs be added. They don't effect Andrew at all; only Lupine and clients use them: In RPC.mesa: -- These Zones are used by stubs for parameter storage. Zones: TYPE = RECORD [ collected: ZONE←NIL, heap: UNCOUNTED ZONE←NIL, mds: MDSZone←NIL ]; -- The StandardZones are assigned by RpcControl.Import&ExportInterface. StandardZones: Zones = []; In addition, to clarify the existence of a default for InterfaceName, I also suggest adding the following constant, which Andrew can again ignore: -- An interface's UniqueName is assigned by RpcControl.Import&ExportInterface. UniqueName: InterfaceName = []; RpcControl would then have these procedures: ExportInterface: PROCEDURE [ user: Principal, password: EncryptionKey, interface: InterfaceName←UniqueName, parameterStorage: Zones←StandardZones ]; UnexportInterface: PROCEDURE; ImportInterface: PROCEDURE [ interface: InterfaceName←UniqueName, parameterStorage: Zones←StandardZones ]; UnimportInterface: PROCEDURE; *start* 01442 00024 US Date: 14 Oct. 1981 11:55 am PDT (Wednesday) From: Nelson.PA Subject: Buffer allocation faults To: Birrell, Taft cc: MBrown, Kolling, Nelson It is nearly certain that Alpine will specify a special heap zone for buffers (descriptor bodies). Lupine will probably allocate descriptor bodies in this way (and other storage similarly): RawStorage: TYPE = MACHINE DEPENDENT RECORD [ words (0): SEQUENCE COMPUTED CARDINAL OF WORD ]; -- The wire protocol for a descriptor is (say) [length, descriptorBodyData...]. bufferVar: LONG DESCRIPTOR FOR ARRAY OF Alpine.Page = DESCRIPTOR[ zones.heap.NEW[RawStorage[length*SIZE[Alpine.Page]]]↑, length] Notice that there is no catchphrase on this NEW (or the invisible FREE). I'm open to suggestions, since heap failure on a server could bring down the server via a remote call. One idea: Lupine can put an ANY on the NEW, and translate it into CallFailed[storageFailure]. Note that it's impossible to catch specific signals here. Second idea: Andrew should put an ANY in the runtime, catching anything that comes from a Dispatcher that he doesn't already handle. Other signals that Lupine can't recover from include: Rope faults, Atom faults, and List faults. Is it better to have catchphrases increasing the size of the stubs, or to put one ANY where it will have the same effect? I'm a novice in this department, what do you two veterans say? Bruce *start* 01292 00024 US Date: 14 Oct. 1981 12:28 pm PDT (Wednesday) From: Nelson.PA Subject: NIL and zero length values To: Birrell, cc: Taft, MBrown, Kolling cc: Nelson Andrew, One of the early problems that Courier (and Envoy?) had is that they treated NIL strings like strings of 0 length. Similarly for a descriptor with NIL base. Clients (depending on the difference) deep-sixed. My question concerns how to represent NIL. The obvious wire protocol for strings is [maxlength, length, characters]. Liaison used length=LAST[CARDINAL] to mean NIL, but this causes problems for the largest string. Should this be continued, or should an escape be used (eg, IF length=LAST[CARDINAL] THEN nextWord is NIL flag). Note that while the escape flag only needs to be one bit, the packing is not worth the trouble and a full word will be used. For TEXT, this problem disappears since the NAT index leaves the top bit for the flag. For descriptors, it seems likely that LONG indices will eventually be valid, and that 2 words of length should be used from the start. Thus an escape takes a yet a third word if LAST[LONG CARD] is not pressed into service as above. Ropes, of course, have LONG INT indices, leaving the top bit again. I don't know if NIL atoms are permissible. Bruce *start* 01013 00024 US Date: 14 OCT 1981 1654-PDT From: NELSON.PA Subject: Beware of the warning in the second paragraph should the # of words in a pagerun ever get too large... To: Taft, MBrown cc: Kolling Date: 14 Oct. 1981 4:48 pm PDT (Wednesday) From: Satterthwaite.PA Subject: Re: Sequence strategies In-reply-to: Your message of 14 Oct. 1981 12:03 pm PDT (Wednesday) To: Nelson I think you are right that the second is not legal as it stands, nor can it be made legal without introducing a type identifier. Then the choice seems to come down to whether you want to talk about sequences of WORDs or sequences of Alpine.Page's. Given the declaration of bufferVar, the latter seems more natural. I think the compiler is neutral on this (though I have forgotten the details of the interactions, if any, between (multiargument) DESCRIPTOR and (computed) SEQUENCE). Be aware that NEW will fail (probably without complaint) if the number of WORDS requested exceeds 2↑16-1, however it is computed. Ed *start* 02169 00024 US Date: 14 Oct. 1981 7:29 pm PDT (Wednesday) From: Nelson.PA Subject: Short Ropes and RNames To: Taft, Atkinson cc: MBrown, Taft, Kolling, Nelson I know this is discussing an optimization before it's time, but the fact is that Lupine is going to generate significantly better (faster and smaller) stubs when the cardinality of a sequence's bounds type is reasonable. For example, if TEXT had the field text: PACKED SEQUENCE maxLength: [0..shortStringLength) OF CHARACTER instead of text: PACKED SEQUENCE maxLength: CARDINAL OF CHARACTER. The reason for such restricted definitions is that dynamic length types can become small enough that any corresponding parameters are guaranteed to fit into one packet. In FileStore8, this applies to RNames (which are currently restricted to 64), and to Strings (which appear to be limited to around 100 characters). This message is just to provoke thought about whether or not it is reasonable to define a short Rope or String type. The first thing in my head is to define ShortROPE = Rope.ShortRef; ShortRef: TYPE = REF shortText RopeRef; RopeRep: TYPE = RECORD [SELECT tag: * FROM text => [length: NAT, text: PACKED SEQUENCE max: CARDINAL OF CHARACTER], shortText => [length: [0..shortRopeLength), text: PACKED SEQUENCE max: [0..shortRopeLength) OF CHARACTER], node => [SELECT case: * FROM substr => [size: Int, base: Ref, start: Int], concat => [size: Int, base,rest: Ref, pos: Int], replace => [size: Int, base,replace: Ref, start,oldPos,newPos: Int], object => [size: Int, base: REF, fetch: FetchType, map: MapType, pieceMap: PieceMapType] ENDCASE] ENDCASE]; By making this RopeRep a suitable aligned machine dependent record, the shortTest variant will be bitwise identical with its friends, and the standard operations can be used. Trying to lengthen a shortText with a standard rope operation would probably cause a fault. There are a lot of obvious problems with this; this is just a half-baked suggestion. *start* 03731 00024 US Date: 14 Oct. 1981 9:54 pm PDT (Wednesday) From: Atkinson.PA Subject: Re: Short Ropes and RNames In-reply-to: Nelson's message of 14 Oct. 1981 7:29 pm PDT (Wednesday) To: Nelson cc: Taft, Atkinson, MBrown, Kolling First, I really do not understand why whatever program is performing the copy does not dynamically check for the short case. I would like to see the code that you intend to generate for the various cases. My own feeling for what should be generated to copy a rope is along the lines of (modulo enough LOOPHOLEs to make the compiler happy, and neglecting stupid mistakes): -- to copy r: ROPE into a buffer at p: LONG POINTER TO CARDINAL -- plimit: LONG POINTER TO CARDINAL has limiting address -- assume that at least one word is available at start IF r = NIL THEN {p↑ ← 0; p ← p + 1} ELSE {WITH r SELECT FROM t: Text => {words: CARDINAL = (t.length+3)/2; -- allow for length np: LONG POINTER TO CARDINAL = p+words; IF LOOPHOLE[np, LONG CARDINAL] < LOOPHOLE[plimit, LONG CARDINAL] THEN {-- short copy p↑ ← t.length; BLT[to: p+1, words: words-1, from: @t[0]]; p ← np; }; ELSE GO TO long; ENDCASE => GO TO long; EXITS long => p ← LongRopeCopy[r, p]}; The above code assumes that you would like to keep the amount of storage used small. For (possibly) faster code one could assume that the short case was always small enough to fit in (let's say) 32 characters, and storage space in the buffer is not an issue. Further suppose that we are working with current Ropes. Then the code for rope copying might be: -- to copy r: ROPE into a buffer at p: LONG POINTER TO CARDINAL -- assume that there is enough room at the start for the short case ShortWords: CARDINAL = 2+(32/2); -- room for short case IF r = NIL THEN {p↑ ← 0; p ← p + ShortWords} ELSE {WITH r SELECT FROM t: Text => {IF t.length <= 32 THEN {-- short copy BLT[to: p, words: (t.length+5)/2, from: t]; p ← p + ShortWords; }; ELSE GO TO long; ENDCASE => GO TO long; EXITS long => p ← LongRopeCopy[r, p]}; I would guess that one of the two methods given above is about as good as you will get even if you had the possibility of a short variant. If the knowledge of static offsets in the buffer is important then you can test for all of the ropes being short before starting any copying, but you must still allow for the long cases for general ropes. Note that you MUST NOT avoid computing the # of words to BLT, since you could run off the end of the rope into an unmapped space (although if a ByteBlt is supported in microcode then you can get by with less computation). Second, Ropes are complex enough without adding more cases to dynamically check for in every Rope operation. I am not inclined to add to the cost and complexity of Rope operations to gain a small improvement in RPC speed, especially since it may slow down the system as a whole. I would like to point out that having three cases instead of two does not seem to me to be making Lupine's job any easier. However, I may be missing something again. In reading your message over, I got the impression that your shortest Rope variant would become the type used for a number of applications. Although one could see this as being possible, I think that anyone who used this assumption and wanted very high efficiency would be unhappy with Ropes for the cost of their generality. One final comment: it is a bad idea to demand extreme efficiency, ease of use, generality, and safety in a single package. Attempting to satisfy all such requirements will result in a package that satisfies no one. Russ *start* 00778 00024 US Date: 15-Oct-81 9:51:48 PDT (Thursday) From: MBrown.PA Subject: Re: Short Ropes and RNames In-reply-to: Nelson's message of 14 Oct. 1981 7:29 pm PDT (Wednesday) To: Nelson cc: Taft, Atkinson, MBrown, Kolling I'm with Russ on this. The stub should check the rope length at runtime and if it is too long, then ERROR. You should invent some means for a programmer to declare (at stub-compile time) her willingness to bound the length of a Rope (or whatever). Ideally, the type system would give you a way of expressing this while still getting something compatible with Rope, but we aren't there yet. Ed Satterthwaite and Jim Donahue should be part of this discussion, just for their information about how the present type system limits you. --mark *start* 01578 00024 US Date: 15 Oct. 1981 10:26 am PDT (Thursday) From: Nelson.PA Subject: Short Ropes To: Atkinson cc: MBrown, Taft, Kolling, Nelson So much for talking about optimization before its time. My main point seemed to be missed by all, because I did not emphasize it enough. The fact that only `length' characters get copied, that long cases get properly handled, and that short cases don't look much different than the short versions of long cases are all true. I never said or meant anything different. However, what a static short declaration does is allow a static determination of the single packet call case. This information allows other optimizations to happen, such as avoiding copying all kinds of static data from the packet to the local frame because there might be another packet. (Yes, even in this latter situation you can (and Lupine does) treat the two cases separately, generating a lot more code to do it.) Of course, I can simply declare that a certain type name means short text, and raise ERROR at runtime otherwise. I realized this at the outset, but my invention of a short text type for Lupine seems a little arbitrary, and the existence of Grapevine short strings, Karen's short stuff, and so on call for a generally-accepted method of doing so. I succeeded in generating some discussion about the issue, but nothing has settled, as I expected. Lupine is happy to generate general-case code in the meantime. I will send this round of msgs to Ed and Jim--an excellent suggestion--after they have settled. Bruce *start* 00846 00024 US Date: 15 Oct. 1981 6:08 pm PDT (Thursday) From: Birrell.pa Subject: Re: NIL and zero length values In-reply-to: Nelson's message of 14 Oct. 1981 12:28 pm PDT (Wednesday) To: Nelson cc: Birrell, Taft, MBrown, Kolling I don't see anyone having a string whose length is LAST[CARDINAL], so using that for NIL might be ok. On the other hand, for DESCRIPTOR the length could easily be LAST[CARDINAL] (e.g. PACKED ARRAY OF BOOLEAN), so some other representation seems needed. I don't see much alternative to using an extra word. You could use an escape mechanism if you can stand the complexity: LAST[CARDINAL] means NIL but LAST[CARDINAL]-1 means the true length is in the next word. Then you only pay the extra word if the array is already very large. This might also be reasonable for optimizing LONG indices. Andrew *start* 00510 00024 US Date: 15 Oct. 1981 6:10 pm PDT (Thursday) From: Birrell.pa Subject: Re: Supporting multiple versions of remote interfaces In-reply-to: Nelson's message of 12 Oct. 1981 7:08 pm PDT (Monday) To: Nelson cc: Birrell, Taft, MBrown, Kolling I think the view that a different interface type can be used to handle such incompatible changes is probably the best. The alternative seems to add unneeded complexity (and reduces the ability for error checking such as duplicate exports). Andrew *start* 00425 00024 US Date: 15 Oct. 1981 6:14 pm PDT (Thursday) From: Birrell.pa Subject: Re: Buffer allocation faults In-reply-to: Nelson's message of 14 Oct. 1981 11:55 am PDT (Wednesday) To: Nelson cc: Birrell, Taft, MBrown, Kolling I can't think of any solution for this other than passing in the errors as parameters of ExprotInterface with the zones, which sounds very ugly. We do need a solution, though! Andrew *start* 00708 00024 US Date: 16 Oct. 1981 10:17 am PDT (Friday) From: Nelson.PA Subject: NIL descriptors To: Birrell cc: Taft, MBrown, Kolling, Nelson If you're willing to go along with LAST[CARD] for the NIL string, then I believe it works with little danger for descriptors too. The reason is that indexType-less descriptors can only be constructed using the 2-arg form of DESCRIPTOR, which requires a length. Thus the probability of this failing dynamically is in the same ballpark (yeah, Dogers!) as a maxLength string. The fact that the static symbol table index type for dynamic descriptors is CARD makes no difference at runtime. Does this clarify things, or did I miss something? Bruce *start* 00780 00024 US Date: 16 Oct. 1981 10:42 am PDT (Friday) From: Birrell.pa Subject: Re: NIL descriptors In-reply-to: Nelson's message of 16 Oct. 1981 10:17 am PDT (Friday) To: Nelson cc: Birrell, Taft, MBrown, Kolling The difficulty with descriptors is that it's (quite) reasonable for someone to declare m: LONG POINTER TO PACKED ARRAY CARDINAL OF BOOLEAN = ......; and then pass DESCRIPTOR[m] as a value-parameter; you shouldn't confuse that case with NIL. If you would confuse that case with NIL, I'd vote for a runtime check to complain if you're about to do so. The reason I'm willing to believe in the absence of maxlength strings is that they occupy 32k words of memory. On the other hand, they're not impossible so maybe all the same remarks apply. Andrew *start* 00472 00024 US Date: 16 Oct. 1981 3:23 pm PDT (Friday) From: Nelson.PA Subject: Re: NIL descriptors To: Birrell cc: Nelson, Taft, MBrown, Kolling I spoke with Ed, and he agrees that while DESCRIPTOR[m↑] (slight error) is legal, in practice the compiler probably blows it on the CARD. It also turns out that SEQUENCES are likely to allow LONG bounds types soon, and thus I will adopt the 2 word philosophy to start with, making LAST[LONG CARD] be NIL. Bruce *start* 01520 00024 US Date: 19 Oct. 1981 1:16 pm PDT (Monday) From: Taft.PA Subject: Exported types To: MBrown, Kolling cc: Taft After consultation with Satterthwaite, I have concluded that the style of usage of exported types exemplified by the Alpine defs files is perfectly ok. That is, it's ok to define TransID as an abstract type in AlpineEnvironment, and then define types in AlpineFile, AlpineTransaction, etc., equal to that type. However, the implementation module that exports the corresponding concrete type must export it to ALL interfaces that refer to the abstract type. A related issue has to do with the definition of types that are interpreted differently in different contexts, such as the LockID. Mesa currently has a restriction of only one concrete type per abstract type. In this case, I propose we do the following: 1) Define LockID as an abstract type in AlpineEnvironment (or wherever is appropriate; currently we don't export any operations that deal in LockIDs). 2) Define it as a concrete type in exactly one place, using some noncommital representation such as ARRAY [0..SIZE[LockID]) OF UNSPECIFIED. 3) In the defs files for programs that interpret LockID in a specific way, define new types FileLockID, FilePageLockID, DataBaseLockID, etc, with whatever representation is appropriate; and also define inline procedures that "convert" between the specific and noncommital representations using LOOPHOLE. I'll go ahead and massage the defs files to test out this approach. Ed *start* 02318 00024 US Date: 20-Oct-81 11:48:58 PDT (Tuesday) From: MBrown.PA Subject: Design for Alpine online backup To: Kolling, Taft cc: MBrown Ed, you mentioned in your Dealer that we don't have a design for backup, and that this is one of the things that will get done in the "tail" of Alpine development. I have given some thought to backup, and I think I have a plausible design. Furthermore, I think it is rather important that backup be done early in the implementation, because of Alpine's goal to provide high reliability storage. My design for backup is based on online pack copying for Alpine volumes and either tape or disk backup of logs. Alpine volume backup will work by having one extra drive and k sets of backup packs (k = 2?). Ron will mount one new backup pack each morning, in an obvious rotation. The pack copying process runs during a relatively quiet period (at night?). It begins by performing a warm shutdown of the system. This aborts all in-progress transactions and forces all disk writes. A special record is written into the log to mark this event. The system then comes back online, with an internal flag set to show that pack #j is in the process of being copied, and a process is spawned to do the copying. The copy reflects the state of the volume at the shutdown point. To implement this, all writes to the copy volume first call a procedure implemented as part of the copy procedure. It consults a bitmap (150 pages of vmem for a T-300) to determine if the pages have already been copied (or maybe the map is done at a logical file level). If not, they are, and marked as such in the map. When the call returns, the write may proceed. Logs may be saved as is, or may be reprocessed to save some space and make them easier to use in media recovery. Saving logs to disk is attractive in that tapes are relatively unreliable and have small capacity. If a log is a T-300, then it may take several days to fill, and the log backup can take place in rotation with the pack copying. The procedure for media recovery is straightforward: find most recent volume copy, get value, replay logs forward and apply updates. This works for single page and whole-volume failures. The procedure is more complex if volumes span multiple packs. What do you think? --mark *start* 00631 00024 US Date: 20 Oct. 1981 11:57 am PDT (Tuesday) From: Kolling.PA Subject: Re: Design for Alpine online backup In-reply-to: MBrown's message of 20-Oct-81 11:48:58 PDT (Tuesday) To: MBrown cc: Kolling, Taft Do I understand this correctly: a pack copy is going on, and meanwhile the system is running and checking before it writes into each disk page to see if that page has been written yet? Since it takes such a short time to copy a pack, do we want the latter extra complexity? Or am I missing something here? Also, are volumes so independent that it's okay to copy one volume out of a volume group? Karen *start* 00756 00024 US Date: 20 Oct. 1981 1:47 pm PDT (Tuesday) From: Kolling.PA Subject: Re: Design for Alpine online backup In-reply-to: MBrown's message of 20-Oct-81 12:17:03 PDT (Tuesday) To: MBrown cc: Kolling, Taft By a high speed copy, I assumed a straight I-know-nothing-about-the-disk-except-#sectors-per-track,-etc. copy with bad pages put into a bad page table somehow. This is pretty simple. Were you proposing something more complex? Is the on-line one doing something more fancy? (Maybe getting a bad page on the log....) What does the high speed pack copy have to do that the online one doesn't? The latter has to handle bad spots also, yes? JCopydisk takes one or two pages. Scaling is easy, all that changes is a few parameters. *start* 00948 00024 US Date: 20-Oct-81 12:17:03 PDT (Tuesday) From: MBrown.PA Subject: Re: Design for Alpine online backup In-reply-to: Kolling's message of 20 Oct. 1981 11:57 am PDT (Tuesday) To: Kolling cc: MBrown, Taft I think that it might be just as hard to write a really high-speed pack copy as to do the online thing I just described (considering bad spots, the need for a check pass, etc), and the high-speed copy would be harder to maintain (it depends on more low-level details.) Doing it online has the advantage of scaling better to T-600s or whatever we get next, and is operationally more convenient (Ron can change packs at a reasonable hour if he wants.) But the offline thing I was advocating before is still a possibility. The only thing that the volumes of a group have in common is the owner database. This is just a file from the point of view of the backup system. So the answer is yes, they are independent. --mark *start* 01522 00024 US Date: 20-Oct-81 14:29:24 PDT (Tuesday) From: MBrown.PA Subject: Re: Design for Alpine online backup In-reply-to: Kolling's message of 20 Oct. 1981 1:47 pm PDT (Tuesday) To: Kolling cc: MBrown, Taft I am assuming that the disk is formatted before the copy begins (bad pages are known, the physical and logical volume marker pages are in place, etc.) A backup pack only needs to be formatted once, as long as the volume it is backing up for is not reformatted. If you do a copy at the level of physical pages (as the ultra-fast copy must be done in order not to lose revolutions and seeks), then bad pages will cause the volume file map and volume allocation map of the two volumes to differ. Of course, since you never (actually, rarely) look at the copy, you can omit building the VAM and VFM and just build them (using the scavenger) when you go to read the copy! But you DO read the copy for the check pass. So the copy pass and check pass must make consistent decisions about where pages should go. This is not hard, it just has to be done right. At any rate, this approach involves using the lowest-level Pilot interfaces; things are strange down there, and will surely change in the redesign. I was assuming that in the online copy case, the copy is done using high-level disk operations (file level), hence the VAM and VFM are updated and bad pages handled as a matter of course. This makes the online copy slower, but it should not take more than a few hours at worst... --mark *start* 04865 00024 US Date: 20 Oct. 1981 3:42 pm PDT (Tuesday) From: Taft.PA Subject: Re: Design for Alpine online backup In-reply-to: MBrown's message of 20-Oct-81 12:17:03 PDT (Tuesday) To: MBrown cc: Kolling, Taft In my opinion, simply copying disk packs as the normal way of doing backup is a total loss, for three reasons: 1. It's nearly impossible to handle bad spots properly. 2. It does not scale upward to handle multi-pack volumes. (I realize Pilot doesn't support these now, but I'm hoping it will eventually, for reasons we have discussed before.) 3. It requires all volumes to be the same size. I think copying file-by-file at a higher level, as is done in IFS, is the right way to do business. I see the following advantages: 1. Backup packs fill up at a lower rate, since you avoid frequent recopying of files that haven't changed. For example, on Ivy before the recent split, backing up the 5 T-300s comprising the primary file system required only 8 backup packs for an entire month. 2. There is no need to disrupt service. People might not notice a daily disruption at 2 am, but programs (e.g., Grapevine's mail archive facility) surely would. As we do more and more things in "background", the nighttime hours will become increasingly busy. The major disadvantage of the file-by-file approach is that recreating the file system from backup takes much longer. For example, reloading all of Ivy (before the split) would have taken about 12 hours, compared to about 1.5 hours to copy 5 disk packs. However, this is something we expect hardly ever to do, so I don't think it's very important. Now, how do we maintain consistency? I have the following proposal, which works at a higher level than Mark's and I think is simpler: Initially, let's ignore the problems caused by file deletion (I'll get back to this later); pretend that the only update operations that occur are modifications to existing files or creation of new ones. Associate with each file a LastBackupLogID and a WrittenSinceBackup flag. The LastBackupLogID is a LogID associated with the most recent transaction that backed up the file (see below); in particular, it is guaranteed to be less than the LogID of any update to the file after its most recent backup. The WrittenSinceBackup flag is set by every committed update to the file. You back up a file by copying the entire file to the backup volume, using normal operations. You write-lock the file, so internal consistency is preserved and it is guaranteed that there are no uncommitted updates by other transactions. The LastBackupLogID is updated and the WrittenSinceBackup flag cleared as part of the transaction. The LastBackupLogID is copied along with the file. The entire file must be copied under one transaction; but it is not required that you copy ALL files under the same transaction. You use the LastBackupLogID and the WrittenSinceBackup flags to determine which files need to be backed up. The former is used to bound the amount of log you must save and the size of the backup set; and the latter is used to detect new updates to the file. The algorithm is fairly obvious, and is essentially how IFS does its continuous incremental backup. To reload the file system, you first reload all files from all backup packs. (It speeds things up if you start with the most recent backup pack, since then you need reload only the most recent version of a file updated multiple times during the lifetime of the backup set.) While doing so, you compute the minimum LastBackupLogID; this determines where you start reading the log. Now you read the log. For each log entry, you check the file to which it refers. If the log entry's LogID is greater than the LastBackupLogID of the file, you perform the logged action; if less, you skip over it. A possible drawback of this scheme is that it doesn't perform well for gigantic files that are updated incrementally. Since it's possible that data bases will use such files, we may have to consider this problem. For backup purposes, we could break up files into "sub-files" that have individual LastBackupLogIDs. Now, how do we handle deletions? The main problem is that the backup set may contain some files that have been deleted since they were last backed up; you don't want to reload these at all. The way IFS handles this is to take a snapshot of the directory after completing the backup. I imagine copying Pilot's volume file map would be equivalent. This snapshot is used solely for the purpose of determining what files existed prior to the snapshot. It needs to be serialized with ongoing transactions somehow, but I suspect only very weakly; I haven't fully thought this out yet. Then, when reloading the file system, you restore only those files listed in the most recent snapshot, and skip over all the others. Ed *start* 01177 00024 US Date: 20-Oct-81 17:39:47 PDT (Tuesday) From: MBrown.PA Subject: Re: Design for Alpine online backup In-reply-to: Taft's message of 20 Oct. 1981 3:42 pm PDT (Tuesday) To: Taft cc: MBrown, Kolling Will files in a backup file system have the same IDs as on the original? Or will there be a separate map from [volumeID, fileID] to fileID in a backup file system? If not, we need lots of confidence in the uniqueness of file IDs on volumes connected to the backup system. How will we treat immutable files (recall that the copies may have different owners and access control)? It would seem that multiple copies are required; this impacts the first question. Also, the LastBackupLogID of an immutable must be updated by the backup process. Can we handle backup of workstation file systems this way? Sounds plausible, but where is the information stored that tells what "backup server" to interrogate to restore a particular file on a workstation FileStore? On the failed workstation? I can imagine other problems, too, since the backup system represents "free" disk quota for users (who use it to simulate a short-term archiving system.) --mark *start* 01518 00024 US Date: 21 Oct. 1981 10:05 am PDT (Wednesday) From: Taft.PA Subject: Re: Design for Alpine online backup In-reply-to: MBrown's message of 20-Oct-81 17:39:47 PDT (Tuesday) To: MBrown cc: Taft, Kolling Hmmm, good questions. 1. It would appear that we need to have a separate [volumeID, fileID] -> fileID map in the backup system, as you suggested. In addition to the reasons you mentioned, there are at least two other reasons for having such a map. It is needed if we are to back up multiple primary volumes onto a single backup set, since otherwise there is no way to tell which files were on which volume. (Well, that's not strictly true: my proposed file map snapshot would give this information also.) And it enables us to treat the backup set as a write once-only medium (until it is re-used), which eliminates the need to do logging while doing backup, and makes it possible to use tapes, optical disks, etc., as the backup medium. 2. Immutable files: it would seem that the LastBackupLogID can't be in the leader page, or else a file's immutable attribute will have to be subverted in order to change it. (I believe such subversion is relatively easy.) Having multiple copies of an immutable file in a single file system seems an unlikely enough case that it's not worth optimizing out the multiple backups. 3. I'm inclined to use this backup mechanism only for file servers; workstation file systems will back themselves onto file servers using quite a different mechanism. Ed *start* 02416 00024 US Date: 21 Oct. 1981 4:58 pm PDT (Wednesday) From: Kolling.PA Subject: a thorn To: mbrown cc: Kolling, taft You may remember the problem Ed pointed out awhile back, about CheckAccessFile. Namely that AF.Open would call AC.CheckAccessFile, which would try to do an Open to read the file properties, which would.... and that you suggested the solution of Open genning up a pseudo-"openFileHandle" before the call on CheckAccessFile. There are a couple of other places an AC that do Opens (on the owner database file) and that I initially thought might wind up with a routine in the Monitor eventually causing another call on the Monitor or something equally horrid. As luck would have it, they don't, if I have a separate monitor protecting the access cache and if CheckAccessFile never causes anything to touch the rest of the AccessControl database. But this "luck" makes me nervous. I would feel better if I had a backdoor call on Open that guaranteed it wouldn't call AccessControl. Then I would know that I made no calls from AC to outside procedures that called AC again. I would only be calling read/write pages and readfileproperties. It would also remove the need for the hack in regular Open and remove the need for the extra monitor, although Open would be less efficient? (I'm still kneading the AC code around, so there may be a simpler solution to this after I've looked at it awhile.....) Alternatively I could flag CheckAccessFile with a big warning that it must be very stealthy and always avoid various conflicts because of being called in this recursive fashion (yuk). Comments? Just for random info, here are the baddies: 1. The procedure to register (and perhaps initialize) a volume group: This will call Open for the owner database, which will call CheckAccessFile, which fortunately doesn't itself or anything it calls look at the volume group (or it would promptly ERROR UnregisteredVolumeGroup.) 2. The first time AC touches the owner database for a given transaction, down in the depths of the monitor it will try to Open it (If I don't protect this Open by the monitor, more than one process for the transaction might try to do it "at once".) This Open will call CheckAccessFile. As it is currently coded, CheckAccessFile gets into the monitor to update the little access cache. Avoid this by two monitors and being careful. Karen *start* 00724 00024 US Date: 22 Oct. 1981 9:31 am PDT (Thursday) From: Taft.PA Subject: Re: a thorn In-reply-to: Kolling's message of 21 Oct. 1981 4:58 pm PDT (Wednesday) To: Kolling cc: mbrown, taft I believe all problems involving recursive Open/CheckAccessFile can easily be circumvented. Observe that the client of the recursive call is always AccessControl, not AccessControl's client, and that AccessControl makes this call as an enabled AlpineWheel. So long as AccessControl checks the caller's AlpineWheel status outside its own monitor (before doing anything else), there is no problem with recursive entries into monitors. I believe this observation applies to the other two cases you mentioned as well. Ed *start* 00707 00024 US Date: 22 Oct. 1981 11:12 am PDT (Thursday) From: Kolling.PA Subject: Re: a thorn In-reply-to: Taft's message of 22 Oct. 1981 9:31 am PDT (Thursday) To: Taft cc: Kolling, mbrown There are two problems. One is the infinite recursion on Open/CheckAccessFile if CheckAccessFile has to Open the client's file to read its file properties. This is avoided by Mark's fix. The problem with AC Opening the owner database can indeed be avoided if CheckAccessFile always checks AlpineWheels first and doesn't make any access cache entries for AlpineWheels and I flag the code to warn future people. (I was checking AlpineWheels last in my checking routines for efficiency reasons.) Karen *start* 02304 00024 US Date: 23 Oct. 1981 4:26 pm PDT (Friday) From: Taft.PA Subject: OpenFileHandle To: MBrown, Kolling cc: Taft I propose to change all present occurrences of OpenFileHandle in the public interfaces to OpenFileID. This is to be consistent with the following usage convention which seems to be evolving: a Handle is a REF to some object; an ID is an uninterpreted string of bits identifying an object. The OpenFileID that we hand to clients is clearly in the latter class (though there is nothing wrong with the implementor using the bits of the ID to encode information). Along with this, I propose to restructure OpenFileMap in the following way. There will now be two exported types: OpenFileMap.ID (= OpenFileID) and OpenFileMap.Handle (which is a REF to an OpenFileMap.Object). There is only one operation that deals with OpenFileMap.IDs: OpenFileMap.GetHandle: PROC [Conversation, ID] RETURNS [Handle]; In addition to locating and returning the Handle, this procedure performs the operations formerly specified for Validate. All other operations in OpenFileMap, and all operations in other internal interfaces, specify an open file by a Handle, not an ID. What I hope to accomplish by this is to make the AlpineFile.Close operation "safe" in the face of multiple clients operating on the same open file simultaneously. In particular, I don't want a Handle to become invalid in the middle of one client's operation because some other client asynchronously closed it. One way to implement this is for OpenFileMap to keep a table of Handles; some bits of the ID are simply an index into this table. Close simply sets the table entry to NIL, so future calls to GetHandle will fail; however, other in-progress calls may already have Handles referring to the open file object, so the object will not disappear until all those calls have completed. (If there is any cleanup activity that is required when the object finally disappears, the garbage collector's "finalization" mechanism can be used.) This organization does have two consequences for internal clients of OpenFileMap: 1) A remote caller's OpenFileID must be converted to a Handle upon each call, and that Handle must be retained for the entire call; 2) Handles must not be retained between calls. Ed *start* 02226 00024 US Date: 27-Oct-81 10:38:37 PST (Tuesday) From: MBrown.PA Subject: "Compound action" problem To: Kolling, Taft cc: MBrown Consider a client of the AlpineFile interface, such as the Owner database. A client action, such as "change space used for owner Foo under transaction t", is implemented as a sequence of AlpineFile calls under transaction t. Between any two calls it is possible for the worker on this machine to receive the "Prepare to commit" command from the coordinator of transaction t. It is reasonable (in my opinion) to treat such a prepare call as an error, and abort the transaction, but first the condition must be detected and communicated to the worker. Only the client can define these "sub-transactions"; they are application-dependent. There seem to be two possible designs: (1) all responsibility lies with the client. The client registers as a cache manager for transaction t, and therefore is called by Prepare. The client detects the problem at this time and makes a failure return to Prepare, which aborts the transaction. With this approach, a client must maintain a data structure of transactions in progress. (Many clients will already have such a data structure.) (2) the system supports a new notion: the "client action", with calls BeginClientAction[t: transaction] -> clientAction, and EndClientAction[c: clientAction]. By bracketing a sequence of calls with BeginClientAction and EndClientAction, an client ensures that either all of the calls will complete, or the transaction will abort. With this approach, the system attaches a new data structure to the worker object to represent client actions in progress. At first I preferred approach (1), but now I like (2). The reason is that I would like to have this degree of safety without forcing the client to register as a (possibly remote) cache manager. In many cases I expect clients to arrange their own protocol for cleaning things up for commit, and as long as these client protocols work the entire issue is moot, but I want extra redundancy. (Since solution 1 is free to implement, there is no reason not to have it, too.) Karen, how is the Owner database handling this problem now? --mark *start* 00900 00024 US Date: 27-Oct-81 17:29:51 PST (Tuesday) From: MBrown.PA Subject: Re: Talking to many servers In-reply-to: Birrell's message of 26 Oct. 1981 9:34 am PST (Monday) To: Birrell cc: MBrown, Taft, Kolling, Nelson, Boggs, Swinehart I am plunging ahead with an implementation of the transaction algorithm, based on your proposal. I think that it will all work out, but I am also becoming convinced that RPC is now complicated enough to discourage many clients. I think that the global frames of client stubs should be more fully hidden. This means lumping Replicate and Import together into one operation, and having this operation return a collectible "interface record" that has finalization established. If the performance is poor, due to the module NEWs and UNNEWs expended in failed binding attempts, then caching of client stub global frames should be implemented. --mark *start* 01747 00024 US Date: 29 Oct. 1981 3:03 pm PST (Thursday) From: Nelson.PA Subject: RPC.mesa, CedarRPC.mesa, binding, and more To: Birrell, MBrown, Taft cc: Kolling, Swinehart, Nelson As a consequence of the "best" solution to Mark's multiple instance problem, it is necessary for Lupine to distinguish Cedar interfaces from nonCedar interfaces. This was something that I hoped to ignore, but is now impossible. As a result, several things improve: Old CedarRPC changes drastically. The new one is a Cedar image of RPC.mesa, which means that Alpine code should use only CedarRpc, never RPC, for all uses--types, authentication routines, signals, and so on. My personal approach would be to IMPORT RPC: CedarRPC in every rpc-using module. All binding is done direcly through the RpcControl modules, not thru the defunct CedarRPC veneer, since they now understand Cedar when TargetLanguage[Cedar] is specified at translation time (the default). Both RPC interfaces now have a Zones record for parameter storage zones, as discussed in a previous msg. Any existing code using CedarRpc or XXRpcControl will have to change a bit. Because of the two dual interfaces, generating conditional stubs is not too terrible, and some parts are cleaner. Updated versions of RPC and CedarRPC--if you need them now--can be found on [Ivy]<Nelson>Lupine>Temp> (.mesa and .bcd). The latest Lupine generates stubs using these interfaces, and the stubs compile correctly. But the multiple instance stuff for Mark is not there yet; it will be several more days. I have or will give you hardcopy of the new interfaces. Please report your impressions with Andrew so that he can adapt/adopt these interfaces into RpcRuntime.df ASAP. Bruce *start* 01977 00024 US Date: 19 Nov. 1981 9:12 am PST (Thursday) From: Birrell.pa Subject: Re: New RNames In-reply-to: Your message of 18-Nov-81 17:05:20 PST (Wednesday) To: MBrown cc: CedarRPC↑, Schroeder, Birrell I've created an "Alpine" registry, and the individuals Mark requested: Carson.alpine Ebbetts.alpine Luther.alpine Monitor.alpine The "friends" of the Alpine registry are CedarRPC↑.pa. The passwords for the individuals I created are initially null, so you can't log in as those individuals. The friends of the registry can change the individuals' passwords to a non-null value if desired; "real" passwords should be at least 8 characters and unpronouncable. The friends of the registry can also adjust individuals' connect-sites, although that shouldn't be necessary. The individuals have no mailboxes, so they can't receive mail. To export an RPC interface whose instance name is one of these individuals, you must provide sufficient credentials to be allowed to update the "connect-site" field of the individual's database entry. Suitable credentials are either the individual's name and non-null password, or the name and password of a friend of the registry. The instance name of an RPC export/import may also be an NLS name string (like "Indigo") or a Pup host address constant (like "3#44#"). It is acceptable to have exports with different type names but the same instance name iff the exports are on the same host. The "production" usage of these names that I envisage is that each name corresponds to an alpine server. Each Alpine server must know its own name and password. Then when exporting an interface, the server uses its own credentials. This may be the best way to proceed in debugging experiments, too. Please don't do anything to the registry if you aren't sure of what you're doing. Consult Mike or me if in doubt. It may be worth (re-)reading the grapevine paper and/or the grapevine interface specification. Andrew *start* 00605 00024 US Date: 20 Nov. 1981 9:17 am PST (Friday) From: Nelson.PA Subject: A bit more on Duplicate Export Problems To: Sturgis, CedarRPC↑ Reply-To: Nelson I forgot to say that the following may also work. The idea is to get the binder to assign an unused name to the remote TransMgr interface, so that it will never conflict with the local one (which continues to be called TransMgr). Alpine: CONFIG = BEGIN AlpineImpl; RpcRuntime; [ignoreMe: TransMgr, TransMgrRpcControl: TransMgrRpcControl] ← TransMgrRpcClientImpl[]; END; I have not tried this. No Mesa in my office! *start* 01109 00024 US Date: 18-Dec-81 19:05:07 PST (Friday) From: Kolling.PA Subject: questions about errors To: mbrown cc: Kolling Consider two types of errors I can get from my calls to AlpineFile for operations on the owner files: 1. something that indicates a system logic error (AccessFailed, etc.) 2. file not reachable due to volume going offline. note that this is not the same as my having been told that a volume is unregistered, rather this is a volume going offline behind my back. It is my belief that if an error of type #1 happens, I should cause an error fatal to the system, without releasing buffers, etc. because I may want to look at them during debugging. Yes? No? It is my belief that if an error of type #2 happens, I should gently back out and tell the caller ERROR VolumeOffline or some such. Yes? No? I don't see any definition in AlpineFile for errors of type #2..... Should I put ANY catches on my calls to AlpineFile, and consider any errors I don't recognize as class #1, class #2, or otherwise acceptable (lock timeout, etc.) as being fatal to the system? Karen