956 CONCURRENCY,DISTRIBUTION,CLIENT-SERVER AND THE INTERNET $30.3 This idea was first made popular by Java in late 1995 and 1996;Java execution engines have become widely available.Plug-ins have since appeared for many other mechanisms.An alternative to providing a specific plug-in is to generate,from any source language,code for a widely available engine,such as a Java engine;several compiler vendors have indeed started to provide generators of Java "bytecode"(the low-level portable code that the Java engine can execute). For the notation of this book the two avenues have been pursued:ISE has a free execution engine;and at the time of writing a project is in progress to generate Java bytecode. Either approach raises the potential of security problems:how much do you trust someone's application?If you are not careful,clicking on an innocent-looking hyperlink could unleash a vicious program that destroys files on your computer,or steals your personal information.More precisely you should not,as a user,be the one asked to be careful:the responsibility is on the provider of an execution engine and the associated library of basic facilities.Some widely publicized Java security failures in 1996 caused considerable worries about the issue. The solution is to use carefully designed and certified execution engines and libraries coming from reputable sources.Often they will have two versions: One version is meant for unlimited Internet usage,based on a severely restricted execution engine. In ISE's tool the only I/O library facilities in this restricted tool only read and write to and from the terminal,not files.The "external"mechanism of the language has also been removed,so that a vicious application cannot cause mischief by going to C,say,to perform file manipulations.The Java "Virtual Machine"(the engine)is also draconian in what it permits Internet"applets" to do with the file system of your computer. The other version has fewer or no such restrictions,and provides the full power of the libraries,file I/O in particular.It is meant for applications that will run ona secure Intranet (internal company network)rather than the wilderness of the Internet. In spite of the insecurity specter,the prospect of unfettered remote execution,a new step in the ongoing revolution in the way we distribute software,has generated enormous excitement,which shows no sign of abating. 30.3 FROM PROCESSES TO OBJECTS To support all these mind-boggling developments,requiring ever more use of concurrent processing,we need powerful software support.How are we going to program these things?Object technology,of course,suggests itself. Robin Milner is said to have exclaimed,in a 1991 workshop at an O-O conference, Cited in [Matsuoka "I can't understand why objects [of-O languages]are not concurrent in the first place". 1993]. Even if only in the second or third place,how do we go about making objects concurrent?
956 CONCURRENCY, DISTRIBUTION, CLIENT-SERVER AND THE INTERNET §30.3 This idea was first made popular by Java in late 1995 and 1996; Java execution engines have become widely available. Plug-ins have since appeared for many other mechanisms. An alternative to providing a specific plug-in is to generate, from any source language, code for a widely available engine, such as a Java engine; several compiler vendors have indeed started to provide generators of Java “bytecode” (the low-level portable code that the Java engine can execute). For the notation of this book the two avenues have been pursued: ISE has a free execution engine; and at the time of writing a project is in progress to generate Java bytecode. Either approach raises the potential of security problems: how much do you trust someone’s application? If you are not careful, clicking on an innocent-looking hyperlink could unleash a vicious program that destroys files on your computer, or steals your personal information. More precisely you should not, as a user, be the one asked to be careful: the responsibility is on the provider of an execution engine and the associated library of basic facilities. Some widely publicized Java security failures in 1996 caused considerable worries about the issue. The solution is to use carefully designed and certified execution engines and libraries coming from reputable sources. Often they will have two versions: • One version is meant for unlimited Internet usage, based on a severely restricted execution engine. In ISE’s tool the only I/O library facilities in this restricted tool only read and write to and from the terminal, not files. The “external” mechanism of the language has also been removed, so that a vicious application cannot cause mischief by going to C, say, to perform file manipulations. The Java “Virtual Machine” (the engine) is also draconian in what it permits Internet “applets” to do with the file system of your computer. • The other version has fewer or no such restrictions, and provides the full power of the libraries, file I/O in particular. It is meant for applications that will run on a secure Intranet (internal company network) rather than the wilderness of the Internet. In spite of the insecurity specter, the prospect of unfettered remote execution, a new step in the ongoing revolution in the way we distribute software, has generated enormous excitement, which shows no sign of abating. 30.3 FROM PROCESSES TO OBJECTS To support all these mind-boggling developments, requiring ever more use of concurrent processing, we need powerful software support. How are we going to program these things? Object technology, of course, suggests itself. Robin Milner is said to have exclaimed, in a 1991 workshop at an O-O conference, “I can’t understand why objects [of O-O languages] are not concurrent in the first place”. Even if only in the second or third place, how do we go about making objects concurrent? Cited in [Matsuoka 1993]
$30.3 FROM PROCESSES TO OBJECTS 957 If we start from non-O-O concurrency work,we will find that it largely relies on the notion of process.A process is a program unit that acts like a special-purpose computer: it executes a certain algorithm,usually repeating it until some external event triggers termination.A typical example is the process that manages a printer,repeatedly executing "Wait until there is at least a job in the print queue" "Get the next print job and remove it from the queue" "Print the job" Various concurrency models differ in how processes are scheduled and synchronized,compete for shared hardware resources,and exchange information.In some concurrent programming languages,you directly describe a process;in others,such as Ada,you may also describe process types,which at run time are instantiated into processes,much as the classes of object-oriented software are instantiated into objects. Similarities The correspondence seems indeed clear.As we start exploring how to combine ideas from concurrent programming and object-oriented software construction,it seems natural to identify processes with objects,and process types with classes.Anyone who has studied concurrent computing and discovers O-O development,or the other way around,will be struck by the similarities between these two technologies: Both rely on autonomous,encapsulated modules:processes or process types;classes. Like processes and unlike the subroutines of sequential,non-O-O approaches, objects will,from each activation to the next,retain the values they contain. To build reasonable concurrent systems,it is indispensable in practice to enforce heavy restrictions on how modules can exchange information;otherwise things quickly get out ofhand.The O-O approach,as we have seen,places similarly severe restrictions on inter-module communication. The basic mechanism for such communication may loosely be described,in both cases,under the general label of"message passing". So it is not surprising that many people have had a"Eureka!"when first thinking, Milner-like,about making objects concurrent.The unification,it seems,should come easily. This first impression is unfortunately wrong:after the similarities,one soon stumbles into the discrepancies Active objects Building on the analogies just summarized,a number of proposals for concurrent O-O mechanisms(see the bibliographical notes)have introduced a notion of"active object". From:Doug Lea, An active object is an object that is also a process:it has its own program to execute.In a “Concurrent Pro- definition from a book on Java: gramming in Java", Addison-Wesley, Each object is a single,identifiable process-like entity (not ulike a Unix 1996. process)with state and behavior
§30.3 FROM PROCESSES TO OBJECTS 957 If we start from non-O-O concurrency work, we will find that it largely relies on the notion of process. A process is a program unit that acts like a special-purpose computer: it executes a certain algorithm, usually repeating it until some external event triggers termination. A typical example is the process that manages a printer, repeatedly executing “Wait until there is at least a job in the print queue” “Get the next print job and remove it from the queue” “Print the job” Various concurrency models differ in how processes are scheduled and synchronized, compete for shared hardware resources, and exchange information. In some concurrent programming languages, you directly describe a process; in others, such as Ada, you may also describe process types, which at run time are instantiated into processes, much as the classes of object-oriented software are instantiated into objects. Similarities The correspondence seems indeed clear. As we start exploring how to combine ideas from concurrent programming and object-oriented software construction, it seems natural to identify processes with objects, and process types with classes. Anyone who has studied concurrent computing and discovers O-O development, or the other way around, will be struck by the similarities between these two technologies: • Both rely on autonomous, encapsulated modules: processes or process types; classes. • Like processes and unlike the subroutines of sequential, non-O-O approaches, objects will, from each activation to the next, retain the values they contain. • To build reasonable concurrent systems, it is indispensable in practice to enforce heavy restrictions on how modules can exchange information; otherwise things quickly get out of hand. The O-O approach, as we have seen, places similarly severe restrictions on inter-module communication. • The basic mechanism for such communication may loosely be described, in both cases, under the general label of “message passing”. So it is not surprising that many people have had a “Eureka!” when first thinking, Milner-like, about making objects concurrent. The unification, it seems, should come easily. This first impression is unfortunately wrong: after the similarities, one soon stumbles into the discrepancies. Active objects Building on the analogies just summarized, a number of proposals for concurrent O-O mechanisms (see the bibliographical notes) have introduced a notion of “active object”. An active object is an object that is also a process: it has its own program to execute. In a definition from a book on Java: Each object is a single, identifiable process-like entity (not unlike a Unix process) with state and behavior. From: Doug Lea, “Concurrent Programming in Java”, Addison-Wesley, 1996
958 CONCURRENCY,DISTRIBUTION,CLIENT-SERVER AND THE INTERNET $30.3 This notion,however,raises difficult problems. The most significant one is easy to see.A process has its own agenda:as illustrated by the printer example,it relentlessly executes a certain sequence of actions.Not so with classes and objects.An object does not do one thing;it is a repository of services (the features of the generating class),and just waits for the next client to solicit one of those services-chosen by the client,not the object.If we make the object active,it becomes responsible for the scheduling of its operations.This creates a conflict with the clients, which have a very clear view of what the scheduling should be:they just want the supplier, whenever they need a particular service,to be ready to provide it immediately! The problem arises in non-object-oriented approaches to concurrency and has led to mechanisms for synchronizing processes-that is to say,specifying when and how each is ready to communicate,waiting if necessary for the other to be ready too.For example in a very simple,unbuffered producer-consumer scheme we may have a producer process that repeatedly executes “Make it known that producer is not ready'” "Perform some computation that produces a valuex" “Make it known that producer is ready” "Wait for consumer to be ready" “Pass x to consumer” and a consumer process that repeatedly executes Handshake "Make it known that consumer is ready" “Wait for producer to be ready” “Get x from producer” "Make it known that consumer is not ready" "Perform some computation that uses the value x" a scheme which we may also view pictorially: producer consumer A simple Produce producer- Wait Wait consumer scheme Communicate Communicate Handshake (passx) ons ume Communication occurs when both processes are ready for each other;this is sometimes called a handshake or rende=-vous.The design of synchronization mechanisms -enabling us in particular to express precisely the instructions to "Make it known that process is ready"and"Wait for process to be ready"-has been a fertile area of research and development for several decades
958 CONCURRENCY, DISTRIBUTION, CLIENT-SERVER AND THE INTERNET §30.3 This notion, however, raises difficult problems. The most significant one is easy to see. A process has its own agenda: as illustrated by the printer example, it relentlessly executes a certain sequence of actions. Not so with classes and objects. An object does not do one thing; it is a repository of services (the features of the generating class), and just waits for the next client to solicit one of those services — chosen by the client, not the object. If we make the object active, it becomes responsible for the scheduling of its operations. This creates a conflict with the clients, which have a very clear view of what the scheduling should be: they just want the supplier, whenever they need a particular service, to be ready to provide it immediately! The problem arises in non-object-oriented approaches to concurrency and has led to mechanisms for synchronizing processes — that is to say, specifying when and how each is ready to communicate, waiting if necessary for the other to be ready too. For example in a very simple, unbuffered producer-consumer scheme we may have a producer process that repeatedly executes a scheme which we may also view pictorially: Communication occurs when both processes are ready for each other; this is sometimes called a handshake or rendez-vous. The design of synchronization mechanisms — enabling us in particular to express precisely the instructions to “Make it known that process is ready” and “Wait for process to be ready” — has been a fertile area of research and development for several decades. “Make it known that producer is not ready” “Perform some computation that produces a value x” “Make it known that producer is ready” “Wait for consumer to be ready” “Pass x to consumer” and a consumer process that repeatedly executes “Make it known that consumer is ready” “Wait for producer to be ready” “Get x from producer” “Make it known that consumer is not ready” “Perform some computation that uses the value x” Handshake A simple producerconsumer scheme Produce Consume Wait Communicate producer consumer Handshake (pass x) Wait Communicate
$30.3 FROM PROCESSES TO OBJECTS 959 All this is fine for processes,the concurrent equivalent of traditional sequential programs which"do one thing";indeed,a concurrent system built with processes is like a sequential system with several main programs.But in the object-oriented approach we have rejected the notion of main program and instead defined software units that stand ready to provide any one of a number of possible features. Reconciling this view with the notion of process requires elaborate synchronization constructs to make sure that each supplier is ready to execute a feature when the client needs it.The reconciliation is particularly delicate when both client and supplier are active objects,since each has its own agenda. All this does not make it impossible to devise mechanisms based on the notion of active object,as evidenced by the abundant literature on the subject (to which the bibliographical notes to this chapter give many references).But this evidence also shows the complexity of the proposed solutions,of which none has gained wide acceptance, suggesting that the active object approach is not the right one. Active objects clash with inheritance Doubts about the suitability of the active object approach grow as one starts looking at how it combines with other O-O mechanisms,especially inheritance. If a class B inherits from a class 4 and both are active (that is to say,describe instances that must be active objects),what happens in B to the description of's process? In many cases you will need to add some new instructions,but without special language mechanisms this means that you will almost always have to redefine and rewrite the entire process part-not an attractive proposition. See“Sequencing Here is an example of special language mechanism.Although the Simula 67 and inheritance'”, language does not support concurrency,it has a notion of active object:a Simula class can, page 1121,as part of the discussion of besides its features,include a set of instructions,called the body of the class,so that we Simula. can talk of executing an object-meaning executing the body of its generating class.The body of a class 4 can include a special instruction inner,which has no effect in the class itself but,in a proper descendant B,stands for the body of B.So if the body of4 reads some initialization,inner,some termination actions and the body of B reads specific B actions then execution of that body actually means executing some initialization;specific B actions,some_termination_actions Although the need for a mechanism of this kind is clear in a language supporting the notion of active object,objections immediately come to mind:the notation is misleading, since if you just read the body of B you will get a wrong view of what the execution does; it forces the parent to plan in detail for its descendants,going against basic O-O concepts (the Open-Closed principle);and it only works in a single-inheritance language
§30.3 FROM PROCESSES TO OBJECTS 959 All this is fine for processes, the concurrent equivalent of traditional sequential programs which “do one thing”; indeed, a concurrent system built with processes is like a sequential system with several main programs. But in the object-oriented approach we have rejected the notion of main program and instead defined software units that stand ready to provide any one of a number of possible features. Reconciling this view with the notion of process requires elaborate synchronization constructs to make sure that each supplier is ready to execute a feature when the client needs it. The reconciliation is particularly delicate when both client and supplier are active objects, since each has its own agenda. All this does not make it impossible to devise mechanisms based on the notion of active object, as evidenced by the abundant literature on the subject (to which the bibliographical notes to this chapter give many references). But this evidence also shows the complexity of the proposed solutions, of which none has gained wide acceptance, suggesting that the active object approach is not the right one. Active objects clash with inheritance Doubts about the suitability of the active object approach grow as one starts looking at how it combines with other O-O mechanisms, especially inheritance. If a class B inherits from a class A and both are active (that is to say, describe instances that must be active objects), what happens in B to the description of A’s process? In many cases you will need to add some new instructions, but without special language mechanisms this means that you will almost always have to redefine and rewrite the entire process part — not an attractive proposition. Here is an example of special language mechanism. Although the Simula 67 language does not support concurrency, it has a notion of active object: a Simula class can, besides its features, include a set of instructions, called the body of the class, so that we can talk of executing an object — meaning executing the body of its generating class. The body of a class A can include a special instruction inner, which has no effect in the class itself but, in a proper descendant B, stands for the body of B. So if the body of A reads some_initialization; inner; some_termination_actions and the body of B reads specific_B_actions then execution of that body actually means executing some_initialization; specific_B_actions; some_termination_actions Although the need for a mechanism of this kind is clear in a language supporting the notion of active object, objections immediately come to mind: the notation is misleading, since if you just read the body of B you will get a wrong view of what the execution does; it forces the parent to plan in detail for its descendants, going against basic O-O concepts (the Open-Closed principle); and it only works in a single-inheritance language. See “Sequencing and inheritance”, page 1121, as part of the discussion of Simula
960 CONCURRENCY,DISTRIBUTION,CLIENT-SERVER AND THE INTERNET $30.3 Even with a different notation,the basic problem will remain:how to combine the process specification of a class with those of its proper descendants;how to reconcile parents'process specifications in the case of multiple inheritance. Later in this chapter we will see other problems,known as the"inheritance anomaly"and “Synchrontation for arising from the use of inheritance with synchronization constraints. concurrent O-O com- Faced with these difficulties,some of the early O-O concurrency proposals preferred putation".page 980 to stay away from inheritance altogether.Although justifiable as a temporary measure to help understand the issues by separating concerns,this exclusion ofinheritance cannot be sustained in a definitive approach to the construction of concurrent object-oriented software;this would be like cutting the arm because the finger itches.(For good measure, some of the literature adds that inheritance is a complex and messy notion anyway,as if telling the patient,after the operation,that having an arm was a bad idea in the first place.) The inference that we may draw is simpler and less extreme.The problem is not object technology per se,in particular inheritance;it is not concurrency;it is not even the combination of these ideas.What causes trouble is the notion of active object. Processes programmed As we prepare to get rid of active objects it is useful to note that we will not really be renouncing anything.An object is able to perform many operations:all the features of its generating class.By tuming it into a process,we select one of these operations as the only one that really counts.There is absolutely no benefit in doing this!Why limit ourselves to one algorithm when we can have as many as we want? Another way to express this observation is that the notion of process need not be a built-in concept in the concurrency mechanism;processes can be programmed simply as routines.Consider for example the concept of printer process cited at the beginning of this chapter.The object-oriented view tells us to focus on the object type,printer,and to treat the process as just one routine,say live,of the corresponding class: indexing description:"Printers handling one print job at a time" note:"A better version,based on a general class PROCESS, %appears below under the name PRINTER" class PRINTER I feature--Status report stop requested:BOOLEAN is do...end oldest:JOB is do ..end feature--Basic operations setup is do ..end wait for job is do ..end remove oldest is do...end print (j:JOB)is do ..end
960 CONCURRENCY, DISTRIBUTION, CLIENT-SERVER AND THE INTERNET §30.3 Even with a different notation, the basic problem will remain: how to combine the process specification of a class with those of its proper descendants; how to reconcile parents’ process specifications in the case of multiple inheritance. Later in this chapter we will see other problems, known as the “inheritance anomaly” and arising from the use of inheritance with synchronization constraints. Faced with these difficulties, some of the early O-O concurrency proposals preferred to stay away from inheritance altogether. Although justifiable as a temporary measure to help understand the issues by separating concerns, this exclusion of inheritance cannot be sustained in a definitive approach to the construction of concurrent object-oriented software; this would be like cutting the arm because the finger itches. (For good measure, some of the literature adds that inheritance is a complex and messy notion anyway, as if telling the patient, after the operation, that having an arm was a bad idea in the first place.) The inference that we may draw is simpler and less extreme. The problem is not object technology per se, in particular inheritance; it is not concurrency; it is not even the combination of these ideas. What causes trouble is the notion of active object. Processes programmed As we prepare to get rid of active objects it is useful to note that we will not really be renouncing anything. An object is able to perform many operations: all the features of its generating class. By turning it into a process, we select one of these operations as the only one that really counts. There is absolutely no benefit in doing this! Why limit ourselves to one algorithm when we can have as many as we want? Another way to express this observation is that the notion of process need not be a built-in concept in the concurrency mechanism; processes can be programmed simply as routines. Consider for example the concept of printer process cited at the beginning of this chapter. The object-oriented view tells us to focus on the object type, printer, and to treat the process as just one routine, say live, of the corresponding class: indexing description: "Printers handling one print job at a time" note: “A better version, based on a general class PROCESS, % %appears below under the name PRINTER" class PRINTER_1 feature -- Status report stop_requested: BOOLEAN is do … end oldest: JOB is do … end feature -- Basic operations setup is do … end wait_for_job is do … end remove_oldest is do … end print (j: JOB) is do … end “Synchronization for concurrent O-O computation”, page 980