From: David C. <dc...@us...> - 2012-07-11 19:56:47
|
This is a writeup of some of my thoughts relating to (parts of) yesterdays discussion. Right now it is possible to write code that only makes sense in the managed backend (e.g. M3R Hadoop compatability layer), the question is what exactly is allowed when you want to target both backends (writing portable X10). (As an aside, it would be good to have a compiler switch that would throw a front end error if the code is not portable. Currently such code compiles fine in the managed backend and causes post compiler errors in the native backend.) This is coming up right now in the context of exception handling. If we are fixed on a design where there are 2 roots to the exception hierarchy (it seems this is necessary because otherwise the managed runtime would have to have x10.lang.Throwble be @NativeRep("java.lang.Throwable") and thus a checked exception) then we have the following options: The simplest (and current) definition no types that come from pre-existing class files (or java standard library) are allowed in portable X10. This makes it hard to write frameworks that can execute user code, such as this one: class FrameWork { static interface Job { def run () : void; } def runJob (j:Job) : Throwable { // asynchronously run j somehow, catching any errors if (j.ranOK()) return null; return j.exception(); } } Examples of this pattern are: * XRX implementations of at, async, next, perhaps some others * PlaceLocalHandle.make * M3R core engine (must catch all exceptions to avoid barrier deadlock) * Some sort of resillient 're-execute this code' framework? * potentially some high level user code? Effect 1 Anyone using this library in an interop context must catch java.lang.Error and java.lang.Runtime exception and package them in their own ExceptionWrapper class that extends x10.lang.Throwable. Otherwise the exceptions will 'escape' . You don't even get an error when this happens, you usually get a deadlock or some other bizarre behaviour. It could be argued that since managed X10 allows interop, that all frameworks that have this property are error-prone unless compiled natively. Effect 2 You have to catch and package all your checked exceptions too, because Job.run does not have a @Throws annotation. If you do this, you are also accounting for effect 1, because you have to catch java.lang.Throwable which includes the unchecked exceptions from effect 1. Alternative -- allow java types in catch clauses The type-theoretical point here is that in native X10, instances of such types will not arise so the whole block can be ignored at codegen time. It would be possible for a predetermined (and documented) list of types to be available, i.e. probably java.lang.Throwable, java.lang.Error, java.lang.RuntimeException and maybe some others. This means that we can simply provide these types in the XRC runtime and existing codegen works fine. That makes implementing this a 5 minute job. This permits using @Throws and catching and rethrowing java.lang.Throwable, which allows the above code to be rewritten: class FrameWork { static interface Job { def run () : void @Throws[java.lang.Throwable] ; } def runJob (j:Job) : Any { // Any is the only type that is above both java.lang.Throwable and x10.lang.Throwable // asynchronously run j somehow, catching all kinds of possible errors if (j.ranOK()) return null; return j.exception(); } } Effect User doesn't have to worry about effect1 and effect2 from the previous section. Note This is actually possible right now using @Native blocks to stop code reaching the native backend, but such code is ugly. We could use it inside XRX classes but it should not be the story for our users. So we don't have to do this if we believe that examples of the above framework will not occur in user code and we don't mind writing ugly difficult code ourselves instead of going for a language solution. Even more power but harder to implement We can actually allow all sorts of references to java types in portable code, as long as these uses are only for threading. I.e. we do not extend, implement, access static methods / fields, or create new instances of such types. We can, however, take parameters of such types, define variables and assign such values, and rethrow them. In fact I think for mixed mode some degree of this will actually be *necessary* because both managed and native 'sides' of the computation will be run through the same frontend and will reach both backends. This requires the codegen on the native side to cleverly ignore such types. E.g instanceof MyJavaClass is always false. Casting always fails, etc. The only value of such a type in the native backend is null, which could be used for optmisations. But 99% of the time this will just be code that is part of a larger portable program but is never even executed on the native backend, but has to be present because it is needed on the managed backend. |
From: David P G. <gr...@us...> - 2012-07-11 20:42:32
|
It doesn't really address the broad question you are asking, but something Olivier reminded me of after the main discussion yesterday is that we have discussed adding an x10.lang.RootNativeException to the standard library. This gets @NativeRep to java.lang.Throwable for Managed X10 and to x10.lang.Throwable for the C++ backend. This at least allows a single root exception object for pieces of the XRX runtime that want to catch everything in a "portable" fashion. --dave |
From: David C. <dc...@us...> - 2012-07-11 21:09:41
|
> This gets @NativeRep to x10.lang.Throwable for the C++ backend. Aren't there some issues with two x10 classes having the same runtime type? I think it is not a soundness violation but it's also not the current semantics. You could establish a new X10 type CheckedThrowable that is above java.lang.Throwable and x10.lang.Throwable, and encourage everyone to use x10.lang.Throwable (and only use that in our APIs). In other words, we have checked and unchecked exceptions in portable X10, but the default is unchecked rather than checked. Either way, I think this has many similarities with my #2 proposal because it requires @Throws anywhere the top might be thrown, i.e. it reintroduces checked exceptions into "portable X10". Where it beats my #2 is that you don't have to write 2 catch clauses, which I like. It also introduces the ability to throw a checked exception from X10 itself (including X10 that compiles to native code) as you create your own hierarchy under CheckedThrowable. This would be a backwards-compatible regression to the old X10 exception model. Phrased like that, it seems like a reasonable solution to the problem of cleanly adding java interop to X10. If mixed-mode X10 is definitely going to happen, we should plan out what we will need for that, because it may well be related and invalidate my last argument. > This at least allows a single root exception object for pieces of > the XRX runtime that want to catch everything in a "portable" fashion. I'm starting to really believe this is not just an XRX problem but something that will occur with any X10 library of significant complexity (e.g. M3R). If you are writing a library that you want to compile for the managed backend and the native backend (this covers a large proportion of our users), then you quite probably have to deal with java exceptions from user callbacks using @Native hacks. That is our current story and it's not good. |
From: David P G. <gr...@us...> - 2012-07-11 21:42:27
|
David Cunningham/Watson/IBM@IBMUS wrote on 07/11/2012 05:09:09 PM: > From: David Cunningham/Watson/IBM@IBMUS > To: X10 core design <x10...@li...>, > Date: 07/11/2012 05:11 PM > Subject: Re: [X10-core] Now we have nterop, what subset of the > language do we define as 'portable'? > > > This gets @NativeRep to x10.lang.Throwable for the C++ backend. > > Aren't there some issues with two x10 classes having the same > runtime type? I think it is not a soundness violation but it's also > not the current semantics. > Fair enough. I was trying to be lazy and it was probably not a good idea. Having it be @NativeRep for C++ to a distinct class that is also the parent class at the C++ level of the @NativeRep impl of x10::lang::Throwable is probably less confusing... --dave |
From: David C. <dc...@us...> - 2012-07-13 21:48:30
|
------- Summary ------- This is based on the email discussion we recently had, a conversation with Vijay over lunch, and various discussions with Avi over the last few weeks. We allow the following in pure X10 code (i.e. without @Native or other hacks), that can be compiled to both managed and native. 1) Catching and rethrowing all unchecked exceptions (including ones from java interop) with multiple catch blocks 2) Catching and rethrowing all exceptions (including ones from java interop) with a single catch block 3) Easy packaging of an exception that is NOT <: x10.lang.Throwable into an exception that is <: x10.lang.Throwable (this is for the boundary between interop x10 code and x10 code that only uses x10.lang.Throwable). 4) A reasonable story for at and finish/async that is implementable before the next release ---------------------- Parser & type checking ---------------------- Background: we have already re-introduced checked exceptions into the language: A checked exception is one that is NOT <: one of * x10.lang.Throwable * x10.interop.java.Error * x10.interop.java.RuntimeException Now we propose making them a core language feature that is visible to native backend programmers as well. This motivates the re-introduction of the 'throws' keyword (retiring @Throws), perhaps in a subsequent release. This is partly because it is a core language feature but also because 'throws' already exists in Java and is well-understood. We will not not allow 'throws' on function types (i.e. closures). Checked exceptions from a closure body must be caught. This could be relaxed if we compile to Java 8. Java unchecked exceptions are permitted in closures. There are some additional java types that are not valid X10 types (like java.lang.Object, which is instead accessible via x10.lang.Any). java.lang.Throwable, now accessed via x10.lang.CheckedThrowable java.lang.Error, now accessed via x10.interop.java.Error; java.lang.RuntimeException, now accessed via x10.interop.java.RuntimeException; java.lang.Exception, now accessed via x10.interop.java.Exception; (necessary because it is the parent of x10.interop.java.RuntimeException) ---------------- Standard library ---------------- Changed classes: package x10.lang; @NativeRep("java", "java.lang.Throwable", ...) class CheckedThrowable { ... } // you can throw any T <: CheckedThrowable Application programmers can extend CheckedThrowable and have a checked exception (even in native X10) but this will have to be locally caught or declared to be thrown using 'throws'. Java checked exceptions such as java.io.IOException can be caught as e:CheckedThrowable (even in native X10). class Throwable extends CheckedThrowable { ... } // Unchecked class WrappedException extends Throwable WrappedException is a simple class written in X10 that can be used to wrap an exception <: x10.lang.CheckedThrowable to force it to be <: x10.lang.Throwable. It will have printStackTrace() overridden to print the internal exception's stack trace. We will encourage people to properly handle (rather than handle and wrap) checked exceptions from interop code. But if they want, they should use this XRX exception to do it rather than roll their own. Especially if they are writing a library. package x10.interop.java; @NativeRep("java", "java.lang.Error", ...) class Error extends x10.lang.CheckedThrowable; In C++ this will be 'just another class' that you can use. @NativeRep("java", "java.lang.Exception", ...) class Exception extends x10.lang.CheckedThrowable; In C++ this will be 'just another class' that you can use. @NativeRep("java", "java.lang.RuntimeException", ...) class RuntimeException extends Exception In C++ this will be 'just another class' that you can use. These allow catching of unchecked exceptions from java without upcasting them to a checked exception. They can then be rethrown without a 'throws' annotation. x10.interop.java.Error and x10.interop.java.RuntimeException are not subtypes of x10.lang.Throwable, which is inconvenient but unavoidable given Java's design. WrappedException can however be used to wrap them, which is useful if you are being called from some legacy X10 code that only handles x10.lang.Throwable, or you just want all your exceptions under a single unchecked root. XRX libraries only throw exceptions <: x10.lang.Throwable but will handle any exception. We advertise this as best practice if the programmer is not biased between native and managed backends. ------- Runtime ------- MultipleExceptions will contain a WrappedException if an uncaught exception e is thrown by the body of an async and e is NOT <: x10.lang.Throwable. This means that Error and RuntimeException will be wrapped even though they are not checked exceptions. To me, this seems the least worst option. 'At' statements will implicitly propagate checked exceptions, i.e. at { throw new CheckedThrowable(); } may only be written if the exception is caught by the home place, or the method 'throws' CheckedThrowable. PlaceLocalHandle.make uses finish async at, and will therefore emit MultipleExceptions containing wrapped exceptions. Static initialisation will not permit an unchecked exception to escape. It will also not wrap, so x10.interop.java.Error and x10.interop.java.RuntimeException can potentially emerge. ------------------- Implementation note ------------------- Runtime.runAt will not have the 'throws' annotation, it will use Thread.stop() to throw a checked exception without needing a 'throws' annotation in the Java code. When desugaring an 'at' into a Runtime.runAt call, the java backend will have to, for each T in the expression's throws() clause, insert one call to the static method x10.runtime.Util.fakeThrows[T](), which is a method that has an empty body but has a 'throws' annotation (the call will be inlined by the JIT). This is 'the other side' of the Thread.stop() approach, where we have to tell the javac type checker that we are indeed throwing a checked exception, or it will reject any catch statements of these checked exceptions on the grounds that they are unreachable. For illustration, see the following java code: import java.io.*; public class Test { public static <E extends Throwable> void fakeThrows() throws E { } public static <E extends Throwable, T> T fakeThrows(T x) throws E { return x; } public static void main (String[] args) { try { Thread.currentThread().stop(new IOException()); Test.<IOException>fakeThrows(); String tmp = Test.<IOException,String>fakeThrows(new String()); } catch (IOException e) { System.out.println("fish"); } } } ------------ Further work ------------ @Suppress[java.io.IOException] { S } will insert a try/catch block around the statement S and rethrow the exception packaged in a WrappedException. It can be used instead of a 'throws' annotation on a method, but it applies to the method body, and therefore is not part of the signature of the method. This should also work on closures, which is useful since 'throws' cannot be used on function types. @SuppressAll { S } will insert a try/catch block around S and rethrow everything in a WrappedException if it not under x10.lang.Throwable. The implementation technique for at desugaring on the managed backend could be used for implementing function types with throws annotations. This would cost in the body of a closure, but only if it had to suppress an exception. It would not increase the cost of a call (after JIT). ---------- Discussion ---------- If you are writing a library that has to handle exceptions from user code, and you do not allow user code to throw any checked exceptions (i.e. the interface you provide for users to extend does not have any 'throws' annotation) you will need to catch all the possible unchecked exceptions. You do this with 3 catch statements, one for x10.lang.Throwable, one for x10.interop.java.Error, and one for x10.interop.java.RuntimeException. You can then either rethrow them, or package them, and rethrow that, at your discretion. If you target just native, you can still do it by catching x10.lang.Throwable, but your code will be fragile if you later decide to compile it to the managed backend. You can also catch CheckedThrowable. This has the advantage that you can allow usercode to throw any exception (i.e. the interface you provide for users to extend has 'throws CheckedThrowable'), but is more tiresome to deal with. You can: 1) Package it & rethrow -- this might confuse / irritate your user, but is very easy. 2) Return it through some other channel. 3) throw it using 'throws CheckedThrowable'. This means your users will have to catch it when they invoke your library, even if the code they supplied did not throw any checked exceptions. 4) The same as (3) but define two versions of your API, one that takes user code that does not throw checked exceptions, and one that does. So you can either deal with CheckedException or you can have 3 catch blocks -- one for x10.lang.Throwable, one for x10.interop.java.Error, and one for x10.interop.java.RuntimeException. |
From: David C. <dc...@us...> - 2012-07-16 21:29:56
|
Followup: First, a typo fix: Static initialisation will not permit an unchecked exception to escape. It will also not wrap, so x10.interop.java.Error and x10.interop.java.RuntimeException can potentially emerge. becomes Static initialisation will not permit a checked exception to escape. It will also not wrap, so x10.interop.java.Error and x10.interop.java.RuntimeException can potentially emerge. Secondly, after talking with Avi some more, I am changing the proposal slightly: It seems to be a bad idea to override printStackTrace() to hide the wrapping exception in the case of WrappedException. The existing java / X10 chained exception model should be used, or mimicked. Also, I now think we should definitely have x10.lang.Throwable @NativeRep to java.lang.RuntimeException. This means x10.interop.java.RuntimeException will go away, leaving only x10.interop.java.Error which simplifies that side of things significantly. More importantly, manyJava exceptions that we can expect to be thrown from XRX or the VM can be @NativeRep versions of X10 exceptions, thus needing no expensive catch / rethrowing as X10 exceptions, or wrapping or weird semantics around == due to such implementation trickery. * java.lang.ArithmeticException * java.lang.ArrayIndexOutOfBoundsException * java.lang.StringIndexOutOfBoundsException * java.lang.NullPointerException The following we should not see due to careful codegen: * java.lang.ClassCastException (Our own RTT system should be throwing x10.lang.ClassCastException before this can happen) * java.lang.AssertionError (X10 assertions should be front end desugaring, not compiled to java assertions) Unfortunately, the following JVM exceptions are under java.lang.Error, not java.lang.RuntimeException and will still not be caught by catch (e: x10.lang.Throwable) * java.lang.OutOfMemoryError * java.lang.StackOverflowError We could * Use the current wrap/rethrow for these (this won't work in the case of OOM since you probably can't make a new object to wrap them with) * My favourite: Remove them from the language (i.e. remove the X10 classes and not throw them in the native backend) -- these are the managed backend equivalent of a segfault or hardware error etc * Change the hierarchy in X10 so that these are not under x10.lang.Throwable: Rename x10.interop.java.Error to x10.lang.Error and put OOM and SOE under Error, so they can be @NativeRep. This means they can't be caught with catch (e:x10.lang.Throwable) but can be caught explicitly if necessary. All other exceptions should either be kept as interop exceptions (i.e. propagated into X10 without an interference) or caught in native XRJ code so that they cannot trouble an X10 programmer. This should simplify the managed backend codegen for catch a lot. Finally, we should disallow generic exceptions, as Java does, and also disallow constrained exceptions until we write the rules for them. These are other sources of current bugs and compiler complexity. |
From: Mikio T. <MT...@jp...> - 2012-07-17 09:22:54
|
Hi Dave, I wonder Avi and newer members of X10 project don't know about it, but I believe that you, Vijay, Dave G, Olivier etc. are all aware of the exception handling mechanism in managed x10 in old days :-) We have overhauled exception handling in late 2010 from the mapping based mechanism (i.e. package x10.lang; @Native("java", "java.lang.NullPointerException") class NullPointerException {} etc.) to the current wrapping based mechanism (i.e. at each try block, catch java.lang.Throwable then wrap known and unknown exceptions with corresponding x10 exception and UnknownJavaThrowable respectively, and rethrow it), and what you are *proposing* is essentially same as the old mechanism. The change was driven by the following strong request from language design side (if I remember correctly, Igor or Bard). "We should print x10 exception as proper x10 type name (e.g. x10.lang.NullPointerException etc.) not as the mapped java exception (e.g. java.lang.NullPointerException) in stack trace," We can implement typeName() to return "x10.lang.NullPointerException" but it was difficult at that time to hook the calls to fillInStackTrace() and set the fake stack trace in a robust and portable way by replacing all known java exception types with corresponding x10 exception types. This is the main reason for which we switched to the wrapping based mechanism, so we need to solve it in order to switch back to the mapping based mechanism. I understand the current double-headed exception hierarchies are painful to use for Java interop programmers and library implementers. Especially the lack of the single top throwable type in X10 requires at least two catch blocks to handle all throwable types. I actually generate such catch blocks (as x10.lang.Throwable and java.lang.Throwable) to implement sticky exception semantics in static initialization. So basically I understand your proposal to introduce a single top throwable type. But I have concern about the time frame. If we change the exception hierarchy, it must be done before we introduce Java interop in X10 2.3. However, the exception handling is delicate and the change of the machinery will have great impact to the stability of the codebase. If the mid August is the hard deadline for X10 2.3, I would say it is risky. I am on vacation on Aug. 11-19. Mikio Takeuchi IBM Research - Tokyo ---------- Forwarded message ---------- From: David Cunningham <dc...@us...> Date: 2012/7/17 Subject: Re: [X10-core] Proposal: Change X10 Exception Hierarchy For Cleaner Java Interop (please respond with comments) To: X10 core design <x10...@li...> Followup: First, a typo fix: Static initialisation will not permit an unchecked exception to escape. It will also not wrap, so x10.interop.java.Error and x10.interop.java.RuntimeException can potentially emerge. becomes Static initialisation will not permit a checked exception to escape. It will also not wrap, so x10.interop.java.Error and x10.interop.java.RuntimeException can potentially emerge. Secondly, after talking with Avi some more, I am changing the proposal slightly: It seems to be a bad idea to override printStackTrace() to hide the wrapping exception in the case of WrappedException. The existing java / X10 chained exception model should be used, or mimicked. Also, I now think we should definitely have x10.lang.Throwable @NativeRep to java.lang.RuntimeException. This means x10.interop.java.RuntimeException will go away, leaving only x10.interop.java.Error which simplifies that side of things significantly. More importantly, manyJava exceptions that we can expect to be thrown from XRX or the VM can be @NativeRep versions of X10 exceptions, thus needing no expensive catch / rethrowing as X10 exceptions, or wrapping or weird semantics around == due to such implementation trickery. * java.lang.ArithmeticException * java.lang.ArrayIndexOutOfBoundsException * java.lang.StringIndexOutOfBoundsException * java.lang.NullPointerException The following we should not see due to careful codegen: * java.lang.ClassCastException (Our own RTT system should be throwing x10.lang.ClassCastException before this can happen) * java.lang.AssertionError (X10 assertions should be front end desugaring, not compiled to java assertions) Unfortunately, the following JVM exceptions are under java.lang.Error, not java.lang.RuntimeException and will still not be caught by catch (e: x10.lang.Throwable) * java.lang.OutOfMemoryError * java.lang.StackOverflowError We could * Use the current wrap/rethrow for these (this won't work in the case of OOM since you probably can't make a new object to wrap them with) * My favourite: Remove them from the language (i.e. remove the X10 classes and not throw them in the native backend) -- these are the managed backend equivalent of a segfault or hardware error etc * Change the hierarchy in X10 so that these are not under x10.lang.Throwable: Rename x10.interop.java.Error to x10.lang.Error and put OOM and SOE under Error, so they can be @NativeRep. This means they can't be caught with catch (e:x10.lang.Throwable) but can be caught explicitly if necessary. All other exceptions should either be kept as interop exceptions (i.e. propagated into X10 without an interference) or caught in native XRJ code so that they cannot trouble an X10 programmer. This should simplify the managed backend codegen for catch a lot. Finally, we should disallow generic exceptions, as Java does, and also disallow constrained exceptions until we write the rules for them. These are other sources of current bugs and compiler complexity. ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ _______________________________________________ X10-core mailing list X10...@li... https://lists.sourceforge.net/lists/listinfo/x10-core |