From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,fb45e48e8dddeabd X-Google-Attributes: gid103376,public X-Google-Thread: ffc1e,fb45e48e8dddeabd X-Google-Attributes: gidffc1e,public From: Tucker Taft Subject: Re: Ada Protected Object Tutorial #1 Date: 1999/12/17 Message-ID: <385AC716.7E65BD5C@averstar.com> X-Deja-AN: 562128221 Content-Transfer-Encoding: 7bit Sender: news@inmet.camb.inmet.com (USENET news) X-Nntp-Posting-Host: houdini.burl.averstar.com References: <839toq$pu$1@bgtnsc03.worldnet.att.net> X-Accept-Language: en Content-Type: text/plain; charset=us-ascii Organization: AverStar (formerly Intermetrics) Burlington, MA USA Mime-Version: 1.0 Newsgroups: comp.programming.threads,comp.lang.ada Date: 1999-12-17T00:00:00+00:00 List-Id: Kaz Kylheku wrote: > ... > > What if a protected procedure of an object calls another > protected procedure? Presumably this is allowed, which means that some > recursive lock has to be used for the implementation, or else the compiler has > to generate code that passes around secret ``already locked'' flags into > procedures. As pointed out in another response, the compiler generates a different call when calling a protected subprogram from the "inside" versus calling it from the "outside" of the protected unit. It can do that because all "unlocked" calls are textually included within the protected unit. This is just one illustration of the advantage provided by the fact that the protected type is a *language* construct rather than simply a part of a class library or operating system API. Although I am sympathetic with the idea of doing as much as possible just with class libraries, etc., synchronization is a place where you can get a lot of advantage from getting the compiler involved. As a second example, the semantics for protected entry bodies are such that the "convenient" thread can execute them when the thread detects that the barrier is true. This capability can result in dramatically fewer thread context switches than an equivalent synchronization mechanism built using typical mutex/condition-variable primitives. Trying to provide this same capability via an "API" is quite awkward, as it requires the programmer to bundle up an operation and "submit" it along with some kind of boolean function, and allow the underlying run-time system decide who evaluates the boolean function and who executes the operation. Almost all APIs instead are set up so that the programmer's code evaluates the boolean condition outside of the run-time system, and must do so repeatedly each time control returns to the user's code, to ensure that the boolean is true by at the moment when the user's code gets control. This can result in even more unnecessary context switches, and/or bugs if the programmer neglects to build the evaluation of the boolean condition into a loop around the condition-variable "wait" operation. As one data point, a classic producer/consumer example with a bounded buffer, when coded using a protected type versus using mutex/condition variables, results in typically half as many locks and unlocks, and one third as many context switches between the producer and the consumer. The mutex/condition variable approach involves so much more work because each thread ends up doing all the work "itself," rather than allowing the other thread to do a little bit of work on its behalf while it already holds the lock. > > How does the user of the protected object compose atomic operations? > > This approach to design is generally wasteful. It's more reasonable to have > public methods which always lock and unlock, and unprotected methods which > assume that an object is already locked. It's also useful for an object > to expose lock and unlock methods, and expose unprotected operations, so that > the user of the object can compose a sequence of operations into an indivisible > block. One of the key principles of the "monitor" construct, on which protected types is based, is that *all* critical sections associated with a given lockable entity are gathered into a single module. Allowing random collections of operations to be made atomic makes any kind of analysis of a real-time system significantly more difficult. Also, if you expose operations that presume all callers already have a lock, either they must themselves check, or you have a situation ripe for bugs. (Note that although Java largely adheres to the monitor model, they violate it in enough ways that the guarantees provided by the monitor model are lost. For example, they allow public unsynchronized operations for objects that also have synchronized operations. Also, they allow synchronized blocks on a particular object *anywhere*, including places that have no other methods accessible on the object.) > ... -- -Tucker Taft stt@averstar.com http://www.averstar.com/~stt/ Technical Director, Distributed IT Solutions (www.averstar.com/tools) AverStar (formerly Intermetrics, Inc.) Burlington, MA USA