Process objects

Designer's notes #14  - Home - Prev - Next
Řyvind Teig,  Trondheim,  Norway (

Let them be black

This is a note about processes-as-objects. It is my 14th note, and my favourite theme so far has only briefly been touched. So, it is long overdue. Actually, the note is about process-oriented programming. I have worked with this type of programming of embedded systems most of my 30+? professional years. I seem to have gone through a darwinian evolution with my understanding of this programming paradigm. And in the future I probably will have to unlearn some, continue momentum, and perhaps even learn more. 

The simplest embedded program is one which runs in a "big loop", executing a set of small to very large state machines. It handles inputs and does its best to deliver expected outputs, in or on time.

Beneath any embedded program, interrupt functions handle things like communication, data conversion, timers, internal and external hw etc.

All access of common variables with the interrupt (often just called "asynchronous I/O") always must be protected. The simplest solution is for the big loop to disable the interrupt in mind, every place where data written by at least one of the parties.We allow Concurrent Read only by both parties, but require Exclusive Write, or CREW. The code block which ensures the exclusiveness protects the "critical region".

Big loop programming soon gets more complicated than necessary. When you discover that you are implementing something that smells scheduling, beware! Somebody has done this before. At this stage you should buy (or ask the guy next door) a small operating system. They are also called real time executive or kernel (rtx, rtk), task scheduler or run time system. Chanches are you'll never regret.

Disregard side functionality that these systems may offer for now (like an http server). This system would do all big loop handling for you. What you would then do is to write more advanced functions and call them threads, tasks or processes. I'll disregard the differences between these rather non unanimously agreed terms here.

Before the rtx starts, you now call process start functions supplied by the rtx, with pointers to your processes. Now, compile, download and breakpoint inside each process - and you would see them get started by the rtx!

Some rtx'es allow you to start (or spawn or fork) child processes from a process. Also, start several instances of each process and then at the same time create and start the child processes. If so, the system is compositionable. When processes stop, they do a join. Most often, with join, all sub-processes must be stopped before the father proceess continues on. Most simple rtx systems are not compositionable. Page 2.

Processes keep their variables in a local storage, unique to each instance of a process. This store is often allocated from the system heap. The local data is often called its context

Processes are there for the programmer to be able to mentally grasp what's going on, and to encapsulate internal state. This makes it easier with processes than without.

Processes may communicate with messages. The transfer mechanism may be asynchronous or synchronous. The latter may be simpler to implement, provided some safe pattern to avoid deadlock is used. The two communicating processes would passively wait (block/descheduled) until the second contender is ready. Then the memcpy will occur, during the rendezvous. It follows from this that a potentially receiving process may passively hold a sender by not listening. Observe that this in no way implies busy polling, neither by the process nor the run-time system.

See [15] for a short example and additional description.

Such a process does not need to know the semantics (behaviour) of other processes, and need only relate to the message sequence diagram (protocol) used between the participants. This is often called WYSIWYG semantics. Asynchronous messaging lacks this feature. It may cause buffer overflow, so producer - consumer traffic must be balanced. With synchronous systems, any necessary asynchronousity is programmed with, or inside processes. 

With both these schemes, semaphores to protect common data are not needed.

Communication by passing pointers around with explicit ownership, by some mechansim, is also viable (also called zero copying). Otherwise, sending pointers in the two message passing systems mentioned, should absolutely be avoided. 

A process may deschedule itself (and rescheduled when some condition has been met: finished communication or timeout, or just to yield for others). This is called non-preemptive or cooperative scheduling. It's preemptive if an interrupt may call the scheduler, which may preemptively deschedule any process with a certain priority. Using many priorities should be avoided.

Object-orientation (OO) (and UML with state machines) are probably used with better success inside processes, than to build black-box encapsulated process data-flow architecures. An OO object is white-box encapsulated, since it leaks encapsulation through inheritance. 

So, using these communicating processes as architetcural building blocks is a alternative to OO-like objects. 

If you are using an object-oriented language like Java, the language designers have designed this so-called  concurrency into it, so that you may combine the two object types, and use each for what they are best at. See JCSP and C++CSP2

The structure of an rtx and its use may be rather complex in ANSI C, but still builds complex systems fine. However, there are concurrent languages designed to handle this much easier. Also see occam-π and transterpreter

Some of my professional experience and personal opinions on these matters are available in these pages. See link below.

4.Oct..07 (initial)

Other publications at