!standard 09.10 (00) 98-04-01 AI95-00159/03 !class binding interpretation 96-09-04 !status ARG Approved 11-0-1 98-04-01 !status work item 96-09-08 !status received 96-09-04 !priority Medium !difficulty Medium !subject Shared Variables in Shared_Passive? !summary 98-03-27 For the purposes of the Shared Variables rules in 9.10, with respect to shared variables in shared passive partitions, a synchronous remote procedure call is considered to be part of the execution of the calling task. For an asynchronous RPC, the call signals the start of the remote body, but the body then proceeds in parallel, and thus does not signal the next action of the calling task. !question 98-04-01 There is no task rendezvous between two partitions, and protected entries are disallowed in Shared_Passive packages, so how can two actions of reading/updating variables declared in a Shared_Passive package performed on two different partitions be sequential as defined by RM95-9.10(11)? !recommendation 98-03-27 (See summary.) !wording 98-03-27 (See summary.) !discussion 98-03-27 9.10 says: 2 Separate tasks normally proceed independently and concurrently with one another. However, task interactions can be used to synchronize the actions of two or more tasks to allow, for example, meaningful communication by the direct updating and reading of variables shared between the tasks. The actions of two different tasks are synchronized in this sense when an action of one task signals an action of the other task; an action A1 is defined to signal an action A2 under the following circumstances: 3 If A1 and A2 are part of the execution of the same task, and the language rules require A1 to be performed before A2; ... 7 If A1 is the action of issuing an entry call, and A2 is part of the corresponding execution of the appropriate entry_body or accept_statement. 8 If A1 is part of the execution of an accept_statement or entry_ body, and A2 is the action of returning from the corresponding entry call; 9 If A1 is part of the execution of a protected procedure body or entry_body for a given protected object, and A2 is part of a later execution of an entry_body for the same protected object; 10 If A1 signals some action that in turn signals A2. Erroneous Execution 11 Given an action of assigning to an object, and an action of reading or updating a part of the same object (or of a neighboring object if the two are not independently addressable), then the execution of the actions is erroneous unless the actions are sequential. Two actions are sequential if one of the following is true: 12 One action signals the other; 13 Both actions occur as part of the execution of the same task; 14 Both actions occur as part of protected actions on the same protected object, and at most one of the actions is part of a call on a protected function of the protected object. A remote procedure call is a procedure call, so 9.10(3) implies that RPC's are signalling, so long as we view the call as taking place within the execution of a single task. The only problem is that asynchronous RPC's are weird; the caller proceeds without awaiting return of the call. Thus, we need a special-case rule for that case. As an example, suppose a task in one partition writes upon a shared variable in a shared passive partition. It may then do an RPC to notify other partitions that it has done writing. The other partitions may then safely read from that shared variable. As a special case, consider a partition that initializes such a shared variable during that partition's elaboration. E.4(14) says: If a remote subprogram call is received by a called partition before the partition has completed its elaboration, the call is kept pending until the called partition completes its elaboration (unless the call is cancelled by the calling partition prior to that). So other partitions may assume that the shared variable has been initialized, so long as they first do an RPC (that does not raise Communication_Error) to the initializing partition. !appendix 96-09-10 !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !from Laurent Guerby 96-08-23 !keywords synchronization !reference 96-5647.a Laurent Guerby 96-8-23>> !discussion There is no task rendez-vous between two partitions, and protected entries are disallowed in Shared_Passive packages, so how can two actions of reading/updating variables declared in a Shared_Passive package performed on two different partitions can be sequencial as defined by RM95-9.10(11)? May be all PCS actions should be considered as signaling. -- Laurent Guerby , Team Ada. "Use the Source, Luke. The Source will be with you, always (GPL)." **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !keywords synchronization !reference as: 96-5647.a Laurent Guerby 96-8-23 !from Anthony Gargaro 96-8-24 !reference 96-5649.a Anthony Gargaro 96-8-24>> !discussion > There is no task rendez-vous between two partitions, and protected >entries are disallowed in Shared_Passive packages, so how can two >actions of reading/updating variables declared in a Shared_Passive >package performed on two different partitions can be sequencial as >defined by RM95-9.10(11)? It would seem that two actions on a variable declared in a shared passive package must be sequential if the variable is included in an entry-less protected object. **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !keywords synchronization !from Robert Dewar 96-8-25 !reference 96-5650.a Robert Dewar 96-8-25>> !discussion Laurent said > There is no task rendez-vous between two partitions, and protected >entries are disallowed in Shared_Passive packages, so how can two >actions of reading/updating variables declared in a Shared_Passive >package performed on two different partitions can be sequencial as >defined by RM95-9.10(11)? Anthony replied It would seem that two actions on a variable declared in a shared passive package must be sequential if the variable is included in an entry-less protected object. Robert notes Sure of course if the object is in an entry-less protected object, then it is not a shared variable in the sense of the rules which Laurent is asking about. So far, Anthony seems to be agreeing with Laurent that shared variables in the sense of the rules in RM95-9.10(11) are not permitted in shared passive packages. Is this the intention? It seems a big limitation to me. **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !from Bob Duff !reference 96-5656.a Robert A Duff 96-8-27>> !discussion > Laurent said > > > There is no task rendez-vous between two partitions, and protected > >entries are disallowed in Shared_Passive packages, so how can two > >actions of reading/updating variables declared in a Shared_Passive > >package performed on two different partitions can be sequencial as > >defined by RM95-9.10(11)? > > Anthony replied > > It would seem that two actions on a variable declared in a shared > passive package must be sequential if the variable is included in an > entry-less protected object. > > Robert notes > > Sure of course if the object is in an entry-less protected object, then > it is not a shared variable in the sense of the rules which Laurent is > asking about. So far, Anthony seems to be agreeing with Laurent that > shared variables in the sense of the rules in RM95-9.10(11) are not > permitted in shared passive packages. Is this the intention? It seems > a big limitation to me. And Bob says: I'm not sure what we're arguing about. 9.10(11) does not use the term "shared variable". As Anthony pointed out, the wording implies that you need to wrap lots of things in entry-less protected objects. Alternatively, you can use pragma Atomic. Why is this a problem? If you were programming such a system in assembly, you would use spin-locks and cache-flushes to make sure things work right. And protected procedures seem to map pretty well onto that way of doing things. Are Laurent and Robert saying (essentially) that every variable in a shared-passive package should have an implicit pragma Atomic on it? Including giant arrays (where an explicit pragma Atomic would be illegal)? Hmm. It seems to me that Ada is a low-enough-level language that one would expect to do something explicit in the cases we're talking about. - Bob **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !from Robert Dewar !reference 96-5658.a Robert Dewar 96-8-28>> !discussion Bob says: I'm not sure what we're arguing about. 9.10(11) does not use the term "shared variable". As Anthony pointed out, the wording implies that you need to wrap lots of things in entry-less protected objects. Alternatively, you can use pragma Atomic. Why is this a problem? If you were programming such a system in assembly, you would use spin-locks and cache-flushes to make sure things work right. And protected procedures seem to map pretty well onto that way of doing things. Are Laurent and Robert saying (essentially) that every variable in a shared-passive package should have an implicit pragma Atomic on it? Including giant arrays (where an explicit pragma Atomic would be illegal)? Hmm. It seems to me that Ada is a low-enough-level language that one would expect to do something explicit in the cases we're talking about. Robert replies "9.10(11) does not use the term 'shared variable'" Um, in my RM, 9.10(11) appears in section 9.10, whose title is, guess what "Shared Variables", so of course 9.10(11) is talking about shared variables! Perhaps I can make myself clearer, here goes with an example: Suppose in an ordinary Ada program, you have a global variable called junk that is read by many tasks throughout the program, but the only time it is assigned is during program startup. Providing that the program startup is properly synchronized in the sense of 9.10.11 with all tasks that read junk, then all is fine. Now, put junk in a shared passive partition, note that the idea is that junk is big, and is NOT atomic, and is NOT even volatile. What you want to do is to have one partition setup the values in junk, and then send messages to other partitions saying, OK: now you can read this data. Well according to my understanding of the RM, this program is erroneous and there is no way to stop it being erroneous. No spin locks or atomic access or anything like that is required, and requiring junk to be put into a protected object is unnecessary overhead, even if your compiler manages to handle special cases of protected objects efficiently (which is in itself VERY trick to do right). THAT's the problem that we are talking about! **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !from Bob Duff !reference 96-5662.a Robert A Duff 96-8-29>> !discussion > THAT's the problem that we are talking about! OK, I think I understand the problem. Do you have a suggested solution? Actually, I'm not so sure it's really a problem. Is it not the responsibility of System.RPC.Do_RPC (which is written by the user) to do whatever is necessary to flush the cache or whatever? That is, it might do an entry call in order to block the RPC (as required by the RM), and that will have the desired effect. In other words, a remote procedure call doesn't really have any semantics -- it's just a macro that expands into a certain set of calls to 'Read/'Write and user-defined stuff in the PCS, and if the user-defined stuff does the wrong thing, it will be wrong. If this really *is* a problem, then it seems to me that the solution is to treat an RPC like an entry call, as far as the definition of "signal" in 9.10 is concerned. Is there not a potentiall efficiency hit for this, though? That is, the implementation might flush the cache twice -- once because it's doing an RPC, and then again because the user's Do_RPC does an entry call to implement the necessary blocking. But the implementation can't eliminate the first cache flush, because it can never be sure what's inside Do_RPC. I thought you and Laurent were suggesting that every variable in a shared passive package act like an atomic variable, or something like that, which seems like overkill to me. Whatever overhead is necessary should be attached to the operation of "tell the other partition that I've written the shared variable" -- the overhead should not be attached to the act of reading or writing the shared variable. - Bob **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !from Robert Dewar !reference 96-5666.a Robert Dewar 96-8-30>> !discussion Bob said: Actually, I'm not so sure it's really a problem. Is it not the responsibility of System.RPC.Do_RPC (which is written by the user) to do whatever is necessary to flush the cache or whatever? That is, it might do an entry call in order to block the RPC (as required by the RM), and that will have the desired effect. In other words, a remote procedure call doesn't really have any semantics -- it's just a macro that expands into a certain set of calls to 'Read/'Write and user-defined stuff in the PCS, and if the user-defined stuff does the wrong thing, it will be wrong. Sure you are describing how an implementation would in practice be able to get this to work without problems, but that's not the subject of this discussion. The subject of this discussion is whether a program that uses shared variables in a shared passive partition is erroneous or not, and it appears that with the current definition of the language, such a program will in general be erroneous, since you cannot count on the Do_RPC (which is NOT written by the user in real life!) having the right semantics. Note incidentally that your suggestion that all such variables be wrapped in protected types is completely bogus, since it would mean that share passive partitions provide no additional functionality not available by other means! The whole idea of shared passive partitions is to provide shared data which does NOT have a procedural interface! If this really *is* a problem, then it seems to me that the solution is to treat an RPC like an entry call, as far as the definition of "signal" in 9.10 is concerned. Is there not a potentiall efficiency hit for this, though? That is, the implementation might flush the cache twice -- once because it's doing an RPC, and then again because the user's Do_RPC does an entry call to implement the necessary blocking. But the implementation can't eliminate the first cache flush, because it can never be sure what's inside Do_RPC. I indeed think that this is the only solution (to say that the RPC operation is a syncrhonizing operation). I think the cache flushing worry is one you don't need to worry about. You are thinking about tightly coupled machines with non-coherent caches, which are not in any case a typical configuration. The more normal cases are either using shared memory in a normal unix environment on a single processor, where the Unix shared memory facility is used, and you do not have to worry about caches even in a multiprocessor, or alternatively a true distributed system (say using an ethernet), where the issue of cache flushing is handled at an entirely different level). Bob, you have always had the image that the main issue with share variable synchronization is cache flushing, but it isn't most of the time, and it certainly is not in this case. The issue is holding copies in registers, and typically you will be making these shared variables volatile in any case, so even that will not arise. I thought you and Laurent were suggesting that every variable in a shared passive package act like an atomic variable, or something like that, which seems like overkill to me. Whatever overhead is necessary should be attached to the operation of "tell the other partition that I've written the shared variable" -- the overhead should not be attached to the act of reading or writing the shared variable. There is really not an overhead issue here. It is almost impossible to imagine an implementation in which things will not work correctly, and indeed if the ARG does nothing in this area, people will still use passive partitions, their programs will all be erroneous, but they will work anyway! **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !from Anthony Gargaro 96-9-01 !reference 96-5673.a Anthony Gargaro 96-9-1>> !discussion Robert notes: >The subject of this discussion is whether a program that uses >shared variables in a shared passive partition is erroneous or not, and >it appears that with the current definition of the language, such a program >will in general be erroneous, since you cannot count on the Do_RPC (which is >NOT written by the user in real life!) having the right semantics. This is correct; a partitioned program cannot rely on remote calls to synchronize access to a variable declared in a shared passive package. Annex E requires that shared passive packages be supported in the absence of remote calls (E.2.3(20)). >Note incidentally that your suggestion that all such variables be wrapped in >protected types is completely bogus, since it would mean that share passive >partitions provide no additional functionality not available by other means! >The whole idea of shared passive partitions is to provide shared data which >does NOT have a procedural interface! A shared passive package allows a variable to be accessed from different partitions with same assurance as that provided for a nonpartitioned program. This is the additional functionality provided by Annex E. There is no requirement in Annex E to provide additional assurance. Consequently, it is my understanding that if access to a shared variable is erroneous for a nonpartitioned program, it will be erroneous if the variable is declared in a shared passive package for a partitioned program. It should be recalled that an important goal of Annex E is that a partitioned program must execute with the same semantics as its nonpartitioned counterpart. **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !from Robert Dewar 96-09-02 !reference 96-5674.a Robert Dewar 96-9-2>> !discussion Anthony says A shared passive package allows a variable to be accessed from different partitions with same assurance as that provided for a nonpartitioned program. This is the additional functionality provided by Annex E. There is no requirement in Annex E to provide additional assurance. Consequently, it is my understanding that if access to a shared variable is erroneous for a nonpartitioned program, it will be erroneous if the variable is declared in a shared passive package for a partitioned program. It should be recalled that an important goal of Annex E is that a partitioned program must execute with the same semantics as its nonpartitioned counterpart. Robert replies Well I suppose that we could rejoice as implementors to discover that we do not need to implement shared memory (the implementation of shared passive partitions is trivial if you take the attitude that shared variables in the 9.10 sense are erroneous -- if the only shared variables allowed are those inside protected types, then shared passive partitions provide no additional capability over RCI partitions). However, I suspect, from many conversations that people expect shared passive partitions to provide shared memory, and indeed in fact in GNAT, we are committed to providing this facility. The two choices are: The language takes the position that all programs using such shared memory are erroneous, but GNAT documents that this works, and programs use it and in fact work fine. The ARG fixes this obvious gap. This has no effect on GNAT users, except that their programs are now non-erroneous, but leaves the language in a more coherent state. Anthony, I am really puzzled, are you saying that you do NOT think of shared passive partitions as providing shared memory capability. YOu say "if access to a shared variable is erroneous for a nonpartitioned program, it will be erroneous for a paritioned progrm". Well yes, but in the nonpartitioned case, you can synchronize with an entry call. From a users point of view, an RPC is a similar synchronization. If you aim to get the same expressive power for shared passive partitions as for non-partioned programs, then you must do something, because in the non-partitioned case, you can syncrhnoize to make the access to the shared data valid, but there is simply no way to do this in the partitioned case. Let me again note my simple example. You wish to have a shared passive partition with a bunch of data in it. This data is for almost all the execution of the program read only, but in the start up phase of the program, this data is computed on the basis of supplied data. This seems a reasonable paradigm! There is no way to do this in a non-erroneous manner in Ada 95! Are you really happy with this conclusion? **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !from Ted Baker 96-9-02 !keywords synchronization !reference 96-5676.a Ted Baker 96-9-2>> !discussion When these features first were proposed, I had thought it would be OK on map the concept of partition onto the concept of UNIX process (say in a SMP machine) and the concept of a shared passive partition onto a UNIX shared-memory object. The unfortunate part of this is that the primitives most appropriate for synchronizing data acccess between threads/tasks (mutexes) are not the most appropriate for synchronization between processes (semaphores), and some systems may not even support primitives that work for interchangeably for both (i.e. thread-level semaphores or process-shared mutexes). Thus, if protected objects are required to be allowed in shared passive partitions, an implementation will at least have to special-case the implementation, and at worst may be prevented from supporting this model. --Ted Baker **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !reference as: 96-5674.a Robert Dewar 96-9-2 !from Anthony Gargaro 96-9-03 !reference 96-5679.a Anthony Gargaro 96-9-3>> !discussion >Robert replies > Well I suppose that we could rejoice as implementors to discover that we > do not need to implement shared memory (the implementation of shared > passive partitions is trivial if you take the attitude that shared > variables in the 9.10 sense are erroneous Intentionally, the annex avoids specifying a shared passive package capability that incurs a significant additional implementation cost. For example, the Rationale (E.1) mentions that "By naming a shared passive library unit (which resides in a passive partition) in a context clause, the referencing unit gains access to data and code that may be shared with other partitions. Different active partitions (executing on separate nodes) may thus shared protected data or call subprograms in such shared passive units. An active partition can obtain mutually exclusive access to data in a shared passive package if the data is encapsulated in a protected object or is specified as atomic." > -- if the only shared variables > allowed are those inside protected types, then shared passive partitions > provide no additional capability over RCI partitions). I do not understand this last point. Remote call interface package specifications cannot include variable declarations. > Anthony, I am really puzzled, are you saying that you do NOT think of shared > passive partitions as providing shared memory capability. E.2.1 requires that data in a passive partition be shared among active partitions. Since a passive partition may be mapped to a storage node then a passive partition does provide for a shared memory capability. Our disagreement is in the interpretation of what a shared memory capability implies. Apparently you have in mind a capability that provides some form of implicit synchronization of partitions (which is of course not precluded by the annex). > YOu say "if access to a shared variable is erroneous for a nonpartitioned > program, it will be erroneous for a paritioned progrm". Well yes, but in > the nonpartitioned case, you can synchronize with an entry call. From a > users point of view, an RPC is a similar synchronization. In the partitioned case synchronization can be achieved if the variable is a protected object. > If you aim to get the same expressive power for shared passive partitions > as for non-partioned programs, then you must do something, because in the > non-partitioned case, you can syncrhnoize to make the access to the shared > data valid, but there is simply no way to do this in the partitioned case. This is correct. However, once again the annex does not require the same expressive power. > Let me again note my simple example. > You wish to have a shared passive partition with a bunch of data in it. > This data is for almost all the execution of the program read only, but > in the start up phase of the program, this data is computed on the basis > of supplied data. It seems that including the data in a protected object is a straightforward method of accomplishing this example by maintaining some kind of "ready" status. The partition that performs the initialization must set the ready status. Thus other partitions must explicitly query this status before attempting to use the data. **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !reference as: 96-5674.a Robert Dewar 96-9-2 !reference as: 96-5679.a Anthony Gargaro !from Robert Dewar !reference 96-5682.a Robert Dewar 96-9-4>> !discussion I am completely puzzled by Anthony's reaction here. If the only shared data that is available is protected objects, then that means the only interface to such data is via a procedural interface. But in this case, shared passive partitions provide ZERO functionality, since of course the normal RPC mechanism can perfectly well provide an absolutely identical interface. Shared data means to me data that can be shared directly. The functionality of shared passive partitions is important, since it corresponds to a feature that has been found useful (shared memory) in programming distributed applications. After all the language allows non-protected variables to appear in shared passive partitions? Why? Was this simply a careless mistake? Given Anthony's viewpoint, it is always erroneous to place a non-protected variable in a shared passive partition, so clearly they should not be allowed if that is really your viewpoint. To me this is a major hole in the language, and one of two things should be done. Either completely remove passive partitions from the language, on the grounds that they are misleading and provide no functionality, or fix them so that they do provide shared data. This is not only an issue of expressive power, but also of efficiency. The requirement that any access to shared data be via protected objects means that you have all the overhead (typically significant when operating over an operating system, as will usually be the case with distributed applications) of protected objects, making this feature useless for efficient interchange of data. As I said before, the issue is largely academic, since it is obvious that convenient access to shared memory is an important requirement, and certainly in GNAT we will implement it regardless, so what we are discussing is whether this should be a required part of Annex E functionality or not. I note that we have found the facilities of Annex E to be very widely applicable, and in particular, they seem well suited for the implementation of MetaH, but this observation is entirely predicated on the availability of shared memory. **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !reference as: 96-5674.a Robert Dewar 96-9-2 !reference as: 96-5679.a Anthony Gargaro !reference 96-5682.a Robert Dewar 96-9-4 !from Tucker Taft 96-09-05 !reference 96-5686.a Tucker Taft 96-9-5>> !discussion Rather than treating an RPC as any special kind of synchronization, I would rather treat an RPC like a normal subprogram call. In particular, this means that the normal "sequentiality" between a caller and a callee within a single task would apply between the caller and the remote callee. In 9.10 terms, this means that the actions occurring as part of executing the remote subprogram body are "sequential" with the actions occurring in the calling task. It is as though they are both part of the same task. -Tuck **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !reference as: 96-5674.a Robert Dewar 96-9-2 !reference as: 96-5679.a Anthony Gargaro !reference 96-5682.a Robert Dewar 96-9-4 !reference 96-5686.a Tucker Taft 96-9-5>> !from Robert Dewar !reference 96-5687.a Robert Dewar 96-9-6>> !discussion Tuck said: Rather than treating an RPC as any special kind of synchronization, I would rather treat an RPC like a normal subprogram call. In particular, this means that the normal "sequentiality" between a caller and a callee within a single task would apply between the caller and the remote callee. In 9.10 terms, this means that the actions occurring as part of executing the remote subprogram body are "sequential" with the actions occurring in the calling task. It is as though they are both part of the same task. Robert replies OK, but how does this relate to the discussion at hand. The issue again is the following. Shared passive partitions allow the use of ordinary variables. But it appears that with the current RM definition. Any program that declares such a variable can only reference it from one partition, making a mockery of the idea of shared data. Either such shared data should be banned in shared passive partitions, in which case they are not much use, or the language should provide some way of making them work as expected. **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !reference as: 96-5674.a Robert Dewar 96-9-2 !reference as: 96-5679.a Anthony Gargaro !reference 96-5682.a Robert Dewar 96-9-4 !reference 96-5686.a Tucker Taft 96-9-5 !reference 96-5687.a Robert Dewar 96-9-6 !from Tucker Taft 96-09-06 !reference 96-5688.a Tucker Taft 96-9-6>> !discussion > Tuck said: > > Rather than treating an RPC as any special kind of synchronization, > I would rather treat an RPC like a normal subprogram call. > In particular, this means that the normal "sequentiality" between > a caller and a callee within a single task would apply between > the caller and the remote callee. > > In 9.10 terms, this means that the actions occurring as part of executing > the remote subprogram body are "sequential" with the actions occurring in > the calling task. It is as though they are both part of the same task. > Robert replies > > OK, but how does this relate to the discussion at hand. > > The issue again is the following. > > Shared passive partitions allow the use of ordinary variables. But it > appears that with the current RM definition. Any program that declares > such a variable can only reference it from one partition, making a > mockery of the idea of shared data. > > Either such shared data should be banned in shared passive partitions, > in which case they are not much use, or the language should provide > some way of making them work as expected. I guess my quick note didn't give enough information. All you need to do to make unprotected data legitimately shareable is for sequentiality to be established between the two tasks that are manipulating the data. If we presume sequentiality between an RPC caller and the RPC callee, then this can be done by having the two tasks both make an RPC to the same partition, and the two remote subprogram bodies signal each other in one of the usual ways. Hence, a task could write a bunch of stuff into an unprotected variable in a shared-passive package, make an RPC to inform the world that they are done, and then one or more other tasks (which have also made appropriate RPCs, presumably) could read from the unprotected variable. This could still be much more efficient than passing the data itself via an RPC. Of course if the variable is marked volatile, then you don't need an RPC to establish sequentiality, and simple "ordering" can be established using a spinlock implemented using a shared entryless protected object or an atomic variable. Robert also mentioned synchronization relative to partition elaboration time. Based on the semantics of a remote subprogram call given in E.4(14), it should be clear that any (non-asynchronous) RPC that returns without Communication_Error is an indication that the remote partition has been elaborated. If we add anything to the RM to cover synchronization in the presence of distribution, it probably makes sense to mention this as well. -Tuck **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !reference as: 96-5674.a Robert Dewar 96-9-2 !reference as: 96-5679.a Anthony Gargaro !reference as: 96-5682.a Robert Dewar 96-9-4 !from Anthony Gargaro 96-9-7 !reference 96-5691.a Anthony Gargaro 96-9-7>> !discussion >Robert notes: >I am completely puzzled by Anthony's reaction here. If the only shared >data that is available is protected objects, then that means the only >interface to such data is via a procedural interface. But in this case, >shared passive partitions provide ZERO functionality, since of course >the normal RPC mechanism can perfectly well provide an absolutely >identical interface. The advantage of using a protected object in a shared passive partition over using the remote call capability is that it avoids the overhead of a remote call which at a minimum requires marshaling/unmarshaling. Furthermore, as is mentioned in subsequent comments, using remote calls to support shared variables still requires that the calls be synchronized. >After all the language allows non-protected variables to appear in shared >passive partitions? Why? Was this simply a careless mistake? Given Anthony's >viewpoint, it is always erroneous to place a non-protected variable in >a shared passive partition, so clearly they should not be allowed if that >is really your viewpoint. It should be recalled that one important aspect in specifying Annex E was to balance functionality with implementation cost. The issue of non-protected variables was discussed at the IRTAW-6 (Ravenscar 92, were you not present) and the sentiment was that requiring support beyond that provided by protected objects and pragma Atomic might be unacceptable to implementors. (Note that at the time Annex E required that Annex C be supported.) >Shared data means to me data that can be shared directly. In the case of the example that you presented in an earlier comment, i.e., a single one-time writer partition and multiple reader partitions, this can be achieved. Consider the case that the writer allocates and writes to the variable using a protected object. Readers may then obtain the access value to the variable using the same protected object so that subsequent reads are made using this access value. >To me this is a major hole in the language, and one of two things should be >done. Either completely remove passive partitions from the language, on the >grounds that they are misleading and provide no functionality, or fix them >so that they do provide shared data. The first position seems too extreme. A possible compromise is to provide a documentation requirement where an implementation must state whether or not access to a non-protected variable is erroneous. >This is not only an issue of expressive power, but also of efficiency. The >requirement that any access to shared data be via protected objects means >that you have all the overhead (typically significant when operating over >an operating system, as will usually be the case with distributed >applications) of protected objects, making this feature useless for efficient >interchange of data. I do not understand why you seem to presume that using a protected object declared in a shared passive package is any less efficient than using a protected object declared elsewhere. Why would there be a difference in efficiency? It seems that only when an implementation supports some form of distributed shared virtual memory does this become an efficiency issue. >As I said before, the issue is largely academic, since it is obvious that >convenient access to shared memory is an important requirement, and certainly >in GNAT we will implement it regardless, so what we are discussing is whether >this should be a required part of Annex E functionality or not. It would be helpful if you would suggest the proposed change that does not preclude the GNAT implementation. If this is an instance of where the implementors believe that the Annex is too conservative, it would be a very appropriate topic for discussion at the next IRTAW. **************************************************************** !section 9.10(00) !subject Shared Variables in Shared_Passive? !reference RM95-9.10, RM95-E.2.1 !reference 96-5647.a Laurent Guerby 96-8-23 !reference 96-5649.a Anthony Gargaro 96-8-24 !reference 96-5656.a Robert A Duff 96-8-27 !keywords synchronization !reference 96-5650.a Robert Dewar 96-8-25 !reference 96-5658.a Robert Dewar 96-8-28 !reference 96-5662.a Robert A Duff 96-8-29 !reference 96-5666.a Robert Dewar 96-8-30 !reference 96-5673.a Anthony Gargaro 96-9-1 !reference 96-5674.a Robert Dewar 96-9-2 !reference 96-5679.a Anthony Gargaro !reference 96-5682.a Robert Dewar 96-9-4 !reference 96-5691.a Anthony Gargaro 96-9-7 !reference 96-5693.a Robert Dewar 96-9-8>> !from Robert Dewar !discussion Anothony said (non-indented text) It should be recalled that one important aspect in specifying Annex E was to balance functionality with implementation cost. The issue of non-protected variables was discussed at the IRTAW-6 (Ravenscar 92, were you not present) and the sentiment was that requiring support beyond that provided by protected objects and pragma Atomic might be unacceptable to implementors. (Note that at the time Annex E required that Annex C be supported.) Rober replies (indented text) Fine, but then one of two things should happen: (a) non-protected objects should be required to have a pragma Atomic or (b) something should be done in the language to make it clear that it is at least optionally possible to allow non-protected non-Atomic shared objects in a non-erroneous manner It makes no sense to have a situation in the language where you allow something syntactically and semantically, which looks like a big useful feature, but which in practice is completely useless since any use is bound to be erroneous. I don't mind this feature being optional, after all the whole annex is optional, so you don't have to implement anything in it, but I would like the option of doing it properly. Note that the implementation is extremely straightforward on a single machine that supports normal Unix shared memory semantics. That's why I think the language should be adjusted to allow it. I do not understand why you seem to presume that using a protected object declared in a shared passive package is any less efficient than using a protected object declared elsewhere. Why would there be a difference in efficiency? It seems that only when an implementation supports some form of distributed shared virtual memory does this become an efficiency issue. You are imagining! I never said that using a protected object declared in a shared package was less efficient than using a protected object declared elsewhere. All I said is that when building over an operating system, using a protected object declared in a shared package can be very inefficient JUST LIKE using a protected object anywhere. The whole design of protected objects makes sense only if you have a light locking mechanism, which you often do not on a multi-processor with an operating system. It would be helpful if you would suggest the proposed change that does not preclude the GNAT implementation. If this is an instance of where the implementors believe that the Annex is too conservative, it would be a very appropriate topic for discussion at the next IRTAW. Nothing precludes the GNAT implementation. As I have said before, the issue can be left moot as far as GNAT is concerned. The situation we have in GNAT is that we have implemented a useful facility which is consistent with the syntactic and semantic requirements of the RM. It is true that all programs using this facility are erroneous, but their effect is well defined and the fact that the programs are erroneous is not going to keep programmers awake at night, only language lawyers! It would be helpful if you would suggest the proposed change that does not preclude the GNAT implementation. If this is an instance of where the implementors believe that the Annex is too conservative, it would be a very appropriate topic for discussion at the next IRTAW. That sounds like a very good idea. By all means lets do this, and in fact I would be happy to volunteer to lead the discussion. You (or whoever) can consider my contributions to the list a position paper on the subject! ****************************************************************