Composite of faces in the group
Concurrency Made Easy

SCOOP: Simple Concurrent Object-Oriented Programming

Overview

SCOOP is an object-oriented programming model for concurrency. It aims to make concurrent programming easier by allowing programmers to apply sequential reasoning techniques when writing concurrent programs. In particular, the model guarantees the absence of common concurrency errors, such as data races.

Trying it out

You can try SCOOP without the need to install anything using our online interface:

Try SCOOP online!

The link above takes you to an implementation of the producer-consumer problem in SCOOP, which we discuss below. Another example is the barbershop problem.

SCOOP is also integrated into the EiffelStudio compiler and development environment, which is available for download. A small tutorial describes how to use SCOOP in EiffelStudio.

Basic concepts

The SCOOP model associates every object with a thread of execution, called its handler. A processor is an autonomous thread of control capable of executing actions on objects. A program variable x can point to an object with the same handler, or to an object with a different handler; in the latter case the reference is said to be separate. The semantics of a call x.f depends on this distinction: if x is not separate, the call is synchronous; if x is separate, the call will be executed asynchronously, by the handler of x. This possibility of asynchronous calls is the main source of concurrency in SCOOP.

Consider the producer-consumer problem as a simple illustration of these concepts. A root class defines the entities producer and consumer. The keyword separate specifies that these entities may be assigned to a handler different from the current one.


      producer: separate PRODUCER
      consumer: separate CONSUMER
      
Both the producer and the consumer access an unbounded buffer

      buffer: separate BUFFER [INTEGER]
      
using routines consume and produce. Since producer is separate, a call such as

      producer.produce (buffer)
      
is executed asynchronously from the calling thread, by the handler of producer.

Data-race freedom

The producer-consumer problem requires that data races on buffer are avoided. SCOOP uses runtime mechanisms that ensure race freedom without the need for explicit synchronization statements in the program. Consider this implementation of routine produce:

      
      produce (buffer: separate BUFFER [INTEGER])
        -- Produce an item and put it into the buffer.
      local
        p: INTEGER
      do
        p := new_item
        buffer.put (p)
      end
      
Since buffer is a separate argument to the routine, the runtime ensures to give routine produce exclusive access to the handler of buffer during the execution of its body. This ensures that the call buffer.put (p) cannot conflict with other concurrent access attempts to the buffer.

Wait conditions

Besides preventing data races on buffer, the producer-consumer problem requires that consumers do not access the buffer if it is empty. Synchronizing on conditions such as "the buffer is not empty" can be expressed in SCOOP using wait conditions: preconditions of a routine (require keyword) make the execution of the routine wait until the condition is true. For example, the precondition of the consume routine ensures that the routine will wait until the buffer is not empty.

      
      consume (buffer: separate BUFFER [INTEGER])
        -- Consume an item from the buffer.
      require
        not (buffer.count = 0)
      local
        c: INTEGER
      do
        c := buffer.item
      end
      

Learn more

To learn more about SCOOP, have a look at our publications and current research directions.