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,show_profile/1]). 64:- autoload(library(thread),[call_in_thread/2]). 65
66:- if((\+current_prolog_flag(xpce,flase),exists_source(library(pce)))). 67:- autoload(library(gui_tracer),[gdebug/0]). 68:- autoload(library(pce),[send/2]). 69:- else. 70gdebug :-
71 debug.
72:- endif. 73
74
75:- set_prolog_flag(generate_debug_info, false). 76
77:- module_transparent
78 tspy/1,
79 tspy/2. 80
88
92
93threads :-
94 threads(Threads),
95 print_message(information, threads(Threads)).
96
97threads(Threads) :-
98 findall(Thread, thread_statistics(_,Thread), Threads).
99
103
104join_threads :-
105 findall(Ripped, rip_thread(Ripped), AllRipped),
106 ( AllRipped == []
107 -> true
108 ; print_message(informational, joined_threads(AllRipped))
109 ).
110
111rip_thread(thread{id:id, status:Status}) :-
112 thread_property(Id, status(Status)),
113 Status \== running,
114 \+ thread_self(Id),
115 thread_join(Id, _).
116
122
123interactor :-
124 interactor(_).
125
126interactor(Title) :-
127 thread_self(Me),
128 thread_create(thread_run_interactor(Me, Title), _Id,
129 [ detached(true),
130 debug(false)
131 ]),
132 thread_get_message(title(Title)).
133
134thread_run_interactor(Creator, Title) :-
135 set_prolog_flag(query_debug_settings, debug(false, false)),
136 attach_console(Title),
137 thread_send_message(Creator, title(Title)),
138 print_message(banner, thread_welcome),
139 prolog.
140
144
145thread_run_interactor :-
146 set_prolog_flag(query_debug_settings, debug(false, false)),
147 attach_console(_Title),
148 print_message(banner, thread_welcome),
149 prolog.
150
156
157:- dynamic
158 has_console/4. 159
160thread_has_console(main) :- !. 161thread_has_console(Id) :-
162 has_console(Id, _, _, _).
163
164thread_has_console :-
165 current_prolog_flag(break_level, _),
166 !.
167thread_has_console :-
168 thread_self(Id),
169 thread_has_console(Id),
170 !.
171
178
179attach_console :-
180 attach_console(_).
181
182attach_console(_) :-
183 thread_has_console,
184 !.
185attach_console(Title) :-
186 thread_self(Id),
187 ( var(Title)
188 -> console_title(Id, Title)
189 ; true
190 ),
191 open_console(Title, In, Out, Err),
192 assert(has_console(Id, In, Out, Err)),
193 set_stream(In, alias(user_input)),
194 set_stream(Out, alias(user_output)),
195 set_stream(Err, alias(user_error)),
196 set_stream(In, alias(current_input)),
197 set_stream(Out, alias(current_output)),
198 enable_line_editing(In,Out,Err),
199 thread_at_exit(detach_console(Id)).
200
201console_title(Thread, Title) :- 202 current_prolog_flag(console_menu_version, qt),
203 !,
204 human_thread_id(Thread, Id),
205 format(atom(Title), 'Thread ~w', [Id]).
206console_title(Thread, Title) :-
207 current_prolog_flag(system_thread_id, SysId),
208 human_thread_id(Thread, Id),
209 format(atom(Title),
210 'SWI-Prolog Thread ~w (~d) Interactor',
211 [Id, SysId]).
212
213human_thread_id(Thread, Alias) :-
214 thread_property(Thread, alias(Alias)),
215 !.
216human_thread_id(Thread, Id) :-
217 thread_property(Thread, id(Id)).
218
223
224:- multifile xterm_args/1. 225:- dynamic xterm_args/1. 226
227:- if(current_predicate(win_open_console/5)). 228
229open_console(Title, In, Out, Err) :-
230 thread_self(Id),
231 regkey(Id, Key),
232 win_open_console(Title, In, Out, Err,
233 [ registry_key(Key)
234 ]).
235
236regkey(Key, Key) :-
237 atom(Key).
238regkey(_, 'Anonymous').
239
240:- else. 241
252
253xterm_args(['-xrm', '*backarrowKeyIsErase: false']).
254xterm_args(['-xrm', '*backarrowKey: false']).
255xterm_args(['-fa', 'Ubuntu Mono', '-fs', 12]).
256xterm_args(['-fg', '#000000']).
257xterm_args(['-bg', '#ffffdd']).
258xterm_args(['-sb', '-sl', 1000, '-rightbar']).
259
260open_console(Title, In, Out, Err) :-
261 findall(Arg, xterm_args(Arg), Args),
262 append(Args, Argv),
263 open_xterm(Title, In, Out, Err, Argv).
264
265:- endif. 266
272
273:- if((current_prolog_flag(readline, editline),
274 exists_source(library(editline)))). 275enable_line_editing(_In, _Out, _Err) :-
276 current_prolog_flag(readline, editline),
277 !,
278 el_wrap.
279:- endif. 280enable_line_editing(_In, _Out, _Err).
281
282:- if(current_predicate(el_unwrap/1)). 283disable_line_editing(_In, _Out, _Err) :-
284 el_unwrap(user_input).
285:- endif. 286disable_line_editing(_In, _Out, _Err).
287
288
292
293detach_console(Id) :-
294 ( retract(has_console(Id, In, Out, Err))
295 -> disable_line_editing(In, Out, Err),
296 close(In, [force(true)]),
297 close(Out, [force(true)]),
298 close(Err, [force(true)])
299 ; true
300 ).
301
302
303 306
312
313tspy(Spec) :-
314 spy(Spec),
315 tdebug.
316
317tspy(Spec, ThreadID) :-
318 spy(Spec),
319 tdebug(ThreadID).
320
321
327
328tdebug :-
329 forall(debug_target(Id), thread_signal(Id, gdebug)).
330
331tdebug(ThreadID) :-
332 thread_signal(ThreadID, gdebug).
333
338
339tnodebug :-
340 forall(debug_target(Id), thread_signal(Id, nodebug)).
341
342tnodebug(ThreadID) :-
343 thread_signal(ThreadID, nodebug).
344
345
346debug_target(Thread) :-
347 thread_property(Thread, status(running)),
348 thread_property(Thread, debug(true)).
349
364
365tbacktrace(Thread) :-
366 tbacktrace(Thread, []).
367
368tbacktrace(Thread, Options) :-
369 merge_options(Options, [clause_references(false)], Options1),
370 ( current_prolog_flag(backtrace_depth, Default)
371 -> true
372 ; Default = 20
373 ),
374 option(depth(Depth), Options1, Default),
375 call_in_thread(Thread, thread_get_prolog_backtrace(Depth, Stack, Options1)),
376 print_prolog_backtrace(user_error, Stack).
377
382
383thread_get_prolog_backtrace(Depth, Stack, Options) :-
384 prolog_current_frame(Frame),
385 signal_frame(Frame, SigFrame),
386 get_prolog_backtrace(Depth, Stack, [frame(SigFrame)|Options]).
387
388signal_frame(Frame, SigFrame) :-
389 prolog_frame_attribute(Frame, clause, _),
390 !,
391 ( prolog_frame_attribute(Frame, parent, Parent)
392 -> signal_frame(Parent, SigFrame)
393 ; SigFrame = Frame
394 ).
395signal_frame(Frame, SigFrame) :-
396 ( prolog_frame_attribute(Frame, parent, Parent)
397 -> SigFrame = Parent
398 ; SigFrame = Frame
399 ).
400
401
402
403 406
410
411tprofile(Thread) :-
412 init_pce,
413 thread_signal(Thread,
414 ( reset_profiler,
415 profiler(_, true)
416 )),
417 format('Running profiler in thread ~w (press RET to show results) ...',
418 [Thread]),
419 flush_output,
420 get_code(_),
421 thread_signal(Thread,
422 ( profiler(_, false),
423 show_profile([])
424 )).
425
426
431
432:- if(exists_source(library(pce))). 433init_pce :-
434 current_prolog_flag(gui, true),
435 !,
436 call(send(@(display), open)). 437:- endif. 438init_pce.
439
440
441 444
445:- multifile
446 user:message_hook/3. 447
448user:message_hook(trace_mode(on), _, Lines) :-
449 \+ thread_has_console,
450 \+ current_prolog_flag(gui_tracer, true),
451 catch(attach_console, _, fail),
452 print_message_lines(user_error, '% ', Lines).
453
454:- multifile
455 prolog:message/3. 456
457prolog:message(thread_welcome) -->
458 { thread_self(Self),
459 human_thread_id(Self, Id)
460 },
461 [ 'SWI-Prolog console for thread ~w'-[Id],
462 nl, nl
463 ].
464prolog:message(joined_threads(Threads)) -->
465 [ 'Joined the following threads'-[], nl ],
466 thread_list(Threads).
467prolog:message(threads(Threads)) -->
468 thread_list(Threads).
469
470thread_list(Threads) -->
471 { maplist(th_id_len, Threads, Lens),
472 max_list(Lens, MaxWidth),
473 LeftColWidth is max(6, MaxWidth),
474 Threads = [H|_]
475 },
476 thread_list_header(H, LeftColWidth),
477 thread_list(Threads, LeftColWidth).
478
479th_id_len(Thread, IdLen) :-
480 write_length(Thread.id, IdLen, [quoted(true)]).
481
482thread_list([], _) --> [].
483thread_list([H|T], CW) -->
484 thread_info(H, CW),
485 ( {T == []}
486 -> []
487 ; [nl],
488 thread_list(T, CW)
489 ).
490
(Thread, CW) -->
492 { _{id:_, status:_, time:_, stacks:_} :< Thread,
493 !,
494 HrWidth is CW+18+13+13
495 },
496 [ '~|~tThread~*+ Status~tTime~18+~tStack use~13+~tallocated~13+'-[CW], nl ],
497 [ '~|~`-t~*+'-[HrWidth], nl ].
498thread_list_header(Thread, CW) -->
499 { _{id:_, status:_} :< Thread,
500 !,
501 HrWidth is CW+7
502 },
503 [ '~|~tThread~*+ Status'-[CW], nl ],
504 [ '~|~`-t~*+'-[HrWidth], nl ].
505
506thread_info(Thread, CW) -->
507 { _{id:Id, status:Status, time:Time, stacks:Stacks} :< Thread },
508 !,
509 [ '~|~t~q~*+ ~w~t~3f~18+~t~D~13+~t~D~13+'-
510 [ Id, CW, Status, Time.cpu, Stacks.total.usage, Stacks.total.allocated
511 ]
512 ].
513thread_info(Thread, CW) -->
514 { _{id:Id, status:Status} :< Thread },
515 !,
516 [ '~|~t~q~*+ ~w'-
517 [ Id, CW, Status
518 ]
519 ]