
/*
 * test program from
 * Debian Bug report logs - #241156
 * electric-fence (2.1.13-0.1) breaks with 2.6.x kernel
 *
 * changed to compile on Windows by Hayati Ayguen
 */

/* $Id: testmt.c,v 1.13 2008/08/03 22:47:58 h_ayguen Exp $ */
/* gcc foo.c -pthread -lefence -g -ggdb -o foo */


/* (defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__)) */
/* remove "|| defined(__CYGWIN__)" in following line
 * to test with pthread library on Win32-Cygwin
 */
#if ( defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) )

#include <windows.h>

#else

#include <pthread.h>
#include <unistd.h>
#include <errno.h>

#define HAVE_PTHREADS

#endif


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "duma.h"


volatile int iKillThreads = 0;

#define MAX_NUM_THREADS   32

volatile int aiCounter[MAX_NUM_THREADS];

/* thread function
 * permanently allocate, initialize and deallocate in a loop
 */
#ifdef  HAVE_PTHREADS
void* poster(void* arg)
#else
DWORD WINAPI poster( LPVOID arg )
#endif
{
  char* foo = NULL;
  int iThreadNo = *((int*)arg);

#if 0
  /* sleep a bit, that other threads can get started
   * before producing high load
   */
#ifdef  HAVE_PTHREADS
  sleep(1);  /* wait 1 sec  */
#else
  Sleep(1000);
#endif
#endif

  for( ; !iKillThreads; )
  {
    foo = (char*)malloc(4096);
    if (foo)
    {
      memset(foo, 0, 4096);
      free(foo);
    }
    ++ aiCounter[iThreadNo];
  }

#ifdef  HAVE_PTHREADS
  pthread_exit(NULL);
#endif
  return 0;
}


#define MAX_NUM_THREADS   32

int main(int argc, char *argv[])
{
  int iSleepTime  = 60; /* in seconds; default = 30 sec */
  int iNumThreads = 4;
  int i;

#ifdef  HAVE_PTHREADS
  pthread_t tId[MAX_NUM_THREADS];
#else
  HANDLE    tId[MAX_NUM_THREADS];
#endif
  int tArg[MAX_NUM_THREADS];


#ifdef DUMA_EXPLICIT_INIT
  /* necessary on some platforms!
   * like on Win32-Cygwin
   */
  duma_init();
#endif

  if ( argc > 1 )
  {
    i = atoi(argv[1]);
    if ( i > 0 )
      iSleepTime = i;
  }
  if ( argc > 2 )
  {
    i = atoi(argv[2]);
    if ( i > 0 )
      iNumThreads = i;
    if ( iNumThreads > MAX_NUM_THREADS )
      iNumThreads = MAX_NUM_THREADS;
  }

  fprintf(stdout, "running %d threads for %d secs ..", iNumThreads, iSleepTime);
  fflush(stdout);

  for ( i = 0; i < iNumThreads; ++i )
  {
    tArg[i] = i;
    aiCounter[i] = 0;
  }

#ifdef  HAVE_PTHREADS
  fprintf(stdout, "creating threads with pthread library .. \n");
  fflush(stdout);
  
  for ( i = 0; i < iNumThreads; ++i )
  {
    pthread_t *pt = &tId[i];
    int r = pthread_create( pt, NULL, poster, &tArg[i] );
    if ( r )
    {
      fprintf(stderr, "\nerror in pthread_create() for thread %d: ", i);
      switch(r)
      {
        case EAGAIN:  fprintf(stderr, "EAGAIN");   break;
        case EINVAL:  fprintf(stderr, "EINVAL");   break;
        case EPERM:   fprintf(stderr, "EPERM");    break;
        default:      fprintf(stderr, "unexpected!"); break;
      }
    }
  }
  fprintf(stdout, ".. creating done\n");
  fflush(stdout);

  /* sleep(iSleepTime); */
  for ( i = 0; i < iSleepTime; ++i )
  {
    sleep(1);  /* wait 1 sec  */
    fprintf(stdout, ".");
    fflush(stdout);
  }
#else
  fprintf(stdout, "creating threads with Win32 API calls\n");
  fflush(stdout);

  for ( i = 0; i < iNumThreads; ++i )
  {
    tId[i] = CreateThread( NULL    /* default security attributes */
                    , 0       /* use default stack size */
                    , poster  /* thread function name */
                    , &tArg[i]/* argument to thread function */
                    , 0       /* use default creation flags */
                    , NULL    /* returns the thread identifier */
                    );
    if ( NULL == tId[i] )
      fprintf(stderr, "\nerror in CreateThread() for thread %d: ", i);
  }
  /* Sleep(iSleepTime*1000); */
  for ( i =0; i < iSleepTime; ++i )
  {
    Sleep(1000);  /* wait 1000 ms  */
    fprintf(stdout, ".");
    fflush(stdout);
  }
#endif

  fprintf(stdout, "..done\n");
  iKillThreads = 1;
  fprintf(stdout, "wating threads to end ..");
  fflush(stdout);

#ifdef  HAVE_PTHREADS
  for ( i = 0; i < iNumThreads; ++i )
  {
    pthread_t *pt = &tId[i];
    int r = pthread_join( *pt, NULL );
    if ( r )
    {
      fprintf(stderr, "\nerror in pthread_join() for thread %d: ", i);
      switch(r)
      {
        case EINVAL:  fprintf(stderr, "EINVAL");   break;
        case ESRCH:   fprintf(stderr, "ESRCH");    break;
        case EDEADLK: fprintf(stderr, "EDEADLK");  break;
        default:      fprintf(stderr, "unexpected!"); break;
      }
    }
  }
#else
  // Wait until all threads have terminated.
  WaitForMultipleObjects( iNumThreads, tId, TRUE, INFINITE);
  for ( i = 0; i < iNumThreads; ++i )
    CloseHandle(tId[i]);
#endif
  fprintf(stdout, "..done\n");

  fprintf(stdout, "state:\n");
  for ( i = 0; i < iNumThreads; ++i )
    fprintf(stdout, "Thread %d did %d (de)allocations\n", i, aiCounter[i]);
  fflush(stdout);

  return 0;
}

