35
36:- module(thread_util,
37 [ threads/0, 38 join_threads/0, 39 with_stopped_threads/2, 40 thread_has_console/0, 41 attach_console/0, 42 attach_console/1, 43
44 tspy/1, 45 tspy/2, 46 tdebug/0,
47 tdebug/1, 48 tnodebug/0,
49 tnodebug/1, 50 tprofile/1, 51 tbacktrace/1, 52 tbacktrace/2 53 ]). 54:- if(( current_predicate(win_open_console/5)
55 ; current_predicate('$open_xterm'/5))). 56:- export(( thread_run_interactor/0, 57 interactor/0,
58 interactor/1 59 )). 60:- endif. 61
62:- meta_predicate
63 with_stopped_threads(0, +). 64
65:- autoload(library(apply),[maplist/3]). 66:- autoload(library(backcomp),[thread_at_exit/1]). 67:- autoload(library(edinburgh),[nodebug/0]). 68:- autoload(library(lists),[max_list/2,append/2]). 69:- autoload(library(option),[merge_options/3,option/3]). 70:- autoload(library(prolog_stack),
71 [print_prolog_backtrace/2,get_prolog_backtrace/3]). 72:- autoload(library(statistics),[thread_statistics/2]). 73:- autoload(library(prolog_profile), [show_profile/1]). 74:- autoload(library(thread),[call_in_thread/2]). 75
76:- if((\+current_prolog_flag(xpce,false),exists_source(library(pce)))). 77:- autoload(library(gui_tracer),[gdebug/0]). 78:- autoload(library(pce),[send/2]). 79:- else. 80gdebug :-
81 debug.
82:- endif. 83
84
85:- set_prolog_flag(generate_debug_info, false). 86
87:- module_transparent
88 tspy/1,
89 tspy/2. 90
98
102
103threads :-
104 threads(Threads),
105 print_message(information, threads(Threads)).
106
107threads(Threads) :-
108 findall(Thread, thread_statistics(_,Thread), Threads).
109
113
114join_threads :-
115 findall(Ripped, rip_thread(Ripped), AllRipped),
116 ( AllRipped == []
117 -> true
118 ; print_message(informational, joined_threads(AllRipped))
119 ).
120
121rip_thread(thread{id:id, status:Status}) :-
122 thread_property(Id, status(Status)),
123 Status \== running,
124 \+ thread_self(Id),
125 thread_join(Id, _).
126
145
146:- dynamic stopped_except/1. 147
148with_stopped_threads(_, _) :-
149 stopped_except(_),
150 !.
151with_stopped_threads(Goal, Options) :-
152 thread_self(Me),
153 setup_call_cleanup(
154 asserta(stopped_except(Me), Ref),
155 ( stop_other_threads(Me, Options),
156 once(Goal)
157 ),
158 erase(Ref)).
159
160stop_other_threads(Me, Options) :-
161 findall(T, stop_thread(Me, T, Options), Stopped),
162 broadcast(stopped_threads(Stopped)).
163
164stop_thread(Me, Thread, Options) :-
165 option(except(Except), Options, []),
166 ( option(stop_nodebug_threads(true), Options)
167 -> thread_property(Thread, status(running))
168 ; debug_target(Thread)
169 ),
170 Me \== Thread,
171 \+ memberchk(Thread, Except),
172 catch(thread_signal(Thread, stopped_except), error(_,_), fail).
173
174stopped_except :-
175 thread_wait(\+ stopped_except(_),
176 [ wait_preds([stopped_except/1])
177 ]).
178
184
185:- dynamic
186 has_console/4. 187
188thread_has_console(main) :- !. 189thread_has_console(Id) :-
190 has_console(Id, _, _, _).
191
192thread_has_console :-
193 current_prolog_flag(break_level, _),
194 !.
195thread_has_console :-
196 thread_self(Id),
197 thread_has_console(Id),
198 !.
199
206
207:- multifile xterm_args/1. 208:- dynamic xterm_args/1. 209
210:- if(current_predicate(win_open_console/5)). 211
212can_open_console.
213
214open_console(Title, In, Out, Err) :-
215 thread_self(Id),
216 regkey(Id, Key),
217 win_open_console(Title, In, Out, Err,
218 [ registry_key(Key)
219 ]).
220
221regkey(Key, Key) :-
222 atom(Key).
223regkey(_, 'Anonymous').
224
225:- elif(current_predicate('$open_xterm'/5)). 226
237
238xterm_args(['-xrm', '*backarrowKeyIsErase: false']).
239xterm_args(['-xrm', '*backarrowKey: false']).
240xterm_args(['-fa', 'Ubuntu Mono', '-fs', 12]).
241xterm_args(['-fg', '#000000']).
242xterm_args(['-bg', '#ffffdd']).
243xterm_args(['-sb', '-sl', 1000, '-rightbar']).
244
245can_open_console :-
246 getenv('DISPLAY', _),
247 absolute_file_name(path(xterm), _XTerm, [access(execute)]).
248
249open_console(Title, In, Out, Err) :-
250 findall(Arg, xterm_args(Arg), Args),
251 append(Args, Argv),
252 '$open_xterm'(Title, In, Out, Err, Argv).
253
254:- endif. 255
262
263attach_console :-
264 attach_console(_).
265
266attach_console(_) :-
267 thread_has_console,
268 !.
269:- if(current_predicate(open_console/4)). 270attach_console(Title) :-
271 can_open_console,
272 !,
273 thread_self(Id),
274 ( var(Title)
275 -> console_title(Id, Title)
276 ; true
277 ),
278 open_console(Title, In, Out, Err),
279 assert(has_console(Id, In, Out, Err)),
280 set_stream(In, alias(user_input)),
281 set_stream(Out, alias(user_output)),
282 set_stream(Err, alias(user_error)),
283 set_stream(In, alias(current_input)),
284 set_stream(Out, alias(current_output)),
285 enable_line_editing(In,Out,Err),
286 thread_at_exit(detach_console(Id)).
287:- endif. 288attach_console(Title) :-
289 print_message(error, cannot_attach_console(Title)),
290 fail.
291
292:- if(current_predicate(open_console/4)). 293console_title(Thread, Title) :- 294 current_prolog_flag(console_menu_version, qt),
295 !,
296 human_thread_id(Thread, Id),
297 format(atom(Title), 'Thread ~w', [Id]).
298console_title(Thread, Title) :-
299 current_prolog_flag(system_thread_id, SysId),
300 human_thread_id(Thread, Id),
301 format(atom(Title),
302 'SWI-Prolog Thread ~w (~d) Interactor',
303 [Id, SysId]).
304
305human_thread_id(Thread, Alias) :-
306 thread_property(Thread, alias(Alias)),
307 !.
308human_thread_id(Thread, Id) :-
309 thread_property(Thread, id(Id)).
310
316
317:- if((current_prolog_flag(readline, editline),
318 exists_source(library(editline)))). 319enable_line_editing(_In, _Out, _Err) :-
320 current_prolog_flag(readline, editline),
321 !,
322 el_wrap.
323:- endif. 324enable_line_editing(_In, _Out, _Err).
325
326:- if(current_predicate(el_unwrap/1)). 327disable_line_editing(_In, _Out, _Err) :-
328 el_unwrap(user_input).
329:- endif. 330disable_line_editing(_In, _Out, _Err).
331
332
336
337detach_console(Id) :-
338 ( retract(has_console(Id, In, Out, Err))
339 -> disable_line_editing(In, Out, Err),
340 close(In, [force(true)]),
341 close(Out, [force(true)]),
342 close(Err, [force(true)])
343 ; true
344 ).
345
351
352interactor :-
353 interactor(_).
354
355interactor(Title) :-
356 can_open_console,
357 !,
358 thread_self(Me),
359 thread_create(thread_run_interactor(Me, Title), _Id,
360 [ detached(true)
361 ]),
362 thread_get_message(Msg),
363 ( Msg = title(Title0)
364 -> Title = Title0
365 ; Msg = throw(Error)
366 -> throw(Error)
367 ; Msg = false
368 -> fail
369 ).
370interactor(Title) :-
371 print_message(error, cannot_attach_console(Title)),
372 fail.
373
374thread_run_interactor(Creator, Title) :-
375 set_prolog_flag(query_debug_settings, debug(false, false)),
376 Error = error(Formal,_),
377 ( catch(attach_console(Title), Error, true)
378 -> ( var(Formal)
379 -> thread_send_message(Creator, title(Title)),
380 print_message(banner, thread_welcome),
381 prolog
382 ; thread_send_message(Creator, throw(Error))
383 )
384 ; thread_send_message(Creator, false)
385 ).
386
390
391thread_run_interactor :-
392 set_prolog_flag(query_debug_settings, debug(false, false)),
393 attach_console(_Title),
394 print_message(banner, thread_welcome),
395 prolog.
396
397:- endif. 398
399 402
408
409tspy(Spec) :-
410 spy(Spec),
411 tdebug.
412
413tspy(Spec, ThreadID) :-
414 spy(Spec),
415 tdebug(ThreadID).
416
417
423
424tdebug :-
425 forall(debug_target(Id), thread_signal(Id, gdebug)).
426
427tdebug(ThreadID) :-
428 thread_signal(ThreadID, gdebug).
429
434
435tnodebug :-
436 forall(debug_target(Id), thread_signal(Id, nodebug)).
437
438tnodebug(ThreadID) :-
439 thread_signal(ThreadID, nodebug).
440
441
442debug_target(Thread) :-
443 thread_property(Thread, status(running)),
444 thread_property(Thread, debug(true)).
445
460
461tbacktrace(Thread) :-
462 tbacktrace(Thread, []).
463
464tbacktrace(Thread, Options) :-
465 merge_options(Options, [clause_references(false)], Options1),
466 ( current_prolog_flag(backtrace_depth, Default)
467 -> true
468 ; Default = 20
469 ),
470 option(depth(Depth), Options1, Default),
471 call_in_thread(Thread, thread_get_prolog_backtrace(Depth, Stack, Options1)),
472 print_prolog_backtrace(user_error, Stack).
473
478
479thread_get_prolog_backtrace(Depth, Stack, Options) :-
480 prolog_current_frame(Frame),
481 signal_frame(Frame, SigFrame),
482 get_prolog_backtrace(Depth, Stack, [frame(SigFrame)|Options]).
483
484signal_frame(Frame, SigFrame) :-
485 prolog_frame_attribute(Frame, clause, _),
486 !,
487 ( prolog_frame_attribute(Frame, parent, Parent)
488 -> signal_frame(Parent, SigFrame)
489 ; SigFrame = Frame
490 ).
491signal_frame(Frame, SigFrame) :-
492 ( prolog_frame_attribute(Frame, parent, Parent)
493 -> SigFrame = Parent
494 ; SigFrame = Frame
495 ).
496
497
498
499 502
506
507tprofile(Thread) :-
508 init_pce,
509 thread_signal(Thread,
510 ( reset_profiler,
511 profiler(_, true)
512 )),
513 format('Running profiler in thread ~w (press RET to show results) ...',
514 [Thread]),
515 flush_output,
516 get_code(_),
517 thread_signal(Thread,
518 ( profiler(_, false),
519 show_profile([])
520 )).
521
522
527
528:- if(exists_source(library(pce))). 529init_pce :-
530 current_prolog_flag(gui, true),
531 !,
532 call(send(@(display), open)). 533:- endif. 534init_pce.
535
536
537 540
541:- multifile
542 user:message_hook/3. 543
544user:message_hook(trace_mode(on), _, Lines) :-
545 \+ thread_has_console,
546 \+ current_prolog_flag(gui_tracer, true),
547 catch(attach_console, _, fail),
548 print_message_lines(user_error, '% ', Lines).
549
550:- multifile
551 prolog:message/3. 552
553prolog:message(thread_welcome) -->
554 { thread_self(Self),
555 human_thread_id(Self, Id)
556 },
557 [ 'SWI-Prolog console for thread ~w'-[Id],
558 nl, nl
559 ].
560prolog:message(joined_threads(Threads)) -->
561 [ 'Joined the following threads'-[], nl ],
562 thread_list(Threads).
563prolog:message(threads(Threads)) -->
564 thread_list(Threads).
565prolog:message(cannot_attach_console(_Title)) -->
566 [ 'Cannot attach a console (requires swipl-win or POSIX pty support)' ].
567
568thread_list(Threads) -->
569 { maplist(th_id_len, Threads, Lens),
570 max_list(Lens, MaxWidth),
571 LeftColWidth is max(6, MaxWidth),
572 Threads = [H|_]
573 },
574 thread_list_header(H, LeftColWidth),
575 thread_list(Threads, LeftColWidth).
576
577th_id_len(Thread, IdLen) :-
578 write_length(Thread.id, IdLen, [quoted(true)]).
579
580thread_list([], _) --> [].
581thread_list([H|T], CW) -->
582 thread_info(H, CW),
583 ( {T == []}
584 -> []
585 ; [nl],
586 thread_list(T, CW)
587 ).
588
(Thread, CW) -->
590 { _{id:_, status:_, time:_, stacks:_} :< Thread,
591 !,
592 HrWidth is CW+18+13+13
593 },
594 [ '~|~tThread~*+ Status~tTime~18+~tStack use~13+~tallocated~13+'-[CW], nl ],
595 [ '~|~`-t~*+'-[HrWidth], nl ].
596thread_list_header(Thread, CW) -->
597 { _{id:_, status:_} :< Thread,
598 !,
599 HrWidth is CW+7
600 },
601 [ '~|~tThread~*+ Status'-[CW], nl ],
602 [ '~|~`-t~*+'-[HrWidth], nl ].
603
604thread_info(Thread, CW) -->
605 { _{id:Id, status:Status, time:Time, stacks:Stacks} :< Thread },
606 !,
607 [ '~|~t~q~*+ ~w~t~3f~18+~t~D~13+~t~D~13+'-
608 [ Id, CW, Status, Time.cpu, Stacks.total.usage, Stacks.total.allocated
609 ]
610 ].
611thread_info(Thread, CW) -->
612 { _{id:Id, status:Status} :< Thread },
613 !,
614 [ '~|~t~q~*+ ~w'-
615 [ Id, CW, Status
616 ]
617 ]