420 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
		
		
			
		
	
	
			420 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| 
								 | 
							
								/*  $Id$
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Part of SWI-Prolog
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Author:        Jan Wielemaker
							 | 
						||
| 
								 | 
							
								    E-mail:        jan@swi.psy.uva.nl
							 | 
						||
| 
								 | 
							
								    WWW:           http://www.swi-prolog.org
							 | 
						||
| 
								 | 
							
								    Copyright (C): 1985-2002, University of Amsterdam
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This program is free software; you can redistribute it and/or
							 | 
						||
| 
								 | 
							
								    modify it under the terms of the GNU General Public License
							 | 
						||
| 
								 | 
							
								    as published by the Free Software Foundation; either version 2
							 | 
						||
| 
								 | 
							
								    of the License, or (at your option) any later version.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This program is distributed in the hope that it will be useful,
							 | 
						||
| 
								 | 
							
								    but WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						||
| 
								 | 
							
								    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							 | 
						||
| 
								 | 
							
								    GNU General Public License for more details.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    You should have received a copy of the GNU Lesser General Public
							 | 
						||
| 
								 | 
							
								    License along with this library; if not, write to the Free Software
							 | 
						||
| 
								 | 
							
								    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    As a special exception, if you link this library with other files,
							 | 
						||
| 
								 | 
							
								    compiled with a Free Software compiler, to produce an executable, this
							 | 
						||
| 
								 | 
							
								    library does not by itself cause the resulting executable to be covered
							 | 
						||
| 
								 | 
							
								    by the GNU General Public License. This exception does not however
							 | 
						||
| 
								 | 
							
								    invalidate any other reasons why the executable file might be covered by
							 | 
						||
| 
								 | 
							
								    the GNU General Public License.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
							 | 
						||
| 
								 | 
							
								Stress-testing sockets, message queues and   threads. These elements are
							 | 
						||
| 
								 | 
							
								the most important and complicated parts   of multi-threaded servers and
							 | 
						||
| 
								 | 
							
								require intensive testing. This program performs the following steps for
							 | 
						||
| 
								 | 
							
								each test:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									* Create a server consisting of an accepting thread and 5 workers.
							 | 
						||
| 
								 | 
							
									* Run the tests from test_def.
							 | 
						||
| 
								 | 
							
									* Destroy the server
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								For simple tests, get yourself a free machine and run
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									?- forever.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								It creates a logfile named <host>-forever.txt
							 | 
						||
| 
								 | 
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- asserta(user:file_search_path(foreign, '.')).
							 | 
						||
| 
								 | 
							
								:- use_module(socket).
							 | 
						||
| 
								 | 
							
								:- use_module(library(debug)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- dynamic
							 | 
						||
| 
								 | 
							
									on_thread/2,
							 | 
						||
| 
								 | 
							
									port/1,
							 | 
						||
| 
								 | 
							
									worker/1.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- discontiguous
							 | 
						||
| 
								 | 
							
									test_impl/1.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       FOREVER		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								forever :-
							 | 
						||
| 
								 | 
							
									forever(_).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								forever(Test) :-
							 | 
						||
| 
								 | 
							
									gethostname(Host),
							 | 
						||
| 
								 | 
							
									atomic_list_concat([Host, -, 'forever.txt'], Log),
							 | 
						||
| 
								 | 
							
									protocol(Log),
							 | 
						||
| 
								 | 
							
									between(1, 10000000, N),
							 | 
						||
| 
								 | 
							
									  get_time(T),
							 | 
						||
| 
								 | 
							
									  convert_time(T, S),
							 | 
						||
| 
								 | 
							
									  format('***** TEST RUN ~w ***** [~w]~n', [N, S]),
							 | 
						||
| 
								 | 
							
									  test(Test),
							 | 
						||
| 
								 | 
							
									fail.
							 | 
						||
| 
								 | 
							
								forever(_).				% takes very long
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       TOPLEVEL		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test :-
							 | 
						||
| 
								 | 
							
									test(_).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test(T) :-
							 | 
						||
| 
								 | 
							
									create_server(5),
							 | 
						||
| 
								 | 
							
									(   test_def(T, Test),
							 | 
						||
| 
								 | 
							
									    (	Test = concurrent(Times, Threads, CTest)
							 | 
						||
| 
								 | 
							
									    ->	run(concurrent(Times, Threads, test_impl(CTest)))
							 | 
						||
| 
								 | 
							
									    ;	run(test_impl(Test))
							 | 
						||
| 
								 | 
							
									    ),
							 | 
						||
| 
								 | 
							
									    fail
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									stop_server.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test_def(s1, echo(100)).
							 | 
						||
| 
								 | 
							
								test_def(s2, big(100000)).
							 | 
						||
| 
								 | 
							
								test_def(s3, big(10000, 10)).
							 | 
						||
| 
								 | 
							
								test_def(s5, timeout).
							 | 
						||
| 
								 | 
							
								test_def(s6, nohost).
							 | 
						||
| 
								 | 
							
								test_def(s7, noport).
							 | 
						||
| 
								 | 
							
								test_def(c1, concurrent(10, 3, echo(100))).
							 | 
						||
| 
								 | 
							
								test_def(c1, concurrent(10, 3, big(10000))).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *		SERVER		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								create_server :-
							 | 
						||
| 
								 | 
							
									create_server(5).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								create_server(Workers) :-
							 | 
						||
| 
								 | 
							
									thread_create(server(Workers), _, [alias(server)]),
							 | 
						||
| 
								 | 
							
									thread_get_message(ready),
							 | 
						||
| 
								 | 
							
									debug(tcp, 'Server started', []).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								stop_server :-
							 | 
						||
| 
								 | 
							
									forall(worker(_), client(exit, _)),
							 | 
						||
| 
								 | 
							
									debug(tcp, 'Workers exited, prepare to join server', []),
							 | 
						||
| 
								 | 
							
									connect,			% make accept return
							 | 
						||
| 
								 | 
							
									thread_join(server).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								server(Workers) :-
							 | 
						||
| 
								 | 
							
									create_pool(Workers),
							 | 
						||
| 
								 | 
							
									tcp_socket(Socket),
							 | 
						||
| 
								 | 
							
									tcp_bind(Socket, Port),
							 | 
						||
| 
								 | 
							
									assert(port(Port)),
							 | 
						||
| 
								 | 
							
									tcp_listen(Socket, 5),
							 | 
						||
| 
								 | 
							
									debug(tcp, 'Server ready on port ~w', [Port]),
							 | 
						||
| 
								 | 
							
									thread_send_message(main, ready),
							 | 
						||
| 
								 | 
							
									repeat,
							 | 
						||
| 
								 | 
							
									  tcp_accept(Socket, Client, _Peer),
							 | 
						||
| 
								 | 
							
									  debug(connect, 'Connection from ~w', [_Peer]),
							 | 
						||
| 
								 | 
							
									  (   worker(_)
							 | 
						||
| 
								 | 
							
									  ->  thread_send_message(queue, Client),
							 | 
						||
| 
								 | 
							
									      fail
							 | 
						||
| 
								 | 
							
									  ;   !,
							 | 
						||
| 
								 | 
							
									      tcp_close_socket(Client)
							 | 
						||
| 
								 | 
							
									  ),
							 | 
						||
| 
								 | 
							
									debug(tcp, 'All workers have gone, stopping', []),
							 | 
						||
| 
								 | 
							
									stop_pool,
							 | 
						||
| 
								 | 
							
									tcp_close_socket(Socket),
							 | 
						||
| 
								 | 
							
									retract(port(Port)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								create_pool(Number) :-
							 | 
						||
| 
								 | 
							
									message_queue_create(queue),
							 | 
						||
| 
								 | 
							
									forall(between(1, Number, _),
							 | 
						||
| 
								 | 
							
									       (   thread_create(work(queue), Id, []),
							 | 
						||
| 
								 | 
							
										   assert(worker(Id))
							 | 
						||
| 
								 | 
							
									       )).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								stop_pool :-
							 | 
						||
| 
								 | 
							
									forall(worker(_),
							 | 
						||
| 
								 | 
							
									       thread_send_message(queue, stop)),
							 | 
						||
| 
								 | 
							
									forall(retract(worker(Id)),
							 | 
						||
| 
								 | 
							
									       thread_join(Id)),
							 | 
						||
| 
								 | 
							
									message_queue_destroy(queue),
							 | 
						||
| 
								 | 
							
									debug(tcp, 'Pool stopped', []).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								work(Queue) :-
							 | 
						||
| 
								 | 
							
									repeat,
							 | 
						||
| 
								 | 
							
									thread_get_message(Queue, Socket),
							 | 
						||
| 
								 | 
							
									handle(Socket, Data),
							 | 
						||
| 
								 | 
							
									Data == exit, !.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								handle(Client, Data) :-
							 | 
						||
| 
								 | 
							
									tcp_open_socket(Client, In, Out),
							 | 
						||
| 
								 | 
							
									read(In, Data),
							 | 
						||
| 
								 | 
							
								%	open_null_stream(Null),
							 | 
						||
| 
								 | 
							
								%	copy_stream_data(In, Null),
							 | 
						||
| 
								 | 
							
								%	close(Null),
							 | 
						||
| 
								 | 
							
									close(In),
							 | 
						||
| 
								 | 
							
									thread_self(Me),
							 | 
						||
| 
								 | 
							
									debug(data, '~p: Received ~W', [Me, Data, [max_depth(10)]]),
							 | 
						||
| 
								 | 
							
									(   debugging(thread)
							 | 
						||
| 
								 | 
							
									->  assert(on_thread(Me, Data))
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									do_work(Data, Answer),
							 | 
						||
| 
								 | 
							
									writeq(Out, Answer),
							 | 
						||
| 
								 | 
							
									write(Out, '.\n'),
							 | 
						||
| 
								 | 
							
									debug(data, 'Sent ~W', [Answer, [max_depth(10)]]),
							 | 
						||
| 
								 | 
							
									flush_output(Out),
							 | 
						||
| 
								 | 
							
									close(Out).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								thread_join(Id) :-
							 | 
						||
| 
								 | 
							
									thread_join(Id, Status),
							 | 
						||
| 
								 | 
							
									(   Status == true
							 | 
						||
| 
								 | 
							
									->  true
							 | 
						||
| 
								 | 
							
									;   format('WRONG join-status for ~w: ~w~n', [Id, Status])
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								do_work(exit, true) :- !,
							 | 
						||
| 
								 | 
							
									thread_self(Me),
							 | 
						||
| 
								 | 
							
									debug(tcp, 'Exit worker ~w', [Me]),
							 | 
						||
| 
								 | 
							
									thread_detach(Me),
							 | 
						||
| 
								 | 
							
									retract(worker(Me)).
							 | 
						||
| 
								 | 
							
								do_work(echo(Data), Data) :- !.
							 | 
						||
| 
								 | 
							
								do_work(sleep(Time), true) :- !,
							 | 
						||
| 
								 | 
							
									sleep(Time).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	      CLIENT		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								host(localhost).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								client(Term, Reply) :-
							 | 
						||
| 
								 | 
							
									client(Term, Reply, 0).
							 | 
						||
| 
								 | 
							
								client(Term, Reply, TimeOut) :-
							 | 
						||
| 
								 | 
							
									host(Host),
							 | 
						||
| 
								 | 
							
									port(Port),
							 | 
						||
| 
								 | 
							
									client(Host, Port, TimeOut, Term, Reply).
							 | 
						||
| 
								 | 
							
								client(Host, Port, Timeout, Term, Reply) :-
							 | 
						||
| 
								 | 
							
									tcp_socket(Socket),
							 | 
						||
| 
								 | 
							
									tcp_connect(Socket, Host:Port),
							 | 
						||
| 
								 | 
							
									tcp_open_socket(Socket, In, Out),
							 | 
						||
| 
								 | 
							
									send_output(Term, Out),
							 | 
						||
| 
								 | 
							
									close(Out),
							 | 
						||
| 
								 | 
							
									(   Timeout == 0
							 | 
						||
| 
								 | 
							
									->  true
							 | 
						||
| 
								 | 
							
									;   set_stream(In, timeout(Timeout))
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									call_cleanup(read(In, Reply), close(In)),
							 | 
						||
| 
								 | 
							
									debug(data, 'Reply = ~W', [Reply, [max_depth(10)]]).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								send_output(List, Out) :-
							 | 
						||
| 
								 | 
							
									is_list(List), !,
							 | 
						||
| 
								 | 
							
									send_list_output(List, Out).
							 | 
						||
| 
								 | 
							
								send_output(Term, Out) :-
							 | 
						||
| 
								 | 
							
									send_term(Term, Out).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								send_term(Term, Out) :-
							 | 
						||
| 
								 | 
							
									writeq(Out, Term),
							 | 
						||
| 
								 | 
							
									write(Out, '.\n').
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								send_list_output([], _).
							 | 
						||
| 
								 | 
							
								send_list_output([H|T], Out) :-
							 | 
						||
| 
								 | 
							
									send_term(H, Out),
							 | 
						||
| 
								 | 
							
									send_list_output(T, Out).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								echo(Term) :-
							 | 
						||
| 
								 | 
							
									client(echo(Term), Reply),
							 | 
						||
| 
								 | 
							
									assertion(Term =@= Reply).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								connect :-
							 | 
						||
| 
								 | 
							
									host(Host),
							 | 
						||
| 
								 | 
							
									port(Port),
							 | 
						||
| 
								 | 
							
									tcp_socket(Socket),
							 | 
						||
| 
								 | 
							
									tcp_connect(Socket, Host:Port),
							 | 
						||
| 
								 | 
							
									tcp_close_socket(Socket).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       TESTS		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	echo(Times)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	Send a lot of short messages to the server
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test_impl(echo(Times)) :- !,
							 | 
						||
| 
								 | 
							
									forall(between(1, Times, N),
							 | 
						||
| 
								 | 
							
									       echo(N)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	big(Size)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	Send a list of 1...Size to the server
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test_impl(big(Size)) :-
							 | 
						||
| 
								 | 
							
									findall(X, between(1, Size, X), L),
							 | 
						||
| 
								 | 
							
									echo(L).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	big(Size, Times)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	Send Times lists of 1...Size to the server
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test_impl(big(Size, Times)) :-
							 | 
						||
| 
								 | 
							
									findall(X, between(1, Size, X), L),
							 | 
						||
| 
								 | 
							
									forall(between(1, Times, _),
							 | 
						||
| 
								 | 
							
									       echo(L)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test_impl(timeout) :-
							 | 
						||
| 
								 | 
							
									catch(client(sleep(2), _Reply, 1), E, true),
							 | 
						||
| 
								 | 
							
									assertion(E = error(timeout_error(read, '$stream'(_)), _G285)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       RUN		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- meta_predicate
							 | 
						||
| 
								 | 
							
									run(:).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								run(Goal) :-
							 | 
						||
| 
								 | 
							
									debug(run, ' ** Run ~p ...', [Goal]),
							 | 
						||
| 
								 | 
							
									call_cleanup(Goal, E, cleanup(Goal, E)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								cleanup(Goal, E) :-
							 | 
						||
| 
								 | 
							
									debug(run, ' ** Finished ~p: ~p', [Goal, E]).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	  SPECIAL TESTS		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test_impl(nohost) :-
							 | 
						||
| 
								 | 
							
									tcp_socket(S),
							 | 
						||
| 
								 | 
							
									catch(tcp_connect(S, 'foo.bar':80), E, true),
							 | 
						||
| 
								 | 
							
									tcp_close_socket(S),
							 | 
						||
| 
								 | 
							
									assertion(E =@= error(socket_error('Host not found'), _)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								test_impl(noport) :-
							 | 
						||
| 
								 | 
							
									tcp_socket(S),
							 | 
						||
| 
								 | 
							
									catch(call_with_time_limit(5, tcp_connect(S, localhost:4321)),
							 | 
						||
| 
								 | 
							
									      E, true),
							 | 
						||
| 
								 | 
							
									tcp_close_socket(S),
							 | 
						||
| 
								 | 
							
									assertion(E = error(socket_error(_), _)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	   DISTRIBUTION		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								on_threads :-
							 | 
						||
| 
								 | 
							
									(   bagof(D, on_thread(Id, D), Ds),
							 | 
						||
| 
								 | 
							
									    length(Ds, L),
							 | 
						||
| 
								 | 
							
									    format('Thread ~w handled ~w~n', [Id, L]),
							 | 
						||
| 
								 | 
							
									    fail
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								clean :-
							 | 
						||
| 
								 | 
							
									retractall(on_thread(_, _)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       CONCUR		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- meta_predicate
							 | 
						||
| 
								 | 
							
									concurrent(+, +, :),
							 | 
						||
| 
								 | 
							
									ok(:).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	concurrent(+Times, +Threads, :Goal)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	Run Goal Times times concurrent in Threads
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								concurrent(Times, 1, Goal) :- !,
							 | 
						||
| 
								 | 
							
									forall(between(1, Times, _),
							 | 
						||
| 
								 | 
							
									       ok(Goal)).
							 | 
						||
| 
								 | 
							
								concurrent(Times, Threads, Goal) :-
							 | 
						||
| 
								 | 
							
									strip_module(Goal, M, G),
							 | 
						||
| 
								 | 
							
									message_queue_create(Done),
							 | 
						||
| 
								 | 
							
									message_queue_create(Queue),
							 | 
						||
| 
								 | 
							
									create_workers(Threads, M, Queue, Done),
							 | 
						||
| 
								 | 
							
									forall(between(1, Times, _),
							 | 
						||
| 
								 | 
							
									       thread_send_message(Queue, goal(G))),
							 | 
						||
| 
								 | 
							
									debug(concurrent, 'Waiting for ~w replies', [Times]),
							 | 
						||
| 
								 | 
							
									wait_n(Times, Done),
							 | 
						||
| 
								 | 
							
									forall(between(1, Threads, _),
							 | 
						||
| 
								 | 
							
									       thread_send_message(Queue, done)),
							 | 
						||
| 
								 | 
							
									debug(concurrent, 'Waiting for ~w threads', [Threads]),
							 | 
						||
| 
								 | 
							
									wait_n(Threads, Done),
							 | 
						||
| 
								 | 
							
									message_queue_destroy(Queue),
							 | 
						||
| 
								 | 
							
									message_queue_destroy(Done),
							 | 
						||
| 
								 | 
							
									debug(concurrent, 'All done', []).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								wait_n(0, _) :- !.
							 | 
						||
| 
								 | 
							
								wait_n(N, Queue) :-
							 | 
						||
| 
								 | 
							
									thread_get_message(Queue, done),
							 | 
						||
| 
								 | 
							
									N2 is N - 1,
							 | 
						||
| 
								 | 
							
									wait_n(N2, Queue).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								create_workers(N, Module, Queue, Done) :-
							 | 
						||
| 
								 | 
							
									N > 0, !,
							 | 
						||
| 
								 | 
							
									thread_create(worker(Module, Queue, Done),
							 | 
						||
| 
								 | 
							
										      _,
							 | 
						||
| 
								 | 
							
										      [ detached(true)
							 | 
						||
| 
								 | 
							
										      ]),
							 | 
						||
| 
								 | 
							
									N2 is N - 1,
							 | 
						||
| 
								 | 
							
									create_workers(N2, Module, Queue, Done).
							 | 
						||
| 
								 | 
							
								create_workers(_, _, _, _).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								worker(Module, Queue, Done) :-
							 | 
						||
| 
								 | 
							
									thread_get_message(Queue, Message),
							 | 
						||
| 
								 | 
							
									(   Message = goal(Goal)
							 | 
						||
| 
								 | 
							
									->  ok(Module:Goal),
							 | 
						||
| 
								 | 
							
									    thread_send_message(Done, done),
							 | 
						||
| 
								 | 
							
									    worker(Module, Queue, Done)
							 | 
						||
| 
								 | 
							
									;   thread_send_message(Done, done)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ok(Goal) :-
							 | 
						||
| 
								 | 
							
									(   catch(Goal, E, true)
							 | 
						||
| 
								 | 
							
									->  (   var(E)
							 | 
						||
| 
								 | 
							
									    ->  true
							 | 
						||
| 
								 | 
							
									    ;   print_message(error, E)
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									;   print_message(warning, failed(Goal))
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       DEBUG		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%:- interactor.
							 | 
						||
| 
								 | 
							
								:- debug(tcp).
							 | 
						||
| 
								 | 
							
								%:- debug(data).
							 | 
						||
| 
								 | 
							
								:- debug(run).
							 | 
						||
| 
								 | 
							
								:- debug(concurrent).
							 |