From: Jean-Pierre Rosen on 20 Nov 2009 05:58 Dmitry A. Kazakov a �crit : > Sometimes I which Ada had "halt at once" > statement which would stop all tasks and hold any I/O. > Aborting the main task comes quite close to that wish, except that controlled objects are finalized. But OTOH, you have these people who want to be absolutely sure that objects get finalized under any circumstance... -- --------------------------------------------------------- J-P. Rosen (rosen(a)adalog.fr) Visit Adalog's web site at http://www.adalog.fr
From: Randy Brukardt on 21 Nov 2009 01:02 "Dmitry A. Kazakov" <mailbox(a)dmitry-kazakov.de> wrote in message news:18wh86jvjvoe0.cofxcc8udm6q$.dlg(a)40tude.net... > On Thu, 19 Nov 2009 17:54:40 -0600, Randy Brukardt wrote: >> "Dmitry A. Kazakov" <mailbox(a)dmitry-kazakov.de> wrote in message >> news:1iipp3bn16fe2.yqa1gz1ru17a$.dlg(a)40tude.net... .... >> That might be true in general, but definitely not in this case: a handler >> for Program_Error is *always* wrong (unless it is a global handler for >> *any* >> exception), as Program_Error always represents a program bug. So I don't >> see >> how "existing" handlers can be confused. > > exception > when Error : others => > some cleanup -- This usually becomes a problem > raise; If that's the case, "some cleanup" is seriously flawed. An "others" handler has to always work no matter what condition come into it, and in particular cannot depend on the code/data it surrounds to be in any particular state. The clean up code needs to be aware that data structures may be corrupt; if it isn't it of course will cascade errors and generally make a mess -- but in that case it is just junk code doing more harm than good. I believe this particular pattern ("others" handler containing explicit cleanup) usually represents a bad design. Clients (or even the implementers of ADTs) often forget to include the cleanup where it is needed. These days, I try to put all needed cleanup into the ADTs, so that they get cleaned up no matter what -- finalization is pretty much the only certainty in Ada. And in that case, you don't need these kind of exception handlers. >> It is necessary to treat all Adjust and Finalize routines like task >> bodies >> in that they need a "when others" handler -- otherwise the exceptions are >> sucked up and you get completely lost. Our programming standard requires >> such handlers (generally, they output Exception_Information to the >> logging >> facility - and almost every significant Ada system needs some sort of >> logging facility). > > Yes, but this does not help. Upon exception propagation > (Constraint_Error), > you get some objects finalized. This in turn causes a snowball of > exceptions in finalized objects, because no design is robust to hold any > error at any place. In the end you have a huge log of meaningless > messages, > which only complicate debugging. That can happen, but it usually indicates too much coupling between objects and/or too little concern about the implications of errors. Finalize routines should never, ever propagate an exception, and need to be written to avoid that - that means avoiding assumptions about other objects. If that's not done, of course you get messy cascades of errors. I realize that it isn't completely possible to avoid these things, but it can be minimized. And the huge log of messages doesn't matter, you only need the first one (presuming every "others" handler logs the data). That's the place the exception was raised, and the information tells you how it got to that point; just ignore the rest of the log (in that sense it is just like fixing syntax errors in most compilers -- the correction isn't good enough to find more than the first error if it is at all significant). > Sometimes I which Ada had "halt at once" > statement which would stop all tasks and hold any I/O. Janus/Ada does have such a thing (always did, it was the first thing implemented on CP/M back in 1980). It works because (to date) we don't use operating systems facilities for tasking, exception handling, or finalization. But it is pretty dangerous, and really can be used only to instantly shut down a misbehaving system -- but of course if there is any real resource management being handled by finalization, you could leak resources such that you don't get them back easily (the obvious example is temporary files, which won't be cleaned up in that scenario). > But of course the proper solution would be contracted exceptions. I don't see how that would help. The problem is used "others" when you really need to list the exceptions that you are expecting. If the compiler is smart enough to be able to prove that no bounded errors occur (and no constraint violations either), maybe that would do some good, but I doubt that you will see such compilers anytime soon. In the absence of that, you have to put Program_Error, Constraint_Error, and Storage_Error into every contract, so you don't gain much. >>> The difference is that for string bound there is a way to do it safe and >>> for 'Access there is none (and I also agree with Robert's response.) >> >> Well, Ada 2012 (or whatever it will be called) should help that out by >> giving you a way to compare accessibilites (via memberships). So at least >> you will be able to make checks to avoid the problem. Better than >> nothing, >> but still not ideal. The better way to avoid the problem is to never, >> ever >> use anonymous access types. (Named access types have checks that are >> always >> made at compile-time for most compilers -- but not Janus/Ada, because of >> generic sharing.) > > The problem is not bound to only anonymous types. It also appears when you > convert access types. The source type (or both) might be a formal generic > parameter, so you cannot statically ensure that a "local" pointer is > converted no a "less local" one. Pool specific pointers need to be > converted though one target object is derived from another. Here > everything > is broken: generic contract, meaningless conversion, meaningless check, > meaningless exception. In this case, whether a check will fail is known statically for any compiler that generates generics via macro substitution (that is all Ada compilers other than Janus/Ada). I can hardly imagine that there exists a compiler that will generate "raise Program_Error" unconditionally without generating a warning! So this is an academic concern at best (I don't think you're using Janus/Ada) -- yes, you can ignore the warnings and run such a program, but there isn't any reason to actually do so unless you're running an ACATS test. (And if you do the conversion in the generic specification, the compiler is required to reject the program.) Randy.
From: Dmitry A. Kazakov on 21 Nov 2009 08:07 On Sat, 21 Nov 2009 00:02:59 -0600, Randy Brukardt wrote: > "Dmitry A. Kazakov" <mailbox(a)dmitry-kazakov.de> wrote in message > news:18wh86jvjvoe0.cofxcc8udm6q$.dlg(a)40tude.net... >> On Thu, 19 Nov 2009 17:54:40 -0600, Randy Brukardt wrote: >>> "Dmitry A. Kazakov" <mailbox(a)dmitry-kazakov.de> wrote in message >>> news:1iipp3bn16fe2.yqa1gz1ru17a$.dlg(a)40tude.net... > ... >>> That might be true in general, but definitely not in this case: a handler >>> for Program_Error is *always* wrong (unless it is a global handler for *any* >>> exception), as Program_Error always represents a program bug. So I don't >>> see how "existing" handlers can be confused. >> >> exception >> when Error : others => >> some cleanup -- This usually becomes a problem >> raise; > > If that's the case, "some cleanup" is seriously flawed. An "others" handler > has to always work no matter what condition come into it, and in particular > cannot depend on the code/data it surrounds to be in any particular state. > The clean up code needs to be aware that data structures may be corrupt; if > it isn't it of course will cascade errors and generally make a mess -- but > in that case it is just junk code doing more harm than good. Right, "others" stands for all "legal" exceptions propagating out of the body, which the programmer was unable to specify, because it is too much work to do. The problem is that "illegal" exceptions indicating broken program do not belong here, and because of the lack of contracts you cannot separate them. The pattern is wrong, but there is no other. > I believe this particular pattern ("others" handler containing explicit > cleanup) usually represents a bad design. Clients (or even the implementers > of ADTs) often forget to include the cleanup where it is needed. These days, > I try to put all needed cleanup into the ADTs, so that they get cleaned up > no matter what -- finalization is pretty much the only certainty in Ada. And > in that case, you don't need these kind of exception handlers. I agree, but that only moves cleanup from one place to another (Finalize). There is no way to get rid of it. Within Finalize cleanup has even more chances to get perplexed, because in Ada you cannot know whether Finalize was called upon normal leaving the scope due to an exception propagation. Further the Ada's construction model is not enough fine to fully accommodate this pattern. For accessibility checks it could also aggravate the problem because accessibility is not invariant to moving from one body to another. The same code might be OK or not OK within different bodies. > And the huge log of messages doesn't matter, you only need the first one > (presuming every "others" handler logs the data). No it will be the last one (it is a stack of errors which gets unwound), but most likely it will be consumed by something else, like Program_Error. That is a reason for the "wrong" pattern with "others". Yes it is bad, but it lets you to log Exception_Information *before* things explode. >> But of course the proper solution would be contracted exceptions. > > I don't see how that would help. The problem is used "others" when you > really need to list the exceptions that you are expecting. If the compiler > is smart enough to be able to prove that no bounded errors occur (and no > constraint violations either), maybe that would do some good, but I doubt > that you will see such compilers anytime soon. In the absence of that, you > have to put Program_Error, Constraint_Error, and Storage_Error into every > contract, so you don't gain much. It should be more intelligent than Java. For example, "I don't do Storage_Error if there is N (static value) free bytes of stack", "I don't raise Program_Error from Initialize/Finalize" etc. An important issue to me is exception contract bound to some precondition, which ensures absence of specified exceptions. When an exception happens, it is not range error, or accessibility check to blame, but the precondition violated. >>>> The difference is that for string bound there is a way to do it safe and >>>> for 'Access there is none (and I also agree with Robert's response.) >>> >>> Well, Ada 2012 (or whatever it will be called) should help that out by >>> giving you a way to compare accessibilites (via memberships). So at least >>> you will be able to make checks to avoid the problem. Better than nothing, >>> but still not ideal. The better way to avoid the problem is to never, ever >>> use anonymous access types. (Named access types have checks that are always >>> made at compile-time for most compilers -- but not Janus/Ada, because of >>> generic sharing.) >> >> The problem is not bound to only anonymous types. It also appears when you >> convert access types. The source type (or both) might be a formal generic >> parameter, so you cannot statically ensure that a "local" pointer is >> converted no a "less local" one. Pool specific pointers need to be >> converted though one target object is derived from another. Here everything >> is broken: generic contract, meaningless conversion, meaningless check, >> meaningless exception. > > In this case, whether a check will fail is known statically for any compiler > that generates generics via macro substitution (that is all Ada compilers > other than Janus/Ada). I can hardly imagine that there exists a compiler > that will generate "raise Program_Error" unconditionally without generating > a warning! Yes it generates that warning, and the warning is ignored. Any warning is ignored, that is one the most fundamental principle of modern software design. (:-)) The code was modified to X.all'Unchecked_Access. Everybody is happy? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: xorque on 22 Nov 2009 00:45 I have to say I'm quite impressed that my question started so much discussion. For this (minor) project, I tried a multitude of different approaches and ended up with one that appeared safe, right until it ended up causing GNAT to have some sort of heart attack: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42140 After some five or six attempts with different methods, I think I'm at the point where I've given up on the idea of using more than one tagged type for this and will likely just expose opaque integer identifiers. The problem was really one of state rather than dispatching, for me. I wanted one 'archiver' type to dispatch operations that were stateless ("Can this archiver open this archive?"), one type to hold per-archive state (the state of an open archive) and one type to hold per-file state (a Stream_IO.File_Type handle or an offset into an open zip file). I believe I sketched an example of the kind of interface I wanted (in more or less completely invalid Ada-ish pseudocode): Archive : access Archiver.Archive_t'Class; File : access Archiver.File_t'Class; for Index in Archivers'Range loop if Archiver.Can_Mount (Archiver => Archivers (Index), Path => "file.zip") then Archive := Archiver.Open_Archive (Archiver => Archivers (Index), Path => "file.zip"); File := Archiver.Open (Archive => Archive, Path => "/directory/file.txt"); Archiver.Close (File); Archiver.Close_Archive (Archive); end if; end loop; This interface would have been entirely internal as somebody actually using the code would have used it through another layer (removing the requirement to test against different archivers and in effect making it impossible to tell that there are even multiple packages involved). To the poster that didn't know what PhysicsFS was: http://icculus.org/physfs/ A small example of how the interface is used (note that the fact that there are multiple archivers involved is completely hidden): http://icculus.org/physfs/physfstut.txt Regards, xw
From: Georg Bauhaus on 22 Nov 2009 06:25
On 11/22/09 6:45 AM, xorque wrote: > > For this (minor) project, I tried a multitude of different approaches > and ended up > with one that appeared safe, right until it ended up causing GNAT to > have some > sort of heart attack: > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=42140 Do you get segmentation fault with GNAT run in Ada mode? (Currently, this would mean options -gnato -fstack-check and no -gnatp, I think.) What happens if you name the access-to-String type used for a component of Archive_t, i.e. something like type String_a is access String; subtype File_Name_a is String_a; type Archive_t is new Ada.Finalization.Limited_Controlled with record Name : File_Name_a; File : Stream_IO.File_Type; end record; |