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_prolog_flag(xpce, true)). 55:- export(( interactor/0,
56 interactor/1 57 )). 58:- autoload(library(epilog),
59 [ epilog/1,
60 epilog_attach/1,
61 ep_has_console/1
62 ]). 63:- endif. 64
65:- meta_predicate
66 with_stopped_threads(0, +). 67
68:- autoload(library(apply),[maplist/3]). 69:- autoload(library(backcomp),[thread_at_exit/1]). 70:- autoload(library(edinburgh),[nodebug/0]). 71:- autoload(library(lists),[max_list/2,append/2]). 72:- autoload(library(option),[merge_options/3,option/3]). 73:- autoload(library(prolog_stack),
74 [print_prolog_backtrace/2,get_prolog_backtrace/3]). 75:- autoload(library(statistics),[thread_statistics/2]). 76:- autoload(library(prolog_profile), [show_profile/1]). 77:- autoload(library(thread),[call_in_thread/2]). 78
79:- set_prolog_flag(generate_debug_info, false). 80
81:- module_transparent
82 tspy/1,
83 tspy/2. 84
92
96
97threads :-
98 threads(Threads),
99 print_message(information, threads(Threads)).
100
101threads(Threads) :-
102 findall(Thread, thread_statistics(_,Thread), Threads).
103
107
108join_threads :-
109 findall(Ripped, rip_thread(Ripped), AllRipped),
110 ( AllRipped == []
111 -> true
112 ; print_message(informational, joined_threads(AllRipped))
113 ).
114
115rip_thread(thread{id:id, status:Status}) :-
116 thread_property(Id, status(Status)),
117 Status \== running,
118 \+ thread_self(Id),
119 thread_join(Id, _).
120
139
140:- dynamic stopped_except/1. 141
142with_stopped_threads(_, _) :-
143 stopped_except(_),
144 !.
145with_stopped_threads(Goal, Options) :-
146 thread_self(Me),
147 setup_call_cleanup(
148 asserta(stopped_except(Me), Ref),
149 ( stop_other_threads(Me, Options),
150 once(Goal)
151 ),
152 erase(Ref)).
153
154stop_other_threads(Me, Options) :-
155 findall(T, stop_thread(Me, T, Options), Stopped),
156 broadcast(stopped_threads(Stopped)).
157
158stop_thread(Me, Thread, Options) :-
159 option(except(Except), Options, []),
160 ( option(stop_nodebug_threads(true), Options)
161 -> thread_property(Thread, status(running))
162 ; debug_target(Thread)
163 ),
164 Me \== Thread,
165 \+ memberchk(Thread, Except),
166 catch(thread_signal(Thread, stopped_except), error(_,_), fail).
167
168stopped_except :-
169 thread_wait(\+ stopped_except(_),
170 [ wait_preds([stopped_except/1])
171 ]).
172
178
179thread_has_console(main) :-
180 !,
181 \+ current_prolog_flag(epilog, true).
182:- if(current_predicate(ep_has_console/1)). 183thread_has_console(Id) :-
184 ep_has_console(Id).
185:- endif. 186
187thread_has_console :-
188 current_prolog_flag(break_level, _),
189 !.
190thread_has_console :-
191 thread_self(Id),
192 thread_has_console(Id),
193 !.
194
201
202attach_console :-
203 attach_console(_).
204
205attach_console(_) :-
206 thread_has_console,
207 !.
208:- if(current_predicate(epilog_attach/1)). 209attach_console(Title) :-
210 thread_self(Me),
211 console_title(Me, Title),
212 epilog_attach([ title(Title)
213 ]).
214:- endif. 215attach_console(Title) :-
216 print_message(error, cannot_attach_console(Title)),
217 fail.
218
219console_title(Thread, Title) :-
220 current_prolog_flag(system_thread_id, SysId),
221 human_thread_id(Thread, Id),
222 format(atom(Title),
223 'SWI-Prolog Thread ~w (~d) Interactor',
224 [Id, SysId]).
225
226human_thread_id(Thread, Alias) :-
227 thread_property(Thread, alias(Alias)),
228 !.
229human_thread_id(Thread, Id) :-
230 thread_property(Thread, id(Id)).
231
237
238interactor :-
239 interactor(_).
240
241:- if(current_predicate(epilog/1)). 242interactor(Title) :-
243 !,
244 ( nonvar(Title)
245 -> Options = [title(Title)]
246 ; Options = []
247 ),
248 epilog([ init(true)
249 | Options
250 ]).
251:- endif. 252interactor(Title) :-
253 print_message(error, cannot_attach_console(Title)),
254 fail.
255
256
257 260
266
267tspy(Spec) :-
268 spy(Spec),
269 tdebug.
270
271tspy(Spec, ThreadID) :-
272 spy(Spec),
273 tdebug(ThreadID).
274
275
281
282tdebug :-
283 forall(debug_target(Id), thread_signal(Id, debug_thread)).
284
285tdebug(ThreadID) :-
286 thread_signal(ThreadID, debug_thread).
287
288debug_thread :-
289 current_prolog_flag(gui, true),
290 !,
291 autoload_call(gdebug).
292debug_thread :-
293 debug.
294
295
300
301tnodebug :-
302 forall(debug_target(Id), thread_signal(Id, nodebug)).
303
304tnodebug(ThreadID) :-
305 thread_signal(ThreadID, nodebug).
306
307
308debug_target(Thread) :-
309 thread_property(Thread, status(running)),
310 thread_property(Thread, debug(true)).
311
326
327tbacktrace(Thread) :-
328 tbacktrace(Thread, []).
329
330tbacktrace(Thread, Options) :-
331 merge_options(Options, [clause_references(false)], Options1),
332 ( current_prolog_flag(backtrace_depth, Default)
333 -> true
334 ; Default = 20
335 ),
336 option(depth(Depth), Options1, Default),
337 call_in_thread(Thread, thread_get_prolog_backtrace(Depth, Stack, Options1)),
338 print_prolog_backtrace(user_error, Stack).
339
344
345thread_get_prolog_backtrace(Depth, Stack, Options) :-
346 prolog_current_frame(Frame),
347 signal_frame(Frame, SigFrame),
348 get_prolog_backtrace(Depth, Stack, [frame(SigFrame)|Options]).
349
350signal_frame(Frame, SigFrame) :-
351 prolog_frame_attribute(Frame, clause, _),
352 !,
353 ( prolog_frame_attribute(Frame, parent, Parent)
354 -> signal_frame(Parent, SigFrame)
355 ; SigFrame = Frame
356 ).
357signal_frame(Frame, SigFrame) :-
358 ( prolog_frame_attribute(Frame, parent, Parent)
359 -> SigFrame = Parent
360 ; SigFrame = Frame
361 ).
362
363
364
365 368
372
373tprofile(Thread) :-
374 init_pce,
375 thread_signal(Thread,
376 ( reset_profiler,
377 profiler(_, true)
378 )),
379 format('Running profiler in thread ~w (press RET to show results) ...',
380 [Thread]),
381 flush_output,
382 get_code(_),
383 thread_signal(Thread,
384 ( profiler(_, false),
385 show_profile([])
386 )).
387
388
393
394:- if(exists_source(library(pce))). 395init_pce :-
396 current_prolog_flag(gui, true),
397 !,
398 autoload_call(send(@(display), open)).
399:- endif. 400init_pce.
401
402
403 406
407:- multifile
408 prolog:message_action/2. 409
410prolog:message_action(trace_mode(on), _Level) :-
411 \+ thread_has_console,
412 \+ current_prolog_flag(gui_tracer, true),
413 catch(attach_console, error(_,_), fail).
414
415:- multifile
416 prolog:message/3. 417
418prolog:message(thread_welcome) -->
419 { thread_self(Self),
420 human_thread_id(Self, Id)
421 },
422 [ 'SWI-Prolog console for thread ~w'-[Id],
423 nl, nl
424 ].
425prolog:message(joined_threads(Threads)) -->
426 [ 'Joined the following threads'-[], nl ],
427 thread_list(Threads).
428prolog:message(threads(Threads)) -->
429 thread_list(Threads).
430prolog:message(cannot_attach_console(_Title)) -->
431 [ 'Cannot attach a console (requires xpce package)' ].
432
433thread_list(Threads) -->
434 { maplist(th_id_len, Threads, Lens),
435 max_list(Lens, MaxWidth),
436 LeftColWidth is max(6, MaxWidth),
437 Threads = [H|_]
438 },
439 thread_list_header(H, LeftColWidth),
440 thread_list(Threads, LeftColWidth).
441
442th_id_len(Thread, IdLen) :-
443 write_length(Thread.id, IdLen, [quoted(true)]).
444
445thread_list([], _) --> [].
446thread_list([H|T], CW) -->
447 thread_info(H, CW),
448 ( {T == []}
449 -> []
450 ; [nl],
451 thread_list(T, CW)
452 ).
453
(Thread, CW) -->
455 { _{id:_, status:_, time:_, stacks:_} :< Thread,
456 !,
457 HrWidth is CW+18+13+13
458 },
459 [ '~|~tThread~*+ Status~tTime~18+~tStack use~13+~tallocated~13+'-[CW], nl ],
460 [ '~|~`-t~*+'-[HrWidth], nl ].
461thread_list_header(Thread, CW) -->
462 { _{id:_, status:_} :< Thread,
463 !,
464 HrWidth is CW+7
465 },
466 [ '~|~tThread~*+ Status'-[CW], nl ],
467 [ '~|~`-t~*+'-[HrWidth], nl ].
468
469thread_info(Thread, CW) -->
470 { _{id:Id, status:Status, time:Time, stacks:Stacks} :< Thread },
471 !,
472 [ '~|~t~q~*+ ~w~t~3f~18+~t~D~13+~t~D~13+'-
473 [ Id, CW, Status, Time.cpu, Stacks.total.usage, Stacks.total.allocated
474 ]
475 ].
476thread_info(Thread, CW) -->
477 { _{id:Id, status:Status} :< Thread },
478 !,
479 [ '~|~t~q~*+ ~w'-
480 [ Id, CW, Status
481 ]
482 ]