[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [MiNT] tfork() bug.



Am Di, 15.05.2012, 23:42 schrieb Alan Hourihane:

>> Can somebody confirm this bug and that I understand it correctly?
>
> A small test case would help. Can you code one up ?

I think it is more about looking at the source, but sure - here it is. And
as I said, the race condition is taking place first.  The PoC is attached.

compile:

m68k-atari-mint-gcc ./poc_tfork.c -Wall -o test_tfork.prg

output of test_tfork.prg:

1: env pointer: 0x158e000
3: env pointer: 0x158e000
4: env pointer: 0x15ae000
Thread 0 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 0: 165
1: env pointer: 0x1590000
3: env pointer: 0x1590000
4: env pointer: 0x15ae000
Thread 1 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 1: 166
1: env pointer: 0x1592000
3: env pointer: 0x1592000
4: env pointer: 0x15ae000
Thread 2 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 2: 167
1: env pointer: 0x1594000
3: env pointer: 0x1594000
4: env pointer: 0x15ae000
Thread 3 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 3: 168
1: env pointer: 0x1596000
3: env pointer: 0x1596000
4: env pointer: 0x15ae000
Thread 4 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 4: 169
1: env pointer: 0x1598000
3: env pointer: 0x1598000
4: env pointer: 0x15ae000
Thread 5 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 5: 170
1: env pointer: 0x159a000
3: env pointer: 0x159a000
4: env pointer: 0x15ae000
Thread 6 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 6: 171
1: env pointer: 0x159c000
3: env pointer: 0x159c000
4: env pointer: 0x15ae000
Thread 7 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 7: 172
1: env pointer: 0x159e000
3: env pointer: 0x159e000
4: env pointer: 0x15ae000
Thread 8 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 8: 173
1: env pointer: 0x15a0000
3: env pointer: 0x15a0000
4: env pointer: 0x15ae000
Thread 9 is active!
2: env pointer: 0x15ae000
start_and_wait_for_several_threads thread 9: 174
Thread 0 is active!
Thread 9 is active!
Thread 8 is active!
Thread 7 is active!
Thread 6 is active!
Thread 5 is active!
Thread 4 is active!
Thread 3 is active!
Thread 2 is active!
Thread 1 is active!
Thread 0 is active!
Thread 1 is active!
Thread 2 is active!
Thread 3 is active!
Thread 4 is active!
Thread 5 is active!
Thread 6 is active!
Thread 7 is active!
Thread 8 is active!
Thread 9 is active!
Thread 0 is active!
Thread 9 is active!
Thread 8 is active!
Thread 7 is active!
Thread 6 is active!
Thread 5 is active!
Thread 4 is active!
Thread 3 is active!
Thread 2 is active!
Thread 1 is active!
Thread 0 is active!
Thread 1 is active!
Thread 2 is active!
Thread 3 is active!
Thread 4 is active!
Thread 5 is active!
Thread 6 is active!
Thread 7 is active!
Thread 8 is active!
Thread 9 is active!

-- 
Greets,
Ole
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include <support.h>
#include <string.h>
#include <unistd.h>
#include <compiler.h>
#include <errno.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <mint/osbind.h>
#include <mint/mintbind.h>
#include <mint/basepage.h>

extern void _setstack( void *newsp );

#define SIZE 4096L

static void __CDECL
startup(register BASEPAGE *b)
{
        register int (*func)(long);
        register long arg;
        _setstack(((char *)b) + SIZE);
        func = (int (*)(long))b->p_dbase;
        arg = b->p_dlen;

        /* If this is a thread, it doesn't need
         * own copy of the environment, right?
         */
	printf( "3: env pointer: %p\n", b->p_env );
        Mfree(b->p_env);
        b->p_env = _base->p_env;
	printf( "4: env pointer: %p\n", b->p_env );

        /* copy from parents basepage for debuggers... */
        b->p_tbase = _base->p_tbase;
        b->p_tlen = _base->p_tlen;
        b->p_dbase = _base->p_dbase;
        b->p_dlen = _base->p_dlen;
        b->p_bbase = _base->p_bbase;
        b->p_blen = _base->p_blen;

        Pterm((*func)(arg));
}


/* this is the the equivalent of tfork(), implemented here so that I can  */
/* ad my own modifications */
/* the TOS fallback was ripped out. */
/* use long instead of int so vfork works OK with -mshort */
long
my_tfork(int (*func)(long), long arg)
{
        register BASEPAGE *b;
        register long pid;

        b = (BASEPAGE *)Pexec(PE_CBASEPAGE, 0L, "", 0L);
        (void)Mshrink(b, SIZE+256);
        b->p_tbase = (char *)startup;
        b->p_dbase = (char *)func;
        b->p_dlen = arg;
        b->p_hitpa = ((char *)b) + SIZE + 256;

	printf( "1: env pointer: %p\n", b->p_env );
 
        pid = Pexec(104, 0L, b, 0L);
	printf( "2: env pointer: %p\n", b->p_env );
        (void)Mfree(b->p_env);  /* free the memory */
        (void)Mfree(b);
        return pid;
}


static int start_and_wait_for_several_threads( int (*func)(long) )
{
	#define NTHREADS 10
	int stats[NTHREADS];
	int tids[NTHREADS];
	int options[NTHREADS];
	bool threads_running = true;
	bool running[NTHREADS];
	int i, err, res=0;

	for( i=0; i<NTHREADS; i++ ) {
		tids[i] = my_tfork( func, i );
		printf("%s thread %d: %d\n", __FUNCTION__, i, tids[i] );
		if( tids[i] > 0 ) {
			threads_running = true;
			running[i] = true;
		}
	}

	/* wait until all threads have finished: */
	while( threads_running ) {
		sleep( 1 );
		for( i=0; i<NTHREADS; i++ ) {
			options[i]  = WNOHANG;
			err = waitpid( tids[i], &stats[i], options[i] );
			if( err == tids[i] && stats[i] == 0 ) {
				running[i] = false;
			}
		}
		threads_running = false;
		for( i=0; i<NTHREADS; i++ ) {
			if( running[i] == true ){
				threads_running = true;
			}
		}
	}
	return( res );
	#undef NTHREADS
}

int test_thread( long arg ) {

	#define NRUNS 5
        int i;
	struct timeval tv;

        for( i=0; i<NRUNS; i++){
		printf("Thread %li is active!\n", arg );
		tv.tv_sec = 0;
       		tv.tv_usec = 90000;  
		select(0, NULL, NULL, NULL, &tv);
        }

        return( 0 );
	#undef NRUNS
}

int main( void ) {
	start_and_wait_for_several_threads( test_thread );
	return( 0 );
}