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 } }