Add test for CONFIG_SEM_PREALLOCHOLDERS > 0

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@1596 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2009-03-13 00:25:05 +00:00
parent e09b3b3ecd
commit 264806e5b2
8 changed files with 233 additions and 101 deletions

View File

@ -1627,9 +1627,18 @@ The system can be re-made subsequently by just typing <code>make</code>.
errorcheck mutexes. Enables <code>pthread_mutexattr_settype()</code>. errorcheck mutexes. Enables <code>pthread_mutexattr_settype()</code>.
</li> </li>
<li> <li>
<code>CONFIG_PRIORITY_INHERITANCE </code>: Set to enable support for <code>CONFIG_PRIORITY_INHERITANCE</code>: Set to enable support for
priority inheritance on mutexes and semaphores. priority inheritance on mutexes and semaphores.
</li> </li>
<li>
<code>CONFIG_SEM_PREALLOCHOLDERS: </code>: This setting is only used
if priority inheritance is enabled.
It defines the maximum number of different threads (minus one) that
can take counts on a semaphore with priority inheritance support.
This may be set to zero if priority inheritance is disabled OR if you
are only using semaphores as mutexes (only one holder) OR if no more
than two threads participate using a counting semaphore.
</li>
</ul> </ul>
<p> <p>

View File

@ -199,6 +199,14 @@ defconfig -- This is a configuration file similar to the Linux
errorcheck mutexes. Enables pthread_mutexattr_settype(). errorcheck mutexes. Enables pthread_mutexattr_settype().
CONFIG_PRIORITY_INHERITANCE - Set to enable support for CONFIG_PRIORITY_INHERITANCE - Set to enable support for
priority inheritance on mutexes and semaphores. priority inheritance on mutexes and semaphores.
CONFIG_SEM_PREALLOCHOLDERS: This setting is only used if priority
inheritance is enabled. It defines the maximum number of
different threads (minus one) that can take counts on a
semaphore with priority inheritance support. This may be
set to zero if priority inheritance is disabled OR if you
are only using semaphores as mutexes (only one holder) OR
if no more than two threads participate using a counting
semaphore.
The following can be used to disable categories of APIs supported The following can be used to disable categories of APIs supported
by the OS. If the compiler supports weak functions, then it by the OS. If the compiler supports weak functions, then it

View File

@ -85,7 +85,14 @@ CONFIG_ARCH_BOARD_SIM=y
# CONFIG_MUTEX_TYPES: Set to enable support for recursive and # CONFIG_MUTEX_TYPES: Set to enable support for recursive and
# errorcheck mutexes. Enables pthread_mutexattr_settype(). # errorcheck mutexes. Enables pthread_mutexattr_settype().
# CONFIG_PRIORITY_INHERITANCE : Set to enable support for priority # CONFIG_PRIORITY_INHERITANCE : Set to enable support for priority
# inheritance on mutexes and semaphores. # inheritance on mutexes and semaphores.
# CONFIG_SEM_PREALLOCHOLDERS: This setting is only used if priority
# inheritance is enabled. It defines the maximum number of different
# threads (minus one) that can take counts on a semaphore with priority
# inheritance support. This may be set to zero if priority inheritance
# is disabled OR if you are only using semaphores as mutexes (only
# one holder) OR if no more than two threads participate using a
# counting semaphore.
# #
CONFIG_EXAMPLE=ostest CONFIG_EXAMPLE=ostest
CONFIG_DEBUG=y CONFIG_DEBUG=y
@ -103,6 +110,7 @@ CONFIG_DEV_CONSOLE=y
CONFIG_DEV_LOWCONSOLE=n CONFIG_DEV_LOWCONSOLE=n
CONFIG_MUTEX_TYPES=y CONFIG_MUTEX_TYPES=y
CONFIG_PRIORITY_INHERITANCE=n CONFIG_PRIORITY_INHERITANCE=n
CONFIG_SEM_PREALLOCHOLDERS=3
# #
# The following can be used to disable categories of # The following can be used to disable categories of

View File

@ -42,6 +42,7 @@
#include <unistd.h> #include <unistd.h>
#include <semaphore.h> #include <semaphore.h>
#include <pthread.h> #include <pthread.h>
#include <errno.h>
#ifdef CONFIG_ARCH_SIM #ifdef CONFIG_ARCH_SIM
# include <nuttx/arch.h> # include <nuttx/arch.h>
@ -55,6 +56,12 @@
* Definitions * Definitions
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_SEM_PREALLOCHOLDERS
# define CONFIG_SEM_PREALLOCHOLDERS 0
#endif
#define NLOWPRI_THREADS (CONFIG_SEM_PREALLOCHOLDERS+1)
#define NHIGHPRI_THREADS 1
/**************************************************************************** /****************************************************************************
* Private Data * Private Data
****************************************************************************/ ****************************************************************************/
@ -72,43 +79,82 @@ enum thstate_e
}; };
static sem_t g_sem; static sem_t g_sem;
static volatile enum thstate_e g_middlestate = NOTSTARTED; static volatile enum thstate_e g_middlestate;
static volatile enum thstate_e g_highstate = NOTSTARTED; static volatile enum thstate_e g_highstate[NHIGHPRI_THREADS];
static volatile enum thstate_e g_lowstate = NOTSTARTED; static volatile enum thstate_e g_lowstate[NLOWPRI_THREADS];
static int g_highpri; static int g_highpri;
static int g_medpri; static int g_medpri;
static int g_lowpri; static int g_lowpri;
/****************************************************************************
* Name: nhighpri_started
****************************************************************************/
static int nhighpri_started(void)
{
int n = 0;
int i;
for (i = 0; i < NHIGHPRI_THREADS; i++)
{
if (g_highstate[i] != NOTSTARTED)
{
n++;
}
}
return n;
}
/****************************************************************************
* Name: nhighpri_running
****************************************************************************/
static int nhighpri_running(void)
{
int n = 0;
int i;
for (i = 0; i < NHIGHPRI_THREADS; i++)
{
if (g_highstate[i] != DONE)
{
n++;
}
}
return n;
}
/**************************************************************************** /****************************************************************************
* Name: highpri_thread * Name: highpri_thread
****************************************************************************/ ****************************************************************************/
static void *highpri_thread(void *parameter) static void *highpri_thread(void *parameter)
{ {
int threadno = (int)parameter;
int ret; int ret;
printf("highpri_thread: Started\n"); printf("highpri_thread-%d: Started\n", threadno);
fflush(stdout); fflush(stdout);
g_highstate = WAITING; g_highstate[threadno-1] = WAITING;
ret = sem_wait(&g_sem); ret = sem_wait(&g_sem);
g_highstate = DONE; g_highstate[threadno-1] = DONE;
if (ret != 0) if (ret != 0)
{ {
printf("highpri_thread: sem_take failed: %d\n", ret); printf("highpri_thread-%d: sem_take failed: %d\n", threadno, ret);
} }
else if (g_middlestate == RUNNING) else if (g_middlestate == RUNNING)
{ {
printf("highpri_thread: SUCCESS midpri_thread is still running!\n"); printf("highpri_thread-%d: SUCCESS midpri_thread is still running!\n", threadno);
} }
else else
{ {
printf("highpri_thread: ERROR -- midpri_thread has already exited!\n"); printf("highpri_thread-%d: ERROR -- midpri_thread has already exited!\n", threadno);
} }
sem_post(&g_sem); sem_post(&g_sem);
printf("medpri_thread: Okay... I'm done!\n"); printf("highpri_thread-%d: Okay... I'm done!\n", threadno);
fflush(stdout); fflush(stdout);
return NULL; return NULL;
} }
@ -156,7 +202,7 @@ static void *medpri_thread(void *parameter)
* will be boosted. * will be boosted.
*/ */
while (g_highstate != DONE) while (nhighpri_running() > 0)
{ {
hog_cpu(); hog_cpu();
} }
@ -175,87 +221,125 @@ static void *lowpri_thread(void *parameter)
{ {
void *retval = (void*)-1; void *retval = (void*)-1;
struct sched_param sparam; struct sched_param sparam;
int threadno = (int)parameter;
int expected;
int count;
int policy; int policy;
int ret; int ret;
int nrunning;
int i;
g_lowstate = RUNNING; g_lowstate[threadno-1] = RUNNING;
printf("lowpri_thread: Started\n"); printf("lowpri_thread-%d: Started\n", threadno);
ret = pthread_getschedparam(pthread_self(), &policy, &sparam); ret = pthread_getschedparam(pthread_self(), &policy, &sparam);
if (ret != 0) if (ret != 0)
{ {
printf("lowpri_thread: ERROR pthread_getschedparam failed: %d\n", ret); printf("lowpri_thread-%d: ERROR pthread_getschedparam failed: %d\n", threadno, ret);
} }
else else
{ {
printf("lowpri_thread: initial priority: %d\n", sparam.sched_priority); printf("lowpri_thread-%d: initial priority: %d\n", threadno, sparam.sched_priority);
if (sparam.sched_priority != g_lowpri) if (sparam.sched_priority != g_lowpri)
{ {
printf(" ERROR should have been %d\n", g_lowpri); printf(" ERROR should have been %d\n", g_lowpri);
} }
} }
g_lowstate = WAITING; g_lowstate[threadno-1] = WAITING;
ret = sem_wait(&g_sem); ret = sem_wait(&g_sem);
if (ret != 0) if (ret != 0)
{ {
printf("lowpri_thread: sem_take failed: %d\n", ret); printf("lowpri_thread-%d: sem_take failed: %d\n", threadno, ret);
} }
else else
{ {
/* Hang on to the thread until the middle priority thread runs */ /* Hang on to the thread until the middle priority thread runs */
while (g_middlestate == NOTSTARTED && g_highstate != WAITING) while (g_middlestate == NOTSTARTED && nhighpri_started() < NHIGHPRI_THREADS)
{ {
printf("lowpri_thread: Waiting for the midle pri task to run\n"); printf("lowpri_thread-%d: Waiting for the midle pri task to run\n", threadno);
printf(" g_middlestate=%d g_highstate=%d\n", (int)g_middlestate, (int)g_highstate); printf(" g_middlestate: %d\n", (int)g_middlestate);
printf(" I still have the semaphore\n"); for (i = 0; i < NHIGHPRI_THREADS; i++)
{
printf(" g_highstate[%d]: %d\n", i, (int)g_highstate[i]);
}
printf(" I still have a count on the semaphore\n");
fflush(stdout); fflush(stdout);
sleep(1); sleep(1);
} }
/* Account for all of the semaphore counts. At any given time if there are 'n'
* running hight prioity tasks, then the semaphore count should be '-n'
*/
sched_lock(); /* Needs to be atomic */
ret = sem_getvalue(&g_sem, &count);
nrunning = nhighpri_running();
sched_unlock();
if (ret < 0)
{
printf("lowpri_thread-%d: ERROR sem_getvalue failed: %d\n", threadno, errno);
}
printf("lowpri_thread-%d: Sem count: %d, No. highpri thread: %d\n", threadno, count, nrunning);
/* The middle priority task is running, let go of the semaphore */ /* The middle priority task is running, let go of the semaphore */
if (g_middlestate == RUNNING && g_highstate == WAITING) if (g_middlestate == RUNNING && nrunning == -count)
{ {
/* Good.. the middle priority task is still running but we got priority! */ /* Good.. the middle priority task is still running and the counts are okay. */
retval = NULL; retval = NULL;
} }
else else
{ {
printf("lowpri_thread: ERROR the middle priority task has already exitted!\n"); printf("lowpri_thread-%d: ERROR the middle priority task has already exitted!\n", threadno);
printf(" g_middlestate=%d g_highstate=%d\n", (int)g_middlestate, (int)g_highstate); printf(" g_middlestate: %d sem count=%d\n", (int)g_middlestate, count);
} for (i = 0; i < NHIGHPRI_THREADS; i++)
{
printf(" g_highstate[%d]: %d\n", i, (int)g_highstate[i]);
}
}
} }
ret = pthread_getschedparam(pthread_self(), &policy, &sparam); ret = pthread_getschedparam(pthread_self(), &policy, &sparam);
sem_post(&g_sem); sem_post(&g_sem);
if (ret != 0) if (ret != 0)
{ {
printf("lowpri_thread: ERROR pthread_getschedparam failed: %d\n", ret); printf("lowpri_thread-%d: ERROR pthread_getschedparam failed: %d\n", threadno, ret);
} }
else else
{ {
printf("lowpri_thread: %s priority before sem_post: %d\n", if (nhighpri_running() > 0)
sparam.sched_priority != g_highpri ? "ERROR" : "SUCCESS", {
expected = g_highpri;
}
else
{
expected = g_lowpri;
}
printf("lowpri_thread-%d: %s priority before sem_post: %d\n",
threadno,
sparam.sched_priority != expected ? "ERROR" : "SUCCESS",
sparam.sched_priority); sparam.sched_priority);
if (sparam.sched_priority != g_highpri) if (sparam.sched_priority != expected)
{ {
printf(" ERROR should have been %d\n", g_highpri); printf(" ERROR should have been %d\n", expected);
} }
} }
ret = pthread_getschedparam(pthread_self(), &policy, &sparam); ret = pthread_getschedparam(pthread_self(), &policy, &sparam);
if (ret != 0) if (ret != 0)
{ {
printf("lowpri_thread: ERROR pthread_getschedparam failed: %d\n", ret); printf("lowpri_thread-%d: ERROR pthread_getschedparam failed: %d\n", threadno, ret);
} }
else else
{ {
printf("lowpri_thread: %s final priority: %d\n", printf("lowpri_thread-%d: %s final priority: %d\n",
threadno,
sparam.sched_priority != g_lowpri ? "ERROR" : "SUCCESS", sparam.sched_priority != g_lowpri ? "ERROR" : "SUCCESS",
sparam.sched_priority); sparam.sched_priority);
@ -265,9 +349,9 @@ static void *lowpri_thread(void *parameter)
} }
} }
printf("lowpri_thread: Okay... I'm done!\n"); printf("lowpri_thread-%d: Okay... I'm done!\n", threadno);
fflush(stdout); fflush(stdout);
g_lowstate = DONE; g_lowstate[threadno-1] = DONE;
return retval; return retval;
} }
#endif /* CONFIG_PRIORITY_INHERITANCE && !CONFIG_DISABLE_SIGNALS && !CONFIG_DISABLE_PTHREAD */ #endif /* CONFIG_PRIORITY_INHERITANCE && !CONFIG_DISABLE_SIGNALS && !CONFIG_DISABLE_PTHREAD */
@ -283,14 +367,15 @@ static void *lowpri_thread(void *parameter)
void priority_inheritance(void) void priority_inheritance(void)
{ {
#if defined(CONFIG_PRIORITY_INHERITANCE) && !defined(CONFIG_DISABLE_SIGNALS) && !defined(CONFIG_DISABLE_PTHREAD) #if defined(CONFIG_PRIORITY_INHERITANCE) && !defined(CONFIG_DISABLE_SIGNALS) && !defined(CONFIG_DISABLE_PTHREAD)
pthread_t lowpri; pthread_t lowpri[NLOWPRI_THREADS];
pthread_t medpri; pthread_t medpri;
pthread_t highpri; pthread_t highpri[NHIGHPRI_THREADS];
pthread_addr_t result; pthread_addr_t result;
pthread_attr_t attr; pthread_attr_t attr;
struct sched_param sparam; struct sched_param sparam;
int my_pri; int my_pri;
int status; int status;
int i;
printf("priority_inheritance: Started\n"); printf("priority_inheritance: Started\n");
@ -306,37 +391,42 @@ void priority_inheritance(void)
g_lowpri = sched_get_priority_min(SCHED_FIFO); g_lowpri = sched_get_priority_min(SCHED_FIFO);
g_medpri = my_pri - 1; g_medpri = my_pri - 1;
sem_init(&g_sem, 0, 1); sem_init(&g_sem, 0, NLOWPRI_THREADS);
/* Start the low priority task */ /* Start the low priority threads */
printf("priority_inheritance: Starting lowpri_thread at %d\n", g_lowpri); for (i = 0; i < NLOWPRI_THREADS; i++)
status = pthread_attr_init(&attr);
if (status != 0)
{ {
printf("priority_inheritance: pthread_attr_init failed, status=%d\n", status); int threadno = i+1;
} printf("priority_inheritance: Starting lowpri_thread-%d (of %d) at %d\n",
sparam.sched_priority = g_lowpri; threadno, NLOWPRI_THREADS, g_lowpri);
status = pthread_attr_setschedparam(&attr,& sparam); status = pthread_attr_init(&attr);
if (status != OK) if (status != 0)
{ {
printf("priority_inheritance: pthread_attr_setschedparam failed, status=%d\n", status); printf("priority_inheritance: pthread_attr_init failed, status=%d\n", status);
} }
else sparam.sched_priority = g_lowpri;
{ status = pthread_attr_setschedparam(&attr,& sparam);
printf("priority_inheritance: Set lowpri_thread priority to %d\n", sparam.sched_priority); if (status != OK)
} {
printf("priority_inheritance: pthread_attr_setschedparam failed, status=%d\n", status);
}
else
{
printf("priority_inheritance: Set lowpri_thread-%d priority to %d\n",
threadno, sparam.sched_priority);
}
status = pthread_create(&lowpri, &attr, lowpri_thread, NULL); status = pthread_create(&lowpri[i], &attr, lowpri_thread, (void*)threadno);
if (status != 0) if (status != 0)
{ {
printf("priority_inheritance: pthread_create failed, status=%d\n", status); printf("priority_inheritance: pthread_create failed, status=%d\n", status);
}
} }
printf("priority_inheritance: Waiting...\n"); printf("priority_inheritance: Waiting...\n");
sleep(2); sleep(2);
/* Start the medium priority task */ /* Start the medium priority thread */
printf("priority_inheritance: Starting medpri_thread at %d\n", g_medpri); printf("priority_inheritance: Starting medpri_thread at %d\n", g_medpri);
status = pthread_attr_init(&attr); status = pthread_attr_init(&attr);
@ -365,44 +455,56 @@ void priority_inheritance(void)
printf("priority_inheritance: Waiting...\n"); printf("priority_inheritance: Waiting...\n");
sleep(1); sleep(1);
/* Start the high priority task */ /* Start the high priority threads */
printf("priority_inheritance: Starting highpri_thread at %d\n", g_highpri); for (i = 0; i < NHIGHPRI_THREADS; i++)
status = pthread_attr_init(&attr);
if (status != 0)
{ {
printf("priority_inheritance: pthread_attr_init failed, status=%d\n", status); int threadno = i+1;
} printf("priority_inheritance: Starting highpri_thread-%d (of %d) at %d\n",
threadno, NHIGHPRI_THREADS, g_highpri);
status = pthread_attr_init(&attr);
if (status != 0)
{
printf("priority_inheritance: pthread_attr_init failed, status=%d\n", status);
}
sparam.sched_priority = g_highpri; sparam.sched_priority = g_highpri;
status = pthread_attr_setschedparam(&attr,& sparam); status = pthread_attr_setschedparam(&attr,& sparam);
if (status != OK) if (status != OK)
{ {
printf("priority_inheritance: pthread_attr_setschedparam failed, status=%d\n", status); printf("priority_inheritance: pthread_attr_setschedparam failed, status=%d\n", status);
} }
else else
{ {
printf("priority_inheritance: Set highpri_thread priority to %d\n", sparam.sched_priority); printf("priority_inheritance: Set highpri_thread-%d priority to %d\n",
} threadno, sparam.sched_priority);
fflush(stdout); }
fflush(stdout);
status = pthread_create(&medpri, &attr, highpri_thread, NULL); status = pthread_create(&highpri[i], &attr, highpri_thread, (void*)threadno);
if (status != 0) if (status != 0)
{ {
printf("priority_inheritance: pthread_create failed, status=%d\n", status); printf("priority_inheritance: pthread_create failed, status=%d\n", status);
}
} }
/* Wait for all thread instances to complete */ /* Wait for all thread instances to complete */
printf("priority_inheritance: Waiting for highpri_thread to complete\n"); for (i = 0; i < NLOWPRI_THREADS; i++)
fflush(stdout); {
(void)pthread_join(highpri, &result); printf("priority_inheritance: Waiting for highpri_thread-%d to complete\n", i+1);
fflush(stdout);
(void)pthread_join(highpri[i], &result);
}
printf("priority_inheritance: Waiting for medpri_thread to complete\n"); printf("priority_inheritance: Waiting for medpri_thread to complete\n");
fflush(stdout); fflush(stdout);
(void)pthread_join(medpri, &result); (void)pthread_join(medpri, &result);
printf("priority_inheritance: Waiting for lowpri_thread to complete\n"); for (i = 0; i < NLOWPRI_THREADS; i++)
fflush(stdout); {
(void)pthread_join(lowpri, &result); printf("priority_inheritance: Waiting for lowpri_thread-%d to complete\n", i+1);
fflush(stdout);
(void)pthread_join(lowpri[i], &result);
}
printf("priority_inheritance: Finished\n"); printf("priority_inheritance: Finished\n");
sem_destroy(&g_sem); sem_destroy(&g_sem);

View File

@ -63,7 +63,7 @@ extern "C" {
#ifdef CONFIG_PRIORITY_INHERITANCE #ifdef CONFIG_PRIORITY_INHERITANCE
struct semholder_s struct semholder_s
{ {
#if !defined(CONFIG_SEM_PREALLOCHOLDERS) || CONFIG_SEM_PREALLOCHOLDERS > 0 #if CONFIG_SEM_PREALLOCHOLDERS > 0
struct semholder_s *flink; /* Implements singly linked list */ struct semholder_s *flink; /* Implements singly linked list */
#endif #endif
void *holder; /* Holder TCB (actual type is _TCB) */ void *holder; /* Holder TCB (actual type is _TCB) */

View File

@ -218,6 +218,9 @@ static inline void sem_freeholder(sem_t *sem, FAR struct semholder_s *pholder)
static int sem_foreachholder(FAR sem_t *sem, holderhandler_t handler, FAR void *arg) static int sem_foreachholder(FAR sem_t *sem, holderhandler_t handler, FAR void *arg)
{ {
struct semholder_s *pholder = &sem->hlist; struct semholder_s *pholder = &sem->hlist;
#if CONFIG_SEM_PREALLOCHOLDERS > 0
struct semholder_s *next;
#endif
int ret = 0; int ret = 0;
#if CONFIG_SEM_PREALLOCHOLDERS > 0 #if CONFIG_SEM_PREALLOCHOLDERS > 0

View File

@ -99,7 +99,9 @@ int sem_init (FAR sem_t *sem, int pshared, unsigned int value)
{ {
sem->semcount = (sint16)value; sem->semcount = (sint16)value;
#ifdef CONFIG_PRIORITY_INHERITANCE #ifdef CONFIG_PRIORITY_INHERITANCE
#if CONFIG_SEM_PREALLOCHOLDERS > 0
sem->hlist.flink = NULL; sem->hlist.flink = NULL;
#endif
sem->hlist.holder = NULL; sem->hlist.holder = NULL;
sem->hlist.counts = 0; sem->hlist.counts = 0;
#endif #endif

View File

@ -243,11 +243,11 @@ int main(int argc, char **argv, char **envp)
printf("/* If priority inheritance is disabled, then do not allocate any\n"); printf("/* If priority inheritance is disabled, then do not allocate any\n");
printf(" * associated resources.\n"); printf(" * associated resources.\n");
printf(" */\n\n"); printf(" */\n\n");
printf("#if !defined(CONFIG_PROIRITY_INHERITANCE) || !defined(CONFIG_SEM_PREALLOCHOLDERSS)\n"); printf("#if !defined(CONFIG_PRIORITY_INHERITANCE) || !defined(CONFIG_SEM_PREALLOCHOLDERS)\n");
printf("# undef CONFIG_SEM_PREALLOCHOLDERS\n"); printf("# undef CONFIG_SEM_PREALLOCHOLDERS\n");
printf("# define CONFIG_SEM_PREALLOCHOLDERSS 0\n"); printf("# define CONFIG_SEM_PREALLOCHOLDERS 0\n");
printf("#endif\n\n"); printf("#endif\n\n");
printf("#if !defined(CONFIG_PROIRITY_INHERITANCE) || !defined(CONFIG_SEM_NNESTPRIO)\n"); printf("#if !defined(CONFIG_PRIORITY_INHERITANCE) || !defined(CONFIG_SEM_NNESTPRIO)\n");
printf("# undef CONFIG_SEM_NNESTPRIO\n"); printf("# undef CONFIG_SEM_NNESTPRIO\n");
printf("# define CONFIG_SEM_NNESTPRIO 0\n"); printf("# define CONFIG_SEM_NNESTPRIO 0\n");
printf("#endif\n\n"); printf("#endif\n\n");