Tabard: Multiple GNU Prolog Engines in a Distributed Environment

October 26, 2006

poll: protocol failure in circuit setup

Filed under: Howto — tabard @ 2:16 pm

If you get the error:

poll: protocol failure in circuit setup

then you may need to edit /etc/inetd.conf and add .1000 to the end of the ‘nowait’ keyword.

October 10, 2006

Howto: Transform a string into a Prolog term

Filed under: Howto — tabard @ 3:56 pm

You can use read_term_from_codes/3 but remember to pass the option end_of_term(eof) if the term doesn’t end with a dot. This is because the default end-of-term delimiter is dot.

Example:
read_term_from_codes(”a(a)”, X, [end_of_term(eof)])

X = a(a)

October 5, 2006

From single-thread to multi-thread

Filed under: Howto — tabard @ 10:53 pm

In terms of programming, when wanting to transform a single-threaded program into multi-threaded one has to get into a different mindset.

In terms of Prolog (Tabard and most Prolog systems that support multi-threading), that means replacing:

  • 1) predicates into thread_send_message()’s
  • 2) unbound variables into thread_get_message()’s

Example: Consider the Prolog goal in single-thread mode:

% pred(+A, +B, -C)
pred([1,2], [[2,2], [3,2]], X).

And now in multi-threading mode, on the master thread side:

thread_send_message(worker_queue, pred([1,2], [[2,2], [3,2]])),
thread_get_message(master_queue, X).

And in the worker thread side:

thread_get_message(worker_queue, Y),
(do processing)
thread_send_message(master, Result).

And now for a real example (tested on SWI-Prolog):

%
% multiple threads wait on a single queue and pick up the first
% goal to execute. This example provides no means to tell when all
% work is done. This must be realised using additional
% synchronisation.
%

trick(a(gnu), b(software)). %answer is message is a(gnu)
trick(_, c(is)). %answer if not

% create_workers(+Id, +N)
%
% Create a pool with given Id and number of workers
% A set of workers wait on a single queue
%
create_workers(WorkerQueue, MasterQueue, N):-
message_queue_create(WorkerQueue),
message_queue_create(MasterQueue),
forall(between(1, N, _),
thread_create(do_work(WorkerQueue, MasterQueue), _, [])).

%
% Workers
%
do_work(WorkerQueue, MasterQueue):-
repeat,
thread_get_message(WorkerQueue, Goal),
% do processing
!, trick(Goal, X),
thread_send_message(MasterQueue, X),
fail.

% Master
%
% work(+Id, +Goal)
%
% Post work to be done by the pool

work(WorkerQueue, MasterQueue, Goal):-
thread_send_message(WorkerQueue, Goal),
thread_get_message(MasterQueue, Result),
write(Result).

September 29, 2006

Initialization

Filed under: Howto, Uncategorized — tabard @ 4:07 pm

To run a program using Tabard the user only needs to include the Tabard library file and then start his program from init/0. An example of such program follows:

:- include(’lib’).

% Master
init:-
pm2_is_master,!,
system(hostname),
write_loop.

% Slaves
init:-
system(hostname),
read_loop.

read_loop:-
mutex_lock,
pl_thread_get_msg(Termo),
write(Termo), nl,
read_loop.

write_loop:-
pl_thread_send_msg(vid(3,0,_,_), a(gnu)),
pl_thread_send_msg(vid(2,0,_,_), b(software)),
pl_thread_send_msg(vid(1,0,_,_), c(is)),
write_loop.

Message-Queues: no read() before write() (mutex)

Filed under: Howto — tabard @ 3:11 pm

Since the SYS V message-queues were not longer being used and since I thought it would be better, everything related to clone(2) has been removed in favor of pthreads. That means Tabard works solely on top of pthreads, with a C thread listener and a Prolog thread on each node. That will make Tabard more portable and eventually work transparently when Linux switches from LinuxThreads to Native Posix Thread Library (NPTL) more elegant.

One thing that needs to be guaranteed in the message-queues is that no read() is made before write(). For that a pthread mutex is used in the following way:

1) mutex is inicializated and a lock is made.

2) Someone sends a message. The listener thread receives it, writes it to the message-queue and an unlock is made.

3) Since an unlock has been made, the Prolog thread will now acquire the lock and retrieve the message from the queue. Finally, a lock is made and we go back to step 1.

September 12, 2006

Clarify the notion of a node

Filed under: Howto — tabard @ 1:26 am

Since PM2 adheres to the SPMD (Single Program Multiple Data) model, the user can write a single program text, a copy of which is launched by a specific load command on each processing node of the current configuration.

The nodes we are considering here are virtual: they only refer to Unix processes located on physical nodes. It looks like common sense pratice to exactly use one virtual node per physical node, so that the two notions just identify together. But nothing in PM2 requires such an identification. PM2 virtual nodes may be located at any physical nodes. The precise association is made by the pm2conf command. For instance, with this command we say 3 machines with 2 Unix processes (processing nodes) in each one:

$ pm2conf kx1 kx1 kx2 kx2 kx3 kx3

Notice this is especially fun because we can say the number of Unix processes we want by repeating the hostname. Since I’m using one Prolog thread per processing node, with Tabard I can also create more Prolog threads by invoking more than one node in each machine. Fun!

September 8, 2006

Kill threads

Filed under: Howto — tabard @ 4:36 pm

A running thread is identified by three things:
1) the cluster node in which the thread is running (abbrev. VP),
2) a local identifier which tells inside the node which thread we’re talking about (abbrev. ThreadId or Tid)
3) it’s PID.

Information about running threads is kept on the master node. This information consists on an array with the same size as the cluster configuration size (or number of cluster nodes) like this:

threads

To kill a thread :

thread_kill(vid(VP, Tid)) calls pm2_thread_kill(VP, Tid)

pm2_thread_kill() looks at the array of running threads to get the PID and then signals the remote node if it’s a remote VP or kills the thread locally if it’s a local thread.

Starting threads on each slave node

Filed under: Howto — tabard @ 11:04 am

When Tabard is started, a Prolog thread is started on each slave node by calling pm2_thread_create/4. This predicate will instanciate a ThreadId for each started thread and those Id’s are all acumulated on a list that thread_create_loop(-Thread_Id_List, +Config_Size) creates.

What pm2_thread_create/4 does is calling the C function pm2_thread_create() that will, by its turn, call create_thread(), that uses send_msg() to instruct a remote node that a new thread must be started on it.

On the remote node side, a new message with instructions to start a thread just arrivied. It’s time to call start_thread() that currently issues a call to Linux specific clone(2) but that in the future I hope will call gprolog’s new thread routine.

The newly created thread will then call Start_Prolog(), responsible for starting the Prolog engine. This means the same initialization/1 runs on all nodes and that we need a way to distinguish in which node we are. With that purpose in mind, the predicate pm2_is_master/0, that succeeds only if the current node is the master one (node rank equals 0), was created.

Blog at WordPress.com.