Let me know what you think of these. So far, I like them better than the original versions, although in solving some limitations of the old macros, I've created some new ones.
This version avoids ({ }) entirely, in favor of Ove's original thought, which was to use __LINE__; therefore, putting multiple __(finally|except) clauses in the same line probably breaks.
Otherwise, these are better macros. They are slightly more easily understood (although still pretty darn confusing, especially in the debugger), probably more resiliant against compiler optimizations, and have much less awful performance. Best of all, they ought to properly re-raise exceptions after executing finally clauses.
There is some cruft in there, btw, which will be fixed before I submit anything to patches.
They still break break/continue, of course, since they still rely on __TRY / __EXCEPT blocks to do the dirty-work.
You can run the original seh_macros.c test against these successfully, except for the "absurdly nested" ones which use macros and presumably would fail quite dramatically due to __LINE__ duplications.
FWIW, compared to the degree of "pressing need" for these macros, I have already spent way too much time on them... But the old ones kind of sucked, and, frankly, it's a fun semantic puzzle that requires pusing the limits of good old cpp just a _bit_ further than may have been intended ... so, here it is.
Note that I'm declaring a variable in the header file here. I guess, I should fix this by creating an internal API in ntdll? I never quite figured out whether global non-static variables in a dll are thread-local, or what... (same for lexically scoped static ones -- real clueless here, and building a new system so I don't have much doco's at my fingertips ATM)
Sooo.... Anyone want to help me decide what's the best way to get a thread-local global variable instantiated? Probably, my previous attempt with TlsAlloc was semantically correct, but that was /way/ too fat a solution, I'd like something much quicker if its at all possible.
Otherwise this won't be threadsafe, which seems like a bad call, esp. since such code will probably end up being used to guard against unacceptable resource leakage..
warning, cut-and-pasted this patch in chunks so it might be bugged... should be clear enough how to duplicate, however, if it fails...
--- ../wine.vanilla/include/wine/exception.h 2003-01-29 21:50:14.000000000 -0600 +++ ./include/wine/exception.h 2003-02-09 01:33:45.000000000 -0600 @@ -21,8 +21,12 @@ #ifndef __WINE_WINE_EXCEPTION_H #define __WINE_WINE_EXCEPTION_H
+/* WRC can't find setjmp.h. Does it not check /usr/include? */ +#if (!(defined(__WRC__))) + #include <setjmp.h> #include "windef.h" +#include "excpt.h"
/* The following definitions allow using exceptions in Wine and Winelib code * @@ -59,6 +63,16 @@ * You can use them to leave a __EXCEPT block though. * * -- AJ + * + * Now we can implement the __try/__except/__finally magic of VC++ as standard (?) macros. + * this is a "good enough" implementation, it's incomplete, but should suffice for most uses. + * + * The macros require some symbols to be found in ntdll.dll.so, so link with that DLL if you + * use them. + * + * If you are using VC++ and this header, #define USE_COMPILER_EXCEPTIONS before you include it. + * + * -gmt */
/* Define this if you want to use your compiler built-in __try/__except support. @@ -146,8 +160,90 @@ extern DWORD __wine_finally_handler( PEXCEPTION_RECORD record, EXCEPTION_FRAME *frame, CONTEXT *context, LPVOID pdispatcher );
+ +#if (!defined(NO_VC_SEH_MACROS)) + +#ifndef _WINE_ALREADY_DEFINED_SEH_MACROS__ +#define _WINE_ALREADY_DEFINED_SEH_MACROS__ + +/* fixme: should be renamed since these are used for except too */ +typedef void (*__p_seh_finally_executer) (); +typedef void (*__p_seh_dotry_executer) (__p_seh_finally_executer); +__p_seh_finally_executer __thread_outermost_handler_ptr; + +#define __SEH_LINEUNIQ_CAT2(x,y) __SEH_LINEUNIQ_ ## y ## x +#define __SEH_LINEUNIQ_CAT1(x,y) __SEH_LINEUNIQ_CAT2(x,y) + +#define __SEH_LINEUNIQ_NULLMACRO(x) x +#define __SEH_LINEUNIQ_MAGIC_LINE __SEH_LINEUNIQ_NULLMACRO( __LINE__ ) + +#define __SEH_LINEUNIQ_ID(x) __SEH_LINEUNIQ_CAT1( __SEH_LINEUNIQ_MAGIC_LINE , x ) + +#define __try \ + do { \ + void dotry(__p_seh_finally_executer fin) { \ + __label__ __seh_macro_exit_spot; \ + auto WINE_EXCEPTION_FILTER(__wine_seh_macro_exception_handler); \ + __TRY { \ + { \ + +#define __finally \ + } \ + __seh_macro_exit_spot: \ + if (0) goto __seh_macro_exit_spot; \ + } __EXCEPT( __wine_seh_macro_exception_handler ) { \ + } __ENDTRY \ + (*fin)(); \ + WINE_EXCEPTION_FILTER(__wine_seh_macro_exception_handler) { \ + (*fin)(); \ + return EXCEPTION_CONTINUE_SEARCH; \ + } \ + } \ + goto __SEH_LINEUNIQ_ID(DETOUR); \ + __SEH_LINEUNIQ_ID(RETOUR): \ + dotry( __thread_outermost_handler_ptr ); \ + } while (0); \ + goto __SEH_LINEUNIQ_ID(AFTERFINALLY); \ + auto void __SEH_LINEUNIQ_ID(Finally_Executer) (); \ + __SEH_LINEUNIQ_ID(DETOUR): \ + __thread_outermost_handler_ptr = & __SEH_LINEUNIQ_ID(Finally_Executer); \ + goto __SEH_LINEUNIQ_ID(RETOUR); \ + __SEH_LINEUNIQ_ID(AFTERFINALLY): ; \ + void __SEH_LINEUNIQ_ID(Finally_Executer) () + +#define __except(...) \ + } \ + __seh_macro_exit_spot: \ + if (0) goto __seh_macro_exit_spot; \ + } __EXCEPT( __wine_seh_macro_exception_handler ) { \ + (*fin)(); \ + } __ENDTRY \ + WINE_EXCEPTION_FILTER(__wine_seh_macro_exception_handler) { \ + return ( __VA_ARGS__ ); \ + } \ + } \ + goto __SEH_LINEUNIQ_ID(DETOUR); \ + __SEH_LINEUNIQ_ID(RETOUR): \ + dotry( __thread_outermost_handler_ptr ); \ + } while (0); \ + goto __SEH_LINEUNIQ_ID(AFTEREXCEPT); \ + auto void __SEH_LINEUNIQ_ID(Except_Executer) (); \ + __SEH_LINEUNIQ_ID(DETOUR): \ + __thread_outermost_handler_ptr = & __SEH_LINEUNIQ_ID(Except_Executer); \ + goto __SEH_LINEUNIQ_ID(RETOUR); \ + __SEH_LINEUNIQ_ID(AFTEREXCEPT): ; \ + void __SEH_LINEUNIQ_ID(Except_Executer) () + +#define __leave goto __seh_macro_exit_spot + +#endif /* _WINE_ALREADY_DEFINED_SEH_MACROS__ */ + +#endif /* NO_VC_SEH_MACROS */ + #endif /* USE_COMPILER_EXCEPTIONS */
+#endif /* __WRC__ */ + static inline EXCEPTION_FRAME * WINE_UNUSED __wine_push_frame( EXCEPTION_FRAME *frame ) { #if defined(__GNUC__) && defined(__i386__)
"Gregory M. Turner" gmturner007@ameritech.net writes:
Let me know what you think of these. So far, I like them better than the original versions, although in solving some limitations of the old macros, I've created some new ones.
Of course the main limitation is that this requires gcc, which means the macros can't be used in Wine itself. They might be useful for Winelib apps though.
Otherwise, these are better macros. They are slightly more easily understood (although still pretty darn confusing, especially in the debugger), probably more resiliant against compiler optimizations, and have much less awful performance. Best of all, they ought to properly re-raise exceptions after executing finally clauses.
You cannot re-raise exceptions after finally, the finally block has to run at unwind time, not from the exception filter. You should probably use the existing __FINALLY macro for that.
Sooo.... Anyone want to help me decide what's the best way to get a thread-local global variable instantiated? Probably, my previous attempt with TlsAlloc was semantically correct, but that was /way/ too fat a solution, I'd like something much quicker if its at all possible.
IMO you shouldn't use a variable at all, you should do everything on the stack. Otherwise it won't work when an exception happens in the middle of the exception code.
On Wednesday 12 February 2003 01:44 pm, Alexandre Julliard wrote:
"Gregory M. Turner" gmturner007@ameritech.net writes:
Let me know what you think of these. So far, I like them better than the original versions, although in solving some limitations of the old macros, I've created some new ones.
Of course the main limitation is that this requires gcc, which means the macros can't be used in Wine itself. They might be useful for Winelib apps though.
actually, since I got rid of "({ })" in this revision, I'm not sure it's gcc-specfic at all... or did I miss something?
Otherwise, these are better macros. They are slightly more easily understood (although still pretty darn confusing, especially in the debugger), probably more resiliant against compiler optimizations, and have much less awful performance. Best of all, they ought to properly re-raise exceptions after executing finally clauses.
You cannot re-raise exceptions after finally, the finally block has to run at unwind time, not from the exception filter. You should probably use the existing __FINALLY macro for that.
This, OTOH, sounds like a more significant issue... I'll look into it.
Sooo.... Anyone want to help me decide what's the best way to get a thread-local global variable instantiated? Probably, my previous attempt with TlsAlloc was semantically correct, but that was /way/ too fat a solution, I'd like something much quicker if its at all possible.
IMO you shouldn't use a variable at all, you should do everything on the stack. Otherwise it won't work when an exception happens in the middle of the exception code.
Yup. I'd prefer this myself... but unfortunately, I haven't found a way to achieve it. The problem is that the finally clause provided by the user needs to be outside any lexical scope I can create (this is simply because I have no way to put some close-brackets after the finally clause like __ENDTRY). So, if I'm going to use the approach of turning the user's finally clause into a function, there's no way to get at it without inheriting it from the "global" (relative to the place the macros are invoked) lexical scope... which I can't do before I begin the scope because I don't know what __LINE__ will be when the user creates their finally clause during the __try.... Sorry, that's pretty unclear, but suffice it to say that there are some nasty issues at play :)
However, there is probably some creative solution that just hasn't come to me yet. In fact I have a couple of ideas already. I'll go back to the drawing board with these one more time and try to get this issue sorted out ... expect at least one more post to wine-devel before I submit a patch on this.
Thanks for looking these over,
ons, 2003-02-12 kl. 23:41 skrev Gregory M. Turner:
On Wednesday 12 February 2003 01:44 pm, Alexandre Julliard wrote:
"Gregory M. Turner" gmturner007@ameritech.net writes:
Let me know what you think of these. So far, I like them better than the original versions, although in solving some limitations of the old macros, I've created some new ones.
Of course the main limitation is that this requires gcc, which means the macros can't be used in Wine itself. They might be useful for Winelib apps though.
actually, since I got rid of "({ })" in this revision, I'm not sure it's gcc-specfic at all... or did I miss something?
I was under the impression that you were still using nested functions, which is kind of a gcc-ism. So getting rid of ({ }) doesn't seem to buy anything, unless you *know* of a different compiler that has one but not the other. So you may as well keep ({ }) if it makes the macros more manageable or flexible.
IMO you shouldn't use a variable at all, you should do everything on the stack. Otherwise it won't work when an exception happens in the middle of the exception code.
Yup. I'd prefer this myself... but unfortunately, I haven't found a way to achieve it. The problem is that the finally clause provided by the user needs to be outside any lexical scope I can create (this is simply because I have no way to put some close-brackets after the finally clause like __ENDTRY). So, if I'm going to use the approach of turning the user's finally clause into a function, there's no way to get at it without inheriting it from the "global" (relative to the place the macros are invoked) lexical scope...
Nested functions in gcc can access any of the parent function's local variables, would that work? (Or would this exception stuff break gcc's mechanism for this? not sure what it does)
By the way, it seems to me that the particular installer I'm working on doesn't need NdrClientCall2 after all. Which installer was it that needed it?
On Wednesday 12 February 2003 08:25 pm, Ove Kaaven wrote:
ons, 2003-02-12 kl. 23:41 skrev Gregory M. Turner:
On Wednesday 12 February 2003 01:44 pm, Alexandre Julliard wrote:
"Gregory M. Turner" gmturner007@ameritech.net writes:
Let me know what you think of these. So far, I like them better than the original versions, although in solving some limitations of the old macros, I've created some new ones.
Of course the main limitation is that this requires gcc, which means the macros can't be used in Wine itself. They might be useful for Winelib apps though.
actually, since I got rid of "({ })" in this revision, I'm not sure it's gcc-specfic at all... or did I miss something?
I was under the impression that you were still using nested functions,
yup. forgot about those.
which is kind of a gcc-ism. So getting rid of ({ }) doesn't seem to buy anything, unless you *know* of a different compiler that has one but not the other. So you may as well keep ({ }) if it makes the macros more manageable or flexible.
makes sense.
IMO you shouldn't use a variable at all, you should do everything on the stack. Otherwise it won't work when an exception happens in the middle of the exception code.
Yup. I'd prefer this myself... but unfortunately, I haven't found a way to achieve it. The problem is that the finally clause provided by the user needs to be outside any lexical scope I can create (this is simply because I have no way to put some close-brackets after the finally clause like __ENDTRY). So, if I'm going to use the approach of turning the user's finally clause into a function, there's no way to get at it without inheriting it from the "global" (relative to the place the macros are invoked) lexical scope...
Nested functions in gcc can access any of the parent function's local variables, would that work? (Or would this exception stuff break gcc's mechanism for this? not sure what it does)
They work. And given ({ }), perhaps they can even be used effectively (otherwise I am stuck because the block starts with __try, and ends with __finally, where __LINE__ is neccesarily different... although maybe there's a work-around in moving more logic into __try... )
The holy grail would be to achieve all this without any gcc'isms. Ultimately, we know we just need some "syntactic sugar" (what a lame term, sorry to use it) around the basic algo used by __TRY & family... Anyways, where there's a will... I'll go back to the drawing board for these one last time I guess.
BTW, does wine really have non-gcc users? Or is it just a general policy to not be gcc-specific within the wine platform core since it might get recycled by other projects, etc...?
By the way, it seems to me that the particular installer I'm working on doesn't need NdrClientCall2 after all. Which installer was it that needed it?
I think it was something along the lines of "a standard Installshield 6 program"... maybe it was something proprietary.
"Gregory M. Turner" gmturner007@ameritech.net writes:
BTW, does wine really have non-gcc users? Or is it just a general policy to not be gcc-specific within the wine platform core since it might get recycled by other projects, etc...?
There are people building Wine with the Sun compiler. Also it makes sense for at least some dlls to allow building them with MSVC to test them under Windows. And in general using different compilers is a good way to shake out bugs from the code.
Alexandre Julliard wrote:
There are people building Wine with the Sun compiler. Also it makes sense for at least some dlls to allow building them with MSVC to test them under Windows. And in general using different compilers is a good way to shake out bugs from the code.
I always build my conformance tests both in the normal way and standalone with msvc. IMHO every executable and DLL we do should run on Windows, too, if it doesn't absolutely have to make Unix system calls. - Dan