The first aspect that is critical from the point of view of interoperability is how the language deals with concurrency within a single process. Concurrency within a process arises when the process has a choice among more than one actions at some point(s) in time. There are three categories of languages in this context:
No concurrency: Some parallel programming paradigms (such as traditional message-passing) do not allow concurrency within a process. Each process has a single thread of control, hence a process will ``block'' while it is waiting for a message that has not yet arrived. During this blocking, the semantics require that there should be no side effects, when the ``receive'' system-call returns, beyond the expected side effect of returning the message.
Concurrent objects: Concurrent object-oriented languages allow concurrency within a process. There may be many objects active on a process, any of which can be scheduled depending on the availability of a message corresponding to a method invocation. Such objects are called message-driven objects.
Multithreading: Another set of languages allows concurrency by threads-- they permit multiple threads of control to be active simultaneously within a process, each with its own stack and program counter. The threads execute concurrently under the control of a thread scheduler.
Most languages can be seen to fall within one of these three categories or combinations of them, as far as internal concurrency is concerned. Other paradigms such as data parallel languages and functional languages can be implemented in one of the above categories. For example, HPF can be implemented using a statically scheduled SPMD style or using message-driven objects [54].