35
36:- module(thread_util,
37 [ thread_run_interactor/0, 38 threads/0, 39 join_threads/0, 40 interactor/0, 41 interactor/1, 42 thread_has_console/0, 43 attach_console/0, 44 attach_console/1, 45
46 tspy/1, 47 tspy/2, 48 tdebug/0,
49 tdebug/1, 50 tnodebug/0,
51 tnodebug/1, 52 tprofile/1, 53 tbacktrace/1, 54 tbacktrace/2 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. 81
89
93
94threads :-
95 threads(Threads),
96 print_message(information, threads(Threads)).
97
98threads(Threads) :-
99 findall(Thread, thread_statistics(_,Thread), Threads).
100
104
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, _).
117
123
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.
141
145
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.
151
157
158:- dynamic
159 has_console/4. 160
161thread_has_console(main) :- !. 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 !.
172
179
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) :- 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)).
219
224
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. 242
253
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. 267
273
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).
288
289
293
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 307
313
314tspy(Spec) :-
315 spy(Spec),
316 tdebug.
317
318tspy(Spec, ThreadID) :-
319 spy(Spec),
320 tdebug(ThreadID).
321
322
328
329tdebug :-
330 forall(debug_target(Id), thread_signal(Id, gdebug)).
331
332tdebug(ThreadID) :-
333 thread_signal(ThreadID, gdebug).
334
339
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)).
350
365
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).
378
383
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 407
411
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 )).
426
427
432
433:- if(exists_source(library(pce))). 434init_pce :-
435 current_prolog_flag(gui, true),
436 !,
437 call(send(@(display), open)). 438:- endif. 439init_pce.
440
441
442 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
458prolog:message(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 ].
465prolog:message(joined_threads(Threads)) -->
466 [ 'Joined the following threads'-[], nl ],
467 thread_list(Threads).
468prolog:message(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
(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 ]