mirror of
https://github.com/apache/nuttx.git
synced 2026-05-26 02:36:11 +08:00
Bugfix: whenever a thread it must report the PID of the main task (even it is not the main task) with SIGCHLD
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5612 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
@@ -4130,4 +4130,9 @@
|
||||
for pthreads. By dividing the TCB structure into these variants,
|
||||
pthreads do not have to be burdened by task-specific data structures
|
||||
(and vice versa).
|
||||
* sched/task_exithook.c adn group_create.c: Fix an error, the
|
||||
task within the task group may exit early leaving a pthread to
|
||||
exit the task group last. In this case, we need to remember the
|
||||
the PID of the main task in the task group and use that PID for
|
||||
signalling SIGCHILD to the parent task group.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ standards, things that could be improved, and ideas for enhancements.
|
||||
|
||||
nuttx/
|
||||
|
||||
(11) Task/Scheduler (sched/)
|
||||
(12) Task/Scheduler (sched/)
|
||||
(1) Memory Managment (mm/)
|
||||
(3) Signals (sched/, arch/)
|
||||
(2) pthreads (sched/)
|
||||
@@ -204,6 +204,48 @@ o Task/Scheduler (sched/)
|
||||
incompatibilities could show up in porting some code).
|
||||
Priority: Low
|
||||
|
||||
Title: SIGNALS IN TASK GROUPS WITH MANY PTHREADS
|
||||
Description: Presumably when you single the task group you would signal
|
||||
using the task ID of the task that created the group (in
|
||||
practice, a different task should not know the IDs of the
|
||||
internal threads created within the task group).
|
||||
|
||||
Here are some of the things that should happen, but don't
|
||||
as of this writing:
|
||||
|
||||
- If a task group receives a signal then one and only one
|
||||
indeterminate thread in the process which is not blocking
|
||||
the signal should receive the signal.
|
||||
|
||||
- If a task group receives a signal and more than one thread
|
||||
is waiting on that signal, then one and only one
|
||||
indeterminate thread out of that waiting group will receive
|
||||
the signal.
|
||||
|
||||
- If any signal which would normally cause the termination of
|
||||
a process is sent to a thread it will result in the parent
|
||||
process and all threads being terminated. (NuttX does not
|
||||
support these default signal actions... that is really
|
||||
another topic).
|
||||
|
||||
On creation a thread does correctly inherits the signal mask of the thread that created it.
|
||||
|
||||
You should be able to control which thread receives the signal
|
||||
by control the signal mask. You should, for example, be able
|
||||
to create a seperate thread whose sole purpose it is to catch
|
||||
signals and respond to them. You can mask out certain signals
|
||||
using sigprocmask() (or pthread_sigmask()). These signals
|
||||
will be effectively disabled and will never be received in
|
||||
these contexts. In the "signal processing" thread, enable the
|
||||
blocked signals. This should now be the only thread who
|
||||
receives the signals.
|
||||
|
||||
At present, the signal APIs will attempt to signal only the
|
||||
thread that is the main task of the task group.
|
||||
Status: Open.
|
||||
Priority: Medium-high for spec compliance; but probably low for everyday
|
||||
embedded applications.
|
||||
|
||||
o Memory Managment (mm/)
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
+21
-16
@@ -77,22 +77,24 @@
|
||||
*/
|
||||
|
||||
#else
|
||||
# if !defined(CONFIG_DISABLE_ENVIRON) /* Environment variables */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# elif defined(CONFIG_SCHED_ATEXIT) /* Group atexit() function */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# elif defined(CONFIG_SCHED_ONEXIT) /* Group on_exit() function */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# elif defined(CONFIG_SCHED_WAITPID) /* Group waitpid() function */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# elif CONFIG_NFILE_DESCRIPTORS > 0 /* File descriptors */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# elif CONFIG_NFILE_STREAMS > 0 /* Standard C buffered I/O */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# elif CONFIG_NSOCKET_DESCRIPTORS > 0 /* Sockets */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# elif !defined(CONFIG_DISABLE_MQUEUE) /* Message queues */
|
||||
# define HAVE_TASK_GROUP 1
|
||||
# if !defined(CONFIG_DISABLE_PTHREAD) && defined(CONFIG_SCHED_HAVE_PARENT)
|
||||
# define HAVE_TASK_GROUP 1 /* pthreads with parent*/
|
||||
# elif !defined(CONFIG_DISABLE_ENVIRON)
|
||||
# define HAVE_TASK_GROUP 1 /* Environment variables */
|
||||
# elif defined(CONFIG_SCHED_ATEXIT)
|
||||
# define HAVE_TASK_GROUP 1 /* Group atexit() function */
|
||||
# elif defined(CONFIG_SCHED_ONEXIT)
|
||||
# define HAVE_TASK_GROUP 1 /* Group on_exit() function */
|
||||
# elif defined(CONFIG_SCHED_WAITPID)
|
||||
# define HAVE_TASK_GROUP 1 /* Group waitpid() function */
|
||||
# elif CONFIG_NFILE_DESCRIPTORS > 0
|
||||
# define HAVE_TASK_GROUP 1 /* File descriptors */
|
||||
# elif CONFIG_NFILE_STREAMS > 0
|
||||
# define HAVE_TASK_GROUP 1 /* Standard C buffered I/O */
|
||||
# elif CONFIG_NSOCKET_DESCRIPTORS > 0
|
||||
# define HAVE_TASK_GROUP 1 /* Sockets */
|
||||
# elif !defined(CONFIG_DISABLE_MQUEUE)
|
||||
# define HAVE_TASK_GROUP 1 /* Message queues */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -293,6 +295,9 @@ struct task_group_s
|
||||
struct task_group_s *flink; /* Supports a singly linked list */
|
||||
gid_t tg_gid; /* The ID of this task group */
|
||||
gid_t tg_pgid; /* The ID of the parent task group */
|
||||
#endif
|
||||
#if !defined(CONFIG_DISABLE_PTHREAD) && defined(CONFIG_SCHED_HAVE_PARENT)
|
||||
pid_t tg_task; /* The ID of the task within the group */
|
||||
#endif
|
||||
uint8_t tg_flags; /* See GROUP_FLAG_* definitions */
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ int clock_abstime2ticks(clockid_t clockid, FAR const struct timespec *abstime,
|
||||
reltime.tv_sec -= 1;
|
||||
}
|
||||
|
||||
/* Convert this relative time into microseconds.*/
|
||||
/* Convert this relative time into microseconds. */
|
||||
|
||||
return clock_time2ticks(&reltime, ticks);
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ static inline void clock_basetime(FAR struct timespec *tp)
|
||||
|
||||
static void clock_inittime(void)
|
||||
{
|
||||
/* (Re-)initialize the time value to match the RTC*/
|
||||
/* (Re-)initialize the time value to match the RTC */
|
||||
|
||||
clock_basetime(&g_basetime);
|
||||
g_system_timer = 0;
|
||||
|
||||
+1
-3
@@ -91,7 +91,7 @@ FAR char *getenv(const char *name)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Get a reference to the thread-private environ in the TCB.*/
|
||||
/* Get a reference to the thread-private environ in the TCB. */
|
||||
|
||||
sched_lock();
|
||||
rtcb = (FAR struct tcb_s*)g_readytorun.head;
|
||||
@@ -131,5 +131,3 @@ errout:
|
||||
|
||||
#endif /* CONFIG_DISABLE_ENVIRON */
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ FAR char **get_environ_ptr( void )
|
||||
|
||||
#else
|
||||
|
||||
/* Return a reference to the thread-private environ in the TCB.*/
|
||||
/* Return a reference to the thread-private environ in the TCB. */
|
||||
|
||||
FAR struct tcb_s *ptcb = (FAR struct tcb_s*)g_readytorun.head;
|
||||
if (ptcb->envp)
|
||||
@@ -100,5 +100,3 @@ FAR char **get_environ_ptr( void )
|
||||
|
||||
#endif /* CONFIG_DISABLE_ENVIRON */
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-3
@@ -121,7 +121,7 @@ int setenv(FAR const char *name, FAR const char *value, int overwrite)
|
||||
}
|
||||
}
|
||||
|
||||
/* Get a reference to the thread-private environ in the TCB.*/
|
||||
/* Get a reference to the thread-private environ in the TCB. */
|
||||
|
||||
sched_lock();
|
||||
rtcb = (FAR struct tcb_s*)g_readytorun.head;
|
||||
@@ -203,5 +203,3 @@ errout:
|
||||
|
||||
#endif /* CONFIG_DISABLE_ENVIRON */
|
||||
|
||||
|
||||
|
||||
|
||||
+23
-10
@@ -174,12 +174,12 @@ void group_assigngid(FAR struct task_group_s *group)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
int group_allocate(FAR struct tcb_s *tcb)
|
||||
int group_allocate(FAR struct task_tcb_s *tcb)
|
||||
{
|
||||
FAR struct task_group_s *group;
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(tcb && !tcb->group);
|
||||
DEBUGASSERT(tcb && !tcb->cmn.group);
|
||||
|
||||
/* Allocate the group structure and assign it to the TCB */
|
||||
|
||||
@@ -191,7 +191,7 @@ int group_allocate(FAR struct tcb_s *tcb)
|
||||
|
||||
/* Attach the group to the TCB */
|
||||
|
||||
tcb->group = group;
|
||||
tcb->cmn.group = group;
|
||||
|
||||
/* Assign the group a unique ID. If g_gidcounter were to wrap before we
|
||||
* finish with task creation, that would be a problem.
|
||||
@@ -207,7 +207,7 @@ int group_allocate(FAR struct tcb_s *tcb)
|
||||
if (ret < 0)
|
||||
{
|
||||
kfree(group);
|
||||
tcb->group = NULL;
|
||||
tcb->cmn.group = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -241,15 +241,15 @@ int group_allocate(FAR struct tcb_s *tcb)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
int group_initialize(FAR struct tcb_s *tcb)
|
||||
int group_initialize(FAR struct task_tcb_s *tcb)
|
||||
{
|
||||
FAR struct task_group_s *group;
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
irqstate_t flags;
|
||||
#endif
|
||||
|
||||
DEBUGASSERT(tcb && tcb->group);
|
||||
group = tcb->group;
|
||||
DEBUGASSERT(tcb && tcb->cmn.group);
|
||||
group = tcb->cmn.group;
|
||||
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
/* Allocate space to hold GROUP_INITIAL_MEMBERS members of the group */
|
||||
@@ -261,9 +261,9 @@ int group_initialize(FAR struct tcb_s *tcb)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Assign the PID of this new task as a member of the group*/
|
||||
/* Assign the PID of this new task as a member of the group. */
|
||||
|
||||
group->tg_members[0] = tcb->pid;
|
||||
group->tg_members[0] = tcb->cmn.pid;
|
||||
|
||||
/* Initialize the non-zero elements of group structure and assign it to
|
||||
* the tcb.
|
||||
@@ -280,8 +280,21 @@ int group_initialize(FAR struct tcb_s *tcb)
|
||||
|
||||
#endif
|
||||
|
||||
group->tg_nmembers = 1; /* Number of members in the group */
|
||||
/* Save the ID of the main task within the group of threads. This needed
|
||||
* for things like SIGCHILD. It ID is also saved in the TCB of the main
|
||||
* task but is also retained in the group which may persist after the main
|
||||
* task has exited.
|
||||
*/
|
||||
|
||||
#if !defined(CONFIG_DISABLE_PTHREAD) && defined(CONFIG_SCHED_HAVE_PARENT)
|
||||
group->tg_task = tcb->cmn.pid;
|
||||
#endif
|
||||
|
||||
/* Mark that there is one member in the group, the main task */
|
||||
|
||||
group->tg_nmembers = 1;
|
||||
return OK;
|
||||
}
|
||||
|
||||
#endif /* HAVE_TASK_GROUP */
|
||||
|
||||
|
||||
@@ -76,16 +76,18 @@ extern FAR struct task_group_s *g_grouphead;
|
||||
/* Task group data structure management */
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
int group_allocate(FAR struct tcb_s *tcb);
|
||||
int group_initialize(FAR struct tcb_s *tcb);
|
||||
int group_bind(FAR struct tcb_s *tcb);
|
||||
int group_join(FAR struct tcb_s *tcb);
|
||||
int group_allocate(FAR struct task_tcb_s *tcb);
|
||||
int group_initialize(FAR struct task_tcb_s *tcb);
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
int group_bind(FAR struct pthread_tcb_s *tcb);
|
||||
int group_join(FAR struct pthread_tcb_s *tcb);
|
||||
#endif
|
||||
void group_leave(FAR struct tcb_s *tcb);
|
||||
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
FAR struct task_group_s *group_find(gid_t gid);
|
||||
int group_addmember(FAR struct task_group_s *group, pid_t pid);
|
||||
int group_removemember(FAR struct task_group_s *group, pid_t pid);
|
||||
int group_removemember(FAR struct task_group_s *group, pid_t pid);
|
||||
#endif
|
||||
|
||||
/* Convenience functions */
|
||||
|
||||
+10
-10
@@ -49,7 +49,7 @@
|
||||
#include "group_internal.h"
|
||||
#include "env_internal.h"
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
#if defined(HAVE_TASK_GROUP) && !defined(CONFIG_DISABLE_PTHREAD)
|
||||
|
||||
/*****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
@@ -99,15 +99,15 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
int group_bind(FAR struct tcb_s *tcb)
|
||||
int group_bind(FAR struct pthread_tcb_s *tcb)
|
||||
{
|
||||
FAR struct tcb_s *ptcb = (FAR struct tcb_s *)g_readytorun.head;
|
||||
|
||||
DEBUGASSERT(ptcb && tcb && ptcb->group && !tcb->group);
|
||||
DEBUGASSERT(ptcb && tcb && ptcb->group && !tcb->cmn.group);
|
||||
|
||||
/* Copy the group reference from the parent to the child */
|
||||
|
||||
tcb->group = ptcb->group;
|
||||
tcb->cmn.group = ptcb->group;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -136,24 +136,24 @@ int group_bind(FAR struct tcb_s *tcb)
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
int group_join(FAR struct tcb_s *tcb)
|
||||
int group_join(FAR struct pthread_tcb_s *tcb)
|
||||
{
|
||||
FAR struct task_group_s *group;
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
int ret;
|
||||
#endif
|
||||
|
||||
DEBUGASSERT(tcb && tcb->group &&
|
||||
tcb->group->tg_nmembers < UINT8_MAX);
|
||||
DEBUGASSERT(tcb && tcb->cmn.group &&
|
||||
tcb->cmn.group->tg_nmembers < UINT8_MAX);
|
||||
|
||||
/* Get the group from the TCB */
|
||||
|
||||
group = tcb->group;
|
||||
group = tcb->cmn.group;
|
||||
|
||||
#ifdef HAVE_GROUP_MEMBERS
|
||||
/* Add the member to the group */
|
||||
|
||||
ret = group_addmember(group, tcb->pid);
|
||||
ret = group_addmember(group, tcb->cmn.pid);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
@@ -224,4 +224,4 @@ int group_addmember(FAR struct task_group_s *group, pid_t pid)
|
||||
}
|
||||
#endif /* HAVE_GROUP_MEMBERS */
|
||||
|
||||
#endif /* HAVE_TASK_GROUP */
|
||||
#endif /* HAVE_TASK_GROUP && !CONFIG_DISABLE_PTHREAD */
|
||||
|
||||
@@ -96,4 +96,5 @@ int group_setupstreams(FAR struct task_tcb_s *tcb)
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NFILE_STREAMS > 0 */
|
||||
#endif /* CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0*/
|
||||
#endif /* CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0 */
|
||||
|
||||
|
||||
@@ -246,4 +246,5 @@ int group_setuptaskfiles(FAR struct task_tcb_s *tcb)
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0*/
|
||||
#endif /* CONFIG_NFILE_DESCRIPTORS > 0 || CONFIG_NSOCKET_DESCRIPTORS > 0 */
|
||||
|
||||
|
||||
+2
-2
@@ -330,7 +330,7 @@ void os_start(void)
|
||||
/* Allocate the IDLE group and suppress child status. */
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
(void)group_allocate(&g_idletcb.cmn);
|
||||
(void)group_allocate(&g_idletcb);
|
||||
#endif
|
||||
|
||||
/* Initialize the interrupt handling subsystem (if included) */
|
||||
@@ -456,7 +456,7 @@ void os_start(void)
|
||||
*/
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
(void)group_initialize(&g_idletcb.cmn);
|
||||
(void)group_initialize(&g_idletcb);
|
||||
g_idletcb.cmn.group->tg_flags = GROUP_FLAG_NOCLDWAIT;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr,
|
||||
*/
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
ret = group_bind((FAR struct tcb_s *)ptcb);
|
||||
ret = group_bind(ptcb);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = ENOMEM;
|
||||
@@ -354,7 +354,7 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr,
|
||||
/* Join the parent's task group */
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
ret = group_join((FAR struct tcb_s *)ptcb);
|
||||
ret = group_join(ptcb);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = ENOMEM;
|
||||
|
||||
+2
-1
@@ -87,4 +87,5 @@ pid_t wait(FAR int *stat_loc)
|
||||
return waitpid((pid_t)-1, stat_loc, 0);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCHED_WAITPID && CONFIG_SCHED_HAVE_PARENT*/
|
||||
#endif /* CONFIG_SCHED_WAITPID && CONFIG_SCHED_HAVE_PARENT */
|
||||
|
||||
|
||||
@@ -405,4 +405,5 @@ errout:
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCHED_WAITPID && CONFIG_SCHED_HAVE_PARENT*/
|
||||
#endif /* CONFIG_SCHED_WAITPID && CONFIG_SCHED_HAVE_PARENT */
|
||||
|
||||
|
||||
+2
-2
@@ -123,7 +123,7 @@ static int thread_create(const char *name, uint8_t ttype, int priority,
|
||||
/* Allocate a new task group */
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
ret = group_allocate((FAR struct tcb_s *)tcb);
|
||||
ret = group_allocate(tcb);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = -ret;
|
||||
@@ -169,7 +169,7 @@ static int thread_create(const char *name, uint8_t ttype, int priority,
|
||||
/* Now we have enough in place that we can join the group */
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
ret = group_initialize((FAR struct tcb_s *)tcb);
|
||||
ret = group_initialize(tcb);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = -ret;
|
||||
|
||||
+10
-1
@@ -343,7 +343,11 @@ static inline void task_sigchild(gid_t pgid, FAR struct tcb_s *ctcb, int status)
|
||||
info.si_signo = SIGCHLD;
|
||||
info.si_code = CLD_EXITED;
|
||||
info.si_value.sival_ptr = NULL;
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
info.si_pid = chgrp->tg_task;
|
||||
#else
|
||||
info.si_pid = ctcb->pid;
|
||||
#endif
|
||||
info.si_status = status;
|
||||
|
||||
/* Send the signal. We need to use this internal interface so that we
|
||||
@@ -356,7 +360,8 @@ static inline void task_sigchild(gid_t pgid, FAR struct tcb_s *ctcb, int status)
|
||||
|
||||
#else /* HAVE_GROUP_MEMBERS */
|
||||
|
||||
static inline void task_sigchild(FAR struct tcb_s *ptcb, FAR struct tcb_s *ctcb, int status)
|
||||
static inline void task_sigchild(FAR struct tcb_s *ptcb,
|
||||
FAR struct tcb_s *ctcb, int status)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
@@ -391,7 +396,11 @@ static inline void task_sigchild(FAR struct tcb_s *ptcb, FAR struct tcb_s *ctcb,
|
||||
info.si_signo = SIGCHLD;
|
||||
info.si_code = CLD_EXITED;
|
||||
info.si_value.sival_ptr = NULL;
|
||||
#ifndef CONFIG_DISABLE_PTHREAD
|
||||
info.si_pid = chgrp->tg_task;
|
||||
#else
|
||||
info.si_pid = ctcb->pid;
|
||||
#endif
|
||||
info.si_status = status;
|
||||
|
||||
/* Send the signal. We need to use this internal interface so that we
|
||||
|
||||
+2
-2
@@ -134,7 +134,7 @@ int task_init(FAR struct tcb_s *tcb, const char *name, int priority,
|
||||
/* Create a new task group */
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
ret = group_allocate(tcb);
|
||||
ret = group_allocate(ttcb);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = -ret;
|
||||
@@ -176,7 +176,7 @@ int task_init(FAR struct tcb_s *tcb, const char *name, int priority,
|
||||
/* Now we have enough in place that we can join the group */
|
||||
|
||||
#ifdef HAVE_TASK_GROUP
|
||||
ret = group_initialize(tcb);
|
||||
ret = group_initialize(ttcb);
|
||||
if (ret < 0)
|
||||
{
|
||||
errcode = -ret;
|
||||
|
||||
Reference in New Issue
Block a user