1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: J.Wielemaker@vu.nl 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 1999-2017, University of Amsterdam 7 VU University Amsterdam 8 All rights reserved. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 17 2. Redistributions in binary form must reproduce the above copyright 18 notice, this list of conditions and the following disclaimer in 19 the documentation and/or other materials provided with the 20 distribution. 21 22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 POSSIBILITY OF SUCH DAMAGE. 34*/ 35 36:- module(thread_util, 37 [ thread_run_interactor/0, % interactor main loop 38 threads/0, % List available threads 39 join_threads/0, % Join all terminated threads 40 interactor/0, % Create a new interactor 41 interactor/1, % ?Title 42 thread_has_console/0, % True if thread has a console 43 attach_console/0, % Create a new console for thread. 44 attach_console/1, % ?Title 45 46 tspy/1, % :Spec 47 tspy/2, % :Spec, +ThreadId 48 tdebug/0, 49 tdebug/1, % +ThreadId 50 tnodebug/0, 51 tnodebug/1, % +ThreadId 52 tprofile/1, % +ThreadId 53 tbacktrace/1, % +ThreadId, 54 tbacktrace/2 % +ThreadId, +Options 55 ]). 56:- autoload(library(apply),[maplist/3]). 57:- autoload(library(backcomp),[thread_at_exit/1]). 58:- autoload(library(edinburgh),[nodebug/0]). 59:- autoload(library(lists),[max_list/2,append/2]). 60:- autoload(library(option),[merge_options/3,option/3]). 61:- autoload(library(prolog_stack), 62 [print_prolog_backtrace/2,get_prolog_backtrace/3]). 63:- autoload(library(statistics),[thread_statistics/2]). 64:- autoload(library(prolog_profile), [show_profile/1]). 65:- autoload(library(thread),[call_in_thread/2]). 66 67:- if((\+current_prolog_flag(xpce,false),exists_source(library(pce)))). 68:- autoload(library(gui_tracer),[gdebug/0]). 69:- autoload(library(pce),[send/2]). 70:- else. 71gdebug :- 72 debug. 73:- endif. 74 75 76:- set_prolog_flag(generate_debug_info, false). 77 78:- module_transparent 79 tspy/1, 80 tspy/2.
94threads :- 95 threads(Threads), 96 print_message(information, threads(Threads)). 97 98threads(Threads) :- 99 findall(Thread, thread_statistics(_,Thread), Threads).
105join_threads :- 106 findall(Ripped, rip_thread(Ripped), AllRipped), 107 ( AllRipped == [] 108 -> true 109 ; print_message(informational, joined_threads(AllRipped)) 110 ). 111 112rip_thread(thread{id:id, status:Status}) :- 113 thread_property(Id, status(Status)), 114 Status \== running, 115 \+ thread_self(Id), 116 thread_join(Id, _).
124interactor :- 125 interactor(_). 126 127interactor(Title) :- 128 thread_self(Me), 129 thread_create(thread_run_interactor(Me, Title), _Id, 130 [ detached(true), 131 debug(false) 132 ]), 133 thread_get_message(title(Title)). 134 135thread_run_interactor(Creator, Title) :- 136 set_prolog_flag(query_debug_settings, debug(false, false)), 137 attach_console(Title), 138 thread_send_message(Creator, title(Title)), 139 print_message(banner, thread_welcome), 140 prolog.
146thread_run_interactor :-
147 set_prolog_flag(query_debug_settings, debug(false, false)),
148 attach_console(_Title),
149 print_message(banner, thread_welcome),
150 prolog.
158:- dynamic 159 has_console/4. % Id, In, Out, Err 160 161thread_has_console(main) :- !. % we assume main has one. 162thread_has_console(Id) :- 163 has_console(Id, _, _, _). 164 165thread_has_console :- 166 current_prolog_flag(break_level, _), 167 !. 168thread_has_console :- 169 thread_self(Id), 170 thread_has_console(Id), 171 !.
180attach_console :- 181 attach_console(_). 182 183attach_console(_) :- 184 thread_has_console, 185 !. 186attach_console(Title) :- 187 thread_self(Id), 188 ( var(Title) 189 -> console_title(Id, Title) 190 ; true 191 ), 192 open_console(Title, In, Out, Err), 193 assert(has_console(Id, In, Out, Err)), 194 set_stream(In, alias(user_input)), 195 set_stream(Out, alias(user_output)), 196 set_stream(Err, alias(user_error)), 197 set_stream(In, alias(current_input)), 198 set_stream(Out, alias(current_output)), 199 enable_line_editing(In,Out,Err), 200 thread_at_exit(detach_console(Id)). 201 202console_title(Thread, Title) :- % uses tabbed consoles 203 current_prolog_flag(console_menu_version, qt), 204 !, 205 human_thread_id(Thread, Id), 206 format(atom(Title), 'Thread ~w', [Id]). 207console_title(Thread, Title) :- 208 current_prolog_flag(system_thread_id, SysId), 209 human_thread_id(Thread, Id), 210 format(atom(Title), 211 'SWI-Prolog Thread ~w (~d) Interactor', 212 [Id, SysId]). 213 214human_thread_id(Thread, Alias) :- 215 thread_property(Thread, alias(Alias)), 216 !. 217human_thread_id(Thread, Id) :- 218 thread_property(Thread, id(Id)).
225:- multifile xterm_args/1. 226:- dynamic xterm_args/1. 227 228:- if(current_predicate(win_open_console/5)). 229 230open_console(Title, In, Out, Err) :- 231 thread_self(Id), 232 regkey(Id, Key), 233 win_open_console(Title, In, Out, Err, 234 [ registry_key(Key) 235 ]). 236 237regkey(Key, Key) :- 238 atom(Key). 239regkey(_, 'Anonymous'). 240 241:- else.
xterm(1)
process opened for additional thread consoles. Each
solution must bind List to a list of atomic values. All solutions
are concatenated using append/2 to form the final argument list.
The defaults set the colors to black-on-light-yellow, enable a scrollbar, set the font using Xft font pattern and prepares the back-arrow key.
254xterm_args(['-xrm', '*backarrowKeyIsErase: false']). 255xterm_args(['-xrm', '*backarrowKey: false']). 256xterm_args(['-fa', 'Ubuntu Mono', '-fs', 12]). 257xterm_args(['-fg', '#000000']). 258xterm_args(['-bg', '#ffffdd']). 259xterm_args(['-sb', '-sl', 1000, '-rightbar']). 260 261open_console(Title, In, Out, Err) :- 262 findall(Arg, xterm_args(Arg), Args), 263 append(Args, Argv), 264 open_xterm(Title, In, Out, Err, Argv). 265 266:- endif.
xterm(1)
based
console if we use the BSD libedit based command line editor.274:- if((current_prolog_flag(readline, editline), 275 exists_source(library(editline)))). 276enable_line_editing(_In, _Out, _Err) :- 277 current_prolog_flag(readline, editline), 278 !, 279 el_wrap. 280:- endif. 281enable_line_editing(_In, _Out, _Err). 282 283:- if(current_predicate(el_unwrap/1)). 284disable_line_editing(_In, _Out, _Err) :- 285 el_unwrap(user_input). 286:- endif. 287disable_line_editing(_In, _Out, _Err).
294detach_console(Id) :- 295 ( retract(has_console(Id, In, Out, Err)) 296 -> disable_line_editing(In, Out, Err), 297 close(In, [force(true)]), 298 close(Out, [force(true)]), 299 close(Err, [force(true)]) 300 ; true 301 ). 302 303 304 /******************************* 305 * DEBUGGING * 306 *******************************/
314tspy(Spec) :- 315 spy(Spec), 316 tdebug. 317 318tspy(Spec, ThreadID) :- 319 spy(Spec), 320 tdebug(ThreadID).
329tdebug :- 330 forall(debug_target(Id), thread_signal(Id, gdebug)). 331 332tdebug(ThreadID) :- 333 thread_signal(ThreadID, gdebug).
340tnodebug :- 341 forall(debug_target(Id), thread_signal(Id, nodebug)). 342 343tnodebug(ThreadID) :- 344 thread_signal(ThreadID, nodebug). 345 346 347debug_target(Thread) :- 348 thread_property(Thread, status(running)), 349 thread_property(Thread, debug(true)).
user_error
of the
calling thread. This is achieved by inserting an interrupt into
Thread using call_in_thread/2. Options:
backtrace_depth
or 20.Other options are passed to get_prolog_backtrace/3.
366tbacktrace(Thread) :- 367 tbacktrace(Thread, []). 368 369tbacktrace(Thread, Options) :- 370 merge_options(Options, [clause_references(false)], Options1), 371 ( current_prolog_flag(backtrace_depth, Default) 372 -> true 373 ; Default = 20 374 ), 375 option(depth(Depth), Options1, Default), 376 call_in_thread(Thread, thread_get_prolog_backtrace(Depth, Stack, Options1)), 377 print_prolog_backtrace(user_error, Stack).
384thread_get_prolog_backtrace(Depth, Stack, Options) :- 385 prolog_current_frame(Frame), 386 signal_frame(Frame, SigFrame), 387 get_prolog_backtrace(Depth, Stack, [frame(SigFrame)|Options]). 388 389signal_frame(Frame, SigFrame) :- 390 prolog_frame_attribute(Frame, clause, _), 391 !, 392 ( prolog_frame_attribute(Frame, parent, Parent) 393 -> signal_frame(Parent, SigFrame) 394 ; SigFrame = Frame 395 ). 396signal_frame(Frame, SigFrame) :- 397 ( prolog_frame_attribute(Frame, parent, Parent) 398 -> SigFrame = Parent 399 ; SigFrame = Frame 400 ). 401 402 403 404 /******************************* 405 * REMOTE PROFILING * 406 *******************************/
412tprofile(Thread) :-
413 init_pce,
414 thread_signal(Thread,
415 ( reset_profiler,
416 profiler(_, true)
417 )),
418 format('Running profiler in thread ~w (press RET to show results) ...',
419 [Thread]),
420 flush_output,
421 get_code(_),
422 thread_signal(Thread,
423 ( profiler(_, false),
424 show_profile([])
425 )).
433:- if(exists_source(library(pce))). 434init_pce :- 435 current_prolog_flag(gui, true), 436 !, 437 call(send(@(display), open)). % avoid autoloading 438:- endif. 439init_pce. 440 441 442 /******************************* 443 * HOOKS * 444 *******************************/ 445 446:- multifile 447 user:message_hook/3. 448 449user:message_hook(trace_mode(on), _, Lines) :- 450 \+ thread_has_console, 451 \+ current_prolog_flag(gui_tracer, true), 452 catch(attach_console, _, fail), 453 print_message_lines(user_error, '% ', Lines). 454 455:- multifile 456 prolog:message/3. 457 458prologmessage(thread_welcome) --> 459 { thread_self(Self), 460 human_thread_id(Self, Id) 461 }, 462 [ 'SWI-Prolog console for thread ~w'-[Id], 463 nl, nl 464 ]. 465prologmessage(joined_threads(Threads)) --> 466 [ 'Joined the following threads'-[], nl ], 467 thread_list(Threads). 468prologmessage(threads(Threads)) --> 469 thread_list(Threads). 470 471thread_list(Threads) --> 472 { maplist(th_id_len, Threads, Lens), 473 max_list(Lens, MaxWidth), 474 LeftColWidth is max(6, MaxWidth), 475 Threads = [H|_] 476 }, 477 thread_list_header(H, LeftColWidth), 478 thread_list(Threads, LeftColWidth). 479 480th_id_len(Thread, IdLen) :- 481 write_length(Thread.id, IdLen, [quoted(true)]). 482 483thread_list([], _) --> []. 484thread_list([H|T], CW) --> 485 thread_info(H, CW), 486 ( {T == []} 487 -> [] 488 ; [nl], 489 thread_list(T, CW) 490 ). 491 492thread_list_header(Thread, CW) --> 493 { _{id:_, status:_, time:_, stacks:_} :< Thread, 494 !, 495 HrWidth is CW+18+13+13 496 }, 497 [ '~|~tThread~*+ Status~tTime~18+~tStack use~13+~tallocated~13+'-[CW], nl ], 498 [ '~|~`-t~*+'-[HrWidth], nl ]. 499thread_list_header(Thread, CW) --> 500 { _{id:_, status:_} :< Thread, 501 !, 502 HrWidth is CW+7 503 }, 504 [ '~|~tThread~*+ Status'-[CW], nl ], 505 [ '~|~`-t~*+'-[HrWidth], nl ]. 506 507thread_info(Thread, CW) --> 508 { _{id:Id, status:Status, time:Time, stacks:Stacks} :< Thread }, 509 !, 510 [ '~|~t~q~*+ ~w~t~3f~18+~t~D~13+~t~D~13+'- 511 [ Id, CW, Status, Time.cpu, Stacks.total.usage, Stacks.total.allocated 512 ] 513 ]. 514thread_info(Thread, CW) --> 515 { _{id:Id, status:Status} :< Thread }, 516 !, 517 [ '~|~t~q~*+ ~w'- 518 [ Id, CW, Status 519 ] 520 ]
Interactive thread utilities
This library provides utilities that are primarily intended for interactive usage in a threaded Prolog environment. It allows for inspecting threads, manage I/O of background threads (depending on the environment) and manipulating the debug status of threads. */