#include <stdio.h>
#include <pthread.h>

/* This is an example code demonstrating the use of pthread_cancel()
 * to topple sibling threads in a loop optimized with parallel
 * execution, for use by the OpenMP article
 * at http://bisqwit.iki.fi/story/howto/openmp/ .
 *
 * Note that this example does not contain any OpenMP code.
 *
 * Copyright (C) 1992,2007 Joel Yliluoma (http://iki.fi/bisqwit/)
 */
 
struct NeedleFinderThreadParam
{
  const char* haystack;
  size_t size;
  char needle;
  
  int threadnum;
  pthread_t* threads;
  
  const char** result;
};

const int n_threads = 2;

/* Actual workslave function for the parallel FindAnyNeedle(). */
void* FindAnyNeedleFunc(void* p)
{
  NeedleFinderThreadParam& params = *(NeedleFinderThreadParam*)p;
  
  /* Make myself cancellable */
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0);
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0);
  
  int mythreadnum = params.threadnum;
  
  size_t begin = (mythreadnum  ) * params.size / n_threads;
  size_t end   = (mythreadnum+1) * params.size / n_threads;
  
  const char* haystack = params.haystack;
  char        needle   = params.needle;

  for(size_t p = begin; p < end; ++p)
    if(haystack[p] == needle)
    {
      /* Found result */
      *params.result = haystack + p;
      /* Topple sibling threads */
      for(int t=0; t<n_threads; ++t)
        if(t != mythreadnum)
        {
          pthread_cancel(params.threads[t]);
        }
      break;
    }
  return NULL;
}

/* Returns any position from the haystack where the needle can
 * be found, or NULL if no such position exists. It is not guaranteed
 * to find the first matching position; it only guarantees to find
 * _a_ matching position if one exists.
 */
const char* FindAnyNeedle(const char* haystack, size_t size, char needle)
{
  pthread_t threads[n_threads];
  NeedleFinderThreadParam params[n_threads];
  
  const char* result = 0;
  for(size_t p=0; p<n_threads; ++p)
  {
    params[p].haystack  = haystack;
    params[p].size      = size;
    params[p].needle    = needle;
    params[p].threadnum = p;
    params[p].threads   = threads;
    params[p].result    = &result;
  }
  for(size_t p=0; p<n_threads; ++p)
  {
    pthread_create(&threads[p], NULL,
        FindAnyNeedleFunc,
        (void*)&params[p]);
  }
  for(size_t p=0; p<n_threads; ++p)
  {
    pthread_join(threads[p], 0);
  }
  return result;
}


/*
Note: This code still has a fatal error: If a thread finds a
result before all the threads are created, the results are
undefined. It will call pthread_cancel() with uninitialized
thread pointers.
*/
