views:

213

answers:

5

Can anyone tell me how to insert a function call (say Yield() ) at random places inside a C function , so that each time the code is run , Yield() gets called from different parts of the code ?

I am faced with such a requirement as I'm using 2 threads in a cooperative threading environment , where unless the running thread yields the processor explicitly , the other (waiting) thread cannot start running. I don't want to place the Yield() call at a single point , since that makes the thread sequence deterministic. Without rewiring the entire environment (from cooperative to pre-emptive) , this is the only solution I can think of in which Thread_1() makes the Yield() call at random places inside it, allowing Thread_2() to take over.

Any insights into a different solution achieving the same end-goals is also welcome!

+3  A: 

A BFI solution is called for, I think

I think you will have to solve this the obvious way. You will need to make a wrapper for Yield() that makes a "random" decision on whether to call the real thing.

If you are not concerned with execution speed then I would make it a real C function, and if you are I might suggest a preprocessor macro.

So, something like:

#define Yield0() ((random() & 0xf) == 0 && Yield())

Choose the mask for the percentage chance of a call you want. For 0xf, and if random() has good low-order bit randomness, then you would see 1 Yield() in 16 calls. If you can use an MT or other high quality random number generator, the low order bits will be directly useful, otherwise you might want to random() >> 3 & ...

And you will just need to put Yield0() calls everywhere.

DigitalRoss
I'm potentially asking a very naive question - is it at all possible to "put" the Yield() calls at random places in the code, via some preprocessed macros ? An even better( and even more ambitious/naive) solution would be to make the calls to Yield() occur from random places in the code , everytime the code is "run". Any ideas ?
shan23
To do that, you'd have to write self-modifying code in assembly. What you're asking for is going to be nearly impossible otherwise.
Chris Lutz
Wow - i'm game for it !! Can you give me a few pointer/links as to how to go about doing so ? I've never done this before, but as with everything , there's always a first time !!
shan23
Self modifying code won't help here. Self-modification is more expensive than the fgetc(urandom) that people are suggesting because you have to get the randomness for that from somewhere and binary grep your code which additionally has to be in a w|x page. If you only want to have the yields at random places as you say(although I doubt it) your best solution is to write a simple preprocessor for your code that randomly replaces yield() by do_nothing().
jbcreix
Unfortunately , writing a pre-processor is not an option - will take up far too much time than allocated. Can't the C preprocessor be used for this ?
shan23
It can, but only with explicitly putting `Yield0()` everywhere, as stated above.
Joey
As for execution speed: The compiler should figure out that it can easily inline a one-line function. So I doubt that there would be a performance difference.
Joey
It would be fun if compiler optimizations hard-coded the return value of random. A preprocessor doesn't mean rewrite cpp but a one-liner in some of these scripting languages people use.
jbcreix
+2  A: 

I'd define a function something like:

void maybe_yield() { 
    if (rand() & 0x10)
        yield();
}

Then sprinkle calls to maybe_yield() throughout your code. Depending on how often you want yield to be called, you can change 0x10 to a constant with more bits set to get yield() called more often. Other than that, be sure to call srand() with a value that changes from one run to the next to get different sequences on different runs.

Jerry Coffin
+2  A: 

Actually, when you're running in a co-operatively threaded environment, you really do want determinism.

But, if you're hell-bent on doing it, you just need to make it random.

#include <stdlib.h>
// And make sure you seed the generator with srand() somewhere.
#define YIELD_CHANCE 15

#define yield Yield
#ifdef YIELD_CHANCE
    #if YIELD_CHANCE > 0
        #if YIELD_CHANCE <= 100
            #undef yield
            void yield(void) {
                if (rand() < (RAND_MAX / (100/YIELD_CHANCE)))
                    Yield();
                }
        #endif
    #endif
#endif

then change your Yield calls to yield and, depending on what value YIELD_CHANCE is set to at compile time, you'll get deterministic or non-deterministic behavior.

If it doesn't exist or is outside the range 1 through 100, yield will yield all the time. If it's within the range, then it will call the Yield function randomly, based on the probability you give it.

paxdiablo
Nice macro - but it still leaves me with the responsibility of "sprinkling" this macro everywhere in the code(1000's of lines !!) - so my question is , can the preprocessor place this macro at random points in the code ?
shan23
No, it can't. You would have to write your *own* preprocessor and ensure it's intelligent enough to not put them at the wrong places.
paxdiablo
+2  A: 

Option A: Why not call yield() when the thread gets stuck? Better yet, why not encapsulate that in every operation which potentially could get stuck:

int disk_read (...)
{
    begin_io ();
    while (!io_completed  &&  !timed_out())
         yield();
    if (timed_out())
        // etc.
     ...
}

Option B: Usually—with cooperative yielding—when the other thread is not ready to run, yield() is a no-op. Therefore, put it everywhere:

void thread1 (...)
{
    yield();
    do_something_a();
    yield();
    do_something_b();
    yield();
    do_something_c();
    ...
}

Option C: Trust that processors are plenty fast and waiting for things occurs often enough that minimal yields() work just fine:

void thread1 (...)
{
    init();
    while (...)
    {
        do_heavy_crunching();
        yield();
        do_something_else();
    }
}

In hundreds of real-world applications, Option C works just fine. The determinism usually helps, not hurts.

wallyk
+1 Nice comprehensive answer - but what I'm really interested in is a varaint of option B - the difference being that it is the preprocessor that "sprinkles" the calls to yield() at random points in the code , simulating a pre-emptive behaviour.I agree cooperative threads are very useful in removing synchronisation problems(entirely) , but what I'm trying to simulate (through my test) is a condition that can happen in real-world multithreaded OS , and hence my endeavours !!
shan23
+1  A: 

You say you don't want a preprocessor, but it makes it so much easier.

   #!/usr/bin/perl
   chomp(my $n =<stdin>);
   open (my $f, '<', $n);
   while (my $l = <$f>) {
        print $l;
        if ($l =~ /^[\s][^\.]/) {
            $r=rand();
           if ( int($r*5) == 1 ) {
                print "\tcall Yield\n";
            }
        }
    }

This perl script(my first ever) will read a filename from stdin and insert a call randomly into gcc -S generated assembly which can then be compiled easily. It might not work as is for your compiler/arch, but regexes can do almost anything.

A nice addition would be to add a yield always before jump instructions for your processor. This saves you the sprinkling. Finally before jumps you could be using a wrapper function that calls random().

jbcreix
Thanks for the script - u r right , it can't be used as is , but i'll try it out...
shan23
One of the advantages of doing it in assembly is that theoretically it is more likely to jump at inconvenient places, so you get real thread-like behavior. The other is that even in the worst case you just have to check for all the opcodes in your processor. Doing something similar to C source or in machine code would require far more work.
jbcreix