A process can apply the PROBE statement to an inport to determine whether messages are pending on the associated channel. A PROBE statement has the general form
probe(inport,empty=logical)
A logical variable specified in the EMPTY=variable specifier is set to false if there is a message ready for receipt on the channel or if the channel has been closed (i.e., reached end-of-channel ), and to true otherwise. In other words, the EMPTY=variable specifier is set to true if a RECEIVE on this inport would block, and to false if it would not.
In addition, IOSTAT= and ERR= specifiers can be included in its control list; these are as in the Fortran INQUIRE statement. Hence, applying a PROBE statement to an undefined port causes an integer value specified in an IOSTAT specifier to be set to a nonzero value and causes the execution to branch to a label provided in an ERR= specifier. See Appendix A for a list of valid IOSTAT values.
Knowledge about sends is presumed to take a nonzero but finite time to become known to a process probing an inport. Hence, a probe of an inport that references a nonempty channel may signal true if the channel values were only recently communicated. However, if applied repeatedly without intervening receives, PROBE will eventually signal false, and will then continue to do so until values are received.
The PROBE statement is useful when a process wishes to interrupt local computation to handle communications that arrive at some unpredictable rate. The process alternates between performing computation and probing for pending messages, and switchs to handling messages when PROBE returns false. For example, this is the behavior that is required when implementing a one-process-per-processor version of a branch-and-bound search algorithm. Each process alternates between advancing the local search and responding to requests for work from other processes:
==
do while (.true.)
call advance_local_search
probe(requests,EMPTY=empty)
if(.not. empty) call hand_out_work
enddo
=1.03
The PROBE statement can also be used to receive data that arrives in
a nondeterministic fashion from several sources.
For example, the following program handles messages of types
and
, received on two ports, p1 and p2, respectively.
==
process handle_msgs(p1,p2)
inport (T1) p1
inport (T2) p2
...
do while(.true.)
probe(p1,EMPTY=e1)
if(.not. e1) then
receive(p1) val1
call handle_msg1(val1)
endif
probe(p2,EMPTY=e2)
if(.not. e2) then
receive(p2) val2
call handle_msg2(val2)
endif
enddo
=1.03
A disadvantage of this program is that if no messages are pending, it
consumes resources by repeatedly probing the two channels. This
``busy waiting'' strategy is acceptable if no other computation can be
performed on the processor on which this process is executing.
In general, however, it is preferable to use a non-busy-waiting
technique. If
, we can introduce a merger to combine the two
message streams. The handle_msgs2 process then performs receive
operations on its single inport, blocking until data is available.
==
merger(in=pi,(out=po(i),i=1,2))
processes
processcall source1(po(1))
processcall source2(po(2))
processcall handle_msgs2(pi)
endprocesses
=1.03
If
, we can use the following technique. Each source
process is augmented with an additional outport of type integer, on
which it sends a distinctive message each time it sends a message.
The integer outports are connected by a merger with an inport which
is passed to the handle_msgs process. This process performs
receive operations on the inport to determine which source process
has pending messages.
==
merger(in=ni,(out=no(i),i=1,2))
channel(in=p1i,out=p1o)
channel(in=p2i,out=p2o)
processes
processcall source1(1,p1o,no(1))
processcall source2(2,p2o,no(2))
processcall handle_msgs(p1i,p2i,ni)
endprocesses
process handle_msgs(p1,p2,pp)
inport (T1) p1
inport (T2) p2
inport (integer) pp
...
do while(.true.)
receive(pp) id
if(id .eq. 1) then
receive(p1) val
else
receive(p2) val
endif
call handle_mesg(val)
enddo
=1.03