All ECLiPSe code is executed by an ECLiPSe engine. This engine can be invoked from a parent program, which is either in a different language (C/C++, Tcl, Java), or is itself an ECLiPSe program. In either case, the interaction between the ECLiPSe engine and the parent execution follows a resume-yield model of control flow: the parent execution transfers control to the engine via a 'resume' operation, and the engine returns control via a 'yield' operation. There are implicit yield operations (such as when the engine execution succeeds, fails or aborts), but the yield/2 predicate provides an explicit way of transferring control.
An engine that executes the yield/2 predicate stops executing, enters the yielded-state, and waits until it is resumed again.
Data can be passed both ways: the ToParent argument is passed from the engine to a parent on yielding, the FromParent argument receives a term from the parent on resumption.
Synchronous operation means that the engine was resumed via the engine_resume/3 predicate (if the resumer is ECLiPSe code), or one of the corresponding primitives in one of the foreign language interfaces (e.g. ec_resume()). In response to a yield/2, the resume-primitive returns, and the status code yielded(ToParent) is passed from the yielding engine to its resumer.
If the engine is resumed again later (e.g. with another call to engine_resume/3), the yield/2 predicate inside the engine succeeds, and engine execution continues. The second argument of engine_resume/3 (FromParent) is passed from the resumer to the engine, and unified with the second argument of yield/2 before engine execution continues.
Asynchronous operation means that the engine was resumed via the engine_resume_thread/2 predicate (if the resumer is ECLiPSe code), or one of the corresponding primitives in one of the foreign language interfaces (e.g. ec_resume_async()). In this case, the engine runs in its own thread, and in response to a yield/2 another thread can join the engine (engine_join/3), pick up the yielded(ToParent) status, and possibly resume the engine again later.
% Communication between engines (synchronous)
?- engine_create(E, []),
engine_resume(E, (
yield(message_to_parent,From),
writeln(received(From))
), S1),
engine_resume(E, message_from_parent, S2).
received(message_from_parent)
E = $&(engine,"376oe7")
S1 = yielded(message_to_parent)
S2 = true
Yes (0.00s cpu)
% Communication between engines (asynchronous)
?- engine_create(E, []),
engine_resume_thread(E, (
yield(message_to_parent,From),
writeln(received(From))
)),
engine_join(E, block, S1),
engine_resume_thread(E, message_from_parent),
engine_join(E, block, S2).
received(message_from_parent)
E = $&(engine,"376oe7")
S1 = yielded(message_to_parent)
S2 = true
Yes (0.00s cpu)
% Embedding situation: ECLiPSe server code
start_server :-
eclipse_server(dummy).
eclipse_server(PrevResult) :-
yield(PrevResult, Request),
process_request(Request, NewResult),
eclipse_server(NewResult).
// C++ client code
ec_init();
post_goal("start_server");
if (EC_resume() == EC_yield)
{
for(;;)
{
// create a request
...
if (EC_resume(request, result) != EC_yield);
break;
...
// use the result
}
}