Principles of Programming Languages
UNIT - VI
Abstract Data Types
Topics
* The Concept of Abstraction
* Introduction to Data Abstraction
* Design Issues for Abstract Data Types
* Language Examples
* Parameterized Abstract Data Types
* Encapsulation Constructs
* Naming Encapsulations
* Object-Oriented Programming
* Design Issues for Object-Oriented Languages
* Support for Object-Oriented Programming in Smalltalk
* Support for Object-Oriented Programming in C++
* Support for Object-Oriented Programming in Java
* Support for Object-Oriented Programming in C#
* Support for Object-Oriented Programming in Ada 95
* Implementation of Object-Oriented Constructs
* Concurrency Introduction
* Introduction to Subprogram-Level Concurrency
* Semaphores
* Monitors
* Message Passing
* Ada Support for Concurrency
* Java Threads
* C# Threads
* Statement-Level Concurrency
The Concept of Abstraction
* An abstraction is a view or representation of an entity that includes only the most significant attributes
* The concept of abstraction is fundamental in programming (and computer science)
* Nearly all programming languages support process abstraction with subprograms
* Nearly all programming languages designed since 1980 support data abstraction
Introduction to Data Abstraction
* An abstract data type is a user-defined data type that satisfies the following two conditions:
> The representation of, and operations on, objects of the type are defined in a single syntactic unit
> The representation of objects of the type is hidden from the program units that use these objects, so the only operations possible are those provided in the type's definition
Advantages of Data Abstraction
* Advantage of the first condition
> Program organization, modifiability (everything associated with a data structure is together), and separate compilation
* Advantage the second condition
> Reliability--by hiding the data representations, user code cannot directly access objects of the type or depend on the representation, allowing the representation to be changed without affecting user code
Language Requirements for ADTs
* A syntactic unit in which to encapsulate the type definition
* A method of making type names and subprogram headers visible to clients, while hiding actual definitions
* Some primitive operations must be built into the language processor
Design Issues
* Can abstract types be parameterized?
* What access controls are provided?
Language Examples: Ada
* The encapsulation construct is called a package
> Specification package (the interface)
> Body package (implementation of the entities named in the specification)
* Information Hiding
> The spec package has two parts, public and private
> The name of the abstract type appears in the public part of the specification package. This part may also include representations of unhidden types
> The representation of the abstract type appears in a part of the specification called the private part
* More restricted form with limited private types Private types have built-in operations for assignment and comparison
Limited private types have NO built-in operations
* Reasons for the public/private spec package: 1. The compiler must be able to see the representation after seeing only the spec package (it cannot see the private part) 2. Clients must see the type name, but not the representation (they also cannot see the private part)
* Having part of the implementation details (the representation) in the spec package and part (the method bodies) in the body package is not good One solution: make all ADTs pointers
Problems with this:
1. Difficulties with pointers
2. Object comparisons
3. Control of object allocation is lost
An Example in Ada
package Stack_Pack is
type stack_type is limited private;
max_size: constant := 100;
function empty(stk: in stack_type) return Boolean;
procedure push(stk: in out stack_type; elem:in Integer);
procedure pop(stk: in out stack_type);
function top(stk: in stack_type) return Integer;
private -- hidden from clients
type list_type is array (1..max_size) of Integer;
type stack_type is record
list: list_type;
topsub: Integer range 0..max_size) := 0;
end record;
end Stack_Pack
Language Examples: C++
* Based on C struct type and Simula 67 classes
* The class is the encapsulation device
* All of the class instances of a class share a single copy of the member functions
* Each instance of a class has its own copy of the class data members
* Instances can be static, stack dynamic, or heap dynamic
Language Examples: C++ (continued)
* Information Hiding
> Private clause for hidden entities
> Public clause for interface entities
> Protected clause for inheritance (Chapter 12)
Language Examples: C++ (continued)
* Constructors:
> Functions to initialize the data members of instances (they do not create the objects)
> May also allocate storage if part of the object is heap-dynamic
> Can include parameters to provide parameterization of the objects
> Implicitly called when an instance is created
> Can be explicitly called
> Name is the same as the class name
Language Examples: C++ (continued)
* Destructors
> Functions to cleanup after an instance is destroyed; usually just to reclaim heap storage
> Implicitly called when the object‘s lifetime ends
> Can be explicitly called
> Name is the class name, preceded by a tilde (~)
An Example in C++
class stack {
private:
int *stackPtr, maxLen, topPtr;
public:
stack() { // a constructor
stackPtr = new int [100];
maxLen = 99;
topPtr = -1;
};
~stack () {delete [] stackPtr;};
void push (int num) {…};
void pop () {…};
int top () {…};
int empty () {…};
}
Evaluation of ADTs in C++ and Ada
* C++ support for ADTs is similar to expressive power of Ada
* Both provide effective mechanisms for encapsulation and information hiding
* Ada packages are more general encapsulations; classes are types
Language Examples: C++ (continued)
* Friend functions or classes - to provide access to private members to some unrelated units or functions
> Necessary in C++
Language Examples: Java
* Similar to C++, except:
> All user-defined types are classes
> All objects are allocated from the heap and accessed through reference variables
> Individual entities in classes have access control modifiers (private or public), rather than clauses
> Java has a second scoping mechanism, package scope, which can be used in place of friends
* All entities in all classes in a package that do not have access control modifiers are visible throughout the package
An Example in Java
class StackClass {
private:
private int [] *stackRef;
private int [] maxLen, topIndex;
public StackClass() { // a constructor
stackRef = new int [100];
maxLen = 99;
topPtr = -1;
};
public void push (int num) {…};
public void pop () {…};
public int top () {…};
public boolean empty () {…};
}
Language Examples: C#
* Based on C++ and Java
* Adds two access modifiers, internal and protected internal
* All class instances are heap dynamic
* Default constructors are available for all classes
* Garbage collection is used for most heap objects, so destructors are rarely used
* Structs are lightweight classes that do not support inheritance
Language Examples: C# (continued)
* Common solution to need for access to data members: accessor methods (getter and setter)
* C# provides properties as a way of implementing getters and setters without requiring explicit method calls
C# Property Example
public class Weather {
public int DegreeDays { //** DegreeDays is a property
get {return degreeDays;}
set {
if(value < 0 || value > 30)
Console.WriteLine(
"Value is out of range: {0}", value);
else degreeDays = value;}
}
private int degreeDays;
...
}
...
Weather w = new Weather();
int degreeDaysToday, oldDegreeDays;
...
w.DegreeDays = degreeDaysToday;
...
oldDegreeDays = w.DegreeDays;
Parameterized Abstract Data Types
* Parameterized ADTs allow designing an ADT that can store any type elements (among other things)
* Also known as generic classes
* C++, Ada, Java 5.0, and C# 2005 provide support for parameterized ADTs
Parameterized ADTs in Ada
* Ada Generic Packages
> Make the stack type more flexible by making the element type and the size of the stack generic generic
Max_Size: Positive;
type Elem_Type is private;
package Generic_Stack is
Type Stack_Type is limited private;
function Top(Stk: in out StackType) return Elem_type;
…
end Generic_Stack;
Package Integer_Stack is new Generic_Stack(100,Integer);
Package Float_Stack is new Generic_Stack(100,Float);
Parameterized ADTs in C++
* Classes can be somewhat generic by writing parameterized constructor functions
class stack {
…
stack (int size) {
stk_ptr = new int [size];
max_len = size - 1;
top = -1;
};
…
}
stack stk(100);
Parameterized ADTs in C++ (continued)
* The stack element type can be parameterized by making the class a templated class template
<class Type> class stack { private:
Type *stackPtr; const int maxLen; int topPtr; public: stack() { stackPtr = new Type[100]; maxLen
= 99; topPtr = -1; } … }
Parameterized Classes in Java 5.0
* Generic parameters must be classes
* Most common generic types are the collection types, such as LinkedList and ArrayList
* Eliminate the need to cast objects that are removed
* Eliminate the problem of having multiple types in a structure
Parameterized Classes in C# 2005
* Similar to those of Java 5.0
* Elements of parameterized structures can be accessed through indexing
Summary
* The concept of ADTs and their use in program design was a milestone in the development of languages
* Two primary features of ADTs are the packaging of data with their associated operations and information hiding
* Ada provides packages that simulate ADTs
* C++ data abstraction is provided by classes
* Java‘s data abstraction is similar to C++
* Ada, C++, Java 5.0, and C# 2005 support parameterized ADTs
Object-Oriented Programming
* Abstract data types
* Inheritance
> Inheritance is the central theme in OOP and languages that support it
* Polymorphism
Inheritance
* Productivity increases can come from reuse
> ADTs are difficult to reuse—always need changes
> All ADTs are independent and at the same level
* Inheritance allows new classes defined in terms of existing ones, i.e., by allowing them to inherit common parts
* Inheritance addresses both of the above concerns--reuse ADTs after minor changes and define classes in a hierarchy
Object-Oriented Concepts
* ADTs are usually called classes
* Class instances are called objects
* A class that inherits is a derived class or a subclass
* The class from which another class inherits is a parent class or superclass
* Subprograms that define operations on objects are called methods
Object-Oriented Concepts (continued)
* Calls to methods are called messages
* The entire collection of methods of an object is called its message protocol or message interface
* Messages have two parts--a method name and the destination object
* In the simplest case, a class inherits all of the entities of its parent
Object-Oriented Concepts (continued)
* Inheritance can be complicated by access controls to encapsulated entities
> A class can hide entities from its subclasses
> A class can hide entities from its clients
> A class can also hide entities for its clients while allowing its subclasses to see them
* Besides inheriting methods as is, a class can modify an inherited method
> The new one overrides the inherited one
> The method in the parent is overriden
Object-Oriented Concepts (continued)
* There are two kinds of variables in a class:
> Class variables - one/class
> Instance variables - one/object
* There are two kinds of methods in a class:
> Class methods – accept messages to the class
> Instance methods – accept messages to objects
* Single vs. Multiple Inheritance
* One disadvantage of inheritance for reuse:
> Creates interdependencies among classes that complicate maintenance
Dynamic Binding
* A polymorphic variable can be defined in a class that is able to reference (or point to) objects of the class and objects of any of its descendants
* When a class hierarchy includes classes that override methods and such methods are called through a polymorphic variable, the binding to the correct method will be dynamic
* Allows software systems to be more easily extended during both development and maintenance
Dynamic Binding Concepts
* An abstract method is one that does not include a definition (it only defines a protocol)
* An abstract class is one that includes at least one virtual method
* An abstract class cannot be instantiated
Design Issues for OOP Languages
* The Exclusivity of Objects
* Are Subclasses Subtypes
* Type Checking and Polymorphism
* Single and Multiple Inheritance
* Object Allocation and DeAllocation
* Dynamic and Static Binding
* Nested Classes
The Exclusivity of Objects
* Everything is an object
> Advantage - elegance and purity
> Disadvantage - slow operations on simple objects
* Add objects to a complete typing system
> Advantage - fast operations on simple objects
> Disadvantage - results in a confusing type system (two kinds of entities)
* Include an imperative-style typing system for primitives but make everything else objects
> Advantage - fast operations on simple objects and a relatively small typing system
> Disadvantage - still some confusion because of the two type systems
Are Subclasses Subtypes?
* Does an ―is-a|| relationship hold between a parent class object and an object of the subclass?
> If a derived class is-a parent class, then objects of the derived class must behave the same as the parent class object
* A derived class is a subtype if it has an is-a relationship with its parent class
> Subclass can only add variables and methods and override inherited methods in ―compatible||
ways
Type Checking and Polymorphism
* Polymorphism may require dynamic type checking of parameters and the return value
> Dynamic type checking is costly and delays error detection
* If overriding methods are restricted to having the same parameter types and return type, the checking can be static
Single and Multiple Inheritance
* Multiple inheritance allows a new class to inherit from two or more classes
* Disadvantages of multiple inheritance:
> Language and implementation complexity (in part due to name collisions)
> Potential inefficiency - dynamic binding costs more with multiple inheritance (but not much)
* Advantage:
> Sometimes it is quite convenient and valuable
Allocation and DeAllocation of Objects
* From where are objects allocated?
> If they behave line the ADTs, they can be allocated from anywhere
* Allocated from the run-time stack
* Explicitly create on the heap (via new)
> If they are all heap-dynamic, references can be uniform thru a pointer or reference variable
> Simplifies assignment - dereferencing can be implicit
> If objects are stack dynamic, there is a problem with regard to subtypes
* Is deallocation explicit or implicit?
Dynamic and Static Binding
* Should all binding of messages to methods be dynamic?
> If none are, you lose the advantages of dynamic binding
> If all are, it is inefficient
* Allow the user to specify
Nested Classes
* If a new class is needed by only one class, there is no reason to define so it can be seen by other classes
> Can the new class be nested inside the class that uses it?
> In some cases, the new class is nested inside a subprogram rather than directly in another class
* Other issues:
> Which facilities of the nesting class should be visible to the nested class and vice versa
Support for OOP in Smalltalk
* Smalltalk is a pure OOP language
> Everything is an object
> All objects have local memory
> All computation is through objects sending messages to objects
> None of the appearances of imperative languages
> All objected are allocated from the heap
> All deallocation is implicit
Support for OOP in Smalltalk (continued)
* Type Checking and Polymorphism
> All binding of messages to methods is dynamic
* The process is to search the object to which the message is sent for the method; if not found, search the superclass, etc. up to the system class which has no superclass
> The only type checking in Smalltalk is dynamic and the only type error occurs when a message is sent to an object that has no matching method
Support for OOP in Smalltalk (continued)
* Inheritance
> A Smalltalk subclass inherits all of the instance variables, instance methods, and class methods of its superclass
> All subclasses are subtypes (nothing can be hidden)
> All inheritance is implementation inheritance
> No multiple inheritance
Support for OOP in Smalltalk (continued)
* Evaluation of Smalltalk
> The syntax of the language is simple and regular
> Good example of power provided by a small language
> Slow compared with conventional compiled imperative languages
> Dynamic binding allows type errors to go undetected until run time
> Introduced the graphical user interface
> Greatest impact: advancement of OOP
Support for OOP in C++
* General Characteristics:
> Evolved from C and SIMULA 67
> Among the most widely used OOP languages
> Mixed typing system
> Constructors and destructors
> Elaborate access controls to class entities
Support for OOP in C++ (continued)
* Inheritance
> A class need not be the subclass of any class
> Access controls for members are
> Private (visible only in the class and friends) (disallows subclasses from being subtypes)
> Public (visible in subclasses and clients)
> Protected (visible in the class and in subclasses, but not clients)
Support for OOP in C++ (continued)
* In addition, the subclassing process can be declared with access controls (private or public), which define potential changes in access by subclasses
> Private derivation - inherited public and protected members are private in the subclasses
> Public derivation public and protected members are also public and protected in subclasses
Inheritance Example in C++
class base_class {
private:
int a;
float x;
protected:
int b;
float y;
public:
int c;
float z;
};
class subclass_1 : public base_class { … };
// In this one, b and y are protected and
// c and z are public
class subclass_2 : private base_class { … };
// In this one, b, y, c, and z are private,
// and no derived class has access to any
// member of base_class
Reexportation in C++
* A member that is not accessible in a subclass (because of private derivation) can be declared to be visible there using the scope resolution operator (::), e.g.,
class subclass_3 : private base_class {
base_class :: c;
…
}
Reexportation (continued)
* One motivation for using private derivation
> A class provides members that must be visible, so they are defined to be public members; a derived class adds some new members, but does not want its clients to see the members of the parent class, even though they had to be public in the parent class definition
Support for OOP in C++ (continued)
* Multiple inheritance is supported
> If there are two inherited members with the same name, they can both be referenced using the scope resolution operator
Support for OOP in C++ (continued)
* Dynamic Binding
> A method can be defined to be virtual, which means that they can be called through polymorphic variables and dynamically bound to messages
> A pure virtual function has no definition at all
> A class that has at least one pure virtual function is an abstract class
Support for OOP in C++ (continued)
* Evaluation
> C++ provides extensive access controls (unlike Smalltalk)
> C++ provides multiple inheritance
> In C++, the programmer must decide at design time which methods will be statically bound and which must be dynamically bound
* Static binding is faster!
> Smalltalk type checking is dynamic (flexible, but somewhat unsafe)
> Because of interpretation and dynamic binding, Smalltalk is ~10 times slower than C++
Support for OOP in Java
* Because of its close relationship to C++, focus is on the differences from that language
* General Characteristics
> All data are objects except the primitive types
> All primitive types have wrapper classes that store one data value
> All objects are heap-dynamic, are referenced through reference variables, and most are allocated with new
> A finalize method is implicitly called when the garbage collector is about to reclaim the storage occupied by the object
Support for OOP in Java (continued)
* Inheritance
> Single inheritance supported only, but there is an abstract class category that provides some of the benefits of multiple inheritance (interface)
> An interface can include only method declarations and named constants, e.g.,
public interface Comparable {
public int comparedTo (Object b);
}
> Methods can be final (cannot be overriden)
Support for OOP in Java (continued)
* Dynamic Binding
> In Java, all messages are dynamically bound to methods, unless the method is final (i.e., it cannot be overriden, therefore dynamic binding serves no purpose)
> Static binding is also used if the methods is static or private both of which disallow overriding
Support for OOP in Java (continued)
* Several varieties of nested classes
* All are hidden from all classes in their package, except for the nesting class
* Nested classes can be anonymous
* A local nested class is defined in a method of its nesting class
> No access specifier is used
Support for OOP in Java (continued)
* Evaluation
> Design decisions to support OOP are similar to C++
> No support for procedural programming
> No parentless classes
> Dynamic binding is used as ―normal|| way to bind method calls to method definitions
> Uses interfaces to provide a simple form of support for multiple inheritance
Support for OOP in C#
* General characteristics
> Support for OOP similar to Java
> Includes both classes and structs
> Classes are similar to Java‘s classes
> Structs are less powerful stack-dynamic constructs (e.g., no inheritance)
Support for OOP in C# (continued)
* Inheritance
> Uses the syntax of C++ for defining classes
> A method inherited from parent class can be replaced in the derived class by marking its definition with new
> The parent class version can still be called explicitly with the prefix base:
base.Draw()
Support for OOP in C#
* Dynamic binding
> To allow dynamic binding of method calls to methods:
* The base class method is marked virtual
* The corresponding methods in derived classes are marked override
> Abstract methods are marked abstract and must be implemented in all subclasses
> All C# classes are ultimately derived from a single root class, Object
Support for OOP in C# (continued)
* Nested Classes
> A C# class that is directly nested in a nesting class behaves like a Java static nested class
> C# does not support nested classes that behave like the non-static classes of
Java
Support for OOP in C#
* Evaluation
> C# is the most recently designed C-based OO language
> The differences between C#‘s and Java‘s support for OOP are relatively minor
Support for OOP in Ada 95
* General Characteristics
> OOP was one of the most important extensions to Ada 83
> Encapsulation container is a package that defines a tagged type
> A tagged type is one in which every object includes a tag to indicate during execution its type (the tags are internal)
> Tagged types can be either private types or records
> No constructors or destructors are implicitly called
Support for OOP in Ada 95 (continued)
* Inheritance
> Subclasses can be derived from tagged types
> New entities are added to the inherited entities by placing them in a record definition
> All subclasses are subtypes
> No support for multiple inheritance
* A comparable effect can be achieved using generic classes
Example of a Tagged Type
Package Person_Pkg is
type Person is tagged private;
procedure Display(P : in out Person);
private
type Person is tagged
record
Name : String(1..30);
Address : String(1..30);
Age : Integer;
end record;
end Person_Pkg;
with Person_Pkg; use Person_Pkg;
package Student_Pkg is
type Student is new Person with
record
Grade_Point_Average : Float;
Grade_Level : Integer;
end record;
procedure Display (St: in Student);
end Student_Pkg;
// Note: Display is being overridden from Person_Pkg
Support for OOP in Ada 95 (continued)
* Dynamic Binding
> Dynamic binding is done using polymorphic variables called classwide types
* For the tagged type Prtdon, the classwide type is Person‘ class
> Other bindings are static
> Any method may be dynamically bound
> Purely abstract base types can be defined in Ada 95 by including the reserved word abstract
Support for OOP in Ada 95 (continued)
* Evaluation
> Ada offers complete support for OOP
> C++ offers better form of inheritance than Ada
> Ada includes no initialization of objects (e.g., constructors)
> Dynamic binding in C-based OOP languages is restricted to pointers and/or references to objects; Ada has no such restriction and is thus more orthogonal
Implementing OO Constructs
* Two interesting and challenging parts
> Storage structures for instance variables
> Dynamic binding of messages to methods
Instance Data Storage
* Class instance records (CIRs) store the state of an object
> Static (built at compile time)
* If a class has a parent, the subclass instance variables are added to the parent CIR
* Because CIR is static, access to all instance variables is done as it is in records
> Efficient
Dynamic Binding of Methods Calls
* Methods in a class that are statically bound need not be involved in the CIR; methods that will be dynamically bound must have entries in the CIR
> Calls to dynamically bound methods can be connected to the corresponding code thru a pointer in the CIR
> The storage structure is sometimes called virtual method tables (vtable)
> Method calls can be represented as offsets from the beginning of the vtable
Summary
* OO programming involves three fundamental concepts: ADTs, inheritance, dynamic binding
* Major design issues: exclusivity of objects, subclasses and subtypes, type checking and polymorphism, single and multiple inheritance, dynamic binding, explicit and implicit deallocation of objects, and nested classes
* Smalltalk is a pure OOL
* C++ has two distinct type system (hybrid)
* Java is not a hybrid language like C++; it supports only OO programming
* C# is based on C++ and Java
* Implementing OOP involves some new data structures
Concurrency Introduction
* Concurrency can occur at four levels:
> Machine instruction level
> High-level language statement level
> Unit level
> Program level
* Because there are no language issues in instruction- and program-level concurrency, they are not addressed here
Multiprocessor Architectures
* Late 1950s - one general-purpose processor and one or more special-purpose processors for input and output operations
* Early 1960s - multiple complete processors, used for program-level concurrency
* Mid-1960s - multiple partial processors, used for instruction-level concurrency
* Single-Instruction Multiple-Data (SIMD) machines
* Multiple-Instruction Multiple-Data (MIMD) machines
> Independent processors that can be synchronized (unit-level concurrency)
Categories of Concurrency
* A thread of control in a program is the sequence of program points reached as control flows through the program
* Categories of Concurrency:
> Physical concurrency - Multiple independent processors ( multiple threads of control)
> Logical concurrency - The appearance of physical concurrency is presented by time-sharing one processor (software can be designed as if there were multiple threads of control)
* Coroutines (quasi-concurrency) have a single thread of control
Motivations for Studying Concurrency
* Involves a different way of designing software that can be very useful—many real-world situations involve concurrency
* Multiprocessor computers capable of physical concurrency are now widely used
Introduction to Subprogram-Level Concurrency
* A task or process is a program unit that can be in concurrent execution with other program units
* Tasks differ from ordinary subprograms in that:
> A task may be implicitly started
> When a program unit starts the execution of a task, it is not necessarily suspended
> When a task‘s execution is completed, control may not return to the caller
* Tasks usually work together
Two General Categories of Tasks
* Heavyweight tasks execute in their own address space
* Lightweight tasks all run in the same address space
* A task is disjoint if it does not communicate with or affect the execution of any other task in the program in any way
Task Synchronization
* A mechanism that controls the order in which tasks execute
* Two kinds of synchronization
> Cooperation synchronization
> Competition synchronization
* Task communication is necessary for synchronization, provided by: - Shared nonlocal variables
> Parameters - Message passing
Kinds of Synchronization
* Cooperation: Task A must wait for task B to complete some specific activity before task A can continue its execution, e.g., the producer-consumer problem
* Competition: Two or more tasks must use some resource that cannot be simultaneously used, e.g., a shared counter
> Competition is usually provided by mutually exclusive access (approaches are discussed later)
Need for Competition Synchronization
Scheduler
* Providing synchronization requires a mechanism for delaying task execution
* Task execution control is maintained by a program called the scheduler, which maps task execution onto available processors
Task Execution States
* New - created but not yet started
* Ready - ready to run but not currently running (no available processor)
* Running
* Blocked - has been running, but cannot now continue (usually waiting for some event to occur)
* Dead - no longer active in any sense
Liveness and Deadlock
* Liveness is a characteristic that a program unit may or may not have - In sequential code, it means the unit will eventually complete its execution
* In a concurrent environment, a task can easily lose its liveness
* If all tasks in a concurrent environment lose their liveness, it is called deadlock
Design Issues for Concurrency
* Competition and cooperation synchronization
* Controlling task scheduling
* How and when tasks start and end execution
* How and when are tasks created
Methods of Providing Synchronization
* Semaphores
* Monitors
* Message Passing
Semaphores
* Dijkstra - 1965
* A semaphore is a data structure consisting of a counter and a queue for storing task descriptors
* Semaphores can be used to implement guards on the code that accesses shared data structures
* Semaphores have only two operations, wait and release (originally called P and V by Dijkstra)
* Semaphores can be used to provide both competition and cooperation synchronization
Cooperation Synchronization with Semaphores
* Example: A shared buffer
* The buffer is implemented as an ADT with the operations DEPOSIT and FETCH as the only ways to access the buffer
* Use two semaphores for cooperation: emptyspots and fullspots
* The semaphore counters are used to store the numbers of empty spots and full spots in the buffer
* DEPOSIT must first check emptyspots to see if there is room in the buffer
* If there is room, the counter of emptyspots is decremented and the value is inserted
* If there is no room, the caller is stored in the queue of emptyspots
* When DEPOSIT is finished, it must increment the counter of fullspots
* FETCH must first check fullspots to see if there is a value
> If there is a full spot, the counter of fullspots is decremented and the value is removed
> If there are no values in the buffer, the caller must be placed in the queue of fullspots
> When FETCH is finished, it increments the counter of emptyspots
* The operations of FETCH and DEPOSIT on the semaphores are accomplished through two semaphore operations named wait and release
Semaphores: Wait Operation
wait(aSemaphore)
if aSemaphore‘s counter > 0 then
decrement aSemaphore‘s counter
else
put the caller in aSemaphore‘s queue
attempt to transfer control to a ready task
-- if the task ready queue is empty,
-- deadlock occurs
end
Semaphores: Release Operation
release(aSemaphore)
if aSemaphore‘s queue is empty then
increment aSemaphore‘s counter
else
put the calling task in the task ready queue
transfer control to a task from aSemaphore‘s queue
end
Producer Consumer Code
semaphore fullspots, emptyspots;
fullstops.count = 0;
emptyspots.count = BUFLEN;
task producer;
loop
-- produce VALUE –-
wait (emptyspots); {wait for space}
DEPOSIT(VALUE);
release(fullspots); {increase filled}
end loop;
end producer;
Producer Consumer Code
task consumer;
loop
wait (fullspots);{wait till not empty}}
FETCH(VALUE);
release(emptyspots); {increase empty}
-- consume VALUE –-
end loop;
end consumer;
Competition Synchronization with Semaphores
* A third semaphore, named access, is used to control access (competition synchronization)
> The counter of access will only have the values 0 and 1
> Such a semaphore is called a binary semaphore
* Note that wait and release must be atomic!
Producer Consumer Code
semaphore access, fullspots, emptyspots;
access.count = 0;
fullstops.count = 0;
emptyspots.count = BUFLEN;
task producer;
loop
-- produce VALUE –-
wait(emptyspots); {wait for space}
wait(access); {wait for access)
DEPOSIT(VALUE);
release(access); {relinquish access}
release(fullspots); {increase filled}
end loop;
end producer;
Producer Consumer Code
task consumer;
loop
wait(fullspots);{wait till not empty}
wait(access); {wait for access}
FETCH(VALUE);
release(access); {relinquish access}
release(emptyspots); {increase empty}
-- consume VALUE –-
end loop;
end consumer;
Evaluation of Semaphores
* Misuse of semaphores can cause failures in cooperation synchronization, e.g., the buffer will overflow if the wait of fullspots is left out
* Misuse of semaphores can cause failures in competition synchronization, e.g., the program will deadlock if the release of access is left out
Monitors
* Ada, Java, C#
* The idea: encapsulate the shared data and its operations to restrict access
* A monitor is an abstract data type for shared data
Competition Synchronization
* Shared data is resident in the monitor (rather than in the client units)
* All access resident in the monitor
> Monitor implementation guarantee synchronized access by allowing only one access at a time
> Calls to monitor procedures are implicitly queued if the monitor is busy at the time of the call
Cooperation Synchronization
* Cooperation between processes is still a programming task
> Programmer must guarantee that a shared buffer does not experience underflow or overflow
Evaluation of Monitors
* A better way to provide competition synchronization than are semaphores
* Semaphores can be used to implement monitors
* Monitors can be used to implement semaphores
* Support for cooperation synchronization is very similar as with semaphores, so it has the same problems
Message Passing
* Message passing is a general model for concurrency
> It can model both semaphores and monitors
> It is not just for competition synchronization
* Central idea: task communication is like seeing a doctor--most of the time she waits for you or you wait for her, but when you are both ready, you get together, or rendezvous
Message Passing Rendezvous
* To support concurrent tasks with message passing, a language needs:
> A mechanism to allow a task to indicate when it is willing to accept messages
> A way to remember who is waiting to have its message accepted and
some ―fair|| way of choosing the next message
* When a sender task‘s message is accepted by a receiver task, the actual message transmission is called a rendezvous
Ada Support for Concurrency
* The Ada 83 Message-Passing Model
> Ada tasks have specification and body parts, like packages; the spec has the interface, which is the collection of entry points:
task Task_Example is
entry ENTRY_1 (Item : in Integer);
end Task_Example;
Task Body
* The body task describes the action that takes place when a rendezvous occurs
* A task that sends a message is suspended while waiting for the message to be accepted and during the rendezvous
* Entry points in the spec are described with accept clauses in the body
accept entry_name (formal parameters) do
…
end entry_name
Example of a Task Body
task body Task_Example is
begin
loop
accept Entry_1 (Item: in Float) do
...
end Entry_1;
end loop;
end Task_Example;
Ada Message Passing Semantics
* The task executes to the top of the accept clause and waits for a message
* During execution of the accept clause, the sender is suspended
* accept parameters can transmit information in either or both directions
* Every accept clause has an associated queue to store waiting messages
Rendezvous Time Lines