text: /****************************************************************************
   * apps/testing/smp/smp_main.c
   *
   *   Copyright (C) 2016 Gregory Nutt. All rights reserved.
   *   Author: Gregory Nutt <gnutt@nuttx.org>
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   * 3. Neither the name NuttX nor the names of its contributors may be
   *    used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <pthread.h>
  #include <string.h>
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  #define HOG_MSEC       1000
  #define YIELD_MSEC     100
  #define IMPOSSIBLE_CPU -1
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  static pthread_barrier_t g_smp_barrier;
  
  #if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT)
  static volatile int g_thread_cpu[CONFIG_TESTING_SMP_NBARRIER_THREADS+1];
  #endif
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: show_cpu / show_cpu_conditional
   *
   * Description:
   *   This depends on internal OS interfaces that are not generally available
   *   but can be accessed (albeit inappropriatley) in a FLAT build
   *
   ****************************************************************************/
  
  #if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT)
  int up_cpu_index(void);
  
  static void show_cpu(FAR const char *caller, int threadno)
  {
    g_thread_cpu[threadno] = up_cpu_index();
    printf("%s[%d]: Running on CPU%d\n", caller, threadno, g_thread_cpu[threadno]);
  }
  
  static void show_cpu_conditional(FAR const char *caller, int threadno)
  {
    int cpu = up_cpu_index();
  
    if (cpu != g_thread_cpu[threadno])
      {
        g_thread_cpu[threadno] = cpu;
        printf("%s[%d]: Now running on CPU%d\n", caller, threadno, cpu);
      }
  }
  #else
  #  define show_cpu(c)
  #  define show_cpu_conditional(c)
  #endif
  
  /****************************************************************************
   * Name: hog_milliseconds
   *
   * Description:
   *   Delay inline for the specified number of milliseconds.
   *
   ****************************************************************************/
  
  static void hog_milliseconds(unsigned int milliseconds)
  {
    volatile unsigned int i;
    volatile unsigned int j;
  
    for (i = 0; i < milliseconds; i++)
      {
        for (j = 0; j < CONFIG_BOARD_LOOPSPERMSEC; j++)
          {
          }
      }
  }
  
  /****************************************************************************
   * Name: hog_time
   *
   * Description:
   *   Delay for awhile, calling pthread_yield() now and then to allow other
   *   pthreads to get CPU time.
   *
   ****************************************************************************/
  
  static void hog_time(FAR const char *caller, int threadno)
  {
    unsigned int remaining = HOG_MSEC;
    unsigned int hogmsec;
  
    while (remaining > 0)
      {
        /* Hog some CPU */
  
        hogmsec = YIELD_MSEC;
        if (hogmsec > remaining)
          {
            hogmsec = remaining;
          }
  
        hog_milliseconds(hogmsec);
        remaining -= hogmsec;
  
        /* Let other threads run */
  
        (void)pthread_yield();
        show_cpu_conditional(caller, threadno);
      }
  }
  
  /****************************************************************************
   * Name: barrier_thread
   ****************************************************************************/
  
  static pthread_addr_t barrier_thread(pthread_addr_t parameter)
  {
    int threadno  = (int)((intptr_t)parameter);
    int ret;
  
    printf("Thread[%d]: Started\n",  threadno);
    show_cpu("Thread", threadno);
  
    /* Hog some CPU time */
  
    hog_time("Thread", threadno);
  
    /* Wait at the barrier until all threads are synchronized. */
  
    printf("Thread[%d]: Calling pthread_barrier_wait()\n",
           threadno);
    fflush(stdout);
    show_cpu_conditional("Thread", threadno);
  
    ret = pthread_barrier_wait(&g_smp_barrier);
    if (ret == 0)
      {
        printf("Thread[%d]: Back with ret=0 (I am not special)\n",
               threadno);
      }
    else if (ret == PTHREAD_BARRIER_SERIAL_THREAD)
      {
        printf("Thread[%d]: Back with "
               "ret=PTHREAD_BARRIER_SERIAL_THREAD (I AM SPECIAL)\n",
               threadno);
      }
    else
      {
        printf("Thread[%d]: ERROR could not get semaphore value\n",
               threadno);
      }
  
    fflush(stdout);
    show_cpu_conditional("Thread", threadno);
  
    /* Hog some more CPU time */
  
    hog_time("Thread", threadno);
  
    /* Then exit */
  
    printf("Thread[%d]: Done\n",  threadno);
    fflush(stdout);
    show_cpu_conditional("Thread", threadno);
    return NULL;
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * smp_main
   ****************************************************************************/
  
  #ifdef BUILD_MODULE
  int main(int argc, FAR char *argv[])
  #else
  int smp_main(int argc, char *argv[])
  #endif
  {
    pthread_t threadid[CONFIG_TESTING_SMP_NBARRIER_THREADS];
    pthread_addr_t result;
    pthread_attr_t attr;
    pthread_barrierattr_t barrierattr;
    int errcode = EXIT_SUCCESS;
    int ret;
    int i;
  
    /* Initialize data */
  
    memset(threadid, 0, sizeof(pthread_t) * CONFIG_TESTING_SMP_NBARRIER_THREADS);
    for (i = 0; i <= CONFIG_TESTING_SMP_NBARRIER_THREADS; i++)
      {
  #if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT)
        g_thread_cpu[i] = IMPOSSIBLE_CPU;
  #endif
        if (i < CONFIG_TESTING_SMP_NBARRIER_THREADS)
          {
            threadid[i] = 0;
          }
      }
  
    show_cpu("  Main", 0);
    printf("  Main[0]: Initializing barrier\n");
  
    ret = pthread_barrierattr_init(&barrierattr);
    if (ret != OK)
      {
        printf("  Main[0]: pthread_barrierattr_init failed, ret=%d\n", ret);
  
        errcode = EXIT_FAILURE;
        goto errout;
      }
  
    ret = pthread_barrier_init(&g_smp_barrier, &barrierattr,
                               CONFIG_TESTING_SMP_NBARRIER_THREADS);
    if (ret != OK)
      {
        printf("  Main[0]: pthread_barrierattr_init failed, ret=%d\n", ret);
  
        errcode = EXIT_FAILURE;
        goto errout_with_attr;
      }
  
    /* Create the barrier */
  
    (void)pthread_barrierattr_init(&barrierattr);
  
    /* Start CONFIG_TESTING_SMP_NBARRIER_THREADS thread instances */
  
    ret = pthread_attr_init(&attr);
    if (ret != OK)
      {
        printf("  Main[0]: pthread_attr_init failed, ret=%d\n", ret);
  
        errcode = EXIT_FAILURE;
        goto errout_with_barrier;
      }
  
    for (i = 0; i < CONFIG_TESTING_SMP_NBARRIER_THREADS; i++)
      {
        ret = pthread_create(&threadid[i], &attr, barrier_thread,
                             (pthread_addr_t)((uintptr_t)i+1));
        if (ret != 0)
          {
            printf("  Main[0]: Error in thread %d create, ret=%d\n",  i+1, ret);
            printf("  Main[0]: Test aborted with waiting threads\n");
  
            errcode = EXIT_FAILURE;
            break;
          }
        else
          {
            printf("  Main[0]: Thread %d created\n", i+1);
          }
  
        show_cpu_conditional("  Main", 0);
      }
  
    fflush(stdout);
    show_cpu_conditional("  Main", 0);
  
    /* Wait for all thread instances to complete */
  
    for (i = 0; i < CONFIG_TESTING_SMP_NBARRIER_THREADS; i++)
      {
        if (threadid[i] != 0)
          {
            ret = pthread_join(threadid[i], &result);
            show_cpu_conditional("  Main", 0);
  
            if (ret != 0)
              {
                printf("  Main[0]: Error in thread %d join, ret=%d\n", i+1, ret);
                errcode = EXIT_FAILURE;
              }
            else
              {
                printf("  Main[0]: Thread %d completed with result=%p\n", i+1, result);
              }
          }
      }
  
    /* Destroy the barrier */
  
  errout_with_barrier:
    ret = pthread_barrier_destroy(&g_smp_barrier);
    if (ret != OK)
      {
        printf("  Main[0]: pthread_barrier_destroy failed, ret=%d\n", ret);
      }
  
  errout_with_attr:
    ret = pthread_barrierattr_destroy(&barrierattr);
    if (ret != OK)
      {
        printf("  Main[0]: pthread_barrierattr_destroy failed, ret=%d\n",
               ret);
      }
  
  errout:
    fflush(stdout);
    show_cpu_conditional("  Main", 0);
    return errcode;
  }
  