* Re: Last chance handler on a PC
2020-01-30 20:02 5% ` Jeffrey R. Carter
@ 2020-01-30 20:26 0% ` Niklas Holsti
0 siblings, 0 replies; 23+ results
From: Niklas Holsti @ 2020-01-30 20:26 UTC (permalink / raw)
On 2020-01-30 22:02, Jeffrey R. Carter wrote:
> On 1/30/20 8:35 PM, ahlan@marriott.org wrote:
>>
>> To catch unhandled exceptions you only need to write a simple
>> procedure and export it as __gnat_last_chance_handler.
>> This is linked into the program in preference to the default last
>> chance handler provided by GNAT.
>> This procedure is called if nothing catches a raised exception.
>> Including those raised during package elaboration.
>
> Doing
>
> Ada.Task_Termination.Set_Specific_Handler
> (T => Ada.Task_Identification.Environment_Task,
> Handler => Last_Chance'access);
>
> should do the same thing more portably. It will be called when the
> environment task terminates for any reason; you would only want it to
> actually do something when Cause = Unhandled_Exception.
Looks good, but to catch all elaboration-time exceptions (in other
packages) the package that executes that call, in its own elaboration
code, must be elaborated before all other packages. Do you have some
easy way to ensure that, without inserting elaboration pragmas in all
other packages?
I had a similar elaboration problem some time ago in an embedded
application, where I wanted to set up some HW error-trap handlers that I
would like to be active also during elaboration, but I found no easy way
to ensure that the trap-handling package would be elaborated before all
other packages.
--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
^ permalink raw reply [relevance 0%]
* Re: Last chance handler on a PC
@ 2020-01-30 20:02 5% ` Jeffrey R. Carter
2020-01-30 20:26 0% ` Niklas Holsti
0 siblings, 1 reply; 23+ results
From: Jeffrey R. Carter @ 2020-01-30 20:02 UTC (permalink / raw)
On 1/30/20 8:35 PM, ahlan@marriott.org wrote:
>
> To catch unhandled exceptions you only need to write a simple procedure and export it as __gnat_last_chance_handler.
> This is linked into the program in preference to the default last chance handler provided by GNAT.
> This procedure is called if nothing catches a raised exception.
> Including those raised during package elaboration.
Doing
Ada.Task_Termination.Set_Specific_Handler
(T => Ada.Task_Identification.Environment_Task,
Handler => Last_Chance'access);
should do the same thing more portably. It will be called when the environment
task terminates for any reason; you would only want it to actually do something
when Cause = Unhandled_Exception.
--
Jeff Carter
"My mind is a raging torrent, flooded with rivulets of
thought, cascading into a waterfall of creative alternatives."
Blazing Saddles
89
^ permalink raw reply [relevance 5%]
* timer_server triggers Task_Termination handler
@ 2016-04-21 10:23 7% Per Dalgas Jakobsen
0 siblings, 0 replies; 23+ results
From: Per Dalgas Jakobsen @ 2016-04-21 10:23 UTC (permalink / raw)
Is it correct behaviour when tasks internal to the GNAT run-time causes
users task_termination handlers to be called?
This behaviour is seen on:
1) Debian Linux: gnat-5 (Ada 2005, Ada 2012).
2) AIX: GNAT Pro 6.1.0w (Ada 2005).
A simple demonstration of the issue:
--------------------------------------------------------------------------------
with Ada.Text_IO;
with Log_Unhandled_Exceptions;
procedure Timer_Server_Noise is
begin
Ada.Text_IO.Put_Line ("Start of main");
select
delay 0.5;
then abort
loop
delay 0.1;
end loop;
end select;
Ada.Text_IO.Put_Line ("End of main");
end Timer_Server_Noise;
--------------------------------------------------------------------------------
with Ada.Exceptions;
with Ada.Task_Identification;
with Ada.Task_Termination;
package Log_Unhandled_Exceptions is
pragma Elaborate_Body;
use Ada.Task_Identification;
use Ada.Task_Termination;
use Ada.Exceptions;
--
protected Last_Wishes is
procedure Log_Any_Exit (Cause : in Cause_Of_Termination;
T : in Task_Id;
E : in Exception_Occurrence);
end;
end Log_Unhandled_Exceptions;
--------------------------------------------------------------------------------
with Ada.Text_IO;
package body Log_Unhandled_Exceptions is
-- Encapsulates the actual log call
procedure Log (Text : in String) is
begin
Ada.Text_IO.Put_Line ("Log_Unhandled_Exceptions >> " &
Text);
end Log;
--
protected body Last_Wishes is
procedure Log_Any_Exit (Cause : in Cause_Of_Termination;
T : in Task_Id;
E : in Exception_Occurrence) is
begin
case Cause is
when Normal =>
Log ("Normal exit of task: " & Image (T));
when Abnormal =>
Log ("Abnormal exit of task: " & Image (T));
when Unhandled_Exception =>
Log ("Unhandled exception in task: " & Image (T));
end case;
end Log_Any_Exit;
end Last_Wishes;
begin
if Current_Task_Fallback_Handler = null then
Set_Dependents_Fallback_Handler (Last_Wishes.Log_Any_Exit'Access);
else
Log ("Fallback handler already set, will not set own handler.");
end if;
if Specific_Handler (Current_Task) = null then
Set_Specific_Handler (Current_Task, Last_Wishes.Log_Any_Exit'Access);
else
Log ("Specific handler already set, will not set own handler.");
end if;
end Log_Unhandled_Exceptions;
--------------------------------------------------------------------------------
~Per
^ permalink raw reply [relevance 7%]
* Re: Tasking troubles, unexpected termination.
2012-10-31 1:05 5% ` Anh Vo
@ 2012-10-31 2:17 6% ` Shark8
0 siblings, 0 replies; 23+ results
From: Shark8 @ 2012-10-31 2:17 UTC (permalink / raw)
Here's the updated code:
---- Scheduling.adb ------------------------------------------
with
Ada.Text_IO,
Ada.Calendar,
Ada.Containers.Indefinite_Vectors,
Ada.Task_Termination,
Ada.Task_Identification,
Task_Debugging;
Procedure Scheduling is
-- Introduce shorthand so convert Strings to access strings.
Function "+" (Item : String) Return Not Null Access String is
( New String'(Item) );
-- Forward declare the Notification type; indicate it has discriminants.
Type Notification(<>);
-- Declare Handle for Notifications.
Type Notification_Handle is Not Null Access Notification;
Type Notification( Message : Not Null Access String;
Expiry : Not Null Access Ada.Calendar.Time
) is null record;
-- Declare the Timing-task.
Task Type Timing ( Resolution : Not Null Access Duration ) is
Entry Add( Event : Notification_Handle );
end Timing;
-- Implementation for the timing-task.
Task body Timing is
-- Package for showing Duration.
Package Decimal_Display is new Ada.Text_IO.Fixed_IO( Duration );
-- Internal package, defining Vectors holding notification handles.
Package Notification_Vector is New Ada.Containers.Indefinite_Vectors
( Index_Type => Positive, Element_Type => Notification_Handle );
Use Notification_Vector;
-- Handle expired messages.
Procedure Handle_Expiration( List : in out Vector ) is
Use Ada.Calendar, Ada.Text_IO;
Length : Positive:= Positive(List.Length);
Now : Time:= Clock;
-- We flag everything to be deleted, as tampering with the cursor is
-- not good.
Type Deletion_Flags is Array(1..Length) of Boolean;
Deletion_Marks : Deletion_Flags:= (Others => False);
procedure Execute(Position : Cursor) is
Item : Constant Notification_Handle:= Element(position);
Index : Constant Positive:= Positive( To_Index(position) );
begin
Deletion_Marks(Index):= Now >= Item.Expiry.All;
--
Ada.Text_IO.Put( ASCII.HT & "Exipration: " );
Decimal_Display.Put( Item.Expiry.All - Now, Fore => 2, Aft => 3 );
Ada.Text_IO.New_Line;
end Execute;
begin
-- Iterate through the vector's elements; old-style iterator.
List.Reverse_Iterate( Process => Execute'Access );
-- Delete flagged elements; iteration bckwards to preserve indicies.
For Index in reverse Deletion_Marks'Range loop
if Deletion_Marks(Index) then
Put_Line( "Message: " & List(Index).Message.All);
List.Delete( Index );
end if;
end loop;
-- Render a report on the new length, if it was altered.
declare
Post_op_length : Natural:= Natural(List.Length);
begin
if Length /= post_op_length then
Put_Line( "Deleted items; New Length:" & post_op_length'Img);
end if;
end;
end Handle_Expiration;
-- Declare a Vector to hold all the nofifications.
Notification_List : Vector:= Empty_Vector;
Use Ada.Task_Termination, Task_Debugging, Ada.Containers, Ada.Calendar;
-- Mark the start-time.
Start : Time:= Clock;
-- Function Elapsed Return String is
-- ( Duration'Image(Clock - Start)(1..7) );
Function Elapsed Return Duration is
( Clock - Start );
begin
-- Set our debugging-handler for this task.
Ada.Task_Termination.Set_Specific_Handler(
T => Ada.Task_Identification.Current_Task,
Handler => Debug.Termination'Access );
-- When there are no items in our internal vector, then we need can only
-- accept Add or terminate the task.
-- When we add an item, then we can either add another item or when the
-- time expires iterate the vector and handling Notifications as needed.
loop
select
accept Add( Event : Notification_Handle ) do
Notification_List.Append( Event );
end add;
while not Notification_List.Is_Empty loop
Ada.Text_IO.Put( "Elapsed:" );
Decimal_Display.Put( Elapsed, Fore => 2, Aft => 3 );
Ada.Text_IO.New_Line;
Handle_Expiration( List => Notification_List );
select
accept Add( Event : Notification_Handle ) do
Notification_List.Append( Event );
Ada.Text_IO.Put_Line( "New Length: " & Notification_List.Length'Img );
Ada.Text_IO.Put( ASCII.HT & "Exipration: " );
Decimal_Display.Put( Event.Expiry.All - Clock, Fore => 2, Aft => 3 );
Ada.Text_IO.New_Line;
end add;
or
delay Timing.Resolution.All;
end select;
end loop;
Ada.Text_IO.Put_Line( "EMPTY." );
or
terminate;
end select;
end loop;
end Timing;
K : Timing( Resolution => New Duration'(2.0) ); -- 2 second resolution.
Now : Ada.Calendar.Time:= Ada.Calendar.Clock;
begin
For Index in 1..10 loop
declare
Use Ada.Calendar;
Item : Notification(
Message => + ("DD"&Positive'Image(Index)),
-- Expire at Now and 3*Index seconds.
Expiry => New Time'( Now + Duration(Index) )
);
begin
K.Add( Event => New Notification'(Item) );
end;
end loop;
-- Add an element in the past... it should immediately be operated on.
K.Add( Event => New Notification'(
Message => + ("Last."),
Expiry => New Ada.Calendar.Time'( Now )
)
);
end Scheduling;
---- Task_Debugging.ads ----------------------------------------------
-- The following are not strictly nessacary, but used in this example for
-- debugging purposes.
With
System.Address_To_Access_Conversions,
Ada.Unchecked_Conversion,
Ada.Exceptions.Traceback,
Ada.Task_Identification,
Ada.Task_Termination;
Package Task_Debugging is
Pragma Elaborate_Body;
Protected Type Debugging is
-- Termination debugging function.
procedure Termination(
Cause : Ada.Task_Termination.Cause_Of_Termination;
T : Ada.Task_Identification.Task_Id;
X : Ada.Exceptions.Exception_Occurrence);
End Debugging;
-- Debug, an instance of our debugging object.
Debug : Debugging;
End Task_Debugging;
---- Task_Debugging.adb ----------------------------------------------
With Ada.Text_IO;
Package Body Task_Debugging is
Protected body Debugging is
-- Termination debugging function.
procedure Termination(
Cause : Ada.Task_Termination.Cause_Of_Termination;
T : Ada.Task_Identification.Task_Id;
X : Ada.Exceptions.Exception_Occurrence) is
Use Ada.Text_IO, Ada.Task_Termination, Ada.Exceptions;
begin
Put_Line("Termination: "& Cause'Img);
case Cause is
When Normal | Abnormal => Null;
When Unhandled_Exception =>
Put_Line( Exception_Name(X)&": "&Exception_Message(X) );
end case;
end Termination;
end Debugging;
End Task_Debugging;
---------------------------------------------------------------------------
I've fixed the original problems (first the "tampering", and second a constraint_error) but there's still something strange going on. The discriminants for notifications seem to be being ignored (or rather the latest one being used).
Here's the output:
C:\Programming\Projects\Scheduler>scheduling.exe
Elapsed: 0.000
Exipration: 0.984
New Length: 2
Exipration: 1.983
Elapsed: 0.002
Exipration: 1.982
Exipration: 1.982
New Length: 3
Exipration: 2.981
Elapsed: 0.004
Exipration: 2.980
Exipration: 2.980
Exipration: 2.980
[...]
Message: Last.
Deleted items; New Length: 10
Elapsed: 2.047
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Exipration: 7.938
Elapsed: 4.051
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Exipration: 5.933
Elapsed: 6.061
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Exipration: 3.923
Elapsed: 8.086
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Exipration: 1.898
Elapsed:10.106
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Exipration: -0.122
Message: DD 10
Message: DD 9
Message: DD 8
Message: DD 7
Message: DD 6
Message: DD 5
Message: DD 4
Message: DD 3
Message: DD 2
Message: DD 1
Deleted items; New Length: 0
EMPTY.
Termination: NORMAL
As you can see, instead of each element being different they're all tagged as having the same expiration.
^ permalink raw reply [relevance 6%]
* Re: Tasking troubles, unexpected termination.
2012-10-30 23:01 5% ` Adam Beneschan
@ 2012-10-31 1:05 5% ` Anh Vo
2012-10-31 2:17 6% ` Shark8
0 siblings, 1 reply; 23+ results
From: Anh Vo @ 2012-10-31 1:05 UTC (permalink / raw)
On Tuesday, October 30, 2012 4:01:11 PM UTC-7, Adam Beneschan wrote:
> On Tuesday, October 30, 2012 3:03:45 PM UTC-7, Shark8 wrote: > Also, if there is some tasking-exceptions interaction, how do I force them to display [and/or handle them] instead of falling off a cliff? Have you looked into Ada.Task_Termination? (RM C.7.3) That's just the first thought off the top of my head; I haven't looked at your code closely. -- Adam
Using Ada.Task_Termination to monitor the tasks in this program, I got the following results showing task K terminated due unhandled exception PROGRAM_ERROR.
C:\Ada_2005\gps_gpr\Aspect_Programming\scheduling
New Length: 2
New Length: 3
New Length: 4
New Length: 5
New Length: 6
New Length: 7
New Length: 8
New Length: 9
New Length: 10
Message: DD 10
Task k_003F5838 ran for 0 and 0.000000000 seconds
Task k_003F5838 is terminated due to UNHANDLED_EXCEPTION termination
Exception name: PROGRAM_ERROR
Message: attempt to tamper with cursors (vector is busy)
Task main_task_003F5008 ran for 0 and 0.109375000 seconds
Task main_task_003F5008 is terminated due to NORMAL termination
[2012-10-30 18:00:35] process terminated successfully (elapsed time: 20.60s)
^ permalink raw reply [relevance 5%]
* Re: Tasking troubles, unexpected termination.
@ 2012-10-30 23:01 5% ` Adam Beneschan
2012-10-31 1:05 5% ` Anh Vo
0 siblings, 1 reply; 23+ results
From: Adam Beneschan @ 2012-10-30 23:01 UTC (permalink / raw)
On Tuesday, October 30, 2012 3:03:45 PM UTC-7, Shark8 wrote:
> Also, if there is some tasking-exceptions interaction, how do I force them to display [and/or handle them] instead of falling off a cliff?
Have you looked into Ada.Task_Termination? (RM C.7.3) That's just the first thought off the top of my head; I haven't looked at your code closely.
-- Adam
^ permalink raw reply [relevance 5%]
* Re: Is Text_IO.Put_Line() thread-safe?
@ 2012-06-14 14:35 5% ` Adam Beneschan
0 siblings, 0 replies; 23+ results
From: Adam Beneschan @ 2012-06-14 14:35 UTC (permalink / raw)
On Thursday, June 14, 2012 6:49:45 AM UTC-7, Robert A Duff wrote:
> It's a good idea to put an exception handler at the bottom of every
> task body, and do some debugging output there. Otherwise, an
> exception will cause the task to silently vanish. This is a
> language design flaw.
Starting with Ada 2005, you can also use Ada.Task_Termination.Set_Dependents_Fallback_Handler in your main program. (See RM C.7.3.) Probably you should do both.
-- Adam
^ permalink raw reply [relevance 5%]
* Re: task abortion
2012-01-26 16:47 5% ` Anh Vo
@ 2012-01-31 13:25 0% ` tonyg
0 siblings, 0 replies; 23+ results
From: tonyg @ 2012-01-31 13:25 UTC (permalink / raw)
On Jan 26, 4:47 pm, Anh Vo <anhvofrc...@gmail.com> wrote:
> On Jan 26, 1:12 am, tonyg <tonytheg...@gmail.com> wrote:
>
>
>
>
>
>
>
>
>
> > On Jan 25, 10:43 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
> > wrote:
>
> > > On Wed, 25 Jan 2012 02:11:55 -0800 (PST), tonyg wrote:
> > > > I want to be able to abort a task if it has not responded for a while,
> > > > and then start a new one in its stead.
>
> > > > I have a pointer going to the concerned task.
>
> > > > Each task has a 'stop accept' in its rendezvous which forces it out of
> > > > its loop so that it ends, but if the task has frozen for some reason
> > > > then I want to abort.
>
> > > What are you going to achieve by that?
>
> > > Consider the scenarios:
>
> > > 1. The task crashed, it is already terminated then.
>
> > > 2. The task is looping somewhere:
>
> > > 2.a. What about the resources it owns? Local resources are usually freed
> > > when the task is aborted. But if you have some globally allocated memory,
> > > semaphores etc, they all get lost unless freed by some local controlled
> > > objects, "holders" releasing the resources upon finalization, as they leave
> > > the scope upon task abort. Now:
>
> > > 2.b. What if the task is frozen in an abort-deferred thing? Finalization is
> > > such a thing. Abort-deferred stuff cannot be aborted. Note that system I/O
> > > is most likely non-abortable. I.e. you would not be able to abort
> > > Ada.Text_IO.Get_Line or recv on a socket etc anyway.
>
> > > All in one, aborting tasks doing complex stuff is not likely to work in a
> > > reasonable way. Aborting tasks doing simple stuff is not needed, such tasks
> > > impose no problems anyway. Ergo, it is not likely you would need to abort
> > > any task.
>
> > > If you decided for aborting, you would have to redesign almost everything
> > > and in an extremely careful way in order to make tasks logically abortable.
> > > That means to make continuation possible and meaningful after aborting some
> > > tasks. Now a guess: making the tasks right and thus preventing a need
> > > aborting them, would probably be far less efforts...
>
> > > --
> > > Regards,
> > > Dmitry A. Kazakovhttp://www.dmitry-kazakov.de
>
> > Since I wrote my message I have discovered the package
> > Ada.Task_Identification. I totally agree with your argument on not
> > having them fail in the first place. My strategy should be then not to
> > abort :), I'm going to use that package just to monitor my tasks and
> > see if any freeze for some IO reason. Thanks for the advice.
>
> Just in case your task disappear which I do not expect it will, you
> can use package Ada.Task_Termination to query the reason (Normal,
> Abnormal, Unhandled_Exception) it dies.
>
> Anh Vo
Thabjs Anh for that
^ permalink raw reply [relevance 0%]
* Re: task abortion
@ 2012-01-26 16:47 5% ` Anh Vo
2012-01-31 13:25 0% ` tonyg
0 siblings, 1 reply; 23+ results
From: Anh Vo @ 2012-01-26 16:47 UTC (permalink / raw)
On Jan 26, 1:12 am, tonyg <tonytheg...@gmail.com> wrote:
> On Jan 25, 10:43 am, "Dmitry A. Kazakov" <mail...@dmitry-kazakov.de>
> wrote:
>
>
>
>
>
>
>
>
>
> > On Wed, 25 Jan 2012 02:11:55 -0800 (PST), tonyg wrote:
> > > I want to be able to abort a task if it has not responded for a while,
> > > and then start a new one in its stead.
>
> > > I have a pointer going to the concerned task.
>
> > > Each task has a 'stop accept' in its rendezvous which forces it out of
> > > its loop so that it ends, but if the task has frozen for some reason
> > > then I want to abort.
>
> > What are you going to achieve by that?
>
> > Consider the scenarios:
>
> > 1. The task crashed, it is already terminated then.
>
> > 2. The task is looping somewhere:
>
> > 2.a. What about the resources it owns? Local resources are usually freed
> > when the task is aborted. But if you have some globally allocated memory,
> > semaphores etc, they all get lost unless freed by some local controlled
> > objects, "holders" releasing the resources upon finalization, as they leave
> > the scope upon task abort. Now:
>
> > 2.b. What if the task is frozen in an abort-deferred thing? Finalization is
> > such a thing. Abort-deferred stuff cannot be aborted. Note that system I/O
> > is most likely non-abortable. I.e. you would not be able to abort
> > Ada.Text_IO.Get_Line or recv on a socket etc anyway.
>
> > All in one, aborting tasks doing complex stuff is not likely to work in a
> > reasonable way. Aborting tasks doing simple stuff is not needed, such tasks
> > impose no problems anyway. Ergo, it is not likely you would need to abort
> > any task.
>
> > If you decided for aborting, you would have to redesign almost everything
> > and in an extremely careful way in order to make tasks logically abortable.
> > That means to make continuation possible and meaningful after aborting some
> > tasks. Now a guess: making the tasks right and thus preventing a need
> > aborting them, would probably be far less efforts...
>
> > --
> > Regards,
> > Dmitry A. Kazakovhttp://www.dmitry-kazakov.de
>
> Since I wrote my message I have discovered the package
> Ada.Task_Identification. I totally agree with your argument on not
> having them fail in the first place. My strategy should be then not to
> abort :), I'm going to use that package just to monitor my tasks and
> see if any freeze for some IO reason. Thanks for the advice.
Just in case your task disappear which I do not expect it will, you
can use package Ada.Task_Termination to query the reason (Normal,
Abnormal, Unhandled_Exception) it dies.
Anh Vo
^ permalink raw reply [relevance 5%]
* Re: Class with task destructor
2011-11-30 2:21 6% ` Adam Beneschan
2011-11-30 8:41 0% ` Dmitry A. Kazakov
@ 2011-12-01 1:58 0% ` Rego, P.
1 sibling, 0 replies; 23+ results
From: Rego, P. @ 2011-12-01 1:58 UTC (permalink / raw)
> Ada.Task_Termination (see C.7.3) may be able to help. You could
> define a protected object with a handler that sets a toggle and an
> entry that waits on the toggle, use Set_Specific_Handler to cause the
> handler to be executed when the task terminates, then call the
> protected entry to wait for the toggle. (Maybe someone's already
> written a publicly available package that defines a
> Delay_Until_Task_Termination routine that could be used on
> T_Ptr.Primary'Identity,
Ok. Great! Thanks.
> and with underscores in the name so it doesn't look like a *&#@^#$ Windows API routine... :)
ok :)
^ permalink raw reply [relevance 0%]
* Re: Class with task destructor
2011-11-30 8:41 0% ` Dmitry A. Kazakov
@ 2011-12-01 0:35 0% ` Randy Brukardt
0 siblings, 0 replies; 23+ results
From: Randy Brukardt @ 2011-12-01 0:35 UTC (permalink / raw)
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
news:ey516bovprzb$.1tjszfc528chj$.dlg@40tude.net...
> On Tue, 29 Nov 2011 18:21:51 -0800 (PST), Adam Beneschan wrote:
...
>> I don't know of a good way
>> around this (besides Ada.Task_Termination).
>
> I am using polling for T'Terminated after the rendezvous.
That's the Ada 83 solution, and there really isn't anything better in the
language. Not sure if there even could be (I'm not a top-level expert on
real-time issues, beyond what the Janus/Ada runtime does).
...
> Not a feature, rather a plain language design bug. Unchecked_Deallocation
> shall wait for the object's finalization. Finalization of a task evidently
> includes its termination. So Unchecked_Deallocation must block until
> termination before it frees anything.
The problem here is that finalization and task termination are different
operations according to the language, and task termination generally has to
be done first. This was done, I believe, so that any running task does not
have to worry about having the objects it accesses finalized.
I usually agree that this is wrong, but actually it doesn't matter what
choice is made -- any choice will be wrong a large part of the time. The
current choice, for instance, makes it possible for finalize routines to
access objects that have already been finalized (and allocate objects of
access types that already have been finalized) - a finalize routine often
needs to protect against this if it needs to access anything outside of the
object being finalized.
We had a lot of trouble with this in Claw, particularly because we wanted to
use a global finalizable object to tell the library tasks when to terminate.
But that doesn't work in Ada, because tasks are awaited before any
library-level finalization is done -- and thus the tasks never get their
shutdown entries called. We eventually found a tricky way to use
Ada.Task_Identification to determine when the main subprogram has exited,
and used that to terminate the tasks. (And then we had to get all of the Ada
95 compilers to implement it properly -- that took a couple of years and an
ACATS test.)
Switching the order of task waiting and finalization would have worked in
this case, but it wouldn't work in general as the library tasks would then
be accessing global objects that have been finalized. Which seems worse than
the original problem.
Point is that all of these things are interelated, and it isn't always
possible to make all of them perfect. (Tucker Taft calls this "moving the
bump under the carpet"; you can move it around to different locations, but
you can't get rid of it without tearing out the carpet. That happens a lot
more often in language design than most people think.)
Randy.
^ permalink raw reply [relevance 0%]
* Re: Class with task destructor
2011-11-30 2:21 6% ` Adam Beneschan
@ 2011-11-30 8:41 0% ` Dmitry A. Kazakov
2011-12-01 0:35 0% ` Randy Brukardt
2011-12-01 1:58 0% ` Rego, P.
1 sibling, 1 reply; 23+ results
From: Dmitry A. Kazakov @ 2011-11-30 8:41 UTC (permalink / raw)
On Tue, 29 Nov 2011 18:21:51 -0800 (PST), Adam Beneschan wrote:
> The problem with waiting on the Finish entry of the task, as you
> attempted to do in your next post, is that it may create a race
> condition. After the rendezvous is completed, there still may be some
> delay between the time the task finishes the ACCEPT and the time it
> actually terminates, and that still makes it possible that the caller
> could try to free the task before it has actually terminated (which is
> a bounded error according to the RM). I don't know of a good way
> around this (besides Ada.Task_Termination).
I am using polling for T'Terminated after the rendezvous.
However I have an impression that it should be safe to call
Unchecked_Deallocation in GNAT prematurely.
> Maybe there are some
> missing features in the language,
Not a feature, rather a plain language design bug. Unchecked_Deallocation
shall wait for the object's finalization. Finalization of a task evidently
includes its termination. So Unchecked_Deallocation must block until
termination before it frees anything.
> such as (for instance) a TERMINATE
> statement that can be used inside an ACCEPT statement that causes the
> task to terminate, completes the rendezvous, and guarantees that when
> the calling task is unblocked, the called task will be terminated.
As for missing features, rather than the Task_Termination hack, there
should be some way for the task to communicate to its master upon
completion, e.g. a rendezvous. A handler has a disadvantage of being a
protected procedure.
Furthermore, if the master does not accept such a notification from a
failed slave, the exceptional state should somehow propagate into the
master. I didn't think about the details, but the language design is unsafe
here.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [relevance 0%]
* Re: Class with task destructor
@ 2011-11-30 2:21 6% ` Adam Beneschan
2011-11-30 8:41 0% ` Dmitry A. Kazakov
2011-12-01 1:58 0% ` Rego, P.
0 siblings, 2 replies; 23+ results
From: Adam Beneschan @ 2011-11-30 2:21 UTC (permalink / raw)
On Nov 29, 5:11 pm, "Rego, P." <pvr...@gmail.com> wrote:
> > You ought to wait until the task has terminated before freeing it.
>
> How could I wait for the task termination?
> Could be something like
>
> procedure Destruct (T : access Test_Class) is
> T_Ptr : Test_Class_Ptr := Test_Class_Ptr (T);
> begin
> T.Primary.Finish;
> DelayUntilTaskTermination (T_Ptr.Primary);
> Free (T_Ptr);
> end Destruct;
>
> ? But is there some package with something like it?
Ada.Task_Termination (see C.7.3) may be able to help. You could
define a protected object with a handler that sets a toggle and an
entry that waits on the toggle, use Set_Specific_Handler to cause the
handler to be executed when the task terminates, then call the
protected entry to wait for the toggle. (Maybe someone's already
written a publicly available package that defines a
Delay_Until_Task_Termination routine that could be used on
T_Ptr.Primary'Identity, and with underscores in the name so it doesn't
look like a *&#@^#$ Windows API routine... :) I haven't tried this,
by the way.
The problem with waiting on the Finish entry of the task, as you
attempted to do in your next post, is that it may create a race
condition. After the rendezvous is completed, there still may be some
delay between the time the task finishes the ACCEPT and the time it
actually terminates, and that still makes it possible that the caller
could try to free the task before it has actually terminated (which is
a bounded error according to the RM). I don't know of a good way
around this (besides Ada.Task_Termination). Maybe there are some
missing features in the language, such as (for instance) a TERMINATE
statement that can be used inside an ACCEPT statement that causes the
task to terminate, completes the rendezvous, and guarantees that when
the calling task is unblocked, the called task will be terminated.
-- Adam
^ permalink raw reply [relevance 6%]
* Re: Tail recursion upon task destruction
2009-11-18 10:31 4% ` stefan-lucks
2009-11-18 17:48 5% ` Dmitry A. Kazakov
@ 2009-11-19 9:25 0% ` Egil Høvik
1 sibling, 0 replies; 23+ results
From: Egil Høvik @ 2009-11-19 9:25 UTC (permalink / raw)
On Nov 18, 11:31 am, stefan-lu...@see-the.signature wrote:
> On Wed, 18 Nov 2009, Dmitry A. Kazakov wrote:
> > Now consider a case when the last screw is removed from the device. This
> > is an operation eventually serviced by the device driver. I.e. within
> > the device driver, you see, it was the last screw of the device and *if*
> > there is no other references to the device, it must fall apart. This is
> > a case where you wanted the device to commit suicide. There is nobody
> > else out there to do this. The device is dangling. This is not the only
> > use case, just one possible case.
>
> OK, so you have a task (a device) which notices that it is no longer
> useful. You would like such a task to do some cleanup and to commit
> "suicide". Unfortunately, it can't do the cleanup after the "suicide",
> because it is "dead" then. And it can't cleanup itself before being
> "dead" because it needs its local memory until the very moment of its
> "death".
>
> But couldn't you just use (or maybe abuse) the features from
> Ada.Task_Termination to do perform the cleanup, after the task has died?
> Even if it the "death" is not by suicide (apart from
> "suicide" = regular termination, the options are "murder" = abort and
> "accident" = unhandled exception).
> See <http://www.adaic.org/standards/05rat/html/Rat-5-2.html#I1150>.
>
> --
> ------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------
> Stefan dot Lucks at uni minus weimar dot de
> ------ I love the taste of Cryptanalysis in the morning! ------
This would still be a bounded error.
A task is not terminated until the task body has been finalized RM-9.3
(5),
and since Task_Termination handlers are executed as part of the
finalization
of task bodies RM-C.7.3(14/2), you would violate RM-13.11.2(11) by
deallocating
the task in the handler.
--
~egilhh
^ permalink raw reply [relevance 0%]
* Re: Tail recursion upon task destruction
2009-11-18 10:31 4% ` stefan-lucks
@ 2009-11-18 17:48 5% ` Dmitry A. Kazakov
2009-11-19 9:25 0% ` Egil Høvik
1 sibling, 0 replies; 23+ results
From: Dmitry A. Kazakov @ 2009-11-18 17:48 UTC (permalink / raw)
On Wed, 18 Nov 2009 11:31:59 +0100, stefan-lucks@see-the.signature wrote:
> On Wed, 18 Nov 2009, Dmitry A. Kazakov wrote:
>
>> Now consider a case when the last screw is removed from the device. This
>> is an operation eventually serviced by the device driver. I.e. within
>> the device driver, you see, it was the last screw of the device and *if*
>> there is no other references to the device, it must fall apart. This is
>> a case where you wanted the device to commit suicide. There is nobody
>> else out there to do this. The device is dangling. This is not the only
>> use case, just one possible case.
>
> OK, so you have a task (a device) which notices that it is no longer
> useful. You would like such a task to do some cleanup and to commit
> "suicide". Unfortunately, it can't do the cleanup after the "suicide",
> because it is "dead" then. And it can't cleanup itself before being
> "dead" because it needs its local memory until the very moment of its
> "death".
>
> But couldn't you just use (or maybe abuse) the features from
> Ada.Task_Termination to do perform the cleanup, after the task has died?
> Even if it the "death" is not by suicide (apart from
> "suicide" = regular termination, the options are "murder" = abort and
> "accident" = unhandled exception).
> See <http://www.adaic.org/standards/05rat/html/Rat-5-2.html#I1150>.
Yes, it is an interesting option. One could terminate the task and from the
handler kill the object. The difficulty is that Ada.Task_Termination is not
generic. It is not possible to pass a reference to the object to the
handler.
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
^ permalink raw reply [relevance 5%]
* Re: Tail recursion upon task destruction
@ 2009-11-18 10:31 4% ` stefan-lucks
2009-11-18 17:48 5% ` Dmitry A. Kazakov
2009-11-19 9:25 0% ` Egil Høvik
0 siblings, 2 replies; 23+ results
From: stefan-lucks @ 2009-11-18 10:31 UTC (permalink / raw)
On Wed, 18 Nov 2009, Dmitry A. Kazakov wrote:
> Now consider a case when the last screw is removed from the device. This
> is an operation eventually serviced by the device driver. I.e. within
> the device driver, you see, it was the last screw of the device and *if*
> there is no other references to the device, it must fall apart. This is
> a case where you wanted the device to commit suicide. There is nobody
> else out there to do this. The device is dangling. This is not the only
> use case, just one possible case.
OK, so you have a task (a device) which notices that it is no longer
useful. You would like such a task to do some cleanup and to commit
"suicide". Unfortunately, it can't do the cleanup after the "suicide",
because it is "dead" then. And it can't cleanup itself before being
"dead" because it needs its local memory until the very moment of its
"death".
But couldn't you just use (or maybe abuse) the features from
Ada.Task_Termination to do perform the cleanup, after the task has died?
Even if it the "death" is not by suicide (apart from
"suicide" = regular termination, the options are "murder" = abort and
"accident" = unhandled exception).
See <http://www.adaic.org/standards/05rat/html/Rat-5-2.html#I1150>.
--
------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------
Stefan dot Lucks at uni minus weimar dot de
------ I love the taste of Cryptanalysis in the morning! ------
^ permalink raw reply [relevance 4%]
* Re: task model
@ 2009-10-16 14:57 5% ` Brad Moore
0 siblings, 0 replies; 23+ results
From: Brad Moore @ 2009-10-16 14:57 UTC (permalink / raw)
Georg Bauhaus wrote:
> 4. add an exception handler to your tasks.
> Have it print a message, for example, and
> then re-raise the exception. Otherwise tasks
> may fail in silence and you never learn what
> has happened.
Or use the Ada 2005 package, Ada.Task_Termination
which allows you to install a handler that
gets called when tasks terminate.
^ permalink raw reply [relevance 5%]
* Re: [Ravenscar] run tasks on events
2008-05-30 15:24 5% ` Alex R. Mosteo
@ 2008-05-30 15:35 5% ` Ed Falis
0 siblings, 0 replies; 23+ results
From: Ed Falis @ 2008-05-30 15:35 UTC (permalink / raw)
On Fri, 30 May 2008 11:24:22 -0400, Alex R. Mosteo
<devnull@mailinator.com> wrote:
> Look also at Ada.Task_Termination, although I don't know if it mix with
> ravenscar.
I may be mistaken, but I beleive the Ravenscar profile prohibits any units
depending on Ada.Task_Termination.
I would also recommend Burns and Wellings "Concurrent and Real Time
Programming in Ada". It is extremely comprehensive.
^ permalink raw reply [relevance 5%]
* Re: [Ravenscar] run tasks on events
@ 2008-05-30 15:24 5% ` Alex R. Mosteo
2008-05-30 15:35 5% ` Ed Falis
0 siblings, 1 reply; 23+ results
From: Alex R. Mosteo @ 2008-05-30 15:24 UTC (permalink / raw)
Sebastian Hanigk wrote:
> Niklas Holsti <niklas.holsti@tidorum.invalid> writes:
>
> Hello!
>
>> Since the Ravenscar profile contains the restriction
>> No_Task_Termination, by LRM D.7.15(1/2) it is implementation defined
>> what happens when a Ravenscar task terminates due to an unhandled
>> exception or for other reasons. It may be reported or signalled in
>> some way, or not.
>
> I'm using GNAT 4.0.2 on Solaris 10 (AMD); it seems that my second test
> task has silently died.
Look also at Ada.Task_Termination, although I don't know if it mix with
ravenscar.
^ permalink raw reply [relevance 5%]
* Re: [Ravenscar] run tasks on events
@ 2008-05-30 11:59 6% ` stefan-lucks
1 sibling, 0 replies; 23+ results
From: stefan-lucks @ 2008-05-30 11:59 UTC (permalink / raw)
On Fri, 30 May 2008, Niklas Holsti wrote:
> In unrestricted Ada, if an exception occurs in a task, and the task does not
> handle the exception, the task by default terminates silently when the
> exception propagates out of the task. If you want to know about exceptions in
> a task, you should put a last-chance, catch-all exception handler in the task
> body ("exception when others => ...").
You don't need this in Ada 05 any more. You can define some callback
procedures, to do something in the case of unhandled exceptions (and
normal and abnormal termination as well, if you want to).
The following is similar to an example in Barnes' book, except that the
usage avoids race conditions:
--------------------------------------------------------------------------
Package defined in
<http://www.adaic.org/standards/05rm/html/RM-C-7-3.html>:
with Ada.Task_Identification; with Ada.Exceptions;
package Ada.Task_Termination is
...
type Cause_Of_Termination is (Normal, Abnormal, Unhandled_Exception);
type Termination_Handler is access protected procedure
(Cause : in Cause_Of_Termination;
T : in Ada.Task_Identification.Task_Id;
X : in Ada.Exceptions.Exception_Occurrence);
procedure Set_Dependents_Fallback_Handler(Handler: in Termination_Handler);
...
end Ada.Task_Termination;
--------------------------------------------------------------------------
Specification of Callback:
protected End_Of is
procedure World(C: Cause_Of_Termination;
T: Task_ID;
X: Exception_Occurrence);
end End_Of;
--------------------------------------------------------------------------
Implementation of Callback:
procedure World(C: Cause_Of_Termination;
T: Task_ID;
X: Exception_Occurrence);
begin
case C is
when Normal =>
null;
when Abnormal =>
Put_Log("abnormal termination of Task" & Image(T));
when Unhandled_Exception =>
Put_Log("unhandled exception in Task" & Image(T));
Put_Log(Exception_Information(X));
end case;
end World;
--------------------------------------------------------------------------
Usage of callback (typically in main program / environment task):
begin
Set_Dependents_Fallback_Handler(End_Of.World);
declare -- any task you want
A_Bunch_Of_Tasks: array (0 .. 1023) of Some_Task_Type;
Alice, Bob: Some_Other_Task_Type;
begin
...
-- Whenever any of our tasks terminates normally or abnormally or
-- propagates an exception, End_Of.World is called.
end;
end;
So long
Stefan
--
------ Stefan Lucks -- Bauhaus-University Weimar -- Germany ------
Stefan dot Lucks at uni minus weimar dot de
------ I love the taste of Cryptanalysis in the morning! ------
^ permalink raw reply [relevance 6%]
* Re: contracted exceptions
2007-06-09 0:19 0% ` Randy Brukardt
@ 2007-06-09 18:04 4% ` Robert A Duff
0 siblings, 0 replies; 23+ results
From: Robert A Duff @ 2007-06-09 18:04 UTC (permalink / raw)
"Randy Brukardt" <randy@rrsoftware.com> writes:
> "Pascal Obry" <pascal@obry.net> wrote in message
> news:4669BBBB.8040806@obry.net...
>> Stefan Lucks a �crit :
>> > If a task fails to handle an exception raised somewhere inside, the task
>> > silently dies -- without notifying anyone. Enforcing the subprograms
>> > used by the task to precisely specify "this subprogram might raise these
>> > exceptions and none else" would help a lot ...
>>
>> That's really different. And think about it, since a task is mapped to a
>> thread which comes with its own stack... who could possibly handle this
>> exception ?
>
> And more importantly, where would it be raised? If it was raised in the
> parent task, that could happen anywhere, which would be a design nightmare
> (a different nightmare than the current one, anyway).
I can think of lots of rules that are not perfect, but are at least
superior to the existing rule of silently ignoring the exception.
The simplest would be to terminate the entire program immediately.
And print an error message on systems where that makes sense.
Ada has no way to terminate the whole program (i.e. call "exit"),
but it should.
Or just print an error message.
Or raise Program_Error in the parent task at the point where that task
awaits its dependents. Option: abort all the siblings as well.
Or put the task to sleep, so the parent waits forever (and the
programmer has to debug a "deadlock").
>> And note that in Ada 2005 you can use Ada.Task_Termination to register
>> termination notifications.
>
> It's unfortunate that the default isn't to notify someone, however.
Right. Ada.Task_Termination is another way, and the default should be
to raise alarms of some sort.
>...Worse,
> this interface might not work in out-of-memory circumstances (one of the
> most common reasons for task failure, at least in Janus/Ada programs where
> the default stack size is rather small).
Right, out-of-memory is a difficult issue. I don't know of any language
that solves it well. You might think you could do:
task body T is
begin
declare
...
begin
...
exception
when others =>
...
end;
end T;
and carefully prove that the handler code cannot raise any exceptions
itself. But you can't prove the absense of Storage_Error in Ada.
My solution: allow the programmer to declare that certain regions of
code cannot run out of memory. The compiler must reserve enough memory
(and if can't, then raise S_E before entering that region). Of course,
what you can do in such a region is implementation dependent.
This would make life difficult for compilers that generate C,
or any other target language that doesn't have sufficient control
over memory use. :-(
- Bob
^ permalink raw reply [relevance 4%]
* Re: contracted exceptions
2007-06-08 20:27 5% ` Pascal Obry
@ 2007-06-09 0:19 0% ` Randy Brukardt
2007-06-09 18:04 4% ` Robert A Duff
0 siblings, 1 reply; 23+ results
From: Randy Brukardt @ 2007-06-09 0:19 UTC (permalink / raw)
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 1157 bytes --]
"Pascal Obry" <pascal@obry.net> wrote in message
news:4669BBBB.8040806@obry.net...
> Stefan Lucks a �crit :
> > If a task fails to handle an exception raised somewhere inside, the task
> > silently dies -- without notifying anyone. Enforcing the subprograms
> > used by the task to precisely specify "this subprogram might raise these
> > exceptions and none else" would help a lot ...
>
> That's really different. And think about it, since a task is mapped to a
> thread which comes with its own stack... who could possibly handle this
> exception ?
And more importantly, where would it be raised? If it was raised in the
parent task, that could happen anywhere, which would be a design nightmare
(a different nightmare than the current one, anyway).
> And note that in Ada 2005 you can use Ada.Task_Termination to register
> termination notifications.
It's unfortunate that the default isn't to notify someone, however. Worse,
this interface might not work in out-of-memory circumstances (one of the
most common reasons for task failure, at least in Janus/Ada programs where
the default stack size is rather small).
Randy.
^ permalink raw reply [relevance 0%]
* Re: contracted exceptions
@ 2007-06-08 20:27 5% ` Pascal Obry
2007-06-09 0:19 0% ` Randy Brukardt
0 siblings, 1 reply; 23+ results
From: Pascal Obry @ 2007-06-08 20:27 UTC (permalink / raw)
To: Stefan Lucks
Stefan Lucks a �crit :
> If a task fails to handle an exception raised somewhere inside, the task
> silently dies -- without notifying anyone. Enforcing the subprograms
> used by the task to precisely specify "this subprogram might raise these
> exceptions and none else" would help a lot ...
That's really different. And think about it, since a task is mapped to a
thread which comes with its own stack... who could possibly handle this
exception ?
And note that in Ada 2005 you can use Ada.Task_Termination to register
termination notifications.
Pascal.
--
--|------------------------------------------------------
--| Pascal Obry Team-Ada Member
--| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE
--|------------------------------------------------------
--| http://www.obry.net
--| "The best way to travel is by means of imagination"
--|
--| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595
^ permalink raw reply [relevance 5%]
Results 1-23 of 23 | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2007-06-06 21:33 Reconsidering assignment Maciej Sobczak
2007-06-07 9:27 ` Dmitry A. Kazakov
2007-06-07 16:54 ` contracted exceptions (was Re: Reconsidering assignment) Ray Blaak
2007-06-08 12:10 ` contracted exceptions Robert A Duff
2007-06-08 15:56 ` Stefan Lucks
2007-06-08 20:27 5% ` Pascal Obry
2007-06-09 0:19 0% ` Randy Brukardt
2007-06-09 18:04 4% ` Robert A Duff
2008-05-28 19:14 [Ravenscar] run tasks on events Sebastian Hanigk
2008-05-30 7:26 ` Niklas Holsti
2008-05-30 8:57 ` Sebastian Hanigk
2008-05-30 10:47 ` Niklas Holsti
2008-05-30 11:17 ` Sebastian Hanigk
2008-05-30 15:24 5% ` Alex R. Mosteo
2008-05-30 15:35 5% ` Ed Falis
2008-05-30 11:59 6% ` stefan-lucks
2009-10-15 17:43 task model Mike
2009-10-15 18:00 ` Ludovic Brenta
2009-10-15 19:15 ` Anh Vo
2009-10-15 19:54 ` Dmitry A. Kazakov
2009-10-16 14:06 ` Georg Bauhaus
2009-10-16 14:57 5% ` Brad Moore
2009-11-17 10:17 Tail recursion upon task destruction Dmitry A. Kazakov
2009-11-17 21:38 ` Randy Brukardt
2009-11-18 8:41 ` Dmitry A. Kazakov
2009-11-18 10:31 4% ` stefan-lucks
2009-11-18 17:48 5% ` Dmitry A. Kazakov
2009-11-19 9:25 0% ` Egil Høvik
2011-11-23 1:50 Class with task destructor Rego, P.
2011-11-23 8:35 ` Dmitry A. Kazakov
2011-11-23 9:05 ` Simon Wright
2011-11-30 1:11 ` Rego, P.
2011-11-30 2:21 6% ` Adam Beneschan
2011-11-30 8:41 0% ` Dmitry A. Kazakov
2011-12-01 0:35 0% ` Randy Brukardt
2011-12-01 1:58 0% ` Rego, P.
2012-01-25 10:11 task abortion tonyg
2012-01-25 10:43 ` Dmitry A. Kazakov
2012-01-26 9:12 ` tonyg
2012-01-26 16:47 5% ` Anh Vo
2012-01-31 13:25 0% ` tonyg
2012-06-14 12:53 Is Text_IO.Put_Line() thread-safe? awdorrin
2012-06-14 13:49 ` Robert A Duff
2012-06-14 14:35 5% ` Adam Beneschan
2012-10-30 22:03 Tasking troubles, unexpected termination Shark8
2012-10-30 23:01 5% ` Adam Beneschan
2012-10-31 1:05 5% ` Anh Vo
2012-10-31 2:17 6% ` Shark8
2016-04-21 10:23 7% timer_server triggers Task_Termination handler Per Dalgas Jakobsen
2020-01-30 8:55 Last chance handler on a PC ahlan
2020-01-30 19:35 ` ahlan
2020-01-30 20:02 5% ` Jeffrey R. Carter
2020-01-30 20:26 0% ` Niklas Holsti
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox