SCOOP for SmallEiffel (DRAFT)

Cyril ADRIAN

Version: draft-4 (June 9th, 2002)

Table of contents

Main chapters

1.Introduction
1.1.Preliminary works
1.2.Copyright
2.Some problems and their solution
2.1.Introduction
2.2.Termination of the system
2.3.Conformance
2.4.Once or not once?
2.4.1.Once queries
2.4.2.GENERAL
2.4.3.A solution
2.4.4.Once commands
2.5.The Garbage Collector
2.6.The Exceptions mechanism
2.7.The SmallEiffel debugger
2.8.Distributed extensions
2.8.1.CCF files
2.8.2."thread" separate objects
2.8.3.Distant objects
2.8.3.1.Introduction
2.8.3.2."server" objects
2.8.3.3."proxy" objects
2.8.3.4."external" objects
3.The SmallEiffel extension
3.1.Syntactic extensions
3.2.Semantics implementation
3.2.1.Introduction
3.2.2.Guards
3.2.3.BASE_CLASS
3.2.4.SMALL_EIFFEL
3.2.5.Separate call
3.2.5.1.Definitions
3.2.5.2.Locking
3.2.5.3.Message passing
3.2.5.3.1.SCOOP features
3.2.5.3.2.Commands
3.2.5.3.3.Queries
3.2.6.Separate object life cycle
3.2.6.1.Creation
3.2.6.2.Destruction
3.2.7.Garbage Collector
3.2.8.Exceptions
3.2.9.SmallEiffel Debugger
3.3.C sources
3.3.1.Introduction
3.3.2.Main structures
3.3.2.1.Subsystem structure
3.3.2.2.Subsystem VFT structure
3.3.2.3.Subsystem factory

Appendices

A.Acknowledgments
B.Glossary
Cwords
Pwords
Swords
Twords
C.Bibliography
[Compton]thesis
[OOSC]book
D.GNU Free Documentation License
D.1.Preamble
D.2.Applicability and definitions
D.3.Verbatim copying
D.4.Copying in quantity
D.5.Modifications
D.6.Combining documents
D.7.Collections of documents
D.8.Aggregation with independent works
D.9.Translation
D.10.Termination
D.11.Future revisions of this license

1. Introduction

1.1. Preliminary works

This document is a draft! Don't hesitate and ask me the latest version.

Some good job was already done for SmallEiffel. Some of the works were just tests and demos, some other were working systems with more or less limited capabilites. Some others were concurrency attempts different from the SCOOP specification (THREAD classes and so on.) This document, and the SmallEiffel implementation that will follow, are mainly based on two works:

The chapter 2 deals with some semantic details to implement a fully functionnal SCOOP subsystem.
The chapter 3 presents such an implementation in SmallEiffel, with some native support in C.

Note 1: only C code production will be discussed here. Some further work will have to be done for compile_to_jvm.
Note 2: even if Michael's thesis is used quite extensively, none of his actual code will be used. We will start the implementation from scratch. The reasons are beyond the scope of this document.

1.2. Copyright

Copyright (C) 2002 Cyril Adrian.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.

The source of this document is an XML file, which may be obtained by simple request to me.

2. Some problems and their solution

2.1. Introduction

This work is partly based on [Compton].

Here are presented some few problems [Compton] (intentionnally or not) left aside. The solution I give to each problem may not be the only one, but it will be the one chosen for our extension of SmallEiffel.

In this work I adopt Michaels's parlance. I will thus speak of processors, subsystems and so on, as defined in [Compton].

I will begin with the once problem associated with some features like io in GENERAL and how the solution I propose may resolve it gracefully.

I will then go on with the Garbage Collector problem.

Also, some consideration is given to the exceptions mechanism. Also, the SmallEiffel debugger will be examined.

At last, I give some thoughts about the future "distributed" extensions.

2.2. Termination of the system

[Compton] explains that very well p. 55.

2.3. Conformance

The conformance is not spoken of by Bertrand Meyer, at least not in that way (he just says that a object entity cannot be attached to a non-separate entity.) More formally, it means:

Thanks to Philippe Ribet to having pointed that out.

If B conforms to A, then:

Hence the shema:
A  -->  sep A
^         ^
|         |
B  -->  sep B
                

2.4. Once or not once?

2.4.1. Once queries

The problem arises from non-separate results. One cannot make such a query execute only once in a system, or such a query would create "traitors". This, of course, must be checked by the compiler.
A simple solution is to change the semantics of once queries, to make them execute once per subsystem.

Michael James Compton presents this solution as the only solution to a SCOOP system ([Compton] p. 33), leaving aside the "once per system" point of view, which is the one traditional to non-concurrent Eiffel.

Indeed this solution is the only one when the result type is non-separate. Consider though, the following piece of code:

x: separate INTEGER_REF is
  once
    create Result
  end
              

In this case, having a "once per thread" solution is not necessarily the best one, nor is it the only available one. The "once per system" view is consistent, since any use of x would lead to a separate evaluation, whether x is really separate or a false separate object.

We can go even further. Consider the following:

class FOOLS_GUILD

feature

  fool: FOOLS_GUILD is
    once
      create Result
      Result := be_a_fool(Result)
    end

feature {NONE}

  be_a_fool(guild: FOOLS_GUILD): FOOLS_GUILD is
    do
      Result := guild.fool
    end

end -- FOOLS_GUILD
              

If anyone calls that fool query, I hope their OS supports thread quotas per user...
The only solution is to indeed give a "once per system" semantics to once queries returning a separate object.

2.4.2. GENERAL

The GENERAL class provides some services to all objects. Consider the following:

class GENERAL
...
feature
  io: STD_INPUT_OUTPUT is
      -- Handle to standard file setup.
      -- To use the standard input/output file.
      -- Has type STD_FILES in ELKS 95.
    once
      !!Result.make;
    ensure
      Result /= Void;
    end;
...
end -- GENERAL
              

Indeed in a SCOOP world we will have a big problem, with an io per subsystem! They all use the same underlying resources, after all.

I propose one solution:

class GENERAL
...
feature
  io: separate STD_INPUT_OUTPUT is
      -- Handle to standard file setup.
      -- To use the standard input/output file.
      -- Has type STD_FILES in ELKS 95.
    local
      lio: STD_INPUT_OUTPUT
    once
      !!lio.make;
      Result := lio;
    ensure
      Result /= Void;
    end;
...
end -- GENERAL
              

We will have one unique io object in the system. Which processor holds the io? The first one which uses io. It is not really a problem, except if the processor is doing heavy computations (in this case, input/output will be delayed). One possibility is to force the creation of io beforehand, which SmallEiffel already does (in this case, the root subsystem will hold the io reference.)

Note that a thread will not be created if the system is not scoop, therefore keeping normal performances in non-concurrent Eiffel systems. Removing the local variable, though simplifying the text, would make every system using input/output a scoop system. This, obviously, is not desired.

One problem remains though: SCOOP as defined by Bertrand Meyer (see [OOSC]) forces one to use separate objects only if such objects are part of the current feature's signature. This is a big problem as it forces the libraries to be redesigned (not to think of the burden for every one who wants to issue a simple put_string...)

I will present a solution in the next chapter.

2.4.3. A solution

Actually the solution will use one property of SmallEiffel: some once functions may be precomputed; and SmallEiffel is able to inline functions.
I thought first that the problem was because of the restriction "a separate object may only be used as target when it is an argument if the current feature." After having read OOSC2, I understand the reasons. The way to alleviate the restriction is not simple. But other solutions exist.

Here is one complete solution. It may not be the best, but it has the immense advantage of being simple, and well adapted to SmallEiffel's ways.
The solution involve a new class, STD_INPUT_OUTPUT_PROXY, and a change in GENERAL to make io an entity of that class. Let's begin with GENERAL:

class GENERAL
...
feature
  io: STD_INPUT_OUTPUT_PROXY is
    once
      create Result.make;
    ensure
      Result /= Void;
    end;
...
end -- GENERAL
              

Now let's describe the STD_INPUT_OUTPUT_PROXY class. Some comments follow.

class STD_INPUT_OUTPUT_PROXY

inherit
  STD_INPUT_OUTPUT
    redefine
      read_character, unread_character, last_character, end_of_input
    end

creation
  make

feature

  read_character is
    do
      read_character_from(remote)
    end

  unread_character is
    do
      unread_character_from(remote)
    end

  last_character: CHARACTER is
    do
      Result := last_character_from(remote)
    end

  end_of_input: BOOLEAN is
    do
      Result := end_of_input_from(remote)
    end

feature {NONE}

  read_character_from(stream: like remote) is
    do
      stream.read_character
    end

  unread_character_from(stream: like remote) is
    do
      stream.unread_character
    end

  last_character_from(stream: like remote): CHARACTER is
    do
      Result := stream.last_character
    end

  end_of_input_from(stream: like remote): BOOLEAN is
    do
      Result := stream.end_of_input
    end

feature {NONE}

  remote: separate STD_INPUT_OUTPUT is
    local
      lio: STD_INPUT_OUTPUT
    once
      create lio.make;
      Result := lio;
    ensure
      Result /= Void;
    end;

end -- STD_INPUT_OUTPUT_PROXY
              

Some comments:

  • `io's type is non separate. Therefore there will be such an object per subsystem.
  • The most interesting part is STD_INPUT_OUTPUT_PROXY. It encapsulates the separate reference to the "true" io object.
  • The trick is that without scoop, the behaviour of SmallEiffel (and of its generated runtime) does not change at all. Once precomputation and function inlining make it all.
  • The libraries have to be adapted a bit, but not much. And the gain is that the same code may be used with and without scoop!
  • In that example only the main features are encapsulated. The other ones (put_string and so on) work as before. It means in particular, that they are not protected from race conditions. It meens also some performance problems (taking the lock for each character.) We should encapsulate the whole interface.
  • Moreover, I only wrote STD_INPUT's main features; STD_OUTPUT's are equivalent.
  • One last comment: the choice was made to have only one io per system. This choice may not be optimal if we have a distributed application, since every output would cross the network. But solving that is simple: let GENERAL provide a local_io: STD_INPUT_OUTPUT with the "old" definition of io. Using that variable would mean "I want local output."

2.4.4. Once commands

One could think that, because they don't return any result, they can keep their initial semantics (once per system.)

Could somebody persuade me that it is true? (or false?) I'm not sure a system-wide once command is ok, but I don't find any counter-example either (try and create a traitor with a command)

It seems that the compiler cannot decide by itself, the solution being in the once extension in ETL3 (followed by a key like debug is)

2.5. The Garbage Collector

The first thing to understand is that the garbage collector is a passive one, it is called to reclaim some memory when some more allocation is to be done. It doesn't work in parallel (like Java's does.)

The second thing is that only one thread ever executes the code of one object. To rephrase it, an object always belong to the same subsystem.

The collecting itself works as usual.

2.6. The Exceptions mechanism

Those quotes are excerpts from mails in the SmallEiffel mailing list.

In general the system may be affected by a processor failure (concurrent systems tend to have a high cohesion/dependability between processors, which in general makes each one of them critical to the system).
Nevertheless I think there is not much room to choose the "right" semantics for exceptions under SCOOP because there is an explicit communication mechanism between processors (message based). If a processor fails, and is not able to recover from this failure, then it should propagate its failure to all the processors that attempt to communicate with it in the future (generating exceptions at that precise time), and it may, or may not, propagate its failure to the system depending on some tunable processor property (which should perhaps be true by default).
In a sequential Eiffel program an unresolved exception is propagated to the entity who requested the service (caller), on the point where that entity depends on the service being successfully done. The same thing should happen , in my opinion, with exceptions under SCOOP (that was what I was thinking when I said that exceptions semantics should be the same).
With this scheme we may -- under SCOOP -- create uncritical processors without putting in danger all the system.
-- Miguel Oliveira e Silva
There are two alternatives: the first is that the exception is raised when the service is first requested -- the command. But this forces the client to block until the results of that service have been computed, it forces every command to the separate class to actually be a query, and it throws away the advantages of parallelization.
The second is that the client, upon realizing it has an exception, asynchonrously informs the client, in effect sending it an interrupt and some form of an exception message. However, this leads to a situation where the client may be in a state that has little or no bearing on the origin of exception, and may not be in a position to correctly handle it. In Miguel's example, what if the read_from and last_character feature calls are separated by other function calls, or indeed are in completely separate features, even in different classes? The upshot is that the entire client application would have to be written in a defensive manner, to handle every concievable exception at all times, which doesn't sound very Eiffel-ian.
-- Greg Compestine

Here is what will be made in SmallEiffel:

A subsystem is to me marked "dirty" if an exception reaches the root call of the subsystem. Any waiting command is (silently) discarded. The subsystem is then made unable to serve any request:

Now, when we say "an exception", which exception? Should we raise the same as the dirty subsystem, or a new one? In this case, do we have access to the original exception code?

Of course, none of it holds if the exception is rescued: the subsystem is then given an opportunity to be saved from Hell.

2.7. The SmallEiffel debugger

To be thought of...

2.8. Distributed extensions

2.8.1. CCF files

CCF stands for Concurrency Configuration File.

When the CCF will be implemented, we will be able to use different types of separate objects. One of the most interesting ones is a "distributed" object.

A first approach would be to create distant objects as we create threads. Of course this is not possible: a program must run on the distant machine to create the object. The communication with the distant object should be encapsulated.

I define three types of separate objects: thread, server and proxy.

Last but not least: should the CCF file be used at compile-time or, as Bertrand Meyer thinks, at execution time? The question as important for such subsystems as servers and proxies (see below.)

2.8.2. "thread" separate objects

Those ones will be the only kind of separate objects in the first version of SCOOP for SmallEiffel. The processor of their subsystem is a POSIX thread.

Maybe later Win32 threads will be implemented also (if anyone wants to do it, please write to me.)

2.8.3. Distant objects

2.8.3.1. Introduction

"Server" and "proxy" objects are meant to communicate. The proxy is the "client-side" part of a separate object. The "server" object is an object able to serve distant requests.

Note: we will speek of "server objects" and "server applications". A server application is able to create and hold one or many server objects.

Bertrand Meyer introduces a different kind of distant objects: "external". We will not implement those objects. The reason is given later.

Two implementations are possible: either CORBA or a proprietary protocol.

CORBA could be implemented using such great projects as Mico/E.

2.8.3.2. "server" objects

Those objects are an extension of thread objects. They run in a special runtime which accepts connections and relays the messages to the object.

Note that in that case, all the features of such an object may be compiled (as an option) if the server is to serve requests from many different clients. The default option is to build a server tailored for a particular client.

2.8.3.3. "proxy" objects

Those objects are another extension of thread objects, which are able to contact a distant "server" object. They send messages and receive the result of the queries.

Note that in that case, the only compiled routines are those used by the client; but the code is not the one defined in the class, but some tailored code which communicates with the server.
Some extra code is also to be produced for the creation of such an object: its "peer" server object has to be created too (it contacts a server application and asks it to build the server object; then the creation procedure is enqueued as usual) -- Question: should a creation procedure be considered as a query? (i.e. it blocks until the invariant is set) Is there a difference between create instructions and create expressions?
Note that some problems of "object version" will have to be thought of.

2.8.3.4. "external" objects

Those external objects are meant to be built via a factory implemented in external (C?) In my opinion this is, at best, anti-Eiffel.

Even more, this is antinatural because the distant system to access must be built in Eiffel (since it must be able to receive and process the requests) Why then use some external to glue two Eiffel systems? Let's stay high level please. (The argument holds even if we use standard mechanism as Corba)

3. The SmallEiffel extension

3.1. Syntactic extensions

Those extensions are pretty trivial. EIFFEL_PARSER will be extended to accept the separate keyword wherever the expanded keyword is accepted.

A new class, namely TYPE_SEPARATE will be created.

3.2. Semantics implementation

3.2.1. Introduction

Note that only the "thread separate objects" implementation is discussed of here. As seen earlier, "server" and "proxy" objects are mere extensions of thread objects.

3.2.2. Guards

Guards are some preconditions which involve separate objects. They must be compiled even in boost mode.

3.2.3. BASE_CLASS

Two new attributes are added to that class:

  • is_separate
    This attribute is true whenever the class itself is declared separate as in
    separate class TEST
    end -- TEST
                      
  • maybe_separate
    This attribute is true whenever some entity declares a separate type of this class as in
    x: separate INTEGER_REF
                      
    Note that only those objects will need to have a pointer on their subsystem; we call those objects communicant. The other ones are always local.
    Even further, the "local objects" (those whose type is not maybe_separate) don't need to be counted as "objects of the subsystem". This simplifies the termination of a subsystem (see later).
    (This may work those objetcs don't communicate with other subsystems, therefore they cannot be sent messages from another susbystem)

3.2.4. SMALL_EIFFEL

One new attribute is added to that class:

  • scoop
    This attribute is true if some separate class or some separate entity is created. To put it in a more formal way:
    • (exists r: RUN_CLASS
        (r.base_class.is_separate and then r.at_run_time)
      ) implies small_eiffel.scoop
                        
    • (exists t: E_TYPE
        (t.is_separate)
      ) implies (t.base_class.maybe_separate)
                        
    • (exists c: CREATE_EXPRESSION
        (c.type.is_separate)
      ) implies (small_eiffel.scoop)
                        
    • (exists c: CREATE_INSTRUCTION
        (c.type.is_separate)
      ) implies (small_eiffel.scoop)
                        
    • small_eiffel.scoop implies
      (for all b: BASE_CLASS
        (b.maybe_separate implies
          all objects of the type of b or descendants
          need a pointer to their subsystem
        )
      )
                        

3.2.5. Separate call

3.2.5.1. Definitions

A separate call is a call upon a separate target. Standard SCOOP requires that such a target be one argument of the "current" feature; we have determined earlier how to broaden the definition to encompass any kind of separate entity.

A SCOOP feature is a feature having one or more separate arguments. This feature requires locking on each and every separate object before proceeding.

I will discuss two parts of the problem.

  • First, the locking, be it done at the entry of a SCOOP feature or (as per our extension) "just in time" (when sending the message.)
  • Second, the message passing itself.

Most of the implementation of the following chapters will be in RUN_FEATURE.

3.2.5.2. Locking

We will use Rhee's lock manager. The one presented by Michael ([Compton] p. 50), as he himself states, only works with a shared-memory paraidgm. SCOOP must provide for distributed solutions, therefore a centralized manager is not enough.

The principle is simple: when entering a SCOOP feature, a subsystem wants some locks on separate objects (or more precisely, on their subsystem.) It is placed on a wait-queue of each subsystem it wants a lock on; when it reaches the top of each queue it is then able to grasp all the locks.

To allow for some performance, a counter is set on the client subsystem, initialized to the number of locks to acquire, and decreased each time the subsystem reaches the top of one of its supplier's wait-queue. When null, the client is on top of all, and therefore may enter the SCOOP feature body.

After having acquired all the locks, the feature has to test its guards. If they don't pass, the locks are relinquished to be acquired again "later" (this notion must be defined)

3.2.5.3. Message passing

3.2.5.3.1. SCOOP features

SCOOP features do not greatly differ from normal features. The difference is that some code is generated at entry and exit to grab and release locks. See above our discussion about the Lock Manager.
Some preconditions of that feature are guards and must be tested until they become true.

3.2.5.3.2. Commands

The run-time generated by SmallEiffel builds three functions for each command call. The names given here support our discussion, they are not the names in the generated C file (the convention is given for each)

  • wrap()
    is a function called (locally) by the client. This function wraps the arguments of the call and sends it to the supplier (subsystem) queue.
    The naming pattern is: w followed by the id of the target, followed by the final name of the target (ASCII'ed for prefix and infix features.)
  • unwrap()
    is a function called by the main loop of a subsystem. It takes the first entry of the queue, unwraps the arguments, and calls the function.
    The naming pattern is: W followed by the id of the target, followed by the final name of the target (ASCII'ed for prefix and infix features.)
  • call()
    is the locally-called function. It is either called by the unwrap() function, or any non-separate call. It is the one generated by SmallEiffel in a non-concurrent system.
    The naming pattern is the one already used: r (or X for late-binding functions) followed by the id of the target, followed by the final name of the target (ASCII'ed for prefix and infix features.)

3.2.5.3.3. Queries

Queries are a little more complex than commands because they involve processors synchronization.

Indeed the handshake is a PV/VP pair (the critical section being between the first P and V). We may build some particular functions which will be executed by each part.

Each subsystem defines two mutexes to be used for query synchronization. Let's call them query_entry and query_exit.

The algorithm executed by the client is the following:

  • Send a special command to the supplier (via the command interface described above), which contains a P(query_entry), a call to the local code, a result setting, and a V(query_exit). This command will be executed by the supplier;
  • Execute locally V(query_entry) and P(query_exit), then use the result.

3.2.6. Separate object life cycle

3.2.6.1. Creation

There are two different kinds of creation:

  • The root subsystem creation is particular, because it involves using the "current thread" (the one automatically created by the operating system.) Apart from this fact, it launches its main loop as described below.
  • Now all other subsystem creations involve creating a new thread and giving it their root procedure. This one is a loop wich waits for messages to be available in the queue and calling the unwrap() function on them.

3.2.6.2. Destruction

A thread must die if:

  • Any object of the subsystem is dead (at least any maybe_separate object---see above), and no more messages are available in its queue (this case is managed by an object counter in each subsystem);
  • Or no object references any object of this subsystem, and no more messages are available in the queue (this case is managed by the garbage collector.)
    Note that here again, it does not mean that the subsystem holds no more objects, but none of them ever communicates with another subsystem.

Now the System Termination happens in two cases:

  • All the threads are dead. This case is trivial and the operating system generally knows that the program is ended.
  • All threads are waiting on empty queues. If no thread has work to do, we must stop (or else wait for the world to fall apart...)

Note: here I speak of "threads" and not of "subsystems". Distributed objects being thread extensions, it follows that a client will stop its execution when all its local threads are dead. The server will continue to run unharmed. It justifies some more the separation between "server" and "proxy" objects.

Important note: the last termination case above is correct only for "thread" and "proxy" objects. A "server" object must not be stopped because it's not doing anything! It must wait for future connections.

3.2.7. Garbage Collector

To be looked at...

3.2.8. Exceptions

To be looked at...

3.2.9. SmallEiffel Debugger

To be looked at...

3.3. C sources

3.3.1. Introduction

The C sources manage most of the subsystems interaction. The exported functions are used by SmallEiffel to generate a customized runtime.

Many types of separate objects will exist. When some object wants to communicate with a separate object, it will have to determine how to send a message. Indeed each separate object is dual-typed:

  • The standard Eiffel type, which describes what messages it can handle,
  • And the "separate type", which describes (among other things) how the messages are to be sent.

In fact the subsystems are typed. Their interface is given in the next chapter.

We had two possibilities for the implementation:

  • Either we try to use the SmallEiffel type inference mechanism
  • Either we use an ad-hoc mechanism.
<DRAFT>
Because I do not well know the type inference engine of SmallEiffel, the second choice was made. Subsystem access is made through VFTs (Virtual Function Tables.)
One could argue that it is slower than what SmallEiffel may produce, and indeed it is, but the overhead is not much compared to the message passing time. And we will change that later.
</DRAFT>

3.3.2. Main structures

3.3.2.1. Subsystem structure

  • int id;
                      
    This identifier is the "type" of subsystem. This identifier is removed in boost mode, since it is only used for checks.
  • se_subsystem_t* next;
                      
    The subsystems are linked together to allow some iteration over all of them (e.g. for termination.)
  • int count;
                      
    The number of instances in the subsystem. When it reaches zero and no message is left in the queue, the thread may be safely removed (if it isn't a server).
  • char* name;
                      
    The name is used for some printing and debugging purposes.
  • se_subsystem_vft_t vft;
                      
    The functions. This structure is detailed below.

3.3.2.2. Subsystem VFT structure

  • int id;
                      
    This identifier is the "type" of subsystem. This identifier is removed in boost mode, since it is only used for checks.
  • se_subsystem_t* (*new)(char* name);
                      
    Create a new subsystem of this type. This function is called via the subsystem factory (see below.)
  • se_subsystem_t* (*from_root)(char* name);
                      
    Make a subsystem from the current thread. Should only be used once, when initializing the system.
  • void (*gc_sweep)(T0* a);
                      
    Called when the object a is to be removed by the GC.
    This object must have a reference on its subsystem. Therefore the objects which do not communicate with other subsystems do not trigger that function.
    Only defined when the garbage collector is used.
  • void (*delete)(se_subsystem_t* subsystem);
                      
    Destroy the subsystem given as parameter.
  • void (*command)(se_subsystem_t* subsystem,
                    void (*command)(void*),
                    void* data,
                    int length);
                      
    Push a command on the command queue of the subsystem, which must have been locked by the caller. The mechanisms of locking are described below.
  • void (*query)(se_subsystem_t* subsystem,
                  void (*command)(void*),
                  void* data,
                  int length,
                  void* result);
                      
    Push a query on the command queue of the subsystem, which must have been locked by the caller. The mechanisms of locking are described below.
    The caller is then blocked till the result is available.
  • void (*print_run_time_stack)(se_subsystem_t* subsystem);
                      
    Print the run time stack of the subsystem.
  • int (*inc_count)(se_subsystem_t* subsystem);
                      
    Atomically increment the number of objects of the subsystem.
  • int (*dec_count)(se_subsystem_t* subsystem);
                      
    Atomically decrement the number of objects of the subsystem.
  • int (*enter)(se_subsystem_t* subsystem,
                 se_subsystem_t* client);
                      
    Called when the client tries to enter a SCOOP function with an object of the subsystem as argument. Returns 1 if the lock could not be obtained, 0 otherwise.
  • void (*exit)(se_subsystem_t* subsystem,
                 se_subsystem_t* client);
                      
    Releases the subsystem lock.
  • void (*wait)(se_subsystem_t* subsystem,
                 se_subsystem_t* client);
                      
    Puts the client in the wait queue of the subsystem.
    Note that this function returns immediately, in order for the client to be put on the wait queues of all its providers.
    The function wait_for_providers() below is used when the client was put in all its suppliers' queues.
  • void (*wait_for_providers)(se_subsystem_t* subsystem);
                      
    Wait until all the providers have told the subsystem that the lock can be acquired (by the signal() function.)

The structure contains some other functions, used internally for inter-subsystem communication:

  • void (*signal)(se_subsystem_t* subsystem,
                   se_subsystem_t* provider);
                      
    Signals the subsystem that it reached the head of the wait queue. It potentially may grab the lock.
  • void (*wait_commit)(se_subsystem_t* subsystem,
                        se_subsystem_t* provider);
                      
    Used by wait(): tells the subsystem that it was successfully enqueued in a wait list. Usually the action is to increment the counter of lists the subsystem is waiting on.
  • void (*unqueue_waiter_commit)(se_subsystem_t* subsystem,
                                  se_subsystem_t* provider);
                      
    Used by unqueue_waiter(): tells the subsystem that it was successfully dequeued from a wait list. Usually the action is to decrement the counter of lists the subsystem is waiting on.

3.3.2.3. Subsystem factory

se_subsystem_t* se_new_subsystem(int type,
                                 char* name);
                
Creates a new subsystem; the type of the subsystem is given.

A. Acknowledgments

First of all, I would like to thank Philippe Ribet, a friend of long time. How well you know how to push me, and how good are your advices always. Cheers, Philippe.
I would also have a thought for Marielle (Philippe's wife), who had to bear my often calling Philippe till late in the night...

I also would like to thank Dominique Colnet, the original designer of SmallEiffel. Dominique, your work is superb :o)

At last I would like to thank all the people who had any ideas to make SCOOP what it is now. Bertrand Meyer, of course, but also Michael James Compton, Miguel Oliveira e Silva and Greg Compestine for all the good ideas.

B. Glossary

C words

Communicant object
An object which can communicate with other subsystems. This property is statically computed as an object which may have separate references.

P words

Processor
Defined by [SCOOP] as "an autonomous thread of control capable of supporting the sequential execution of instructions on one or more objects.""

Proxy
A subsystem intended to delegate the requests to a server subsystem acros a network.

S words

Server
A subsystem intended to process requests coming from one or more proxy subsystems.

Subsystem
A set of objects linked together, whose routines are executed by one Processor.
An object may access an object from another subsystem through a separate reference.

T words

Thread
The most frequent type of subsystem, with a thread as processor. The Proxy and Server types are but specialisations of that one.

C. Bibliography

[Compton] thesis

SCOOP -- An Investigation of Concurrency in Eiffel

Michael Compton Compton, December 2000
http://cs.anu.edu.au/~Richard.Walker/eiffel/scoop/mc-thesis.ps

[OOSC] book

Object-Oriented Software Construction -- 2nd edition

Bertrand Meyer, 1997, Prentice Hall

The SCOOP part is the chapter 30 (pp. 951 to 1036)

The following article sums it up:
http://www.eiffel.com/doc/manuals/technology/concurrency/CONCURRENCY.html

D. GNU Free Documentation License

D.1. Preamble

Version 1.1, March 2000

Copyright (C) 2000 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA  02111-1307, USA

Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
            

The purpose of this License is to make a manual, textbook, or other written document free in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others. This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software. We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.

D.2. Applicability and definitions

This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License.

The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque". Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, @acronym{SGML} or @acronym{XML} using a publicly available @acronym{DTD}, and standard-conforming simple @acronym{HTML} designed for human modification. Opaque formats include PostScript, @acronym{PDF}, proprietary formats that can be read and edited only by proprietary word processors, @acronym{SGML} or @acronym{XML} for which the @acronym{DTD} and/or processing tools are not generally available, and the machine-generated @acronym{HTML} produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.

D.3. Verbatim copying

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3. You may also lend copies, under the same conditions stated above, and you may publicly display copies.

D.4. Copying in quantity

If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects. If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public. It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.

D.5. Modifications

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

1. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.

2. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).

3. State on the Title page the name of the publisher of the Modified Version, as the publisher.

4. Preserve all the copyright notices of the Document.

5. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.

6. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.

7. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.

8. Include an unaltered copy of this License.

9. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.

10. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.

11. In any section entitled "Acknowledgments" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgments and/or dedications given therein.

12. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.

13. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.

14. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles. You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard. You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one. The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.

D.6. Combining documents

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice. The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work. In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgments", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."

D.7. Collections of documents

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects. You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.

D.8. Aggregation with independent works

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document. If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.

D.9. Translation

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.

D.10. Termination

You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.

D.11. Future revisions of this license

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/. Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.