Academia.edu no longer supports Internet Explorer.
To browse Academia.edu and the wider internet faster and more securely, please take a few seconds to upgrade your browser.
1999, BRICS Report Series
…
51 pages
1 file
See back inner page for a list of recent BRICS Report Series publications. Copies may be obtained by contacting:
2006
Abstract This Works in Progress department discusses the University of Malaga's work in developing software tools to assist testing and debugging communication-intensive mobile applications. It also discusses two projects by a research group from the SDM College of Engineering and Technology and the Walchand College of Engineering and Technology.
… and Applications (SEA), Marina del Rey, …
A FRAMEWORK FOR DESIGN AND IMPLEMENTATION OF MOBILE ACTIVE OBJECTS Giancarlo Fortino1 , Wilma Russo1 , Eugenio Zimeo2 1 DEIS, University of Calabria, Via P. Bucci, Rende (CS), Italy 2 RCOST, Dept. of ...
Lecture Notes in Computer Science, 1996
Distributed systems are composed of multiple objects. Objects support abstract operations and are distributed in not only xed stations but also mobile ones. While the object is moving from one location to others, the quality of service (QoS) supported by the object is changed. We model the movement of the object as changes of QoS supported by the object. We discuss how to support users with QoS required while QoS of the object is being changed. We present the migration and replication methods as a general framework to treat the disconnected operations. In addition, we present an optimistic concurrency control to maintain the mutual consistency among the replicas.
2008
Abstract Paper-based artefacts (eg, forms, manuals, questionnaires) are used ubiquitously and pervasively to support a wide set of activities. Education and Health Care are major examples of such endeavour. However, given the passiveness of the medium itself, several problems (eg, communication, adjustment, lack of interactivity) are usually encountered within these activities which hinder human efficiency or prevent users from achieving desired goals.
arXiv (Cornell University), 2010
A theory, graphical notation, mathematical calculus and implementation for finding whether two given expressions can, at execution time, denote references attached to the same object. Intended as the seed for a comprehensive solution to the "frame problem" and as an alternative (for the specific issue of determining aliases) to separation logic, shape analysis, ownership types and dynamic frames 1 .
The Journal of Object Technology, 2010
A theory, graphical notation, mathematical calculus and implementation for finding whether two given expressions can, at execution time, denote references attached to the same object. Intended as the basis for a comprehensive solution to the "frame problem" and as an alternative (for the specific issue of determining aliases) to separation logic, shape analysis, ownership types and dynamic frames.
of BS Psychology Program. I understand that the research aims to seek the ___________ The end goal of which is in compliance to the requirements of BS Psychology Program.
Como en la primera, la CUARTA REVOLUCIÓN INDUSTRIAL, LA REVOLUCIÓN TECNOLÓGICA 4.0 no han reclamado escuelas y universidades para desarrollarse, porque la SOCIEDAD DEL CONOCIMIENTO ha generado sus propios circuitos productivos y ha creado sus recursos humanos. Pero para poder alcanzar y mantener el desarrollo posterior, para disminuir la brecha entre países ricos y países pobres, entre sectores enriquecidos y sectores empobrecidos, reclamaron buenas escuelas y – ahora sí, - buenas universidades, BUEN NIVEL SUPERIOR. Por esa razón muchas y poderosas empresas multinacionales arman sus cadenas de escuelas y fundan sus universidades, porque, así como fundamentan sus avances en la sólida arquitectura de sus fábricas, empresas y bancos, saben que deben hundir sus raíces en la inversión en CAPITAL HUMANO que le asegure la excelencia del presente y la continuidad en el futuro.
Το Ιερό Μουσουλμανικό ή Ισλαμικό Δίκαιο αποκαλείται στα αραβικά sharia, που σημαίνει 'οδός που ακολουθεί το Νόμο του Θεού'. Η sharia είναι ο Ιερός Νόμος που ρυθμίζει συνολικά τη ζωή των Μουσουλμάνων οπουδήποτε και αν κατοικούν. Στη sharia οφείλεται η δημιουργία του γενικού πλαισίου που είχε καθιερώσει το Ισλάμ κατά το παρελθόν ως πολιτική δύναμη. Αν θεωρήσουμε πως το Κοράνιο αποτελεί το 'σύνταγμα του Ισλάμ', τότε η sharia συνιστά το σύνολο (corpus) των κανόνων που καθιστούν εφαρμόσιμες τις εντολές αυτού του ιερού βιβλίου 1 .
Introduction
Mobile object systems have been investigated for more than one decade, starting out with early representants, like Emerald [JLHB88], and recently a whole series of scientific workshops has been dedicated to mobile object systems [Vit99]. While mobility in object systems has well-known advantages, for example as support for load balancing or mobile agents, there is no common agreement on how mobile objects should actually be realized.
In this paper, our main goal is not to argue for one and against another particular way to implement mobile objects, but rather to study in considerable formal detail one proposal that was already suggested for the above-mentioned Emerald: the proposal that object migration can be derived from two other primitives-cloning and aliasing-by just performing them one after the other. Migration = Cloning ; Aliasing This particular style of programming mobile objects was also advocated by Cardelli within the context of the lexically-scoped distributed programming language Obliq [Car95]. Since we are aiming at a formal investigation into mobile objects, we prefer to study migration in Obliq rather than in Emerald since the former can easily be seen as a proper extension of Abadi and Cardelli's theoretically well-founded object calculi [AC96].
Obliq is well-suited for the above style of migration. Lexical scoping in distributed settings makes program analysis easier since the binding of variables is only determined by their location in the program text, and not by the execution site. While immutable values can be copied from site to site, mutable values are stationary; when they are exchanged between programs on different sites only network references are transmitted. Accordingly, since objects are mutable, the migration of an object does not physically move the object, but instead creates a clone of the object at the target site and then turns the original (local) object into an alias-sometimes called proxy-for the new (remote) object.
In concurrent and distributed programs, it is important that certain state changes in parts of the running system may happen transparently from the point of view of the rest of the system. Ensuring that the implementation of such state changes is in fact transparent can be a difficult task since the programmer must in principle anticipate all possible execution scenarios. So the question arises naturally, whether object migration in Obliq is transparent to the object's clients, and how that can be stated formally.
Intuitively, migration of an object a to some other site works transparently, or safely, if (1) after the migration a client of a cannot tell that a is now an alias, and if (2) during migration it is not possible to interact with a in a way that prevents the migration operation from proper completion. In Obliq, mobile objects are therefore required to be protected and serialized : protection guarantees that aliases are persistent; serialization guarantees atomicity of the two-phase migration operation. Unfortunately, Obliq is not equipped with a formal semantics, except for an unpublished note by Talcott [Tal96], which provides a configuration-style semantics for a subset of Obliq excluding migration, so no formal treatment of the safety of migration has been possible up to now. The aim of our project [Nes99] is to remedy the lack of formality and to reason formally about migration.
From Migration to Surrogation
We formalize the safety of migration based on an abstraction of it in a distribution-free setting. Indeed, since Obliq is lexically scoped, with respect to the results of Obliq computations we may safely ignore all aspects of distribution unless distribution sites fail. Following this idea, we define Øjeblik as Obliq's concurrent core. On the other hand, Øjeblik can also be seen as a particular concurrent extension of the Imperative Object Calculus [AC96]. In this core language, the surrogation of an object a is described as the creation of a clone b of a and then turning a itself into a proxy for b, which forwards future request for methods of a to b. This precisely models migration, except that neither a nor b are attached to any distribution site at all.
Aliasing Models for Mobile Objects
For a formal study of surrogation, we have to come up with a formal semantics. However, there are many possible design choices. As in Obliq, the creation of stationary aliases due to surrogation usually results in general alias chains in a running system and, as it turns out, it is essential for the safety of migration to get just the modeling of nodes in alias chains right. A detailed discussion and formal presentation of four different aliasing models for Øjeblik represents the core of this paper. Interestingly, one of these models corresponds to the implementation of Obliq [Car94], while another one corresponds to Talcott's formal semantics for a subset of Obliq [Tal96], the only formal semantics for Obliq that we are aware of. Both of these models have severe problems with respect to the transparency of migration, as we shall exhibit formally by means of simple counterexamples.
Outline In Section 2, we present in detail our language Øjeblik for typed protected serialized concurrent objects. In Section 3, we informally develop four different aliasing models trying to emphasize the different design decisions behind them. In Section 4, we then formalize the models in terms of a configuration-style global-view semantics. To strengthen the meaningfulness of the semantics, we also collect a series of run-time properties of configurations and formulate them as invariants. In particular, we have a subject reduction theorem, so the typing of Øjeblik terms is compatible with the operational semantics. In Section 5, we investigate the safety of surrogation in the various aliasing models, where it turns out to be crucial whether an operation on an object occurs internally (requested by some method from within the object itself) or externally (requested from the outside of the object). This distinction enables us to draw several fundamental conclusions: (1) surrogation cannot be generally safe if its internal use is permitted, which is independent of the aliasing model; however, (2) only our so-called forwarder aliasing models have a chance to be safe for external surrogation; finally, (3) we provide a type system that statically guarantees that surrogations will always be external. In Section 6, we sum up our observations, we offer some interpretations, and we sketch some future work.
Related work Apart from Talcott [Tal96], closest to our work and like ours based on Abadi and Cardelli's object calculus [AC96] are two concurrent object calculi, one by Gordon and Hankin [GH98], and one by Di Blasio and Fisher [DF96]. However, no account on object migration has been addressed in these works. Sekiguchi and Yonezawa present an encoding of coreObliq into their calculus λdist, but they do not consider aliasing [SY97]. Emerald [JLHB88] has many things in common with Obliq, including a similar style of migration by means of cloning and aliasing, as proposed at an early stage. In Distributed Oz [VHB + 97] object migration is a primitive notion, so objects are physically mobile and travel according to a provably safe mobile state protocol from site to site, wherever they are needed or intend to go.
Typed Protected Serialized Concurrent Objects
In the predecessors of this paper, we presented Øjeblik as an untyped language [HKMN99,NHKM99]. However, types can be added in a straightforward manner, which we do here in order to provide a more complete semantic model of Øjeblik, e.g., by investigating its compatibility with reduction. Another interesting point is that subtyping cannot be straightforwardly introduced due to peculiarities of aliasing (cf. § 2.2).
For the sake of simplicity, in comparison with Obliq [Car95], we omit ground values (like numbers, booleans, strings, etc.), data operations, and procedures, we restrict field selection to method invocation, we restrict multiple cloning to single cloning, we omit flexibility of object attributes, we replace field aliasing with object aliasing, we omit explicit distribution, and we omit exceptions and advanced synchronization, so we get a feasible, but still non-trivial language.
t h r e a dt y p e
Syntax and Informal Semantics
The set L of typed Øjeblik-expressions is generated as shown in Table 1, where method labels l and variables s, x, y, z are taken from countable sets L and X, respectively. Our type system extends the typing rules for the Imperative Object Calculus of [AC96] with a family of thread types Thr(A). Pairsx j :B j denote sequences
Table 1
Function types A→B do only occur in object types [l j :B j →C j ] j∈J , so they are not first-class types. Nevertheless, we sometimes abbreviate such object types by [l j :A j ] j∈J , when we intend to clarify that a type is not a thread type. Whenever appropriate or unambiguous, we deliberately omit the type information in bindings.
The remainder of this subsection presents an informal explanation of the semantics of Øjeblik terms, as suggested for Obliq [Car95], first ignoring aspects of protection and serialization, which are then explained as a second step. In the presentation, we assume that all terms are well-typed according to § 2.2, which formalizes that operations will always be "understandable". Here, we mean that operations are always only requested on objects with a matching interface, but we avoid the term "understood" here, since operations may be invalid due to protection, or delayed due to serialization (see below).
Computation follows the call-by-value (leftmost-innermost) evaluation order of Obliq. In particular, in the following, whenever we use a term a, we implicitly assume that we have first evaluated a to some actual value, i.e., in most cases to an object reference.
Objects An object record [l j =m j ] j∈J is a finite collection of updatable named methods l j =m j , more generally called fields, for pairwise distinct labels l j . In a method ς(s,x)b, the letter ς denotes a binder for the self variable s and argument variablesx within the body b. Moreover, every object in Øjeblik comes equipped with special methods for cloning, aliasing, surrogation, and ping, which cannot be overwritten by the update operation.
Method invocation a.l c with field l of the object a containing the method ς(s,x)b results in the body b with the self variable s replaced by (a reference to) the enclosing object a, and the formal parametersx replaced by (references to) the actual parametersc of the invocation.
Method update a.l⇐m overwrites the current content of the named field l in object a with method m and evaluates to the modified object.
The operation a.clone creates an object with the same fields as the original object and initializes the fields to the same entries as in the original object.
The operation a.alias b replaces object a with an alias to b, written a b, regardless of whether a is already an alias or still an object record; if b itself is an alias, e.g. b c, then we consequently and naturally create an alias chain a b c. From the computational point of view, requests arriving at a after the operation a.alias b should be forwarded to b.
The operation a.surrogate represents our abstraction of migration: by calling it, object a is turned into an alias to a clone of itself, which is implemented by providing a uniform method surrogate=ς(s)s.alias s.clone . Behaving like standard methods, surrogation is forwarded by aliased objects. This is indeed a behavior that is required if surrogation shall correctly mimic migration, because an object should be surrogatable more than once, so double-surrogation a.surrogate; a.surrogate (where ; denotes sequential composition, as defined below) should obviously be equivalent to a.surrogate.surrogate. Without forwarding, the surrogation of an already surrogated object would mistakenly surrogate the proxy.
The operation a.ping is also implemented by providing a uniform method: ping=ς(s)s. Thus, a.ping returns the "identity" of an object o resulting from the evaluation of a; note that, due to aliasing and forwarding, this could be the "identity" of the current endpoint of an alias chain potentially starting at object o. We add the a.ping method uniformly to Øjeblik objects, because it allows us to conveniently express an algebraic equation for the correctness of surrogation in Section 5. Furthermore, such a method could be used by clients for garbage collection of references to surrogated servers by interrogating the current identity and using it directly instead of the former indirect reference.
Scoping Apart from the binding of variables in method bodies, Øjeblik also offers explicit scope declarations. An expression let x = a in b first evaluates a, binding the result to x, and then evaluates b within the scope of the new binding.
We use the standard inductive definition fv(a) to denote the free variables of term a with respect to our two forms of binding. Øjeblik only admits non-recursive expressions let x = a in b, i.e., with x ∈ fv(a). Then, a; b denotes let x = a in b, where x ∈ fv(b). Finally, L 0 denotes the set of closed Øjeblik terms a ∈ L, i.e., where fv(a) = ∅.
Concurrency While objects represent persistent stateful structural entities, computational activity takes place within threads. Apart from the main thread that is initially started up with the execution of a term, new separate threads can be created by the fork command. The term fork a returns a new thread identifier to denote the thread evaluating a. The result of a fork'ed computation is grabbed by the join command. If a evaluates to a thread identifier, then join a potentially blocks until that thread finishes and returns the thread's result, or blocks forever, if a join on thread a was already performed earlier.
Self-Infliction, Serialization, Protection
The current method of a thread is the last method invoked in it that has not yet completed. The current self of a thread is the self of its current method. An Øjeblik operation is self-inflicted, also called internal, if it addresses the current self; an operation is external if it is not self-inflicted.
In concurrent object-based settings, the invariant that at most one thread at a time may be active within an object is often called serialization. The simplest way to ensure serialization is to associate with an object a mutex that is locked when a thread enters the object and released when the thread exits the object. However, this approach is too restrictive-for instance, it prevents recursion. Based on the notion of thread, so-called reentrant mutexes, as in Java, can be used to allow an operation to re-enter an object under the assumption that this operation belongs to the same thread as the operation that is currently active in the object. In Obliq, however, the more cautious idea of selfserialization requires, based on the above notion of self-infliction, that the mutex is always acquired for external operations, but never for self-inflicted ones. Note that this concept allows a method to recursively call its siblings through self, but it excludes the kind of inter-object mutual recursion, where a method in an object a calls a method in another object b, which then tries to 'call back' another method in a.
Based on self-infliction, objects are protected against external modifications in a natural way: updates, cloning, and aliasing, are only allowed if these operations are selfinflicted. In Øjeblik, for simplicity, all objects are both protected and serialized.
Executive Summary It is instructive to classify the operations on Øjeblik objects, as we do in the following table, not only according to the protection conditions (subject to self-infliction, or not), but also with respect to the node of action denoting the node where the operation is finally carried out (locally at the initially called node, or only at the endpoint of a chain starting at the called node). All of these properties are unambiguously stated in Obliq's informal semantics: According to this table, we sometimes use the term "local request" to denote a request for cloning or aliasing and "endpoint request" for the others. Similarly, we sometimes use the term "unconstrained request" for those operations that are not subject to protection. Note the hybrid role of update requests, which are endpoint requests as well as subject to apparently local protection.
Types and Typing, but no Subtyping
In
with arbitrary types B 1 and B 2 and terms a and b of the required types, the alias operation in the term is well-typed, as we can give a the type [l 1 :B 1 ] by (T-Subsum). However, the program will fail at run-time when, after the aliasing, we activate the method l 2 on x, as l 2 is not available in y. So, we refrain from giving an account of subtyping (cf. § 6). Although the informal semantics of Obliq is reasonably clear at first sight, its formalization enforces one to reason about even the slightest detail. In doing so, we discovered several fundamental facts on alias chains, implied by a few basic assumptions on Øjeblik's operations, that are worth being spelled out explicitly ( § 3.1). Based on them and the classification of operations in Section 2.1, a whole range of design choices for the forwarding of requests come into play preparing the ground to properly introduce the various aliasing models discussed in this paper ( § 3.2).
Facts: On the Stability of Alias Chains
As a matter of fact, according to the operations' character with respect to self-infliction and the intended node of action, a node x in an alias chain can be unstable, which means that if it currently points to node y, it may later on point to different node z. In order to clarify this phenomenon, we distinguish two cases based on the notion of a task, which is the run-time entity that is created by method invocation within a single object. A thread may then actually be seen as a stack of tasks connected via invocations. Now, a node can be active, in which case it contains running tasks, or not. The punchline of this subsection is then that an alias node can not become stable before it has terminated its current tasks. Below, we introduce pictures where we use single/double boxes to denote inactive/active nodes, and single/double arrows to denote unstable/stable aliases. Furthermore, dashed boxes and dotted arrows denote unspecified respective entities.
Inactive Nodes: No Tasks By definition, the only way to receive a self-inflicted request is to have already at least one local task running. In other words, if there is no local task, then each incoming request is doomed to be external. Now, consider the example term let z = [ l="bar" ] in let y = [ l="foo" ] in let x = [ l=ς(s, w)s.alias w ] in x.l y ; x.l z after it carried out the call x.l y , which means that the object referred to by x has turned itself into an alias for y and then terminated its activity. We depict the situation as follows
where, in general, the node x may itself be referred to by other aliases, while y and z may be either an alias or an object record. In fact, the alias x y is stable in the very sense: no re-aliasing operation on x to another node will ever possibly take place since it could only be carried out in a self-inflicted way by one of its own methods, but any request to such a method potentially starting such a self-inflicted operation, e.g., by calling x.l z , is itself forwarded to y such that it can never take place in x.
Active Nodes: At Least One Task As an example, let us first consider the term let z = [ l="bar" ] in let y = [ l="foo" ] in let x = [ l=ς(s, w)s.alias w ; "bla" ] in x.l y just after object x has accepted the request for method l and turned itself into an alias for y.
Since x continues to operate on itself, according to "bla" in method l, x is an active alias node.
The alias x y is marked as unstable since "bla" may contain further self-inflicted requests, e.g., to perform a re-aliasing or a cloning. Thus, if "bla" calls s.alias z or s.clone, we get
and such changes may continue as long some current task in x is active. Here, the re-aliased x remains active, thus unstable, until all current tasks in x, in our example according to "bla", have terminated. Note that the cloning of an active unstable alias x y provides a new inactive stable alias x y, because only the state of x is copied, not its tasks. Generalizing the above example, we may consider the case where several tasks of the current thread are running in an alias or an object. However, by the definition of synchronous method invocation, only one of them may be active-namely the one on top of the thread's call-stack-while the others must be blocked. Now, note that it is the active task or any of its ancestors in the call-stack who turned the current node into an alias (in the example it is method l); otherwise, the node would be stable and the current tasks would not exist, but have been created in one of the successors of the stable alias node.
Design Choices: Four Aliasing Models
Apart from the above-mentioned facts about the stability of alias nodes, Obliq's informal semantics is rather imprecise on the behavior of alias nodes. In particular, there is quite some freedom on how to precisely model serialization and protection in aliased objects.
There are many possible variants, some of which we list below. First, we provide an abstract characterization. Then, we refine the description of the individual models by means of a comparison on how requests are forwarded, respectively.
How much Protection and Serialization?
In this paper, we introduce and study • a conservative model (C) that keeps protection and serialization unchanged, • a relaxed model (R) that relaxes protection to some extent, but ignores serialization, • a forwarder model (F ) that relaxes protection even more, and • a serialized model (S) that reintroduces some serialization to the forwarder model.
Our motivation for investigating and presenting all of the above four models is that C corresponds to Cardelli's implementation [Car94], while R corresponds to Talcott's operational semantics [Tal96]. As we will see in Section 5, neither of these two models provides sufficiently nice properties regarding the safety of surrogation. Therefore, we developed ourselves the model F as a consequent generalization of R that does well for surrogation, and S as a version of F with better programming and implementation properties.
Request Forwarding: Which? What? When? Each request arriving at an alias node must carry with it the knowledge about its "current self", i.e., an identification of the caller; this is required since otherwise self-infliction could not be checked locally by the requested node. The essential design choices to be made are the following:
1. Which requests (cf. table on page 7) are forwarded?
(protection) 2. What is the current self of forwarded requests?
(protection/serialization) 3. When does the forwarding take place?
(serialization)
We use these questions to compare the individual above-mentioned aliasing models. Which? The most intuitive model with respect to the first question, at least at first sight, is probably model R: only endpoint requests are forwarded, local requests are handled locally. In contrast, model C is more restrictive for the case of updates. Recall that updates have a hybrid status in that they are protected endpoint requests: they can only perform meaningfully on object records, i.e., on endpoints of alias chains, and they can only be carried out when self-inflicted. Since in model C, as in Cardelli's implementation, object aliasing is just as a macro for universal field aliasing, alias nodes are just the original objects with field redirection, so protection and serialization are still fully intact. Therefore, only unconstrained requests are forwarded, while the others-including updates-are either performed or blocked. If updates are self-inflicted in an intermediate node, then in model R they will be rejected only at the endpoint, while in model C already in the successor. The models F /S are more liberal than model R and forward all requests except for self-inflicted local ones, because they are the only ones that can be carried out successfully in the requested node. This behavior will be of vital importance in Section 5.
What? This question has just two possible answers: either forwarding changes the current self of the request, or it does not. If it changes it, which is the case only in model C, then the new current self is the one of requested node. In the models R/F /S, however, forwarding does not change the current self. Although the difference is apparently minor, its impact is crucial for both protection and serialization: for example, consider a forwarded update request that keeps the same current self as it started out with-it may finally reach the endpoint of the alias chain and be self-inflicted there! The impact in the models F /S is even bigger, because, as mentioned above, even cloning and aliasing requests may be forwarded.
When? The third question just concerns how much serialization is present in alias nodes. Here it is crucial to recall the fact that alias nodes are unstable as long as they are active (cf. § 3.1). Should those external requests that are to be forwarded wait until the alias becomes stable, or should they be forwarded immediately, i.e., independent of stability? Here, the four models that we discuss behave again differently: model C, as explained before, keeps serialization in aliases, so all external requests have to wait. Furthermore, the alias is afterwards locked until the forwarded request has successfully signaled termination from its point of action, so we may call this forwarding game lockand-go. The models R and F completely ignore serialization in alias nodes, so forwarding of all intended requests is immediate. It is only now that we introduce our model S as a variant of model F : here, all external requests to an alias node are blocked until the current tasks have finished. Then, the pending requests are forwarded without waiting for the previously forwarded request to signal termination; we call this forwarding game touch-and-go since external requests in alias nodes have to wait for the appearance of the node's mutex, but once it becomes available-in fact, just touchable-they will not grab it, but will simply go ahead. As we will argue later on ( § 4.4), in comparison with model F , model S is better suited for programming since it enhances the predictability of program behaviors. It can also be used for path-compression, i.e., the optimization of alias chains to turns aliases into aliases for the farthest stable successor.
We summarize this rather complex discussion on design choices for aliasing models in Øjeblik by the following table. The important aspects are that the lower a model is listed in the table the more requests are forwarded, which prevents a client of an alias node to observe that the node really is an alias. This, however, only holds if the current self of forwarded requests is not changed by the alias node they pass. There would, of course be more variants and combinations of different design decisions, but we chose the above due to their existing counterparts in Obliq (C/R) on the one hand, and due to their better surrogation properties (F /S) on the other hand.
Design Consequences
The notion of current self is essential for the meaning of selfinfliction. In fact, in the models R, F , and S, which have in common that they do not change the current self of forwarded requests, self-infliction mutates naturally into pre-infliction: a thread may reenter an object or alias node from another node if it has only visited predecessors of the current node since its last visit of the current node. In terms of an implementation using object mutexes, this amounts to a generalization of self-inflicted mutexes towards reentrant mutexes, but in a controlled way, so we may coin the term preentrant mutexes for this purpose. At Aalborg University, we have developed a prototype implementation, which allows us to experiment with the feasibility of such mutexes in practise [NOI + 99].
Four Operational Semantics for Øjeblik
Our various transition semantics for Øjeblik terms follow loosely the one sketched by Talcott [Tal96]. Her semantics addresses a larger subset of Obliq than we do with Øjeblik, in particular including distribution concepts, but nevertheless excludes, for example, migration and join. The section is organized by first presenting the common basic concepts ( § 4.1) of the formal semantics for the various aliasing models ( § 4.2). We then offer behavioral semantics ( § 4.3), examples ( § 4.4), and an investigation of various properties of our semantics that prove the meaningfulness of our semantics ( § 4.5-4.6).
Common Basic Concepts
The semantics performs local changes on global run-time configurations, which are mappings from references v ∈ R to run-time entities. More precisely, a configuration C maps task references t ∈ R T to tasks T, and object references o ∈ R O to objects O (see below). We use dom X (C) to denote dom(C)∩R X for X ∈ {T , O}, and ↑ for undefined references.
a, b ::= . . , we refer to the-possibly undefined-current self and parent of the task associated with reference t in C. We reserve the task references t m , t g ∈ R T for special purposes.
Alias chains
The partial function ali C :
computes the alias chain, starting at reference o, where • denotes concatenation of (sets of) strings of references, in general possibly ending with ↑. This computation obviously only terminates, if there are no cycles in the chain. The endpoint of an alias chain is denoted by end(ali C (o)); if it exists, then the semantics will guarantee that it is associated with an object record O (cf. Lemma 4.5.1). We write o ∈ ali C (o) if o occurs in the string representing the alias chain starting at o and we sometimes abbreviate finite alias chains
As a specialization of the above function, we define We sometimes refer to object references as nodes, reflecting the fact that may be nodes in an alias chain. A node o ∈ dom O (C) is active if there is t ∈ dom T (C) with s C (t) = o, otherwise it is called idle. An alias node is a node o ∈ dom O (C) with C(o) = o for o ∈ dom O (C). An alias node is stable (c.f. § 3.1), if it is idle.
Threads Since tasks, in general, refer explicitly to their parent, we can build up task chains, which start in tasks that have no parent assigned. Such chains precisely correspond to Øjeblik threads, as derived from a task as the task's ancestors, similar to call-stacks in an implementation. Let his C :
be the history of a task t, where denotes the empty string.
Furthermore, let trc C :
be the trace of a task, representing the string of object references that occur as the current self of itself and its ancestors. For example, if C = {t 0 := ↑, ↑, a 0 , t 1 := t 0 , s 1 , a 1 , t 2 := ↑, ↑, a 2 }, then his C (t 1 ) = t 1 t 0 and trc C (t 1 ) = s 1 . A task with reference t =t g is current in C, if (1) it is defined in C, (2) it is not the parent of any other task in C, and (3) it does not have parent t g . The threads of C are the histories of the current tasks in C:
In the above example, there are just the two threads t 1 t 0 and t 2 .
Evaluation Table 4 also contains grammars for generating redexes r and evaluation contexts e[•], which we use to control the evaluation [FF86] of (the expression part of) run-time tasks. The contexts are designed such that evaluation proceeds leftmost-innermost. Note that fork a is a redex independent of a being a value; similarly, an expression let x = a in b becomes a redex as soon as the bound expression a has reduced to a value. According to this definition, a simple algorithm computes for every closed run-time expression a ∈ R a unique pair of a redex r and a context e [•] such that a = e[r].
Table 4
Evaluation of Øjeblik run-time expressions
Behaviors The semantics of a closed term a ∈ L 0 is given by assigning to it the initial configuration [ [ [ a ]]] := {t m := ↑, ↑, a , t g := ↑, ↑, t m }. The task referred to by t m represents the start of the so-called main thread; the task reference t g is used as the parent of all garbage task references, i.e., references to threads that are accomplished during reduction, and that have already been join'ed. Both, t m and t g are always defined in configurations.
The behavior of configurations is generated from (subsets of) the rules in Tables 5-12. In each case we pick some task and object references in a particular configuration C, which under the respective conditions may enable a transition to take place in C. In the premises, note that the expressions of tasks are always in unique context-redex decomposed form. In the conclusions of the rules, C{t:=T, o:=O} means that the mapping C is either extended or overwritten with the association of task reference t with task T , and object reference o with run-time object O.
Table 5
The rules in Table 5 describe the local activity in a single task t in a straightforward manner; recall that let is not recursive. Furthermore, we assume that the value v is either a task or an object reference whose actual run-time entity is accessible through C.
The rules in Table 6 exhibit the interplay of fork and join: in rule (Fork), a new task t is spawned off, which runs the expression a without current self. In rule (Join), the parent referring to its child t is returned a value v. Note that fork'ed tasks do not know their parent, so they indeed represent initial tasks of new threads. As soon as a thread t is join'ed, it is marked as garbage by means of the special reference t g as its parent; no further attempt to join t will succeed, and t can not be reused after the first join.
Table 6
The rules in Table 7 exemplify the synchronous method invocation protocol. In the rule schema (X -Inv), a call to an object results in the creation of a new (callee-) task within the target object, while the caller-task is delayed, which is syntactically represented by the term wait inserted into its evaluation context. In rule (Ret), this caller-callee pair can communicate the result as soon as the callee-expression has reduced to a value; the task then disappears. A reference to a callee-task may even be reused after a successful (Ret), because the semantics guarantees that there is exactly one task, namely the caller-task, that is wait'ing for the callee-task to finish (cf. Lemma 4.5.2); in particular, references to callee-tasks never occur in run-time expressions-only references to fork'ed tasks can. Like the other rules of the operational semantics in Tables 8-12, especially the rules (Ali)/(Cln)/(Upd) for protected operations on objects, the rule (X -Inv) for invocation crucially depends on how aliased objects should behave. Therefore, we parameterized the above rule schema, and start splitting up our presentation into different variants corresponding to different aliasing models X , as introduced in the next subsection.
Table 7
Self-Infliction
In order to formalize the test for the either self-inflicted or pre-inflicted character of operations on objects, we introduce some suitable notation based on the information of alias chains in configurations. Let s be the current self of the caller-task, and o be the requested object reference. Then the following definitions are obvious. :s = o pre-infliction for endpoint ops (R/F /S) : s = end(ali C (o)) pre-infliction for local ops (F /S)
:
In addition, we have to be able to check formally that an intended node o is currently available to deal with requests from task t:
An object o is available for task t in C, if o is idle, or it it is the same as the current self of t, such that operations from t on o would be self-inflicted.
Aliasing Models
According to the four models informally proposed in Section 3.2, we now provide coherent formal semantics for each of them, where the inclusion of a rule to the semantics of a particular aliasing model is indicated by prefixing its name with C, R, F , or S.
The Conservative Model
Each alias node is protected. Thus, the rules in Table 8, all of which address protected operations on object o-which can only happen if they are self-inflicted-require the premise o = s. Note that for simplicity we do not generate run-time errors (as in Obliq) for invalid access to protected operations; the calls to such operations just block forever. As exemplified in rule (X -Inv), the rules (C-Inv 1 )/(C-Inv 2 ) in Table 9 formalize synchronous method invocation by means of new task t in the target object o, while forcing the caller t to wait. The difference between these two rules is, whether the target o is an object record or an alias. In the former case, the available method body is instantiated immediately; in the latter case, instead, a new call is placed in the target object o, but now directed to the new target o , according to the aliasing information in C.
Table 8
Table 9
Each alias node is serialized. To this aim, the use of Avail C (o, t) checks for availability of the current call from task t with respect to the target o. Note that a method call on an aliased object o in rule (C-Inv 2 ) creates a forwarding sub-task t with current self o. Essentially, this means that o's mutex is now locked by this task and that the current self of the forwarded call has been changed, according to the discussion in § 3.2.
The Relaxed Model
While protection is kept in alias nodes, serialization is ignored. Talcott [Tal96] proposes a scheme for method calls in Obliq, which is optimized in the sense that it directly addresses the endpoint of a chain, if it exists, and instantly creates a sub-task there. In rule (R/F -Inv) of Table 10, this scheme is formalized in terms of the functions end and ali C . In particular, no tasks are created in intermediate nodes between o andô, which also means that none of the intermediate nodes is locked, and the availability of the endpoint is checked with respect to the current self s of the calling task t.
Although aliased objects are protected, updates on aliased objects are modeled as in rule (R/F -Upd): if an update on o is originating from the current endpoint of its alias chain (as checked by the condition end(ali C (o)) = s), then it is forwarded there to take effect, there. Otherwise, the caller is blocked. It is this peculiar behavior that inspired us to formally introduce the notion of pre-infliction (cf. § 4.1).
While it is a convenient abstract optimization to immediately forward requests to the endpoint of a chain, as it is it does not give rise to a proper implementation technique, because unstable nodes are transparently traversed although they may give rise to different alias chain later on (c.f. § 3.1).
The Forwarder Model
We 'learn' from the peculiar modeling of update transitions in the relaxed model, and generalize it to also apply to cloning and aliasing: the rules in Table 11 replace the former tests o = s for immediate self-infliction with a test for self-infliction on successors in the alias chain starting from the entry in the chain. In effect, this behavior prescribes an implementation of an aliased object as a pure forwarder for external and a partial forwarder for internal requests: partial since requests for invocation and update are forwarded, while requests for cloning and aliasing are accepted, if self-inflicted.
Table 11
Protected transitions in the forwarder model
The Serialized Model
The step from the forwarder model to the serialized model is simple. To every rule for calling an operation at an object, we add the requirement that all alias nodes that have to be traversed in the alias chain starting from the entry node up to the point of action have to be available for the calling task. In the rules for endpoint operations (S-Inv) and (S-Upd), we add ∀ȯ ∈ ali C (o) : Avail C (ȯ, t) to check the chain until the endpoint. In the rules for local operations (S-Cln) and (S-Ali), we instead add ∀ȯ ∈ pre C (o, s) : Avail C (ȯ, t) to check the chain until we reach self-infliction.
Roundup
Whenever we refer to a particular aliasing model, we use the notation X [ [
Behavioral Semantics
Based on a may-variant of convergence [Mor68] of terms, we define a contextual notion of equivalence [GHL97] uniformly for the four semantics of Øjeblik. In the context of a concurrent language with fork, threads may nondeterministically affect the outcome and convergence of evaluation. So, with respect to our goal of reasoning about surrogation, we regard must-variants of convergence as too strong (see also the testing semantics of the Join Calculus [Lan96]; stronger versions of equivalence based on barbed bisimulation can be found in [BFL98]). Note that the definition of typed equivalence does not use the type information to equate more terms-it just reduces the number of contexts that are taken into account for equivalence. We have the following close relation between the typed and untyped equivalence. We have not proved this conjecture, but we strongly believe that it holds since the only way an ill-typed context can invalidate a ∼ = b is basically by using method labels inappropriately. Then, we conjecture that any such misbehaving context could be simulated by a well-typed one that simply diverges in the case that the ill-typed context produces a message-not-understood. We also expect the conjecture to hold when we take ground values into account. However, note that it would not hold in the context of subtyping: where the typing system would allow us to compare two objects a := [ l 1 =c 1 , l 2 =c 2 ] and b := [ l 1 =c 1 ] at the common type [ l 1 : C 1 ], thus rendering them equal, although the ill-typed context (c.f. We postpone the study of properties of our four semantics until after the next subsection, where we expose several sometimes surprising example behaviors that can be formulated in terms of Øjeblik.
Definition 4.3.1 (Convergence
Peculiarities of Infliction, Aliasing, and Fork
As an abbreviation, we use the method definitions l=id and k=Ω to denote l=ς ( F (and also model R), then the main thread may reach a terminating state; otherwise, as in the serialized forwarder model S (and also model C), the competing request may not pass through until the current method has terminated itself. However, then the second alias request must have been performed, and the pending request on label k will again be doomed to fail, i.e., it diverges. We are not aware of a good application of this behavior, but at least we found it interesting to observe that it can be formulated within Øjeblik.
Figure
Example 4.4.4 (Reflexive Threads
Invariant Run-Time Properties
In this subsection, we collect facts and lemmas about the well-definedness of our operational semantics. Although we concentrate our explanations and proofs here on the serialized model S, which is our reference semantics, the results also hold for the other models.
Essentially, we collect properties of configurations that are invariant under reduction, under the assumption that we start out with an initial configuration that arises from a static term, as we may call terms in the original Øjeblik syntax, following the terminology of [GHL97]. Subject reduction, i.e., the invariance of typing under reduction, is then developed and proved in the next subsection. We start with a simple fact.
For all o ∈ dom O (C) :
there is at most one task t ∈ dom T (C) with o = s C (t) and t current in C.
For all t ∈ R T , o ∈ R O :
if A configuration C is closed if all object and task references occurring in run-time terms of task or in run-time object records are also captured within C, and if all task references occurring in run-time terms are mapped to thread-initial tasks, which also may have become garbage. All configurations reachable in our semantics are closed in that sense. Serialization in Øjeblik, and also in general, means that each object is inhabited by at most one thread. Self-serialization adds the requirement that whenever a thread successfully re-calls an object it has actually never left the object since its first visit.
Definition 4.5.4 (Self-Serialization). A configuration C is
• serialized, if for all t 1 , t 2 ∈ cur(C):
• self-serialized, if, in addition, for all sequences t n . . . t 0 ∈ thr(C):
Note that this definition is compatible with pre-infliction. In the models R, F , and S, there is no book-keeping on traversed predecessor nodes, when requests are forwarded. Thus, when checking for the intersection of traces, we only see the "nodes of action". The only rule cases of interest are, when tasks are added, which is for (Inv) and (Fork), because only then the invariant may be broken. The actual proof in these cases is mere algebra. If tasks are removed from the current configuration, as with (Ret), then tasks are always only removed from the top of threads. Moreover, threads are never split up into two, so there is no danger of possibly invalidating the invariant that way. If we neither add nor remove tasks, then the invariant holds trivially, except for the case of (Join), which is the only rule that changes the parent of a task. Yet, also this case is harmless since it does not interfere with the condition of serialization due to initial and garbage tasks not having a current self and, thus, no trace.
Reduction vs Typing
We show that reduction preserves typing and, consequently, that every configuration reachable from a well-typed term is also well-typed. Therefore, we have to extend our type system to the domain of run-time configurations C, which involves all run-time extensions: references v, tasks T , proxies o, and run-time terms a ∈ L R . For convenience in the typing of run-time terms, especially to keep track of the types and occurrences of wait, we introduce the task-annotated construct wait t , which is uniquely determined on its creation within the (Inv)-rules. Accordingly, we may strengthen Lemma 4.5.2-3. Note that the second property of this lemma now tells us that there are never two different parents waiting for the same task to finish. In Lemma 4.5.2-3, no such confusion could arise since parents did not know their children-with annotated wait, parents learn about their children, so we must explicitly prove that parents do not share their children.
Proof. By induction on the length of
Since we annotate all binding occurrences of variables with types, together with the dynamic annotation of all occurrences of wait, and also the fact that we record types for all references ever created, the proof of subject reduction is smooth, even in the critical cases of method invocation and return, as well as the interplay between fork and join. Note that it is also because of subject reduction that we model garbage threads explicitly; as long as a thread may be referred to, we need some typing assumption to preserve typability. As new forms of typing judgments we need Γ v:A, Γ T :A, Γ o:A, and Γ a:A, where the type environments Γ now may also include pairs v:A as assumptions on the types of references. The extended type system consists of the former rules in Table 2, now extended to admit run-time terms, and the additional rules in Table 14. The rules (T-Task) and (T-Thrd) derive the type of a task directly from the type of its enclosed runtime expression, where thread types only appear for orphan or garbage tasks. Similarly, the rule (T-Proxy) derives the type of proxies from the type of the target object. Recall from Lemma 4.5.2 that run-time expressions within tasks are always closed terms, so the type environment Γ in these cases will just have to contain typings for references, not for variables. Moreover, there are trivial rules to extract the types of references from the assumptions (T-Ref) and to extract the type of wait from its annotated task (T-Wait).
Table 2
, we present the rules for static typing. Type environments Γ are finite lists of pairs of variables and types; they can also be seen as functions, where Γ(x) searches in Γ from right to left for the first occurrence of x and returns the associated type. Type judgments are of the form Γ a:A and express that term a has type A under the assumptions Γ on the free variables of a. The typing rules themselves are not surprising. The operations clone, alias, surrogate, ping, and update, all yield a result of the same type as the object that they address. While fork packs a type into a thread type, join unpacks it accordingly. The rules for variables, let, objects, and invocations are standard.The only potential surprise is that we cannot assume the usual subtyping rules of the Imperative Object Calculus[AC96]. These rules state that [l j :B] j∈J <: [l j :B] j∈I if I⊆J (T-SubObj) and that Γ a:B if Γ a:A and Γ A<:
Table 14
Independent of the underlying aliasing model, C[x]⇓ since x returns immediately, while C[x.surrogate] ⇓, because x.surrogate sends the request into a loop along the trivially cyclic alias chain. Due to the above problem, we refine the safety equation to include some trivial request to check whether the object before surrogation is actually reachable: This equation detects cyclic chains by means of the simple ping-request which travels to the endpoint of the alias chain possibly starting at x. For the above context, C[x.ping] ⇓.
The typability of a configuration C follows the standard idea of a sanity check that all of the run-time entities mentioned in the configuration, via references v, have to be typable under the assumptions that Γ makes of the types of those references (c.f. [Rep92]).
For subject reduction, we further need a series of standard lemmas, which we present without proofs, that allow us to manipulate type environments. In the following, q ranges over variables and references, and refs(a) extracts the references that occur within term a. The following typing results hold for all aliasing variants of our operational semantics.
The first two lemmas allow us to add and remove type assumptions about variables or references not used in the term being typed. Proof. By induction on the length of the reduction sequence.
(base case) Initial configurations of well-typed terms are well-typed:
Assume, by induction, that there is Γ n−1 such that Γ n−1 C n−1 . Then, Theorem 4.6.8 concludes the proof.
A final remark is due on the notion of type preservation. Indeed, we allow in our semantics that task references that are allocated for invocations are released after a successful termination in rule (Ret). This is witnessed by the fact that, only in this case, the type environment may shrink. If, at some later moment in evaluation, the reference would be reused, this may in general be with a different type. Yet, this does not harm for subject reduction since one needs at least two steps to "change" the type of a task reference, while Theorem 4.6.8 is addresses single steps only. On the other hand, we may easily provide a semantics where task references are not reusable such that type environments never shrink.
On the Safety of Surrogation
The goal of this section is manifold. We motivate how to formulate the safety of surrogation in Øjeblik as an equation ( § 5.1). We study the two different kinds of external ( § 5.2) and self-inflicted ( § 5.3) surrogation and discuss how the various aliasing models behave in each of the cases. Then, we show how to formalize the fact that a certain surrogation is uniformly external ( § 5.4) and hint at a formal proof for the case of external surrogation ( § 5.5) that we report on in a companion paper. Finally, we provide a simple extension of Øjeblik's type system that guarantees uniformly external surrogation ( § 5.6).
Safety as an Equation
We formalize the idea that an object before and after surrogation should behave the same in all possible contexts as an equation using the notion of typed equivalence of Definition 4.3.3. Let a ∈ L be an Øjeblik term. Our first attempt was to require:
It turns out that this conjecture is rather naive, and indeed wrong with all four aliasing models. In the remainder of this section we adapt and narrow down the above equation such that it becomes true, and provably so. The following discussion also generalizes to migration in a distributed lexically-scoped setting, like Obliq. The simplest case of Equation 5 is where a is an Øjeblik object O. In this case the surrogation is surely safe in all four semantics, because (1) the process of surrogation is carried out correctly since, due to serialization, only the surrogation thread can interact with the object O, i.e., there cannot be any interference with another thread or activity, and (2) every interaction with O is mimicked identically by O.surrogate, which suffices since after surrogation nobody has access to the previous O.
In the general case, however, neither of the two above arguments holds. The reason is mainly because of possible copying of a reference to the former object such that, after surrogation, requests can still be directed to that reference. If these requests are subject to protection and serialization, then the semantics of aliasing models comes into the play. Observing that a = let x = a in x (in all contexts, the let just adds one unconditional step after reducing a) and that the notion of equivalence takes all well-typed Øjeblik contexts into account, Equation 5 can be reduced to the problem of surrogation on variables:
However, there is an inherent problem with that equation, which is exhibited by the following context that, as the example in Equation (2), creates a self-alias via method call: A closer look at Øjeblik examples, as we will have in the following two subsections, shows that for the safety of surrogation it is crucial to distinguish, whether or not the call x.surrogate within a given context C[•] is "external for" x, or whether it is selfinflicted. Note that, unfortunately, this problem is undecidable, as already observed for Obliq [Car95]-only at run-time may we observe which case applies. Intuitively, this means that we must therefore execute a term C[x.ping] until that particular access to x.ping appears at the top-level for evaluation.
Aliasing Models for External Surrogation
We provide two single-threaded, distinguishing examples that exhibit unsafe surrogations in the conservative and relaxed models, while the forwarder models behave safely. The two examples have a very similar structure, only differing in the object O that, for the sake of well-typedness, has to serve single-parameter methods on label l.
C[•]
This context resembles the term of Equation (1) in that we intend to apply the context C [•] to two different requests: on the one hand x.ping, and on the other hand its surrogating counterpart x.surrogate. Applying our semantics we get that C[x.ping] and C[x.surrogate] roughly leads to x.l x and x .l x , respectively, where x is bound to the surrogation result, which is definitely different from x itself.
In Table 15, we show the initial steps according to our operational semantics: we indicate the applied transition rules, as well as the respective uniquely defined redex for each following step. (We deliberately omit the garbage reference task associated with t g since it is not used in the examples.) Here, we are not specific about the aliasing model on which the semantics we follow is based, because all models, so far, coincide. Note the two additional steps (Cln) and (Ali) needed to perform the surrogation in between the applications of (Inv) and (Ret). Note further the different resulting states X and S, where either o or o is called, respectively, from within the main task t m with o as parameter.
Table 15
[s.ping]⇓ and C 3 [s.surrogate] ⇓, because in C 3 [s.ping] the cloning of y is allowed, while in C 3 [s.surrogate] the corresponding call is blocked due to protection. Problems Another problem arises by externally sending a request to the surrogation target, but now via the surrogation source, e.g., for updates: [s.ping]⇓ and C 4 [s.surrogate] ⇓. By sending an update to itself after having surrogated, as in C 4 [s.surrogate], the update of y is blocked, while without previous surrogation the call succeeds and the whole term converges. [s.ping] ⇓ and C 5 [s.surrogate]⇓. Whereas C 5 [s.ping] diverges since the alias call to s also affects y in that case, the counterpart C 5 [s.surrogate] converges since the re-aliasing of s does not affect the target y.
[ [ [ C[x.ping] Table 15. Then, we can trace some more reductions now specific to O 1 : Here we were not specific about the semantics we follow when applying rule (Inv) since the behavior is the same in either case (C-Inv 1 ) or (R/F/S-Inv) and leads to convergence (X 1 ⇓ and thus C 1 [x]⇓).
In contrast, for the execution of S 1 , the various semantics exhibit different behaviors (which we indicate by subscripts) after the first common invocation step: Table 15. As before, we now trace some more reductions of X 2 and S 2 : Immediately, we observe the well-behavior of X 2 in all aliasing models since the cloning requested within task t is obviously self-inflicted and leads to convergence (X 2 ⇓, and thus C 2 [x.ping]⇓).
In contrast, the behavior of the corresponding S 2 after the first step is quite different for the respective models, due to the external cloning request. In the model C, we immediately get S 2 → C . However, also in the model R, we get S 2 → R ; the reason is that forwarding, which helped us in dealing with Example (9), was only proposed for invocations and updates. This is exactly the motivation for our forwarder models F and S, which remedy the above situation, because they recognize and accept pre-infliction also for cloning and aliasing. Consequently, they lead to convergence (S 2 ⇓ and thus C 2 [x.surrogate]⇓): 10is self-inflicted at run-time. However, among our four candidate semantics only the two forwarder models handle these requests properly for C 2 [x.surrogate].
Self-Inflicted Surrogation
A particular class of examples is represented by objects that perform surrogation in a selfinflicted way such that they afterwards-as long as the current method is active-still can perform self-inflicted operations on the surrogated object. Due to these examples, surrogation cannot be safe in general, not even in the forwarder models. We classify two different sets of examples depending on whether they exhibit problems with access to a self-surrogated source or the target thereof. As for the detailed explanation of the examples in terms of their explicit reductions and reachable configurations, we omit them here and leave them as an exercise; they are similar to the developments in the previous subsection.
Target Problems An immediate source of problems is due to the incorrect external use of the surrogation target by means of protected operations, e.g., cloning: All of these examples of observable self-surrogation can be interpreted as programming errors. In each case, the current method has the complete and local knowledge about the object and its surrogated counterpart at hand-because it itself performed the surrogation-but nevertheless operates on either of the two in an obviously "stupid" and avoidable manner. In these cases, it is not the fault of the semantics, but the fault of the programmer.
On the Absence of Self-Surrogation
The previous subsection exhibited that self-inflicted surrogation is inherently problematic. Thus, we should try to achieve a safety theorem at least for those cases where surrogation is guaranteed to be always external. Although this is an undecidable criterion, it can be formally defined and then used in formal proofs.
It suffices to concentrate the exposition on endpoint operations op ∈ {ping, surrogate}. Recall from Subsection 4.1 the characterization of self-infliction for the various aliasing models. We use the respective case for endpoint operations op in the context of the serialized forwarder model, which says that a particular request o.op with current self s is pre-inflicted in a particular run-time configuration C if s = end(ali C (o)).
The above definition only helps us to address the question whether a request is external in a particular configuration. However, since concurrent threads may dynamically change the state of the object or alias that a request is directed to, we need a more general definition to express that a request will be external in all reachable configurations.
There are two ways to check for self-surrogation: either (1) consider all surrogation requests in a given term, or (2) consider only one particular request under study and trace it during the computation that brings it to top-level, i.e., in redex position. While the former is quite restrictive, the latter faces the problem to ensure during the reduction sequence that we can keep track on what happens to a particular request under study; in fact, the object or variable that it is syntactically addressed to is going to be replaced during evaluation by some object reference, and the request itself might be installed as subterm of some other piece of code on a task different from the initial one, which can even happen several times.
An obvious technique to trace a request is to tag the subterm of interest with some meta-information that does not affect the semantics, e.g. by means of overlining. To this aim, we may extend the syntax with overlined operations for ping, and surrogate (there would not be a problem doing the same for invocation, update, cloning, and aliasing). For this extended syntax we disallow rules with overlined redexes, but we allow to perform substitutions on them possibly replacing a variable with a reference. If we then start the evaluation of a term with at most one overlined subterm, then the evaluation of the expression inside a task is automatically stopped whenever an overlined subterm appears as a redex at top-level. Note that there can appear several instances of the one overlined redex that we started out with in the case that it was a subterm of a method body, which creates a new copy each time it is called.
Towards a Safety Theorem for Provers
According to the above remarks, we concentrate our efforts on reasoning about the equation x.ping = x.surrogate with respect to only those contexts that are uniformly external, because we conjecture that the forwarder models, especially the serialized version S will allow us to prove a theorem for these cases. According to Definition 4.3.1, this means: We restrain ourselves to call this conjecture a theorem, because we did not carry it out for the very notion of convergence as introduced in this paper, as we will explain in the following paragraph. Further comments on this matter can be found in the Conclusions.
In previous work on the Imperative Object Calculus (IOC) [AC96], equivalence between IOC terms was defined in a contextual way [GHL97], similar to Definition 4.3.1. In many cases, it seems simpler to use a semantics by translation into π-calculus [MPW92] to establish the equivalence between terms [KS98]. The main advantage is the large number of equivalences and algebraic laws that can be used to reason about expressions. Since IOC is (almost) a concurrency-free subset of Øjeblik, we chose a similar path for establishing the safety of external surrogation. An early sketch of a proof for a restricted version of Theorem 5.5.1, where only an inductively defined subset of 'external' contexts is taken into account, is found in [HKMN99], but the underlying π-calculus translation had several flaws. In our recent paper [MHKN99], we show the non-trivial proof based on a translation of Øjeblik that carefully follows the serialized model, and with respect to the corresponding notions of convergence and uniformly external contexts based on that π-calculus semantics.
Towards a Safety Theorem for Programmers
We argued earlier that it is the programmer of an object who is often responsible for potential problems caused by self-inflicted surrogation. In order to help a programmer to avoid these problematic cases, it would be useful to provide some syntactic criteria or programming guidelines, although they necessarily would tend to be rather restrictive. Note that the standard technique of restricting protected operations on the literal self, i.e., the innermost surrounding binding for s, does not work in the context of aliasing-see the above counterexamples. Yet, those examples of self-surrogation could be avoided if one was guaranteed that surrogation was the last operation performed on an object by the currently inhabiting tasks. If no operation is afterwards directed to s, then none of the above observable "mistakes" could be done. However, the fact that not only the current task may continue after the surrogation to operate on s, but also potentially other tasks in the call-stack that wait for the current task to return, complicates the quest for a syntactic criterion for a programmer.
There are two ways out of the dilemma of self-surrogation that we can imagine:
1. An application of data-flow analysis could verify that the self-inflicted surrogation is definitely the last operation called in the current self-inflicted method call-stack.
2. A type system could ensure that we never encounter self-surrogation at all, or at least provide us with warnings that self-surrogations might occur.
Here, we propose a solution based on the latter idea. The observation is that, if a evaluates to the current self, or to a node in an alias chain leading to the current self, then a must have the same type as the type of the current self (c.f. rule (T-Proxy) in Table 16). This implies, that if we ensure that the type of a is not the same as for the current self, then a.op cannot result in op being an internal operation. Such a check can be easily incorporated into the type system of Table 2. In the resulting system, judgments are of the form Γ D a:A, where D denotes the type of the self variable for the method enclosing a.
Table 16
Typing rules ensuring external surrogate operations
In Table 16 we present the modifications of the type system; the rules missing are as the ones in Table 2 with replaced by D .
The following theorem witnesses a static guarantee for uniformly external contexts provided by the type system. Here, as well as in the rule (T-Fork), we have to assign some current-self type to the top-level judgment. Since, by definition, threads do not have a current self, the natural choice is to use a thread type Thr(A) as the current-self type. The reason is that thread types never face the danger of being confused. Note that we could also simply use the type Thr([ ]) in these cases. aliasing, re-aliasing could be initiated by activities of local siblings, which makes sense even after the current aliasing method has terminated. Note that in this proposal there is no need for protection as a requirement for surrogation since object aliases would be immediately stable if re-aliasing was prevented. (We do not question the general usefulness of protection, although this may be subject to further discussion.)
If the design of Obliq was not based on mutexes for self-infliction, but rather on reentrant mutexes, then the counterexample of Equation (9) would be resolved properly, but there would still be the other counterexample of Equation (10), where pre-entrant cloning would be needed, and also the counterexamples with respect to self-surrogation would still apply. Thus, if the concept of being reentrant would include protection in addition to serialization, then this design proposal would become as good as forwarder models, and even more liberal. However, the increased liberty of reentrant mutexes for serialization was already an argument for Cardelli to reject them for Obliq, and to prefer self-infliction. In this paper, we give formal and practical arguments for why pre-entrant mutexes and protection are the best choice from the point of view of transparent (external) migration.
Is Migration = Cloning ; Aliasing a good idea, after all? Since we completely ignored in our study the question of performance, or the feasibility of a run-time system to deal with ever-growing alias chains, it is hard to compare this style of migration to others that have been proposed, for example for Distributed Oz [VHB + 97]. (These matters go well beyond the scope of this paper, but we may note here that the compression of alias chains should be straightforward based on our formal notion of stable alias nodes.) However, we would like to emphasize that the intuitive simplicity of the concept does not quite compensate for the complexity that it imposes on formal reasoning, and also not for the sometimes unpredicted and surprising outcome of computations. It should be very interesting to study the safety of migration of other styles.
Table 1 :
Table 2 : Typing Rules 3 Intermezzo: Towards Formal Semantics
Table 5 :
Table 6 :
Table 7 :
Table 8 :
Table 9 :
UN Women Discussion Papers, 2021
PHOÎNIX, Rio de Janeiro, 25-1, 2019
Electronics and Electrical Engineering, 2011
Egyptian Academic Journal of Biological Sciences, B. Zoology
Археологическое изучение Болгарского городища: Изд-во АН РТ, 2024
1914-1918 - on line International Encyclopedia of the First World War, ed. by Ute Daniel, Peter Gatrell, Oliver Janz, Heather Jones, Jennifer Keene, Alan Kramer, and Bill Nesson, issued by Freie Universitat Berlin, 2015
BMC Cardiovascular Disorders, 2021
Trafik ve Ulaşım Araştırmaları Dergisi, 2021
Iyunim: Reviews in Children's Literature, 2025
Current osteoporosis reports, 2017
Les Ruines Modernes, Témoins Présent de l'Histoire de Beyrouth., 2022