nuttx-apps/examples/smp/smp_main.c

351 lines
9.9 KiB
C

/****************************************************************************
* examples/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_EXAMPLES_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 CONFIG_BUILD_KERNEL
int main(int argc, FAR char *argv[])
#else
int smp_main(int argc, char *argv[])
#endif
{
pthread_t threadid[CONFIG_EXAMPLES_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_EXAMPLES_SMP_NBARRIER_THREADS);
for (i = 0; i <= CONFIG_EXAMPLES_SMP_NBARRIER_THREADS; i++)
{
#if defined(CONFIG_SMP) && defined(CONFIG_BUILD_FLAT)
g_thread_cpu[i] = IMPOSSIBLE_CPU;
#endif
if (i < CONFIG_EXAMPLES_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_EXAMPLES_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_EXAMPLES_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_EXAMPLES_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_EXAMPLES_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;
}