* Termination of periodic tasks @ 2014-06-15 10:10 Natasha Kerensikova 2014-06-15 12:11 ` Dmitry A. Kazakov ` (2 more replies) 0 siblings, 3 replies; 25+ messages in thread From: Natasha Kerensikova @ 2014-06-15 10:10 UTC (permalink / raw) Hello, I'm still struggling with the termination of tasks hidden in the private part or the body of libraries, this time with a periodic task. Basically, I'm looking for a way to have some subprogram called at roughly regular intervals. Ada.Real_Time.Timing_Events would be functionally what I needed, except it's accuracy is vastly more than I need, so it's overhead is mostly wasted. For the uses I currently have in mind, the periodic subprogram would be a kind of GC for slowly moving resources, called every 15 min to every day, with a time tolerance exceeding minutes. While in GNARL Ada.Real_Time.Timing_Events is implemented using a task awaken every 100 ms. And same as before, the task should be able to terminate quickly when the program terminates. I would be perfectly happy with something like: task body Periodic_Task is loop select delay 86_400.0; or terminate; end select; Periodic_Subprogram; end loop; end task; Except that's not available in Ada. The "or terminate" part is only available when waiting for an entry, but this only move the problem to the periodic calling of the entry. A timed select to wait for an entry that exists the loop would be fine too, but how can I detect program termination to call the entry? Could something be worked with a user-defined Finalize? I looked at how GNARL is doing it, but it's using an RL-specific primitive to move the task one level above the main master, so that it's aborted instead of waited-for on program termination. I'm a bit reluctant to use a RL-internal primitive... That leaves Ada.Real_Time.Timing_Events and its comparatively large overhead, even though (I think) I can afford it, it's not very satisfying. How would you do it? Thanks in advance for your help, Natasha ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-15 10:10 Termination of periodic tasks Natasha Kerensikova @ 2014-06-15 12:11 ` Dmitry A. Kazakov 2014-06-15 15:23 ` J-P. Rosen 2014-06-15 16:54 ` Jeffrey Carter 2 siblings, 0 replies; 25+ messages in thread From: Dmitry A. Kazakov @ 2014-06-15 12:11 UTC (permalink / raw) On Sun, 15 Jun 2014 10:10:20 +0000 (UTC), Natasha Kerensikova wrote: [...] > How would you do it? You have an exit event (protected object) fired from the Finalize of the object containing a pointer to the task (task component would not work). The task does a timed entry call for the event. From the delay alternative it calls the worker subprogram. When the entry gets accepted the task exits. After Finalize fires the event. It awaits task termination (doing tight polling of T'Terminated) and then calls to Unchecked_Deallocate of the task pointer. Typically for this active object pattern the task would have a class-wide access discriminant of the object. The worker subprogram would be a primitive operation of the object. So the call from the task body were dispatching. When the type is derived from and Finalize gets overridden, the task shall be stopped *before* doing the object's finalization. This is the rationale for the design of task-components awaited before entering Finalize. Without an active terminate alternative that does not work. And to have a terminate alternative is close to impossible in any realistic design. So, the task components must be pointers. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-15 10:10 Termination of periodic tasks Natasha Kerensikova 2014-06-15 12:11 ` Dmitry A. Kazakov @ 2014-06-15 15:23 ` J-P. Rosen 2014-06-16 13:54 ` Natasha Kerensikova 2014-06-15 16:54 ` Jeffrey Carter 2 siblings, 1 reply; 25+ messages in thread From: J-P. Rosen @ 2014-06-15 15:23 UTC (permalink / raw) Le 15/06/2014 12:10, Natasha Kerensikova a écrit : > And same as before, the task should be able to terminate quickly when > the program terminates. Why not have a "stop" entry called by the main program when it terminates (possibly through an exported subprogram if you don't want to have the task public)? -- J-P. Rosen Adalog 2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00 http://www.adalog.fr ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-15 15:23 ` J-P. Rosen @ 2014-06-16 13:54 ` Natasha Kerensikova 2014-06-17 20:14 ` Charles H. Sampson 0 siblings, 1 reply; 25+ messages in thread From: Natasha Kerensikova @ 2014-06-16 13:54 UTC (permalink / raw) Hello, On 2014-06-15, J-P. Rosen <rosen@adalog.fr> wrote: > Le 15/06/2014 12:10, Natasha Kerensikova a écrit : >> And same as before, the task should be able to terminate quickly when >> the program terminates. > Why not have a "stop" entry called by the main program when it > terminates (possibly through an exported subprogram if you don't want to > have the task public)? Mostly because I believe this to be too heavy for a burden for the client, and somewhat of an abstraction leak. As I wrote, the situations in which I felt the need are internal resource collectors in some libraries. Since it's purely internal stuff, it's of no business to the client, and the implementation should be changeable at any time. Moreover, in that situation, if the client forgets the required "Finalize" or "On_Exit" call, the program can't stop at runtime, and that feels like a harsh and late effect for such a common human error. Finally, should the practice become wide spread, this means imposing on the client a maintenance burden linear in the number of library used. This isn't much of a problem in the current state of Ada, with a small niche of mostly-low-complexity programs and comparatively few libraries, but would like my libraries to survive a sudden (widely unexpected) surge of Ada popularity. Now none of these are technical reasons, and I would implement such a solution if there is no "better" choice. These are only feelings coming from internalization of best practices learned the hard way in huge and complex software development environments. Thanks of your help, Natasha ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-16 13:54 ` Natasha Kerensikova @ 2014-06-17 20:14 ` Charles H. Sampson 2014-06-18 7:32 ` Dmitry A. Kazakov 0 siblings, 1 reply; 25+ messages in thread From: Charles H. Sampson @ 2014-06-17 20:14 UTC (permalink / raw) Natasha Kerensikova <lithiumcat@instinctive.eu> wrote: > Hello, > > On 2014-06-15, J-P. Rosen <rosen@adalog.fr> wrote: > > Le 15/06/2014 12:10, Natasha Kerensikova a écrit : > >> And same as before, the task should be able to terminate quickly when > >> the program terminates. > > Why not have a "stop" entry called by the main program when it > > terminates (possibly through an exported subprogram if you don't want to > > have the task public)? > > Mostly because I believe this to be too heavy for a burden for the > client, and somewhat of an abstraction leak. I'm having trouble understanding how this is too heavy. In most programs I've written, there's a possibility of stopping. Usually the need to stop is detected by or, most commonly, propagated to the main program. The main program then signals all of its library packages to do whatever is necessary for stopping. An exported Stop subprogram seems a quite natural way to do that. I've even used implementations that have exported Stop subprograms in all library packages, some of them null. That enforces the abstractions in that the main program doesn't need to know which library packages need to be wrapped up. Charlie -- Nobody in this country got rich on his own. You built a factory--good. But you moved your goods on roads we all paid for. You hired workers we all paid to educate. So keep a big hunk of the money from your factory. But take a hunk and pay it forward. Elizabeth Warren (paraphrased) ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 20:14 ` Charles H. Sampson @ 2014-06-18 7:32 ` Dmitry A. Kazakov 0 siblings, 0 replies; 25+ messages in thread From: Dmitry A. Kazakov @ 2014-06-18 7:32 UTC (permalink / raw) On Tue, 17 Jun 2014 13:14:23 -0700, Charles H. Sampson wrote: > Natasha Kerensikova <lithiumcat@instinctive.eu> wrote: > >> Hello, >> >> On 2014-06-15, J-P. Rosen <rosen@adalog.fr> wrote: >>> Le 15/06/2014 12:10, Natasha Kerensikova a écrit : >>>> And same as before, the task should be able to terminate quickly when >>>> the program terminates. >>> Why not have a "stop" entry called by the main program when it >>> terminates (possibly through an exported subprogram if you don't want to >>> have the task public)? >> >> Mostly because I believe this to be too heavy for a burden for the >> client, and somewhat of an abstraction leak. > > I'm having trouble understanding how this is too heavy. In most > programs I've written, there's a possibility of stopping. Usually the > need to stop is detected by or, most commonly, propagated to the main > program. The main program then signals all of its library packages to do > whatever is necessary for stopping. An exported Stop subprogram seems a > quite natural way to do that. That is true. I am writing a lot of code with tasking and was unaware of the issue with library level tasks. > I've even used implementations that have exported Stop subprograms > in all library packages, some of them null. That enforces the > abstractions in that the main program doesn't need to know which library > packages need to be wrapped up. I prefer a stateless design of packages with explicit objects maintaining the state, created by the client. That eliminates the problem of task termination too. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-15 10:10 Termination of periodic tasks Natasha Kerensikova 2014-06-15 12:11 ` Dmitry A. Kazakov 2014-06-15 15:23 ` J-P. Rosen @ 2014-06-15 16:54 ` Jeffrey Carter 2014-06-16 14:02 ` Natasha Kerensikova 2 siblings, 1 reply; 25+ messages in thread From: Jeffrey Carter @ 2014-06-15 16:54 UTC (permalink / raw) On 06/15/2014 03:10 AM, Natasha Kerensikova wrote: > > A timed select to wait for an entry that exists the loop would be fine > too, but how can I detect program termination to call the entry? > Could something be worked with a user-defined Finalize? ARM 7.6.1 says, "For the finalization of a master, dependent tasks are first awaited, as explained in 9.3. Then each object ... is finalized if the object was successfully initialized and still exists." So the task has to terminate before Finalize is called. -- Jeff Carter "I spun around, and there I was, face to face with a six-year-old kid. Well, I just threw my guns down and walked away. Little bastard shot me in the ass." Blazing Saddles 40 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-15 16:54 ` Jeffrey Carter @ 2014-06-16 14:02 ` Natasha Kerensikova 2014-06-16 15:08 ` Dmitry A. Kazakov 2014-06-16 17:08 ` Jeffrey Carter 0 siblings, 2 replies; 25+ messages in thread From: Natasha Kerensikova @ 2014-06-16 14:02 UTC (permalink / raw) Hello, On 2014-06-15, Jeffrey Carter <spam.jrcarter.not@spam.not.acm.org> wrote: > On 06/15/2014 03:10 AM, Natasha Kerensikova wrote: >> >> A timed select to wait for an entry that exists the loop would be fine >> too, but how can I detect program termination to call the entry? >> Could something be worked with a user-defined Finalize? > > ARM 7.6.1 says, "For the finalization of a master, dependent tasks are first > awaited, as explained in 9.3. Then each object ... is finalized if the object > was successfully initialized and still exists." > > So the task has to terminate before Finalize is called. That seems at odds with the solution proposed by Dmitry, isn't it? Considering the following skeleton spec, package To_Be_Discussed is task type Typed_Task is entry Terminate; end Typed_Task; type Task_Access is access Typed_Task; task Singleton_Task is entry Terminate; end Singleton_Task; type Watcher is new Ada.Finalization.Limited_Controlled with record Signal_Target : Task_Access; end record; overriding procedure Finalize (Object : in out Watcher); end To_Be_Discussed; As far as I can tell, Singleton_Task and task(s) created using an allocator for Task_Access have the same master. I understood Dimtry's solution as calling Terminate entry from Watcher.Finalize. However what you quote seems to indicate that Watcher.Finalize won't be called until Singleton_Task and any allocated task in a Task_Access are termninated by themselves. So I'm still lost on how to solve my problem, though I still have to experiment with ideas like the specification above. Thanks for your help, Natasha ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-16 14:02 ` Natasha Kerensikova @ 2014-06-16 15:08 ` Dmitry A. Kazakov 2014-06-16 17:08 ` Jeffrey Carter 1 sibling, 0 replies; 25+ messages in thread From: Dmitry A. Kazakov @ 2014-06-16 15:08 UTC (permalink / raw) On Mon, 16 Jun 2014 14:02:47 +0000 (UTC), Natasha Kerensikova wrote: > On 2014-06-15, Jeffrey Carter <spam.jrcarter.not@spam.not.acm.org> wrote: >> On 06/15/2014 03:10 AM, Natasha Kerensikova wrote: >>> >>> A timed select to wait for an entry that exists the loop would be fine >>> too, but how can I detect program termination to call the entry? >>> Could something be worked with a user-defined Finalize? >> >> ARM 7.6.1 says, "For the finalization of a master, dependent tasks are first >> awaited, as explained in 9.3. Then each object ... is finalized if the object >> was successfully initialized and still exists." >> >> So the task has to terminate before Finalize is called. > > That seems at odds with the solution proposed by Dmitry, isn't it? > > Considering the following skeleton spec, > > package To_Be_Discussed is > > task type Typed_Task is > entry Terminate; > end Typed_Task; > > type Task_Access is access Typed_Task; > > task Singleton_Task is > entry Terminate; > end Singleton_Task; > > type Watcher is new Ada.Finalization.Limited_Controlled with record > Signal_Target : Task_Access; > end record; > > overriding procedure Finalize (Object : in out Watcher); > > end To_Be_Discussed; > > As far as I can tell, Singleton_Task and task(s) created using an > allocator for Task_Access have the same master. > > I understood Dimtry's solution as calling Terminate entry from > Watcher.Finalize. > > However what you quote seems to indicate that Watcher.Finalize won't be > called until Singleton_Task and any allocated task in a Task_Access are > termninated by themselves. It will be called. The master here is not the instance of Watcher. Watcher would happen to be the master only if Signal_Target were its component of the Typed_Task. Since you are using rather an access to Typed_Task, everything is OK. When using the solution with an entry the difference to signalling an event is that you have to catch Tasking_Error if the task ended prematurely. Another difference is that when you have a pool of tasks, they may share the same exiting event. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-16 14:02 ` Natasha Kerensikova 2014-06-16 15:08 ` Dmitry A. Kazakov @ 2014-06-16 17:08 ` Jeffrey Carter 2014-06-17 6:57 ` Natasha Kerensikova 1 sibling, 1 reply; 25+ messages in thread From: Jeffrey Carter @ 2014-06-16 17:08 UTC (permalink / raw) On 06/16/2014 07:02 AM, Natasha Kerensikova wrote: > > package To_Be_Discussed is > > task type Typed_Task is > entry Terminate; > end Typed_Task; > > type Task_Access is access Typed_Task; > > task Singleton_Task is > entry Terminate; > end Singleton_Task; > > type Watcher is new Ada.Finalization.Limited_Controlled with record > Signal_Target : Task_Access; > end record; > > overriding procedure Finalize (Object : in out Watcher); > > end To_Be_Discussed; ARM 9.3 says, "If the task is created by the evaluation of an allocator for a given access type, it depends on each master that includes the elaboration of the declaration of the ultimate ancestor of the given access type." The master of a task designated by a Task_Access is the environment task, assuming To_Be_Discussed to be a library unit. Meanwhile, finalization of an object of type Watcher will happen when you exit the scope of whatever unit declares the object. Those will usually be different if the object is declared outside of To_Be_Discussed. I'm not clear about the case where the object is declared inside To_Be_Discussed. -- Jeff Carter "Hello! Smelly English K...niggets." Monty Python & the Holy Grail 08 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-16 17:08 ` Jeffrey Carter @ 2014-06-17 6:57 ` Natasha Kerensikova 2014-06-17 7:37 ` Dmitry A. Kazakov ` (2 more replies) 0 siblings, 3 replies; 25+ messages in thread From: Natasha Kerensikova @ 2014-06-17 6:57 UTC (permalink / raw) Hello, On 2014-06-16, Jeffrey Carter <spam.jrcarter.not@spam.not.acm.org> wrote: > ARM 9.3 says, "If the task is created by the evaluation of an allocator for a > given access type, it depends on each master that includes the elaboration of > the declaration of the ultimate ancestor of the given access type." > > The master of a task designated by a Task_Access is the environment task, > assuming To_Be_Discussed to be a library unit. Meanwhile, finalization of an > object of type Watcher will happen when you exit the scope of whatever unit > declares the object. Those will usually be different if the object is declared > outside of To_Be_Discussed. I'm not clear about the case where the object is > declared inside To_Be_Discussed. So let's consider instead real code that actually (more or less) works: https://github.com/faelys/natools/blob/trunk/src/natools-cron.ads https://github.com/faelys/natools/blob/trunk/src/natools-cron.adb (I whipped it together yesterday after thinking about Dmitry's suggestion, but it's still only a rough draft, there are issues to iron out before using it for real, like handling map key collisions. However all comments are still welcome.) Global_Worker here is either null or an access to a Worker created by an allocator. Since as far as I can tell Natools.Cron is library-level, the master of the worker task is the environment task. In this implementation, the task terminates by reaching the end of its body whenever the protected map becomes empty. Storage is released only right before requested again when the protected become non-empty again, so at program termination the task memory is "leaked", but program termination is supposed to reclaim everything anyway. The finalization of a Cron_Entry removes the corresponding element from the protected map, so the worker task terminates after all Cron_Entry are finalized (or Reset to an empty state). This leaves the problem of Cron_Entry objects declared in library level packages: with Natools.Cron; package Test_Periodic is type Periodic_Action is new Natools.Cron.Callback with null record; overriding procedure Run (Self : in out Periodic_Action); Global_Entry : Natools.Cron.Cron_Entry; end Test_Periodic; with Ada.Text_IO; package body Test_Periodic is overriding procedure Run (Self : in out Periodic_Action) is begin Ada.Text_IO.Put_Line ("Hello"); end Run; begin Global_Entry.Set (1.0, Periodic_Actoin'(null record)); end Test_Periodic; Now the master of Global_Entry is also the environment task. So Global_Entry will not be finalized before the worker task terminates, but by design the worker task will not terminate before Global_Entry is finalized or Reset. So unless at some point Global_Entry.Reset is called, this package will cause the program to never terminate. Is there a way to work around such a situation? Or should I just document this as a caveat (among many others, as you can see at the beginning of the spec)? Thanks for your help, Natasha ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 6:57 ` Natasha Kerensikova @ 2014-06-17 7:37 ` Dmitry A. Kazakov 2014-06-17 7:47 ` Natasha Kerensikova 2014-06-17 12:02 ` Jacob Sparre Andersen 2014-06-17 17:53 ` Jeffrey Carter 2 siblings, 1 reply; 25+ messages in thread From: Dmitry A. Kazakov @ 2014-06-17 7:37 UTC (permalink / raw) On Tue, 17 Jun 2014 06:57:38 +0000 (UTC), Natasha Kerensikova wrote: [...] > Is there a way to work around such a situation? The solution is always same: wrap a task pointer in a controlled object. Manage the task from the object's Initialize and Finalize. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 7:37 ` Dmitry A. Kazakov @ 2014-06-17 7:47 ` Natasha Kerensikova 2014-06-17 8:45 ` Dmitry A. Kazakov 0 siblings, 1 reply; 25+ messages in thread From: Natasha Kerensikova @ 2014-06-17 7:47 UTC (permalink / raw) Hello, On 2014-06-17, Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote: > On Tue, 17 Jun 2014 06:57:38 +0000 (UTC), Natasha Kerensikova wrote: > > [...] >> Is there a way to work around such a situation? > > The solution is always same: wrap a task pointer in a controlled object. > Manage the task from the object's Initialize and Finalize. > But if I change Global_Worker from an access-to-task to a controlled object, its master will still be the environment task, so Finalize wouldn't be called before termination of the worker task. Or am I missing something? Is there a special construct that yield an earlier call to Finalize than Cron_Entry? Would you happen to have a working example? Thanks for your help, Natasha ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 7:47 ` Natasha Kerensikova @ 2014-06-17 8:45 ` Dmitry A. Kazakov 2014-06-17 9:00 ` Natasha Kerensikova 0 siblings, 1 reply; 25+ messages in thread From: Dmitry A. Kazakov @ 2014-06-17 8:45 UTC (permalink / raw) On Tue, 17 Jun 2014 07:47:49 +0000 (UTC), Natasha Kerensikova wrote: > Hello, > > On 2014-06-17, Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote: >> On Tue, 17 Jun 2014 06:57:38 +0000 (UTC), Natasha Kerensikova wrote: >> >> [...] >>> Is there a way to work around such a situation? >> >> The solution is always same: wrap a task pointer in a controlled object. >> Manage the task from the object's Initialize and Finalize. >> > But if I change Global_Worker from an access-to-task to a controlled > object, its master will still be the environment task, so Finalize > wouldn't be called before termination of the worker task. > > Or am I missing something? You should put it in a separate package to ensure different scopes of the controlled object and the access-to-task type. > Is there a special construct that yield an > earlier call to Finalize than Cron_Entry? Would you happen to have a > working example? with Ada.Finalization; package Workers is type Manager is new Ada.Finalization.Limited_Controlled with private; private task type Worker is entry Live; entry Die; end Worker; type Worker_Ptr is access Worker; type Manager is new Ada.Finalization.Limited_Controlled with record Staff : Worker_Ptr; end record; overriding procedure Initialize (Handler : in out Manager); overriding procedure Finalize (Handler : in out Manager); end Workers; -------------------------------------------------------------- with Ada.Unchecked_Deallocation; with Ada.Text_IO; use Ada.Text_IO; package body Workers is task body Worker is begin accept Live; Put_Line ("I am born!"); loop select accept Die; exit; else delay 1.0; Put_Line ("I am alive!"); end select; end loop; Put_Line ("You killed me!"); end Worker; procedure Initialize (Handler : in out Manager) is begin Handler.Staff := new Worker; Handler.Staff.Live; end Initialize; procedure Finalize (Handler : in out Manager) is procedure Free is new Ada.Unchecked_Deallocation (Worker, Worker_Ptr); begin if Handler.Staff /= null then Handler.Staff.Die; while not Handler.Staff'Terminated loop delay 0.01; end loop; Free (Handler.Staff); end if; end Finalize; end Workers; ------------------------------------------------------- with Ada.Text_IO; use Ada.Text_IO; with Workers; use Workers; procedure Test is X : Manager; begin delay 5.0; Put_Line ("Good bye!"); end Test; ----------------------------------------------------- X is finalized before Workers (and Worker_Ptr), so it does not wait for all instances to terminate. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 8:45 ` Dmitry A. Kazakov @ 2014-06-17 9:00 ` Natasha Kerensikova 2014-06-17 12:55 ` Dmitry A. Kazakov 0 siblings, 1 reply; 25+ messages in thread From: Natasha Kerensikova @ 2014-06-17 9:00 UTC (permalink / raw) Hello, On 2014-06-17, Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote: > [...] > ------------------------------------------------------- > with Ada.Text_IO; use Ada.Text_IO; > with Workers; use Workers; > > procedure Test is > X : Manager; > begin > delay 5.0; > Put_Line ("Good bye!"); > end Test; > ----------------------------------------------------- > > X is finalized before Workers (and Worker_Ptr), so it does not wait for all > instances to terminate. As far as I can tell from my experimentations, this only works because Test here is a procedure. The code I linked on github a few posts ago also works fine that situation, since procedure-wide Cron_Entry objects are finalized, and when the last one is finalized the tasks terminates. The problem I'm trying to work around is when your X here is declare in a package rather than a procedure: with Workers; use Workers; package Test_Library is procedure Hello; X : Manager; end Test_Library; with Ada.Text_IO; use Ada.Text_IO; package body Test_Library is procedure Hello is begin Put_Line ("Library procedure called"); end Hello; end Test_Library; with Ada.Text_IO; use Ada.Text_IO; with Test_Library; use Test_Library; procedure Test_Main is begin Hello; delay 5.0; Put_Line ("Good bye!"); end Test_Main; Now there you get the catch-22 situation I'm hoping to solve (or failing that, document). Thanks for your help, Natasha ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 9:00 ` Natasha Kerensikova @ 2014-06-17 12:55 ` Dmitry A. Kazakov 2014-06-17 14:51 ` J-P. Rosen 0 siblings, 1 reply; 25+ messages in thread From: Dmitry A. Kazakov @ 2014-06-17 12:55 UTC (permalink / raw) On Tue, 17 Jun 2014 09:00:47 +0000 (UTC), Natasha Kerensikova wrote: > On 2014-06-17, Dmitry A. Kazakov <mailbox@dmitry-kazakov.de> wrote: >> [...] >> ------------------------------------------------------- >> with Ada.Text_IO; use Ada.Text_IO; >> with Workers; use Workers; >> >> procedure Test is >> X : Manager; >> begin >> delay 5.0; >> Put_Line ("Good bye!"); >> end Test; >> ----------------------------------------------------- >> >> X is finalized before Workers (and Worker_Ptr), so it does not wait for all >> instances to terminate. > > As far as I can tell from my experimentations, this only works because > Test here is a procedure. The code I linked on github a few posts ago > also works fine that situation, since procedure-wide Cron_Entry objects > are finalized, and when the last one is finalized the tasks terminates. > > The problem I'm trying to work around is when your X here is declare in > a package rather than a procedure: > > with Workers; use Workers; > package Test_Library is > procedure Hello; > > X : Manager; > end Test_Library; OK. Moving it to the library level would make X and the task sharing the same master. In which case any Finalize to be called after completion of all dependent tasks. When X is in a procedure then its master is the procedure and things work. If you want a library level task, then the controlling object to shoot it down must be at least one level deeper. I thought about a schema when a dedicated library-level task could trigger killing other library tasks: task body Killer is X : Event; -- Want its Finialize called upon completion begin select accept Dummy; -- No matter or terminate; end select; end Killer; and then from the Event's Finalize you could kill all other tasks pending. I am not a language lawyer and cannot tell if this schema must work or not. The RM is silent about the order in which tasks are awaited. If the tasks shall be able to complete in any possible order this must be considered illegal. [Which I would say is a bit over the top a requirement.] If there shall exist at least one order in which they can complete, it could be legal. But let's ask our language lawyers... -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 12:55 ` Dmitry A. Kazakov @ 2014-06-17 14:51 ` J-P. Rosen 2014-06-17 16:44 ` Dmitry A. Kazakov 0 siblings, 1 reply; 25+ messages in thread From: J-P. Rosen @ 2014-06-17 14:51 UTC (permalink / raw) Le 17/06/2014 14:55, Dmitry A. Kazakov a écrit : > I am not a language lawyer and cannot tell if this schema must work or not. It won't > The RM is silent about the order in which tasks are awaited. If the tasks > shall be able to complete in any possible order this must be considered > illegal. [Which I would say is a bit over the top a requirement.] If there > shall exist at least one order in which they can complete, it could be > legal. But let's ask our language lawyers... There is no order, because all tasks terminate together (9.3 (6..9)). From the task you consider, you go to its master; if all tasks that depend on that master are terminatable, then the whole bunch terminates together. Therefore, adding a task cannot make tasks terminate if they could not terminate without the extra task. -- J-P. Rosen Adalog 2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00 http://www.adalog.fr ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 14:51 ` J-P. Rosen @ 2014-06-17 16:44 ` Dmitry A. Kazakov 2014-06-17 20:00 ` Randy Brukardt 0 siblings, 1 reply; 25+ messages in thread From: Dmitry A. Kazakov @ 2014-06-17 16:44 UTC (permalink / raw) On Tue, 17 Jun 2014 16:51:07 +0200, J-P. Rosen wrote: > Le 17/06/2014 14:55, Dmitry A. Kazakov a écrit : >> I am not a language lawyer and cannot tell if this schema must work or not. > It won't > >> The RM is silent about the order in which tasks are awaited. If the tasks >> shall be able to complete in any possible order this must be considered >> illegal. [Which I would say is a bit over the top a requirement.] If there >> shall exist at least one order in which they can complete, it could be >> legal. But let's ask our language lawyers... > There is no order, because all tasks terminate together (9.3 (6..9)). > > From the task you consider, you go to its master; if all tasks that > depend on that master are terminatable, then the whole bunch terminates > together. Therefore, adding a task cannot make tasks terminate if they > could not terminate without the extra task. Thank you for clarification. Thus per design there is no way to make a non-trivial library-level task to complete without means outside the library level. Either some OS-specific stuff or an explicit action/declaration in a nested scope is required. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 16:44 ` Dmitry A. Kazakov @ 2014-06-17 20:00 ` Randy Brukardt 2014-06-17 20:16 ` Jeffrey Carter 2014-06-17 21:30 ` Simon Wright 0 siblings, 2 replies; 25+ messages in thread From: Randy Brukardt @ 2014-06-17 20:00 UTC (permalink / raw) "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:k4bw956nq1hk.1fbqgpp5xu777$.dlg@40tude.net... ... > Thus per design there is no way to make a non-trivial library-level task > to > complete without means outside the library level. Either some OS-specific > stuff or an explicit action/declaration in a nested scope is required. Actually, there is a way, at least if you want to ensure that the program cleans itself up properly. We invented it for Claw, and I put it into the ACATS so it's pretty certain that all compilers support it. The trick is to use Ada.Task_Identification to find out whether the environment task is trying to exit. if not Is_Callable(Environment_Task) then return; -- Exit this task. end if; Is_Callable will only be False for the environment task if the main subprogram has exited and we're waiting for library-level tasks to complete. In that case, we want to kill off this task. (Note: Not all Ada 95 compilers did this at the time, some always returned true from it no matter what. But that would fail ACATS test CXC7004 in modern compilers, so it's unlikely that many get this wrong. One might want to look at that ACATS test for a complete example of the method.) It can be clunky to get this into the task somewhere; it works best if the task is actively polling (as the message loop task in Claw is always doing). Note: function Environment_Task was added to the package in Ada 2012. For earlier Ada, one needs to have the elaboration of the package containing the task squirrel away the task id: Environment_Task_Id : constant Task_Id := Current_Task; Randy. ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 20:00 ` Randy Brukardt @ 2014-06-17 20:16 ` Jeffrey Carter 2014-06-17 21:30 ` Simon Wright 1 sibling, 0 replies; 25+ messages in thread From: Jeffrey Carter @ 2014-06-17 20:16 UTC (permalink / raw) On 06/17/2014 01:00 PM, Randy Brukardt wrote: > > Actually, there is a way, at least if you want to ensure that the program > cleans itself up properly. We invented it for Claw, and I put it into the > ACATS so it's pretty certain that all compilers support it. > > The trick is to use Ada.Task_Identification to find out whether the > environment task is trying to exit. > > if not Is_Callable(Environment_Task) then > return; -- Exit this task. > end if; That's good to know. I'll try to add it to my bag of tricks (though of course not needing any tricks is better). -- Jeff Carter "Whatever it is, I'm against it." Horse Feathers 46 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 20:00 ` Randy Brukardt 2014-06-17 20:16 ` Jeffrey Carter @ 2014-06-17 21:30 ` Simon Wright 1 sibling, 0 replies; 25+ messages in thread From: Simon Wright @ 2014-06-17 21:30 UTC (permalink / raw) "Randy Brukardt" <randy@rrsoftware.com> writes: > But that would fail ACATS test CXC7004 in modern compilers, so it's > unlikely that many get this wrong. Interestingly, FSF GCC is stuck on ACATS 2.4 or thereabouts, and has no CXC tests at all. One hopes that AdaCore are more up-to-date in-house. Perhaps getting current/next ACATS into the GCC testsuite would be a worthwhile exercise for me ... ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 6:57 ` Natasha Kerensikova 2014-06-17 7:37 ` Dmitry A. Kazakov @ 2014-06-17 12:02 ` Jacob Sparre Andersen 2014-06-17 19:32 ` Natasha Kerensikova 2014-06-17 17:53 ` Jeffrey Carter 2 siblings, 1 reply; 25+ messages in thread From: Jacob Sparre Andersen @ 2014-06-17 12:02 UTC (permalink / raw) Natasha Kerensikova wrote: > So let's consider instead real code that actually (more or less) > works: > https://github.com/faelys/natools/blob/trunk/src/natools-cron.ads > https://github.com/faelys/natools/blob/trunk/src/natools-cron.adb (I > whipped it together yesterday after thinking about Dmitry's > suggestion, but it's still only a rough draft, there are issues to > iron out before using it for real, like handling map key > collisions. However all comments are still welcome.) I've posted a pull request which eliminates all explicit memory management from the package. :-) ... But not solved your actual problem. :-( Greetings, Jacob -- "They that can give up essential liberty to obtain a little temporary safety deserve neither liberty nor safety." -- Benjamin Franklin ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 12:02 ` Jacob Sparre Andersen @ 2014-06-17 19:32 ` Natasha Kerensikova 0 siblings, 0 replies; 25+ messages in thread From: Natasha Kerensikova @ 2014-06-17 19:32 UTC (permalink / raw) On 2014-06-17, Jacob Sparre Andersen <jacob@jacob-sparre.dk> wrote: > Natasha Kerensikova wrote: > >> So let's consider instead real code that actually (more or less) >> works: >> https://github.com/faelys/natools/blob/trunk/src/natools-cron.ads >> https://github.com/faelys/natools/blob/trunk/src/natools-cron.adb (I >> whipped it together yesterday after thinking about Dmitry's >> suggestion, but it's still only a rough draft, there are issues to >> iron out before using it for real, like handling map key >> collisions. However all comments are still welcome.) > > I've posted a pull request which eliminates all explicit memory > management from the package. :-) I was hesitating between posting my comments here or on the pull request. I decided to post them here so that others can offer their views and their explanations on the points I raise. If that's not the best choice, please tell me so and I won't do it again. > ... But not solved your actual problem. :-( Basically that's my main concern: you turned a partial solution (that works fine as long as global objects are explicitly reset) into a complete non-solution (that never terminates). I'm afraid I'll have to reject the pull request for this reasons. But still, your version intrigues me. What go to such great lengths only to eliminate memory management? Maybe I'm too scarred by having forged myself into a C programmer that can code for years without committing any resource leak, but I have trouble to see what is so wrong with memory management to want so much to eliminate it. I understand that the less resource management the better is a good rule of thumb, just like the less code the better. But isn't it a relatively mild benefit to balance against all costs? The core idea around which the package has been built, is to have a worker task that terminate whenever the job map is empty, hoping it would happen at program termination. And since it can also happen temporarily during the program execution, there needs a way to "unterminate" it when the job map is no longer empty. Since as far as I know there is no way to restart a terminated task, reusing the task object. So the only solution is to deallocate the terminate task object and allocate a new one, ready to start. My understanding of "explicit memory allocation" is stuff that involves "new" keyword and Ada.Unchecked_Deallocation instances, and the task-restart trick is the only explicit memory allocation I see in the original Natools.Cron. Getting rid of it means either an interminable static task like your proposal, or hiding the allocation/deallocation behind a container or some other wrapper, that seem of little value in that situation. However, your proposal also eliminates the uses of Ada.Finalization and of Natools.References. Even though I don't consider them as explicit memory management, it seems you do. To me, Natools.Reference is as much of an abstraction as Indefinite_Ordered_Maps, so I don't see any explictness being remove through the use of the latter instead of the former. And Ada.Finalization is about any resource management, and I would say less for memory than for other resources. Anyway, it feels like an extremely mild mechanism, I can't see why one would want to avoid it. Moreover, by removing Natools.References, you no longer have reference semantics, so you cannot move references from one part of the code to another, for example from the map inside the protected object to the task. I would guess that's why you moved the callback execution from the task body to a procedure in the protected object. However, this has serious drawbacks: * First, it becomes a bounded error to perform any potentially blocking operation, for example Text_IO.Put_Line which I used in my little example to test the package while developing it. * Second, it locks the database while running the callback, so the callback cannot change its own period or unregister itself from the service. Also, not having references means copying the object around, which you also do (but could have avoided using Update_Element), so you can no longer store any mutable internal state in the callback object. My little test example had a counter in it, and that is no longer possible. Lastly, you conflated the callback interface with the job identification, but probably realized that there can still be several copies of the same objects, so you had to rely on a new Event_ID type. However the Event_ID are ever-increasing values of Positive, without any overflow management. One could probably hope that no sane implementation-defined limit would be reached in this package, but even though it's a fair assumption I would rather not assume it if that has no cost. Or is it only to solve the key collision issue? In that case I had something else in mind (I will probably commit it tomorrow, no that I have committed a test case for the issue). So in summary, you did indeed eliminate explicit and not-so-explicit-to-me memory management, but at the cost of: - breaking the partial solution to the problem considered, - forbidding potentially blocking operations in the callbacks, - forbidding callback management from within callbacks, - making it harder to write callbacks that inherit from Ada.Finalization.* (e.g. because they hold resources like files) or anything else, - introducing software aging with an implementation-defined threshold. Is it really worth it? Is there a lesson here I'm failing to grasp? Thanks for your comments, Natasha ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 6:57 ` Natasha Kerensikova 2014-06-17 7:37 ` Dmitry A. Kazakov 2014-06-17 12:02 ` Jacob Sparre Andersen @ 2014-06-17 17:53 ` Jeffrey Carter 2014-06-17 20:03 ` Randy Brukardt 2 siblings, 1 reply; 25+ messages in thread From: Jeffrey Carter @ 2014-06-17 17:53 UTC (permalink / raw) On 06/16/2014 11:57 PM, Natasha Kerensikova wrote: > > Now the master of Global_Entry is also the environment task. So > Global_Entry will not be finalized before the worker task terminates, > but by design the worker task will not terminate before Global_Entry is > finalized or Reset. > > So unless at some point Global_Entry.Reset is called, this package will > cause the program to never terminate. > > Is there a way to work around such a situation? Or should I just > document this as a caveat (among many others, as you can see at the > beginning of the spec)? AFAICT, there is no way for this situation to terminate without outside intervention. -- Jeff Carter "Saving keystrokes is the job of the text editor, not the programming language." Preben Randhol 64 ^ permalink raw reply [flat|nested] 25+ messages in thread
* Re: Termination of periodic tasks 2014-06-17 17:53 ` Jeffrey Carter @ 2014-06-17 20:03 ` Randy Brukardt 0 siblings, 0 replies; 25+ messages in thread From: Randy Brukardt @ 2014-06-17 20:03 UTC (permalink / raw) "Jeffrey Carter" <spam.jrcarter.not@spam.not.acm.org> wrote in message news:lnpvb7$oat$3@dont-email.me... > On 06/16/2014 11:57 PM, Natasha Kerensikova wrote: >> >> Now the master of Global_Entry is also the environment task. So >> Global_Entry will not be finalized before the worker task terminates, >> but by design the worker task will not terminate before Global_Entry is >> finalized or Reset. >> >> So unless at some point Global_Entry.Reset is called, this package will >> cause the program to never terminate. >> >> Is there a way to work around such a situation? Or should I just >> document this as a caveat (among many others, as you can see at the >> beginning of the spec)? > > AFAICT, there is no way for this situation to terminate without outside > intervention. That's certainly true for this code as written, but there is a way to write the code so it will terminate. See my other message. Note that you need both methods if you want to allow the objects to be declared anywhere: finalization for objects in nested scopes, and the Is_Callable trick for those declared at library-level. Randy. ^ permalink raw reply [flat|nested] 25+ messages in thread
end of thread, other threads:[~2014-06-18 7:32 UTC | newest] Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2014-06-15 10:10 Termination of periodic tasks Natasha Kerensikova 2014-06-15 12:11 ` Dmitry A. Kazakov 2014-06-15 15:23 ` J-P. Rosen 2014-06-16 13:54 ` Natasha Kerensikova 2014-06-17 20:14 ` Charles H. Sampson 2014-06-18 7:32 ` Dmitry A. Kazakov 2014-06-15 16:54 ` Jeffrey Carter 2014-06-16 14:02 ` Natasha Kerensikova 2014-06-16 15:08 ` Dmitry A. Kazakov 2014-06-16 17:08 ` Jeffrey Carter 2014-06-17 6:57 ` Natasha Kerensikova 2014-06-17 7:37 ` Dmitry A. Kazakov 2014-06-17 7:47 ` Natasha Kerensikova 2014-06-17 8:45 ` Dmitry A. Kazakov 2014-06-17 9:00 ` Natasha Kerensikova 2014-06-17 12:55 ` Dmitry A. Kazakov 2014-06-17 14:51 ` J-P. Rosen 2014-06-17 16:44 ` Dmitry A. Kazakov 2014-06-17 20:00 ` Randy Brukardt 2014-06-17 20:16 ` Jeffrey Carter 2014-06-17 21:30 ` Simon Wright 2014-06-17 12:02 ` Jacob Sparre Andersen 2014-06-17 19:32 ` Natasha Kerensikova 2014-06-17 17:53 ` Jeffrey Carter 2014-06-17 20:03 ` Randy Brukardt
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox