Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Is this setjmp/jongjmp usable legal?

Reply
Thread Tools

Is this setjmp/jongjmp usable legal?

 
 
Player
Guest
Posts: n/a
 
      01-03-2013
I'm not quite familiar with setjmp/longjmp (and neither with macros either)but I was trying to make something similar to this "yielding return" construct from C#, namely: when a function X "yields returns" some value everything happens like it has actually returned that value; however the state (local vars, IP) is saved, so that when X is called again it starts execution in the next statement after the last "yield return".

Is this a portable legal solution, as long as I keep local variables volatile:
1 #include <stdio.h>
2 #include <setjmp.h>
3 #define STATE_FUNC static jmp_buf _env; static int first_time = 1; if(first_time) first_time = 0; else longjmp(_env, 1)
4 #define yield(X) do { if (!setjmp(_env)) return X; } while(0)
5
6 int next_integer()
7 {
8 STATE_FUNC;
9 while(1) {
10 yield(1);
11 yield(2);
12 yield(3);
13 }
14 }
15
16
17 int main()
18 {
19 int p = 20;
20 while(p--) printf("%d", next_integer());
21 return 0;
22 }
 
Reply With Quote
 
 
 
 
Tim Rentsch
Guest
Posts: n/a
 
      01-03-2013
Player <(E-Mail Removed)> writes:

> I'm not quite familiar with setjmp/longjmp (and neither with macros
> either) but I was trying to make something similar to this "yielding
> return" construct from C#, namely: when a function X "yields returns"
> some value everything happens like it has actually returned that
> value; however the state (local vars, IP) is saved, so that when X is
> called again it starts execution in the next statement after the last
> "yield return".
>
> Is this a portable legal solution, as long as I keep local variables
> volatile:
>
> 1 #include <stdio.h>
> 2 #include <setjmp.h>
> 3 #define STATE_FUNC static jmp_buf _env; static int first_time = 1; \
> if (first_time) first_time = 0; else longjmp(_env, 1)
> 4 #define yield(X) do { if (!setjmp(_env)) return X; } while(0)
> 5
> 6 int next_integer()
> 7 {
> 8 STATE_FUNC;
> 9 while(1) {
> 10 yield(1);
> 11 yield(2);
> 12 yield(3);
> 13 }
> 14 }
> 15
> 16
> 17 int main()
> 18 {
> 19 int p = 20;
> 20 while(p--) printf("%d", next_integer());
> 21 return 0;
> 22 }


No. The function invocation containing the setjmp() call has
exited. Attempting to do a longjmp() on a buffer filled by a
setjmp() whose containing function's activation has exited is
big no-no, what the ISO standard calls 'undefined behavior'.
This means your program can misbehave in any way at all, and
there is nothing to be done about it. The worst thing is, trying
it in simple cases like this may appear to work, but later the
program might fail in a way worse than you can imagine, and at
the worst possible moment. Using setjmp() like this is not
the way to do what you are hoping to achieve, either portably
or practically.
 
Reply With Quote
 
 
 
 
James Kuyper
Guest
Posts: n/a
 
      01-03-2013
On 01/03/2013 01:11 PM, Player wrote:
> I'm not quite familiar with setjmp/longjmp (and neither with macros either) but I was trying to make something similar to this "yielding return" construct from C#, namely: when a function X "yields returns" some value everything happens like it has actually returned that value; however the state (local vars, IP) is saved, so that when X is called again it starts execution in the next statement after the last "yield return".
>
> Is this a portable legal solution, as long as I keep local variables volatile:
> 1 #include <stdio.h>
> 2 #include <setjmp.h>
> 3 #define STATE_FUNC static jmp_buf _env; static int first_time = 1; if (first_time) first_time = 0; else longjmp(_env, 1)
> 4 #define yield(X) do { if (!setjmp(_env)) return X; } while(0)
> 5
> 6 int next_integer()
> 7 {
> 8 STATE_FUNC;
> 9 while(1) {
> 10 yield(1);
> 11 yield(2);
> 12 yield(3);
> 13 }
> 14 }
> 15
> 16
> 17 int main()
> 18 {
> 19 int p = 20;
> 20 while(p--) printf("%d", next_integer());
> 21 return 0;


It's implementation-defined whether the last character in a text stream
must by a new-line character. It's safest to assume that it is required.
To avoid problems, just add

putchar('\n');

> 22 }


Note: you should not display source code with line numbers; it's
inconvenient for anyone who wants to copy your code and try to compile it.

I didn't notice any other problems with your code, but that doesn't mean
as much as it should - I often miss things. After fixing that issue, it
compiles without error messages using gcc and my preferred options. When
executed, it does what I thought it was supposed to do.

However, I think this is fundamentally a bad idea. Many people coming to
C from other languages have the bright idea of using various kinds of
macros to make C work like the other language. These schemes usually
produce code that is easily understood only by the person who wrote it
(and often not even by that person, if they come back to the code a few
years later). I'm a C expert, and I had to do a lot of careful thinking
to reach the (possibly incorrect) conclusion that this code is correct.
You'd be much better off getting familiar with the C way of doing
things, rather than trying to make C work like C#.

This example was obviously written solely to test out your idea; there's
clearly much simpler ways of producing the same output. Can you give a
simple example of C# code that doesn't just demonstrate this feature,
but has a good reason for using it? Be sure to explain any other
features of C# that the code relies on, for the benefit of those of us
who don't know C#. We could show you what the corresponding C way would
be to achieve similar results. The best way to do it in C might, in
fact, make use of something like your STATE_FUNC and yield() macros -
but I doubt it. It's more likely to involve a substantial rearrangement
of the code, so it's not necessary to call setjmp() or longjmp() to make
things happen in the desired order.

 
Reply With Quote
 
Eric Sosman
Guest
Posts: n/a
 
      01-03-2013
On 1/3/2013 1:11 PM, Player wrote:
> I'm not quite familiar with setjmp/longjmp (and neither with macros either) but I was trying to make something similar to this "yielding return" construct from C#, namely: when a function X "yields returns" some value everything happens like it has actually returned that value; however the state (local vars, IP) is saved, so that when X is called again it starts execution in the next statement after the last "yield return".
>
> Is this a portable legal solution, as long as I keep local variables volatile:
> 1 #include <stdio.h>
> 2 #include <setjmp.h>
> 3 #define STATE_FUNC static jmp_buf _env; static int first_time = 1; if (first_time) first_time = 0; else longjmp(_env, 1)
> 4 #define yield(X) do { if (!setjmp(_env)) return X; } while(0)
> 5
> 6 int next_integer()
> 7 {
> 8 STATE_FUNC;
> 9 while(1) {
> 10 yield(1);
> 11 yield(2);
> 12 yield(3);
> 13 }
> 14 }
> 15
> 16
> 17 int main()
> 18 {
> 19 int p = 20;
> 20 while(p--) printf("%d", next_integer());
> 21 return 0;
> 22 }


No: The `return' actually returns, destroying its function's
entire invocation and all the context that accompanies it. When
the function is re-entered a brand-new invocation begins, having
no connection to the previous one. You cannot expect longjmp()
to restore all of a destroyed context.

Concrete example: On many systems, entering a function reserves
a greater or lesser amount of stack space for the function's use.
Returning releases that space, allowing subsequent functions (or
the system itself) to use it. When your next_integer() function
releases its stack space, printf() and all the things printf()
calls are at liberty to reserve it, use it, and release it in
their turn. Re-entering next_integer() will reserve the space
again (or perhaps an equivalent amount of space at some other
memory address!), but it may have been scribbled on in the meantime
and anything the prior next_integer() invocation may have stored
there -- like `volatile' variables -- may well be garbage.

What problem are you trying to solve?

--
Eric Sosman
http://www.velocityreviews.com/forums/(E-Mail Removed)d
 
Reply With Quote
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      01-03-2013
James Kuyper <(E-Mail Removed)> wrote:

(snip)

> Note: you should not display source code with line numbers; it's
> inconvenient for anyone who wants to copy your code and try to
> compile it.


Sometimes it does make the explanation easier, though.

And it isn't that hard to remove them. One (ex) command in vi,
I presume also in many other editors.

-- glen
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      01-03-2013
On 01/03/2013 02:52 PM, glen herrmannsfeldt wrote:
> James Kuyper <(E-Mail Removed)> wrote:
>
> (snip)
>
>> Note: you should not display source code with line numbers; it's
>> inconvenient for anyone who wants to copy your code and try to
>> compile it.

>
> Sometimes it does make the explanation easier, though.


Comments are more useful than line numbers for that purpose, in my
experience.

> And it isn't that hard to remove them. One (ex) command in vi,


Well, yes, but the ex commands supported by vi are a pretty powerful
system; the command needed for this case wasn't very complicated, but
I've known many people who were only moderately familiar with vi who
would not have been able to figure out how to do it.

> I presume also in many other editors.


I've used many text editors other than 'vi' which had no method I could
use to remove the line numbers that was any faster than manually going
to each line and deleting them.

 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      01-03-2013
On 01/03/2013 02:18 PM, James Kuyper wrote:
> On 01/03/2013 01:11 PM, Player wrote:
>> I'm not quite familiar with setjmp/longjmp (and neither with macros either) but I was trying to make something similar to this "yielding return" construct from C#, namely: when a function X "yields returns" some value everything happens like it has actually returned that value; however the state (local vars, IP) is saved, so that when X is called again it starts execution in the next statement after the last "yield return".
>>
>> Is this a portable legal solution, as long as I keep local variables volatile:
>> 1 #include <stdio.h>
>> 2 #include <setjmp.h>
>> 3 #define STATE_FUNC static jmp_buf _env; static int first_time = 1; if (first_time) first_time = 0; else longjmp(_env, 1)
>> 4 #define yield(X) do { if (!setjmp(_env)) return X; } while(0)
>> 5
>> 6 int next_integer()
>> 7 {
>> 8 STATE_FUNC;
>> 9 while(1) {
>> 10 yield(1);
>> 11 yield(2);
>> 12 yield(3);
>> 13 }
>> 14 }
>> 15
>> 16
>> 17 int main()
>> 18 {
>> 19 int p = 20;
>> 20 while(p--) printf("%d", next_integer());
>> 21 return 0;

....
> I didn't notice any other problems with your code, but that doesn't mean
> as much as it should - I often miss things. ...


And as it turned out, I did miss a very important issue, which Eric
pointed out. I've almost never used longjmp(), which explains why I
forgot that rule. Let me explain in more detail than he did.

Calling longjmp() when the corresponding setjmp() occurred inside a
function or thread that has already terminated has undefined behavior.
Termination includes executing a return statement, or implicitly
returning when the final '}' of a function definition is reached, or
calling longjmp(), abort(), exit(), _Exit(), quick_exit() or thrd_exit()
from within the function or any of its subroutines. In other words, the
only places from which you can call longjmp() with defined behavior are
inside the same function in which you made the corresponding setjmp()
call, or in a subroutine of that function.

Your function always returns immediately after calling setjmp(), which
guarantees that you will never be able to safely make use of the jmp_buf
filled in by setjmp(). The fact that your program actually appears to
work is one of the options allowed by the phrase "undefined behavior".
That phrase also allows your program to fail catastrophically, or to
malfunction in a subtle way that might (in a more complicated program)
take you years to notice.

 
Reply With Quote
 
Jorgen Grahn
Guest
Posts: n/a
 
      01-03-2013
On Thu, 2013-01-03, Eric Sosman wrote:
> On 1/3/2013 1:11 PM, Player wrote:


>> I'm not quite familiar with setjmp/longjmp (and neither with macros
>> either) but I was trying to make something similar to this "yielding
>> return" construct from C#, namely: when a function X "yields returns"
>> some value everything happens like it has actually returned that
>> value; however the state (local vars, IP) is saved, so that when X is
>> called again it starts execution in the next statement after the last
>> "yield return".


> What problem are you trying to solve?


I don't know, but I *do* know yield as implemented in Python[1].

It's neat precisely because it's not easily implemented via anything
else. In interesting cases you have to keep track of state manually,
in a problem-specific manner; the interface tends to become "advance
this state machine one step and look for a generated value".

At that point ... yeah, what problem is the OP trying to solve?
He won't be able to create a general mechanism, that's for sure.

/Jorgen

[1] I suspect both Python and C# borrowed it from elsewhere.

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
 
Reply With Quote
 
Player
Guest
Posts: n/a
 
      01-03-2013
- About the line numbers:
Yes, I wasn't sure about that either, but I thought it wouldn't hurt since maybe something like "sed s/[0-9]*\ //g" would et rid of them; anyway, If I post code I'll omit it.

- About the "yield return", I was writting an 'iterative' parser that needsto update some global state at each keypress (im using curses) and then, at some time, I thought that this yield return thing would save me a lot of boiler plate code. Anyway, it's usefull for many other things: on articles about C#'s yield return feature there are lots of examples, mainly focused on get things done without locking the UI thread (btw I'm far from being a C# fan, I just liked that feature).

- So, I see the problem is the specification of setjmp was made (maybe) already thinking that the implementation would just save the registers, and once the function which calles setjmp is returned anything the contents at which SP pointed may have already changed.

- Is there a portable way of doing such a thing? Maybe using getcontext/setcontext?
 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      01-03-2013
On 1/3/2013 17:18, Player wrote:
>
> - Is there a portable way of doing such a thing? Maybe using getcontext/setcontext?
>


That's a common approach for POSIX, but it's not Standard C. If you're
using POSIX, you'll be able to find some examples.

- Shao Miller
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
IIS on XP Pro is now so slow it's not usable. =?Utf-8?B?V2ViQnVpbGRlcjQ1MQ==?= ASP .Net 6 09-22-2005 06:50 PM
How to join aspnet_Profile and aspnet_Users and get usable data. RedEye ASP .Net 0 09-01-2005 04:41 PM
Oldest usable computer for development ? msswasstastic@aol.com HTML 6 06-13-2005 06:52 PM
menu usable? chlori HTML 61 11-07-2004 10:56 PM
Which fixed size (width) font are usable/recommendable ? Arthur Connor HTML 9 01-07-2004 01:27 AM



Advertisments