diff options
Diffstat (limited to 'nuttx/sched')
-rw-r--r-- | nuttx/sched/Kconfig | 115 | ||||
-rw-r--r-- | nuttx/sched/Makefile | 177 | ||||
-rw-r--r-- | nuttx/sched/os_internal.h | 16 | ||||
-rw-r--r-- | nuttx/sched/os_start.c | 13 | ||||
-rw-r--r-- | nuttx/sched/pthread_create.c | 11 | ||||
-rw-r--r-- | nuttx/sched/sched_waitid.c | 77 | ||||
-rw-r--r-- | nuttx/sched/sched_waitpid.c | 62 | ||||
-rw-r--r-- | nuttx/sched/sig_action.c | 157 | ||||
-rw-r--r-- | nuttx/sched/task_childstatus.c | 396 | ||||
-rw-r--r-- | nuttx/sched/task_create.c | 16 | ||||
-rw-r--r-- | nuttx/sched/task_exithook.c | 109 | ||||
-rw-r--r-- | nuttx/sched/task_init.c | 3 | ||||
-rw-r--r-- | nuttx/sched/task_posixspawn.c | 674 | ||||
-rw-r--r-- | nuttx/sched/task_reparent.c | 163 | ||||
-rw-r--r-- | nuttx/sched/task_setup.c | 68 | ||||
-rw-r--r-- | nuttx/sched/task_vfork.c | 9 |
16 files changed, 1832 insertions, 234 deletions
diff --git a/nuttx/sched/Kconfig b/nuttx/sched/Kconfig index 6d53a03aa..fe9a88085 100644 --- a/nuttx/sched/Kconfig +++ b/nuttx/sched/Kconfig @@ -4,7 +4,7 @@ # config MSEC_PER_TICK - int "tick timer" + int "Milliseconds per system timer tick" default 10 ---help--- The default system timer is 100Hz or MSEC_PER_TICK=10. This setting @@ -12,7 +12,7 @@ config MSEC_PER_TICK system timer interrupts at some interrupt interval other than 10 msec. config RR_INTERVAL - int "round robin timeslice" + int "Round robin timeslice (MSEC)" default 0 ---help--- The round robin timeslice will be set this number of milliseconds; @@ -39,16 +39,91 @@ config TASK_NAME_SIZE disable. config SCHED_HAVE_PARENT - bool "Remember Parent" + bool "Support parent/child task relationships" default n ---help--- - Remember the ID of the parent thread when a new child thread is + Remember the ID of the parent task when a new child task is created. This support enables some additional features (such as SIGCHLD) and modifies the behavior of other interfaces. For example, it makes waitpid() more standards complete by restricting the waited-for tasks to the children of the caller. Default: disabled. +config SCHED_CHILD_STATUS + bool "Retain child exit status" + default n + depends on SCHED_HAVE_PARENT + ---help--- + If this option is selected, then the exit status of the child task + will be retained after the child task exits. This option should be + selected if you require knowledge of a child process' exit status. + Without this setting, wait(), waitpid() or waitid() may fail. For + example, if you do: + + 1) Start child task + 2) Wait for exit status (using wait(), waitpid(), or waitid()). + + This can fail because the child task may run to completion before + the wait begins. There is a non-standard work-around in this case: + The above sequence will work if you disable pre-emption using + sched_lock() prior to starting the child task, then re-enable pre- + emption with sched_unlock() after the wait completes. This works + because the child task is not permitted to run until the wait is in + place. + + The standard solution would be to enable SCHED_CHILD_STATUS. In + this case the exit status of the child task is retained after the + child exits and the wait will successful obtain the child task's + exit status whether it is called before the child task exits or not. + + Warning: If you enable this feature, then your application must + either (1) take responsibility for reaping the child status with wait(), + waitpid(), or waitid(), or (2) suppress retention of child status. + If you do not reap the child status, then you have a memory leak and + your system will eventually fail. + + Retention of child status can be suppressed on the parent using logic like: + + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + int ret = sigaction(SIGCHLD, &sa, NULL); + +config PREALLOC_CHILDSTATUS + int "Number of pre-allocated child status" + default 0 + depends on SCHED_CHILD_STATUS + ---help--- + To prevent runaway child status allocations and to improve + allocation performance, child task exit status structures are pre- + allocated when the system boots. This setting determines the number + of child status structures that will be pre-allocated. If this + setting is not defined or if it is defined to be zero then a value + of 2*MAX_TASKS is used. + + Note that there cannot be more that CONFIG_MAX_TASKS tasks in total. + However, the number of child status structures may need to be + significantly larger because this number includes the maximum number + of tasks that are running PLUS the number of tasks that have exit'ed + without having their exit status reaped (via wait(), waitid(), or + waitpid()). + + Obviously, if tasks spawn children indefinitely and never have the + exit status reaped, then you may have a memory leak! If you enable + the SCHED_CHILD_STATUS feature, then your application must take + responsibility for either (1) reaping the child status with wait(), + waitpid(), or waitid() or it must (2) suppress retention of child + status. Otherwise, your system will eventually fail. + + Retention of child status can be suppressed on the parent using logic like: + + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + int ret = sigaction(SIGCHLD, &sa, NULL); + config JULIAN_TIME bool "Enables Julian time conversions" default n @@ -56,15 +131,15 @@ config JULIAN_TIME Enables Julian time conversions config START_YEAR - int "start year" + int "Start year" default 2013 config START_MONTH - int "start month" + int "Start month" default 1 config START_DAY - int "start day" + int "Start day" default 1 config DEV_CONSOLE @@ -88,7 +163,7 @@ config PRIORITY_INHERITANCE Set to enable support for priority inheritance on mutexes and semaphores. config SEM_PREALLOCHOLDERS - int "Pre-allocated holders" + int "Number of pre-allocated holders" default 16 depends on PRIORITY_INHERITANCE ---help--- @@ -372,7 +447,7 @@ endif comment "Sizes of configurable things (0 disables)" config MAX_TASKS - int "Max tasks" + int "Max number of tasks" default 32 ---help--- The maximum number of simultaneously active tasks. This value must be @@ -386,33 +461,32 @@ config MAX_TASK_ARGS receive (i.e., maxmum value of 'argc') config NPTHREAD_KEYS - int "Number of pthread keys" + int "Maximum number of pthread keys" default 4 ---help--- The number of items of thread- specific data that can be retained config NFILE_DESCRIPTORS - int "Max file descriptors" + int "Maximum number of file descriptors per task" default 16 ---help--- - The maximum number of file - descriptors (one for each open) + The maximum number of file descriptors per task (one for each open) config NFILE_STREAMS - int "Max file streams" + int "Maximum number of FILE streams" default 16 ---help--- The maximum number of streams that can be fopen'ed config NAME_MAX - int "name max" + int "Maximum size of a file name" default 32 ---help--- The maximum size of a file name. config PREALLOC_MQ_MSGS - int "Pre-allocated messages" + int "Number of pre-allocated messages" default 32 ---help--- The number of pre-allocated message structures. The system manages @@ -426,21 +500,20 @@ config MQ_MAXMSGSIZE setting (does not include other message structure overhead. config MAX_WDOGPARMS - int "max watchdog parms" + int "Maximum number of watchdog parameters" default 4 ---help--- - Maximum number of parameters that - can be passed to a watchdog handler + Maximum number of parameters that can be passed to a watchdog handler config PREALLOC_WDOGS - int "Pre-allocated watchdogs" + int "Number of pre-allocated watchdog timers" default 32 ---help--- The number of pre-allocated watchdog structures. The system manages a pool of preallocated watchdog structures to minimize dynamic allocations config PREALLOC_TIMERS - int "Pre-allocated timers" + int "Number of pre-allocated POSIX timers" default 8 ---help--- The number of pre-allocated POSIX timer structures. The system manages a diff --git a/nuttx/sched/Makefile b/nuttx/sched/Makefile index 3d6b58bac..1ad244450 100644 --- a/nuttx/sched/Makefile +++ b/nuttx/sched/Makefile @@ -35,162 +35,175 @@ -include $(TOPDIR)/Make.defs -ASRCS = -AOBJS = $(ASRCS:.S=$(OBJEXT)) - -MISC_SRCS = os_start.c os_bringup.c errno_getptr.c errno_get.c errno_set.c \ - sched_garbage.c sched_setupstreams.c sched_getfiles.c sched_getsockets.c \ - sched_getstreams.c sched_setupidlefiles.c sched_setuptaskfiles.c \ - sched_setuppthreadfiles.c sched_releasefiles.c - -TSK_SRCS = prctl.c task_create.c task_init.c task_setup.c task_activate.c \ - task_start.c task_delete.c task_deletecurrent.c task_exithook.c \ - task_restart.c task_vfork.c exit.c getpid.c sched_addreadytorun.c \ - sched_removereadytorun.c sched_addprioritized.c sched_mergepending.c \ - sched_addblocked.c sched_removeblocked.c sched_free.c sched_gettcb.c \ - sched_verifytcb.c sched_releasetcb.c - -SCHED_SRCS = sched_setparam.c sched_setpriority.c sched_getparam.c \ - sched_setscheduler.c sched_getscheduler.c \ - sched_yield.c sched_rrgetinterval.c sched_foreach.c \ - sched_lock.c sched_unlock.c sched_lockcount.c sched_self.c +ASRCS = +AOBJS = $(ASRCS:.S=$(OBJEXT)) + +MISC_SRCS = os_start.c os_bringup.c errno_getptr.c errno_get.c errno_set.c +MISC_SRCS += sched_garbage.c sched_setupstreams.c sched_getfiles.c sched_getsockets.c +MISC_SRCS += sched_getstreams.c sched_setupidlefiles.c sched_setuptaskfiles.c +MISC_SRCS += sched_setuppthreadfiles.c sched_releasefiles.c + +TSK_SRCS = prctl.c task_create.c task_init.c task_setup.c task_activate.c +TSK_SRCS += task_start.c task_delete.c task_deletecurrent.c task_exithook.c +TSK_SRCS += task_restart.c task_vfork.c exit.c getpid.c sched_addreadytorun.c +TSK_SRCS += sched_removereadytorun.c sched_addprioritized.c sched_mergepending.c +TSK_SRCS += sched_addblocked.c sched_removeblocked.c sched_free.c sched_gettcb.c +TSK_SRCS += sched_verifytcb.c sched_releasetcb.c + +ifneq ($(CONFIG_BINFMT_DISABLE),y) +ifeq ($(CONFIG_LIBC_EXECFUNCS),y) +TSK_SRCS += task_posixspawn.c +endif +endif + +SCHED_SRCS = sched_setparam.c sched_setpriority.c sched_getparam.c +SCHED_SRCS += sched_setscheduler.c sched_getscheduler.c +SCHED_SRCS += sched_yield.c sched_rrgetinterval.c sched_foreach.c +SCHED_SRCS += sched_lock.c sched_unlock.c sched_lockcount.c sched_self.c ifeq ($(CONFIG_SCHED_ATEXIT),y) -SCHED_SRCS += atexit.c +SCHED_SRCS += atexit.c endif ifeq ($(CONFIG_SCHED_ONEXIT),y) -SCHED_SRCS += on_exit.c +SCHED_SRCS += on_exit.c endif ifeq ($(CONFIG_PRIORITY_INHERITANCE),y) -SCHED_SRCS += sched_reprioritize.c +SCHED_SRCS += sched_reprioritize.c +endif + +ifeq ($(CONFIG_SCHED_HAVE_PARENT),y) +SCHED_SRCS += task_reparent.c +ifeq ($(CONFIG_SCHED_CHILD_STATUS),y) +SCHED_SRCS += task_childstatus.c +endif endif ifeq ($(CONFIG_SCHED_WAITPID),y) -SCHED_SRCS += sched_waitpid.c +SCHED_SRCS += sched_waitpid.c ifeq ($(CONFIG_SCHED_HAVE_PARENT),y) -SCHED_SRCS += sched_waitid.c sched_wait.c +SCHED_SRCS += sched_waitid.c sched_wait.c endif endif -ENV_SRCS = env_getenvironptr.c env_dup.c env_share.c env_release.c \ - env_findvar.c env_removevar.c \ - env_clearenv.c env_getenv.c env_putenv.c env_setenv.c env_unsetenv.c +ENV_SRCS = env_getenvironptr.c env_dup.c env_share.c env_release.c +ENV_SRCS += env_findvar.c env_removevar.c +ENV_SRCS += env_clearenv.c env_getenv.c env_putenv.c env_setenv.c env_unsetenv.c -WDOG_SRCS = wd_initialize.c wd_create.c wd_start.c wd_cancel.c wd_delete.c \ - wd_gettime.c +WDOG_SRCS = wd_initialize.c wd_create.c wd_start.c wd_cancel.c wd_delete.c +WDOG_SRCS += wd_gettime.c -TIME_SRCS = sched_processtimer.c +TIME_SRCS = sched_processtimer.c ifneq ($(CONFIG_DISABLE_SIGNALS),y) -TIME_SRCS += sleep.c usleep.c +TIME_SRCS += sleep.c usleep.c endif -CLOCK_SRCS = clock_initialize.c clock_settime.c clock_gettime.c clock_getres.c \ - clock_time2ticks.c clock_abstime2ticks.c clock_ticks2time.c \ - clock_gettimeofday.c clock_systimer.c +CLOCK_SRCS = clock_initialize.c clock_settime.c clock_gettime.c clock_getres.c +CLOCK_SRCS += clock_time2ticks.c clock_abstime2ticks.c clock_ticks2time.c +CLOCK_SRCS += clock_gettimeofday.c clock_systimer.c -SIGNAL_SRCS = sig_initialize.c \ - sig_action.c sig_procmask.c sig_pending.c sig_suspend.c \ - sig_kill.c sig_queue.c sig_waitinfo.c sig_timedwait.c \ - sig_findaction.c sig_allocatependingsigaction.c \ - sig_releasependingsigaction.c sig_unmaskpendingsignal.c \ - sig_removependingsignal.c sig_releasependingsignal.c sig_lowest.c \ - sig_mqnotempty.c sig_cleanup.c sig_received.c sig_deliver.c pause.c +SIGNAL_SRCS = sig_initialize.c +SIGNAL_SRCS += sig_action.c sig_procmask.c sig_pending.c sig_suspend.c +SIGNAL_SRCS += sig_kill.c sig_queue.c sig_waitinfo.c sig_timedwait.c +SIGNAL_SRCS += sig_findaction.c sig_allocatependingsigaction.c +SIGNAL_SRCS += sig_releasependingsigaction.c sig_unmaskpendingsignal.c +SIGNAL_SRCS += sig_removependingsignal.c sig_releasependingsignal.c sig_lowest.c +SIGNAL_SRCS += sig_mqnotempty.c sig_cleanup.c sig_received.c sig_deliver.c pause.c -MQUEUE_SRCS = mq_open.c mq_close.c mq_unlink.c mq_send.c mq_timedsend.c\ - mq_sndinternal.c mq_receive.c mq_timedreceive.c mq_rcvinternal.c \ - mq_initialize.c mq_descreate.c mq_findnamed.c mq_msgfree.c mq_msgqfree.c +MQUEUE_SRCS = mq_open.c mq_close.c mq_unlink.c mq_send.c mq_timedsend.c +MQUEUE_SRCS += mq_sndinternal.c mq_receive.c mq_timedreceive.c mq_rcvinternal.c +MQUEUE_SRCS += mq_initialize.c mq_descreate.c mq_findnamed.c mq_msgfree.c mq_msgqfree.c ifneq ($(CONFIG_DISABLE_SIGNALS),y) -MQUEUE_SRCS += mq_waitirq.c +MQUEUE_SRCS += mq_waitirq.c endif ifneq ($(CONFIG_DISABLE_SIGNALS),y) -MQUEUE_SRCS += mq_notify.c +MQUEUE_SRCS += mq_notify.c endif -PTHREAD_SRCS = pthread_create.c pthread_exit.c pthread_join.c pthread_detach.c \ - pthread_yield.c pthread_getschedparam.c pthread_setschedparam.c \ - pthread_mutexinit.c pthread_mutexdestroy.c \ - pthread_mutexlock.c pthread_mutextrylock.c pthread_mutexunlock.c \ - pthread_condinit.c pthread_conddestroy.c \ - pthread_condwait.c pthread_condsignal.c pthread_condbroadcast.c \ - pthread_barrierinit.c pthread_barrierdestroy.c pthread_barrierwait.c \ - pthread_cancel.c pthread_setcancelstate.c \ - pthread_keycreate.c pthread_setspecific.c pthread_getspecific.c pthread_keydelete.c \ - pthread_initialize.c pthread_completejoin.c pthread_findjoininfo.c \ - pthread_removejoininfo.c pthread_once.c pthread_setschedprio.c +PTHREAD_SRCS = pthread_create.c pthread_exit.c pthread_join.c pthread_detach.c +PTHREAD_SRCS += pthread_yield.c pthread_getschedparam.c pthread_setschedparam.c +PTHREAD_SRCS += pthread_mutexinit.c pthread_mutexdestroy.c +PTHREAD_SRCS += pthread_mutexlock.c pthread_mutextrylock.c pthread_mutexunlock.c +PTHREAD_SRCS += pthread_condinit.c pthread_conddestroy.c +PTHREAD_SRCS += pthread_condwait.c pthread_condsignal.c pthread_condbroadcast.c +PTHREAD_SRCS += pthread_barrierinit.c pthread_barrierdestroy.c pthread_barrierwait.c +PTHREAD_SRCS += pthread_cancel.c pthread_setcancelstate.c +PTHREAD_SRCS += pthread_keycreate.c pthread_setspecific.c pthread_getspecific.c pthread_keydelete.c +PTHREAD_SRCS += pthread_initialize.c pthread_completejoin.c pthread_findjoininfo.c +PTHREAD_SRCS += pthread_removejoininfo.c pthread_once.c pthread_setschedprio.c ifneq ($(CONFIG_DISABLE_SIGNALS),y) -PTHREAD_SRCS += pthread_condtimedwait.c pthread_kill.c pthread_sigmask.c +PTHREAD_SRCS += pthread_condtimedwait.c pthread_kill.c pthread_sigmask.c endif -SEM_SRCS = sem_initialize.c sem_destroy.c sem_open.c sem_close.c sem_unlink.c \ - sem_wait.c sem_trywait.c sem_timedwait.c sem_post.c sem_findnamed.c +SEM_SRCS = sem_initialize.c sem_destroy.c sem_open.c sem_close.c sem_unlink.c +SEM_SRCS += sem_wait.c sem_trywait.c sem_timedwait.c sem_post.c sem_findnamed.c ifneq ($(CONFIG_DISABLE_SIGNALS),y) -SEM_SRCS += sem_waitirq.c +SEM_SRCS += sem_waitirq.c endif ifeq ($(CONFIG_PRIORITY_INHERITANCE),y) -SEM_SRCS += sem_holder.c +SEM_SRCS += sem_holder.c endif ifneq ($(CONFIG_DISABLE_POSIX_TIMERS),y) -TIMER_SRCS = timer_initialize.c timer_create.c timer_delete.c timer_getoverrun.c \ - timer_gettime.c timer_settime.c timer_release.c +TIMER_SRCS += timer_initialize.c timer_create.c timer_delete.c timer_getoverrun.c +TIMER_SRCS += timer_gettime.c timer_settime.c timer_release.c endif ifeq ($(CONFIG_SCHED_WORKQUEUE),y) -WORK_SRCS = work_thread.c work_queue.c work_cancel.c work_signal.c +WORK_SRCS = work_thread.c work_queue.c work_cancel.c work_signal.c endif ifeq ($(CONFIG_PAGING),y) -PGFILL_SRCS = pg_miss.c pg_worker.c +PGFILL_SRCS = pg_miss.c pg_worker.c endif -IRQ_SRCS = irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c +IRQ_SRCS = irq_initialize.c irq_attach.c irq_dispatch.c irq_unexpectedisr.c -KMM_SRCS = kmm_initialize.c kmm_addregion.c kmm_semaphore.c \ - kmm_kmalloc.c kmm_kzalloc.c kmm_krealloc.c kmm_kfree.c +KMM_SRCS = kmm_initialize.c kmm_addregion.c kmm_semaphore.c +KMM_SRCS = kmm_kmalloc.c kmm_kzalloc.c kmm_krealloc.c kmm_kfree.c -CSRCS = $(MISC_SRCS) $(TSK_SRCS) $(SCHED_SRCS) $(WDOG_SRCS) $(TIME_SRCS) \ - $(SEM_SRCS) $(TIMER_SRCS) $(WORK_SRCS) $(PGFILL_SRCS) $(IRQ_SRCS) +CSRCS = $(MISC_SRCS) $(TSK_SRCS) $(SCHED_SRCS) $(WDOG_SRCS) $(TIME_SRCS) \ + $(SEM_SRCS) $(TIMER_SRCS) $(WORK_SRCS) $(PGFILL_SRCS) $(IRQ_SRCS) ifneq ($(CONFIG_DISABLE_CLOCK),y) -CSRCS += $(CLOCK_SRCS) +CSRCS += $(CLOCK_SRCS) endif ifneq ($(CONFIG_DISABLE_PTHREAD),y) -CSRCS += $(PTHREAD_SRCS) +CSRCS += $(PTHREAD_SRCS) endif ifneq ($(CONFIG_DISABLE_SIGNALS),y) -CSRCS += $(SIGNAL_SRCS) +CSRCS += $(SIGNAL_SRCS) endif ifneq ($(CONFIG_DISABLE_MQUEUE),y) -CSRCS += $(MQUEUE_SRCS) +CSRCS += $(MQUEUE_SRCS) endif ifneq ($(CONFIG_DISABLE_ENVIRON),y) -CSRCS += $(ENV_SRCS) +CSRCS += $(ENV_SRCS) endif ifeq ($(CONFIG_NUTTX_KERNEL),y) -CSRCS += $(KMM_SRCS) +CSRCS += $(KMM_SRCS) endif -COBJS = $(CSRCS:.c=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) -SRCS = $(ASRCS) $(CSRCS) -OBJS = $(AOBJS) $(COBJS) +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) -BIN = libsched$(LIBEXT) +BIN = libsched$(LIBEXT) -all: $(BIN) +all: $(BIN) .PHONY: context depend clean distclean $(AOBJS): %$(OBJEXT): %.S diff --git a/nuttx/sched/os_internal.h b/nuttx/sched/os_internal.h index 32d9fb4ac..7d5095bad 100644 --- a/nuttx/sched/os_internal.h +++ b/nuttx/sched/os_internal.h @@ -1,7 +1,7 @@ /**************************************************************************** * sched/os_internal.h * - * Copyright (C) 2007-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -264,10 +264,22 @@ extern const tasklist_t g_tasklisttable[NUM_TASK_STATES]; int os_bringup(void); void task_start(void); int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, - main_t main); + main_t main, uint8_t ttype); int task_argsetup(FAR _TCB *tcb, FAR const char *name, FAR const char *argv[]); void task_exithook(FAR _TCB *tcb, int status); int task_deletecurrent(void); +#ifdef CONFIG_SCHED_HAVE_PARENT +#ifdef CONFIG_SCHED_CHILD_STATUS +void weak_function task_initialize(void); +FAR struct child_status_s *task_allocchild(void); +void task_freechild(FAR struct child_status_s *status); +void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child); +FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid); +FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid); +void task_removechildren(FAR _TCB *tcb); +#endif +int task_reparent(pid_t ppid, pid_t chpid); +#endif #ifndef CONFIG_CUSTOM_STACK int kernel_thread(FAR const char *name, int priority, int stack_size, main_t entry, FAR const char *argv[]); diff --git a/nuttx/sched/os_start.c b/nuttx/sched/os_start.c index a53ac2aa8..a6d4e83b9 100644 --- a/nuttx/sched/os_start.c +++ b/nuttx/sched/os_start.c @@ -286,7 +286,7 @@ void os_start(void) /* Initialize the processor-specific portion of the TCB */ - g_idletcb.flags = TCB_FLAG_TTYPE_KERNEL; + g_idletcb.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_NOCLDWAIT); up_initial_state(&g_idletcb); /* Initialize the semaphore facility(if in link). This has to be done @@ -314,6 +314,17 @@ void os_start(void) kmm_initialize((void*)CONFIG_HEAP_BASE, CONFIG_HEAP_SIZE); #endif + /* Initialize tasking data structures */ + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) +#ifdef CONFIG_HAVE_WEAKFUNCTIONS + if (task_initialize != NULL) +#endif + { + task_initialize(); + } +#endif + /* Initialize the interrupt handling subsystem (if included) */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS diff --git a/nuttx/sched/pthread_create.c b/nuttx/sched/pthread_create.c index dc2db2916..f4d0d8fdf 100644 --- a/nuttx/sched/pthread_create.c +++ b/nuttx/sched/pthread_create.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/pthread_create.c * - * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -354,15 +354,10 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, #endif } - /* Mark this task as a pthread (this setting will be needed in - * task_schedsetup() when up_initial_state() is called. - */ - - ptcb->flags |= TCB_FLAG_TTYPE_PTHREAD; - /* Initialize the task control block */ - ret = task_schedsetup(ptcb, priority, pthread_start, (main_t)start_routine); + ret = task_schedsetup(ptcb, priority, pthread_start, (main_t)start_routine, + TCB_FLAG_TTYPE_PTHREAD); if (ret != OK) { sched_releasetcb(ptcb); diff --git a/nuttx/sched/sched_waitid.c b/nuttx/sched/sched_waitid.c index eabc69afe..37ee26ce0 100644 --- a/nuttx/sched/sched_waitid.c +++ b/nuttx/sched/sched_waitid.c @@ -155,12 +155,33 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) sched_lock(); - /* Verify that this task actually has children and that the the requeste + /* Verify that this task actually has children and that the the requested * TCB is actually a child of this task. */ +#ifdef CONFIG_SCHED_CHILD_STATUS + if (rtcb->children == NULL) + { + /* There are no children */ + + err = ECHILD; + goto errout_with_errno; + } + else if (idtype == P_PID) + { + if (task_findchild(rtcb, (pid_t)id) == NULL) + { + /* This specific pid is not a child */ + + err = ECHILD; + goto errout_with_errno; + } + } +#else if (rtcb->nchildren == 0) { + /* There are no children */ + err = ECHILD; goto errout_with_errno; } @@ -175,11 +196,64 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) goto errout_with_errno; } } +#endif /* Loop until the child that we are waiting for dies */ for (;;) { +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Check if the task has already died. Signals are not queued in + * NuttX. So a possibility is that the child has died and we + * missed the death of child signal (we got some other signal + * instead). + */ + + DEBUGASSERT(rtcb->children); + if (rtcb->children == NULL) + { + /* This should not happen. I am just wasting your FLASH. */ + + err = ECHILD; + goto errout_with_errno; + } + else if (idtype == P_PID) + { + FAR struct child_status_s *child; + + /* We are waiting for a specific PID. Get the current status + * of the child task. + */ + + child = task_findchild(rtcb, (pid_t)id); + DEBUGASSERT(child); + if (!child) + { + /* Yikes! The child status entry just disappeared! */ + + err = ECHILD; + goto errout_with_errno; + } + + /* Did the child exit? */ + + if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) + { + /* The child has exited. Return the saved exit status */ + + info->si_signo = SIGCHLD; + info->si_code = CLD_EXITED; + info->si_value.sival_ptr = NULL; + info->si_pid = (pid_t)id; + info->si_status = child->ch_status; + + /* Discard the child entry and break out of the loop */ + + (void)task_removechild(rtcb, (pid_t)id); + task_freechild(child); + } + } +#else /* Check if the task has already died. Signals are not queued in * NuttX. So a possibility is that the child has died and we * missed the death of child signal (we got some other signal @@ -197,6 +271,7 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) err = EINTR; goto errout_with_errno; } +#endif /* Wait for any death-of-child signal */ diff --git a/nuttx/sched/sched_waitpid.c b/nuttx/sched/sched_waitpid.c index dc715b2e9..fe3f7167d 100644 --- a/nuttx/sched/sched_waitpid.c +++ b/nuttx/sched/sched_waitpid.c @@ -274,6 +274,9 @@ errout: pid_t waitpid(pid_t pid, int *stat_loc, int options) { FAR _TCB *rtcb = (FAR _TCB *)g_readytorun.head; +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; +#endif FAR struct siginfo info; sigset_t sigset; int err; @@ -300,12 +303,33 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) sched_lock(); - /* Verify that this task actually has children and that the the requeste + /* Verify that this task actually has children and that the the request * TCB is actually a child of this task. */ +#ifdef CONFIG_SCHED_CHILD_STATUS + if (rtcb->children == NULL) + { + /* There are no children */ + + err = ECHILD; + goto errout_with_errno; + } + else if (pid != (pid_t)-1) + { + /* This specific pid is not a child */ + + if (task_findchild(rtcb, pid) == NULL) + { + err = ECHILD; + goto errout_with_errno; + } + } +#else if (rtcb->nchildren == 0) { + /* There are no children */ + err = ECHILD; goto errout_with_errno; } @@ -320,6 +344,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) goto errout_with_errno; } } +#endif /* Loop until the child that we are waiting for dies */ @@ -337,7 +362,12 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) * chilren. */ +#ifdef CONFIG_SCHED_CHILD_STATUS + DEBUGASSERT(rtcb->children); + if (rtcb->children == NULL) +#else if (rtcb->nchildren == 0) +#endif { /* There were one or more children when we started so they * must have exit'ed. There are just no bread crumbs left @@ -351,6 +381,35 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) } else { +#ifdef CONFIG_SCHED_CHILD_STATUS + /* We are waiting for a specific PID. Get the current status + * of the child task. + */ + + child = task_findchild(rtcb, pid); + DEBUGASSERT(child); + if (!child) + { + /* Yikes! The child status entry just disappeared! */ + + err = ECHILD; + goto errout_with_errno; + } + + /* Did the child exit? */ + + if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) + { + /* The child has exited. Return the saved exit status */ + + *stat_loc = child->ch_status; + + /* Discard the child entry and break out of the loop */ + + (void)task_removechild(rtcb, pid); + task_freechild(child); + } +#else /* We are waiting for a specific PID. We can use kill() with * signal number 0 to determine if that task is still alive. */ @@ -368,6 +427,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) err = ECHILD; goto errout_with_errno; } +#endif } /* Wait for any death-of-child signal */ diff --git a/nuttx/sched/sig_action.c b/nuttx/sched/sig_action.c index fef5f1558..708667993 100644 --- a/nuttx/sched/sig_action.c +++ b/nuttx/sched/sig_action.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/sig_action.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ #include <signal.h> #include <queue.h> #include <sched.h> +#include <errno.h> #include "os_internal.h" #include "sig_internal.h" @@ -156,10 +157,11 @@ static FAR sigactq_t *sig_allocateaction(void) * Assumptions: * * POSIX Compatibility: - * - Special values of sa_handler in the struct sigaction - * act input not handled (SIG_DFL, SIG_IGN). - * - All sa_flags in struct sigaction of act input are - * ignored (all treated like SA_SIGINFO). + * - There are no default actions so the special value SIG_DFL is treated + * like SIG_IGN. + * - All sa_flags in struct sigaction of act input are ignored (all + * treated like SA_SIGINFO). The one exception is if CONFIG_SCHED_CHILDSTATUS + * is defined; then SA_NOCLDWAIT is supported but only for SIGCHLD * ****************************************************************************/ @@ -167,90 +169,129 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction * { FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head; FAR sigactq_t *sigact; - int ret = ERROR; /* Assume failure */ + int ret; /* Since sigactions can only be installed from the running thread of * execution, no special precautions should be necessary. */ - /* Verify the signal */ + /* Verify the signal number */ - if (GOOD_SIGNO(signo)) + if (!GOOD_SIGNO(signo)) { - ret = OK; /* Assume success */ + set_errno(EINVAL); + return ERROR; + } - /* Find the signal in the sigactionq */ + /* Find the signal in the sigactionq */ - sigact = sig_findaction(rtcb, signo); + sigact = sig_findaction(rtcb, signo); - /* Return the old sigaction value if so requested */ + /* Return the old sigaction value if so requested */ - if (oact) + if (oact) + { + if (sigact) { - if (sigact) - { - COPY_SIGACTION(oact, &sigact->act); - } - else - { - /* There isn't an old value */ - - oact->sa_u._sa_handler = NULL; - oact->sa_mask = NULL_SIGNAL_SET; - oact->sa_flags = 0; - } + COPY_SIGACTION(oact, &sigact->act); + } + else + { + /* There isn't an old value */ + + oact->sa_u._sa_handler = NULL; + oact->sa_mask = NULL_SIGNAL_SET; + oact->sa_flags = 0; } + } + + /* If the argument act is a null pointer, signal handling is unchanged; + * thus, the call can be used to enquire about the current handling of + * a given signal. + */ - /* If no sigaction was found, but one is needed, then - * allocate one. + if (!act) + { + return OK; + } + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + + /* Handle a special case. Retention of child status can be suppressed + * if signo == SIGCHLD and sa_flags == SA_NOCLDWAIT. + * + * POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated + * when a child process terminates. In NuttX, a SIGCHLD signal is + * generated in this case; but in some other implementations, it may not + * be. + */ + + if (signo == SIGCHLD && (act->sa_flags & SA_NOCLDWAIT) != 0) + { + irqstate_t flags; + + /* We do require a critical section to muck with the TCB values that + * can be modified by the child thread. */ - if (!sigact && act && act->sa_u._sa_handler) - { - sigact = sig_allocateaction(); + flags = irqsave(); - /* An error has occurred if we could not allocate the sigaction */ + /* Mark that status should be not be retained */ - if (!sigact) - { - ret = ERROR; - } - else - { - /* Put the signal number in the queue entry */ + rtcb->flags |= TCB_FLAG_NOCLDWAIT; - sigact->signo = (uint8_t)signo; + /* Free all pending exit status */ - /* Add the new sigaction to sigactionq */ + task_removechildren(rtcb); + irqrestore(flags); + } +#endif - sq_addlast((FAR sq_entry_t*)sigact, &rtcb->sigactionq); - } - } + /* Handle the case where no sigaction is supplied (SIG_IGN) */ + + if (act->sa_u._sa_handler == SIG_IGN) + { + /* If there is a old sigaction, remove it from sigactionq */ + + sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq); + + /* And deallocate it */ + + sig_releaseaction(sigact); + } - /* Set the new sigaction if so requested */ + /* A sigaction has been supplied */ - if ((sigact) && (act)) + else + { + /* Check if a sigaction was found */ + + if (!sigact) { - /* Check if it is a request to install a new handler */ + /* No sigaction was found, but one is needed. Allocate one. */ - if (act->sa_u._sa_handler) - { - COPY_SIGACTION(&sigact->act, act); - } + sigact = sig_allocateaction(); - /* No.. It is a request to remove the old handler */ + /* An error has occurred if we could not allocate the sigaction */ - else - { - /* Remove the old sigaction from sigactionq */ + if (!sigact) + { + set_errno(ENOMEM); + return ERROR; + } + + /* Put the signal number in the queue entry */ - sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq); + sigact->signo = (uint8_t)signo; - /* And deallocate it */ + /* Add the new sigaction to sigactionq */ - sig_releaseaction(sigact); - } + sq_addlast((FAR sq_entry_t*)sigact, &rtcb->sigactionq); } + + /* Set the new sigaction */ + + COPY_SIGACTION(&sigact->act, act); } return ret; diff --git a/nuttx/sched/task_childstatus.c b/nuttx/sched/task_childstatus.c new file mode 100644 index 000000000..0f6d36c29 --- /dev/null +++ b/nuttx/sched/task_childstatus.c @@ -0,0 +1,396 @@ +/***************************************************************************** + * sched/task_childstatus.c + * + * Copyright (C) 2013 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 <nuttx/config.h> + +#include <sched.h> +#include <errno.h> +#include <debug.h> + +#include "os_internal.h" + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + +/***************************************************************************** + * Pre-processor Definitions + *****************************************************************************/ +/* Note that there cannot be more that CONFIG_MAX_TASKS tasks in total. + * However, the number of child status structures may need to be significantly + * larger because this number includes the maximum number of tasks that are + * running PLUS the number of tasks that have exit'ed without having their + * exit status reaped (via wait(), waitid(), or waitpid()). + * + * Obviously, if tasks spawn children indefinitely and never have the exit + * status reaped, then you have a memory leak! + */ + +#if !defined(CONFIG_PREALLOC_CHILDSTATUS) || CONFIG_PREALLOC_CHILDSTATUS == 0 +# undef CONFIG_PREALLOC_CHILDSTATUS +# define CONFIG_PREALLOC_CHILDSTATUS (2*CONFIG_MAX_TASKS) +#endif + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_CHILDSTATUS +#endif + +/***************************************************************************** + * Private Types + *****************************************************************************/ +/* Globals are maintained in a structure to minimize name collisions. */ + +struct child_pool_s +{ + struct child_status_s alloc[CONFIG_PREALLOC_CHILDSTATUS]; + FAR struct child_status_s *freelist; +}; + +/***************************************************************************** + * Private Data + *****************************************************************************/ + +static struct child_pool_s g_child_pool; + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: task_dumpchildren + * + * Description: + * Dump all of the children when the part TCB list is modified. + * + * Parameters: + * tcb - The parent TCB. + * + * Return Value: + * None. + * + * Assumptions: + * Called early in initialization. No special precautions are required. + * + *****************************************************************************/ + +#ifdef CONFIG_DEBUG_CHILDSTATUS +static void task_dumpchildren(FAR _TCB *tcb, FAR const char *msg) +{ + FAR struct child_status_s *child; + int i; + + dbg("Parent TCB=%p: %s\n", tcb, msg); + for (i = 0, child = tcb->children; child; i++, child = child->flink) + { + dbg(" %d. ch_flags=%02x ch_pid=%d ch_status=%d\n", + i, child->ch_flags, child->ch_pid, child->ch_status); + } +} +#else +# task_dumpchildren(t,m) +#endif + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: task_initialize + * + * Description: + * Initialize task related status. At present, this includes only the + * initialize of the child status pool. + * + * Parameters: + * None. + * + * Return Value: + * None. + * + * Assumptions: + * Called early in initialization. No special precautions are required. + * + *****************************************************************************/ + +void task_initialize(void) +{ + FAR struct child_status_s *curr; + FAR struct child_status_s *prev; + int i; + + /* Save all of the child status structures in a free list */ + + prev = &g_child_pool.alloc[0]; + g_child_pool.freelist = prev; + for (i = 0; i < CONFIG_PREALLOC_CHILDSTATUS; i++) + { + curr = &g_child_pool.alloc[i]; + prev->flink = curr; + prev = curr; + } +} + +/***************************************************************************** + * Name: task_allocchild + * + * Description: + * Allocate a child status structure by removing the next entry from a + * free list. + * + * Parameters: + * None. + * + * Return Value: + * On success, a non-NULL pointer to a child status structure. NULL is + * returned if there are no remaining, pre-allocated child status structures. + * + * Assumptions: + * Called during task creation in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +FAR struct child_status_s *task_allocchild(void) +{ + FAR struct child_status_s *ret; + + /* Return the status block at the head of the free list */ + + ret = g_child_pool.freelist; + if (ret) + { + g_child_pool.freelist = ret->flink; + ret->flink = NULL; + } + + return ret; +} + +/***************************************************************************** + * Name: task_freechild + * + * Description: + * Release a child status structure by returning it to a free list. + * + * Parameters: + * status - The child status structure to be freed. + * + * Return Value: + * None. + * + * Assumptions: + * Called during task creation in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +void task_freechild(FAR struct child_status_s *child) +{ + /* Return the child status structure to the free list */ + + if (child) + { + child->flink = g_child_pool.freelist; + g_child_pool.freelist = child; + } +} + +/***************************************************************************** + * Name: task_addchild + * + * Description: + * Add a child status structure in the given TCB. + * + * Parameters: + * tcb - The TCB of the parent task to containing the child status. + * child - The structure to be added + * + * Return Value: + * N + * + * Assumptions: + * Called during task creation processing in a safe context. No special + * precautions are required here. + * + *****************************************************************************/ + +void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child) +{ + /* Add the entry into the TCB list of children */ + + child->flink = tcb->children; + tcb->children = child; + + task_dumpchildren(tcb, "task_addchild"); +} + +/***************************************************************************** + * Name: task_findchild + * + * Description: + * Find a child status structure in the given TCB. A reference to the + * child structure is returned, but the child remains the the TCB's list + * of children. + * + * Parameters: + * tcb - The TCB of the parent task to containing the child status. + * pid - The ID of the child to find. + * + * Return Value: + * On success, a non-NULL pointer to a child status structure. NULL is + * returned if there is child status structure for that pid in the TCB. + * + * Assumptions: + * Called during SIGCHLD processing in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid) +{ + FAR struct child_status_s *child; + + /* Find the status structure with the matching PID */ + + for (child = tcb->children; child; child = child->flink) + { + if (child->ch_pid == pid) + { + return child; + } + } + + return NULL; +} + +/***************************************************************************** + * Name: task_removechild + * + * Description: + * Remove one child structure from the TCB. The child is removed, but is + * not yet freed. task_freechild must be called in order to free the child + * status structure. + * + * Parameters: + * tcb - The TCB of the parent task to containing the child status. + * pid - The ID of the child to find. + * + * Return Value: + * On success, a non-NULL pointer to a child status structure. NULL is + * returned if there is child status structure for that pid in the TCB. + * + * Assumptions: + * Called during SIGCHLD processing in a safe context. No special precautions + * are required here. + * + *****************************************************************************/ + +FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid) +{ + FAR struct child_status_s *curr; + FAR struct child_status_s *prev; + + /* Find the status structure with the matching PID */ + + for (prev = NULL, curr = tcb->children; + curr; + prev = curr, curr = curr->flink) + { + if (curr->ch_pid == pid) + { + break; + } + } + + /* Did we find it? If so, remove it from the TCB. */ + + if (curr) + { + /* Do we remove it from mid-list? Or from the head of the list? */ + + if (prev) + { + prev->flink = curr->flink; + } + else + { + tcb->children = curr->flink; + } + + curr->flink = NULL; + task_dumpchildren(tcb, "task_removechild"); + } + + return curr; +} + +/***************************************************************************** + * Name: task_removechildren + * + * Description: + * Remove and free all child structure from the TCB. + * + * Parameters: + * tcb - The TCB of the parent task to containing the child status. + * + * Return Value: + * None. + * + * Assumptions: + * Called during task exit processing in a safe context. No special + * precautions are required here. + * + *****************************************************************************/ + +void task_removechildren(FAR _TCB *tcb) +{ + FAR struct child_status_s *curr; + FAR struct child_status_s *next; + + /* Remove all child structures for the TCB and return them to the freelist */ + + for (curr = tcb->children; curr; curr = next) + { + next = curr->flink; + task_freechild(curr); + } + + tcb->children = NULL; + task_dumpchildren(tcb, "task_removechildren"); +} + +#endif /* CONFIG_SCHED_HAVE_PARENT && CONFIG_SCHED_CHILD_STATUS */ diff --git a/nuttx/sched/task_create.c b/nuttx/sched/task_create.c index 801706cbf..2ed929ab0 100644 --- a/nuttx/sched/task_create.c +++ b/nuttx/sched/task_create.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/task_create.c * - * Copyright (C) 2007-2010 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2010, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -81,7 +81,7 @@ * * Input Parameters: * name - Name of the new task - * type - Type of the new task + * ttype - Type of the new task * priority - Priority of the new task * stack_size - size (in bytes) of the stack needed * entry - Entry point of a new task @@ -99,10 +99,10 @@ ****************************************************************************/ #ifndef CONFIG_CUSTOM_STACK -static int thread_create(const char *name, uint8_t type, int priority, +static int thread_create(const char *name, uint8_t ttype, int priority, int stack_size, main_t entry, const char **argv) #else -static int thread_create(const char *name, uint8_t type, int priority, +static int thread_create(const char *name, uint8_t ttype, int priority, main_t entry, const char **argv) #endif { @@ -142,15 +142,9 @@ static int thread_create(const char *name, uint8_t type, int priority, } #endif - /* Mark the type of this thread (this setting will be needed in - * task_schedsetup() when up_initial_state() is called. - */ - - tcb->flags |= type; - /* Initialize the task control block */ - ret = task_schedsetup(tcb, priority, task_start, entry); + ret = task_schedsetup(tcb, priority, task_start, entry, ttype); if (ret != OK) { goto errout_with_tcb; diff --git a/nuttx/sched/task_exithook.c b/nuttx/sched/task_exithook.c index 1106f2885..1813c12ed 100644 --- a/nuttx/sched/task_exithook.c +++ b/nuttx/sched/task_exithook.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/task_exithook.c * - * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2011-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -202,51 +202,86 @@ static inline void task_sigchild(FAR _TCB *tcb, int status) FAR _TCB *ptcb; siginfo_t info; - /* Keep things stationary through the following */ + /* Only exiting tasks should generate SIGCHLD. pthreads use other + * mechansims. + */ - sched_lock(); + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_TASK) + { + /* Keep things stationary through the following */ - /* Get the TCB of the receiving task */ + sched_lock(); - ptcb = sched_gettcb(tcb->parent); - if (!ptcb) - { - /* The parent no longer exists... bail */ + /* Get the TCB of the receiving task */ - sched_unlock(); - return; - } + ptcb = sched_gettcb(tcb->parent); + if (!ptcb) + { + /* The parent no longer exists... bail */ - /* Decrement the number of children from this parent */ + sched_unlock(); + return; + } - DEBUGASSERT(ptcb->nchildren > 0); - ptcb->nchildren--; +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Check if the parent task has suppressed retention of child exit + * status information. Only 'tasks' report exit status, not pthreads. + * pthreads have a different mechanism. + */ - /* Set the parent to an impossible PID. We do this because under certain - * conditions, task_exithook() can be called multiple times. If this - * function is called again, sched_gettcb() will fail on the invalid - * parent PID above, nchildren will be decremented once and all will be - * well. - */ + if ((ptcb->flags & TCB_FLAG_NOCLDWAIT) == 0) + { + FAR struct child_status_s *child; - tcb->parent = INVALID_PROCESS_ID; + /* No.. Find the exit status entry for this task in the parent TCB */ - /* Create the siginfo structure. We don't actually know the cause. That - * is a bug. Let's just say that the child task just exit-ted for now. - */ + child = task_findchild(ptcb, getpid()); + DEBUGASSERT(child); + if (child) + { + /* Mark that the child has exit'ed */ - info.si_signo = SIGCHLD; - info.si_code = CLD_EXITED; - info.si_value.sival_ptr = NULL; - info.si_pid = tcb->pid; - info.si_status = status; + child->ch_flags |= CHILD_FLAG_EXITED; - /* Send the signal. We need to use this internal interface so that we can - * provide the correct si_code value with the signal. - */ + /* Save the exit status */ + + child->ch_status = status; + } + } +#else + /* Decrement the number of children from this parent */ - (void)sig_received(ptcb, &info); - sched_unlock(); + DEBUGASSERT(ptcb->nchildren > 0); + ptcb->nchildren--; +#endif + + /* Set the parent to an impossible PID. We do this because under + * certain conditions, task_exithook() can be called multiple times. + * If this function is called again, sched_gettcb() will fail on the + * invalid parent PID above, nchildren will be decremented once and + * all will be well. + */ + + tcb->parent = INVALID_PROCESS_ID; + + /* Create the siginfo structure. We don't actually know the cause. + * That is a bug. Let's just say that the child task just exit-ted + * for now. + */ + + info.si_signo = SIGCHLD; + info.si_code = CLD_EXITED; + info.si_value.sival_ptr = NULL; + info.si_pid = tcb->pid; + info.si_status = status; + + /* Send the signal. We need to use this internal interface so that we + * can provide the correct si_code value with the signal. + */ + + (void)sig_received(ptcb, &info); + sched_unlock(); + } } #else # define task_sigchild(tcb,status) @@ -344,6 +379,12 @@ void task_exithook(FAR _TCB *tcb, int status) (void)lib_flushall(tcb->streams); #endif + /* Discard any un-reaped child status (no zombies here!) */ + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + task_removechildren(tcb); +#endif + /* Free all file-related resources now. This gets called again * just be be certain when the TCB is delallocated. However, we * really need to close files as soon as possible while we still diff --git a/nuttx/sched/task_init.c b/nuttx/sched/task_init.c index 31fc5ef70..0f0fdc68e 100644 --- a/nuttx/sched/task_init.c +++ b/nuttx/sched/task_init.c @@ -141,7 +141,8 @@ int task_init(FAR _TCB *tcb, const char *name, int priority, /* Initialize the task control block */ - ret = task_schedsetup(tcb, priority, task_start, entry); + ret = task_schedsetup(tcb, priority, task_start, entry, + TCB_FLAG_TTYPE_TASK); if (ret == OK) { /* Setup to pass parameters to the new task */ diff --git a/nuttx/sched/task_posixspawn.c b/nuttx/sched/task_posixspawn.c new file mode 100644 index 000000000..7bb9c9a4d --- /dev/null +++ b/nuttx/sched/task_posixspawn.c @@ -0,0 +1,674 @@ +/**************************************************************************** + * sched/task_posixspawn.c + * + * Copyright (C) 2013 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 <nuttx/config.h> + +#include <sys/wait.h> +#include <unistd.h> +#include <semaphore.h> +#include <signal.h> +#include <sched.h> +#include <fcntl.h> +#include <spawn.h> +#include <errno.h> +#include <assert.h> +#include <debug.h> + +#include <nuttx/binfmt/binfmt.h> +#include <nuttx/spawn.h> + +#include "os_internal.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct spawn_parms_s +{ + int result; + FAR pid_t *pid; + FAR const char *path; + FAR const posix_spawn_file_actions_t *file_actions; + FAR const posix_spawnattr_t *attr; + FAR char *const *argv; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static sem_t g_ps_parmsem = SEM_INITIALIZER(1); +#ifndef CONFIG_SCHED_WAITPID +static sem_t g_ps_execsem = SEM_INITIALIZER(0); +#endif +static struct spawn_parms_s g_ps_parms; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spawn_semtake and spawn_semgive + * + * Description: + * Give and take semaphores + * + * Input Parameters: + * + * sem - The semaphore to act on. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void spawn_semtake(FAR sem_t *sem) +{ + int ret; + + do + { + ret = sem_wait(sem); + ASSERT(ret == 0 || errno == EINTR); + } + while (ret != 0); +} + +#define spawn_semgive(sem) sem_post(sem) + +/**************************************************************************** + * Name: spawn_exec + * + * Description: + * Execute the task from the file system. + * + * Input Parameters: + * + * pidp - Upon successful completion, this will return the task ID of the + * child task in the variable pointed to by a non-NULL 'pid' argument.| + * + * path - The 'path' argument identifies the file to execute. If + * CONFIG_BINFMT_EXEPATH is defined, this may be either a relative or + * or an absolute path. Otherwise, it must be an absolute path. + * + * attr - If the value of the 'attr' parameter is NULL, the all default + * values for the POSIX spawn attributes will be used. Otherwise, the + * attributes will be set according to the spawn flags. The + * following spawn flags are supported: + * + * - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param + * value. + * - POSIX_SPAWN_SETSCHEDULER: Set the new tasks scheduler priority to + * the sched_policy value. + * + * NOTE: POSIX_SPAWN_SETSIGMASK is handled in ps_proxy(). + * + * argv - argv[] is the argument list for the new task. argv[] is an + * array of pointers to null-terminated strings. The list is terminated + * with a null pointer. + * + * Returned Value: + * This function will return zero on success. Otherwise, an error number + * will be returned as the function return value to indicate the error. + * This errno value may be that set by execv(), sched_setpolicy(), or + * sched_setparam(). + * + ****************************************************************************/ + +static int spawn_exec(FAR pid_t *pidp, FAR const char *path, + FAR const posix_spawnattr_t *attr, + FAR char *const argv[]) +{ + struct sched_param param; + FAR const struct symtab_s *symtab; + int nsymbols; + int pid; + int ret = OK; + + DEBUGASSERT(path); + + /* Get the current symbol table selection */ + + exec_getsymtab(&symtab, &nsymbols); + + /* Disable pre-emption so that we can modify the task parameters after + * we start the new task; the new task will not actually begin execution + * until we re-enable pre-emption. + */ + + sched_lock(); + + /* Start the task */ + + pid = exec(path, (FAR const char **)argv, symtab, nsymbols); + if (pid < 0) + { + ret = errno; + sdbg("ERROR: exec failed: %d\n", ret); + goto errout; + } + + /* Return the task ID to the caller */ + + if (pid) + { + *pidp = pid; + } + + /* Now set the attributes. Note that we ignore all of the return values + * here because we have already successfully started the task. If we + * return an error value, then we would also have to stop the task. + */ + + if (attr) + { + /* If we are only setting the priority, then call sched_setparm() + * to set the priority of the of the new task. + */ + + if ((attr->flags & POSIX_SPAWN_SETSCHEDPARAM) != 0) + { + /* Get the priority from the attrributes */ + + param.sched_priority = attr->priority; + + /* If we are setting *both* the priority and the scheduler, + * then we will call sched_setscheduler() below. + */ + + if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) == 0) + { + svdbg("Setting priority=%d for pid=%d\n", + param.sched_priority, pid); + + (void)sched_setparam(pid, ¶m); + } + } + + /* If we are only changing the scheduling policy, then reset + * the priority to the default value (the same as this thread) in + * preparation for the sched_setscheduler() call below. + */ + + else if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) + { + (void)sched_getparam(0, ¶m); + } + + /* Are we setting the scheduling policy? If so, use the priority + * setting determined above. + */ + + if ((attr->flags & POSIX_SPAWN_SETSCHEDULER) != 0) + { + svdbg("Setting policy=%d priority=%d for pid=%d\n", + attr->policy, param.sched_priority, pid); + + (void)sched_setscheduler(pid, attr->policy, ¶m); + } + } + + /* Re-enable pre-emption and return */ + +errout: + sched_unlock(); + return ret; +} + +/**************************************************************************** + * Name: spawn_close, spawn_dup2, and spawn_open + * + * Description: + * Implement individual file actions + * + * Input Parameters: + * action - describes the action to be performed + * + * Returned Value: + * posix_spawn() and posix_spawnp() will return zero on success. + * Otherwise, an error number will be returned as the function return + * value to indicate the error. + * + ****************************************************************************/ + +static inline int spawn_close(FAR struct spawn_close_file_action_s *action) +{ + /* The return value from close() is ignored */ + + svdbg("Closing fd=%d\n", action->fd); + + (void)close(action->fd); + return OK; +} + +static inline int spawn_dup2(FAR struct spawn_dup2_file_action_s *action) +{ + int ret; + + /* Perform the dup */ + + svdbg("Dup'ing %d->%d\n", action->fd1, action->fd2); + + ret = dup2(action->fd1, action->fd2); + if (ret < 0) + { + int errcode = errno; + + sdbg("ERROR: dup2 failed: %d\n", errcode); + return errcode; + } + + return OK; +} + +static inline int spawn_open(FAR struct spawn_open_file_action_s *action) +{ + int fd; + int ret = OK; + + /* Open the file */ + + svdbg("Open'ing path=%s oflags=%04x mode=%04x\n", + action->path, action->oflags, action->mode); + + fd = open(action->path, action->oflags, action->mode); + if (fd < 0) + { + ret = errno; + sdbg("ERROR: open failed: %d\n", ret); + } + + /* Does the return file descriptor happen to match the required file + * desciptor number? + */ + + else if (fd != action->fd) + { + /* No.. dup2 to get the correct file number */ + + svdbg("Dup'ing %d->%d\n", fd, action->fd); + + ret = dup2(fd, action->fd); + if (ret < 0) + { + ret = errno; + sdbg("ERROR: dup2 failed: %d\n", ret); + } + + svdbg("Closing fd=%d\n", fd); + close(fd); + } + + return ret; +} + +/**************************************************************************** + * Name: spawn_proxy + * + * Description: + * Perform file_actions, then execute the task from the file system. + * + * Input Parameters: + * Standard task start-up parameters + * + * Returned Value: + * Standard task return value. + * + ****************************************************************************/ + +static int spawn_proxy(int argc, char *argv[]) +{ + FAR struct spawn_general_file_action_s *entry; + FAR const posix_spawnattr_t *attr = g_ps_parms.attr; + int ret = OK; + + /* Perform file actions and/or set a custom signal mask. We get here only + * if the file_actions parameter to posix_spawn[p] was non-NULL and/or the + * option to change the signal mask was selected. + */ + +#ifndef CONFIG_DISABLE_SIGNALS + DEBUGASSERT((g_ps_parms.file_actions && *g_ps_parms.file_actions) || + (attr && (attr->flags & POSIX_SPAWN_SETSIGMASK) != 0)); +#else + DEBUGASSERT(g_ps_parms.file_actions && *g_ps_parms.file_actions); +#endif + + /* Check if we need to change the signal mask */ + +#ifndef CONFIG_DISABLE_SIGNALS + if (attr && (attr->flags & POSIX_SPAWN_SETSIGMASK) != 0) + { + (void)sigprocmask(SIG_SETMASK, &attr->sigmask, NULL); + } + + /* Were we also requested to perform file actions? */ + + if (g_ps_parms.file_actions) +#endif + { + /* Execute each file action */ + + for (entry = (FAR struct spawn_general_file_action_s *)*g_ps_parms.file_actions; + entry && ret == OK; + entry = entry->flink) + { + switch (entry->action) + { + case SPAWN_FILE_ACTION_CLOSE: + ret = spawn_close((FAR struct spawn_close_file_action_s *)entry); + break; + + case SPAWN_FILE_ACTION_DUP2: + ret = spawn_dup2((FAR struct spawn_dup2_file_action_s *)entry); + break; + + case SPAWN_FILE_ACTION_OPEN: + ret = spawn_open((FAR struct spawn_open_file_action_s *)entry); + break; + + case SPAWN_FILE_ACTION_NONE: + default: + sdbg("ERROR: Unknown action: %d\n", entry->action); + ret = EINVAL; + break; + } + } + } + + /* Check for failures */ + + if (ret == OK) + { + /* Start the task */ + + ret = spawn_exec(g_ps_parms.pid, g_ps_parms.path, attr, + g_ps_parms.argv); + +#ifdef CONFIG_SCHED_HAVE_PARENT + if (ret == OK) + { + /* Change of the parent of the task we just spawned to our parent. + * What should we do in the event of a failure? + */ + + int tmp = task_reparent(0, *g_ps_parms.pid); + if (tmp < 0) + { + sdbg("ERROR: task_reparent() failed: %d\n", tmp); + } + } +#endif + } + + /* Post the semaphore to inform the parent task that we have completed + * what we need to do. + */ + + g_ps_parms.result = ret; +#ifndef CONFIG_SCHED_WAITPID + spawn_semgive(&g_ps_execsem); +#endif + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: posix_spawn + * + * Description: + * The posix_spawn() and posix_spawnp() functions will create a new, + * child task, constructed from a regular executable file. + * + * Input Parameters: + * + * pid - Upon successful completion, posix_spawn() and posix_spawnp() will + * return the task ID of the child task to the parent task, in the + * variable pointed to by a non-NULL 'pid' argument. If the 'pid' + * argument is a null pointer, the process ID of the child is not + * returned to the caller. + * + * path - The 'path' argument to posix_spawn() is the absolute path that + * identifies the file to execute. The 'path' argument to posix_spawnp() + * may also be a relative path and will be used to construct a pathname + * that identifies the file to execute. In the case of a relative path, + * the path prefix for the file will be obtained by a search of the + * directories passed as the environment variable PATH. + * + * NOTE: NuttX provides only one implementation: If + * CONFIG_BINFMT_EXEPATH is defined, then only posix_spawnp() behavior + * is supported; otherwise, only posix_spawn behavior is supported. + * + * file_actions - If 'file_actions' is a null pointer, then file + * descriptors open in the calling process will remain open in the + * child process (unless CONFIG_FDCLONE_STDIO is defined). If + * 'file_actions' is not NULL, then the file descriptors open in the + * child process will be those open in the calling process as modified + * by the spawn file actions object pointed to by file_actions. + * + * attr - If the value of the 'attr' parameter is NULL, the all default + * values for the POSIX spawn attributes will be used. Otherwise, the + * attributes will be set according to the spawn flags. The + * posix_spawnattr_t spawn attributes object type is defined in spawn.h. + * It will contains these attributes, not all of which are supported by + * NuttX: + * + * - POSIX_SPAWN_SETPGROUP: Setting of the new task's process group is + * not supported. NuttX does not support process groups. + * - POSIX_SPAWN_SETSCHEDPARAM: Set new tasks priority to the sched_param + * value. + * - POSIX_SPAWN_SETSCHEDULER: Set the new task's scheduler policy to + * the sched_policy value. + * - POSIX_SPAWN_RESETIDS: Resetting of the effective user ID of the child + * process is not supported. NuttX does not support effective user + * IDs. + * - POSIX_SPAWN_SETSIGMASK: Set the new task's signal mask. + * - POSIX_SPAWN_SETSIGDEF: Resetting signal default actions is not + * supported. NuttX does not support default signal actions. + * + * argv - argv[] is the argument list for the new task. argv[] is an + * array of pointers to null-terminated strings. The list is terminated + * with a null pointer. + * + * envp - The envp[] argument is not used by NuttX and may be NULL. In + * standard implementations, envp[] is an array of character pointers to + * null-terminated strings that provide the environment for the new + * process image. The environment array is terminated by a null pointer. + * In NuttX, the envp[] argument is ignored and the new task will simply + * inherit the environment of the parent task. + * + * Returned Value: + * posix_spawn() and posix_spawnp() will return zero on success. + * Otherwise, an error number will be returned as the function return + * value to indicate the error: + * + * - EINVAL: The value specified by 'file_actions' or 'attr' is invalid. + * - Any errors that might have been return if vfork() and excec[l|v]() + * had been called. + * + * Assumptions/Limitations: + * - NuttX provides only posix_spawn() or posix_spawnp() behavior + * depending upon the setting of CONFIG_BINFMT_EXEPATH: If + * CONFIG_BINFMT_EXEPATH is defined, then only posix_spawnp() behavior + * is supported; otherwise, only posix_spawn behavior is supported. + * - The 'envp' argument is not used and the 'environ' variable is not + * altered (NuttX does not support the 'environ' variable). + * - Process groups are not supported (POSIX_SPAWN_SETPGROUP). + * - Effective user IDs are not supported (POSIX_SPAWN_RESETIDS). + * - Signal default actions cannot be modified in the newly task executed + * because NuttX does not support default signal actions + * (POSIX_SPAWN_SETSIGDEF). + * + * POSIX Compatibility + * - The value of the argv[0] received by the child task is assigned by + * NuttX. For the caller of posix_spawn(), the provided argv[0] will + * correspond to argv[1] received by the new task. + * + ****************************************************************************/ + +#ifdef CONFIG_BINFMT_EXEPATH +int posix_spawnp(FAR pid_t *pid, FAR const char *path, + FAR const posix_spawn_file_actions_t *file_actions, + FAR const posix_spawnattr_t *attr, + FAR char *const argv[], FAR char *const envp[]) +#else +int posix_spawn(FAR pid_t *pid, FAR const char *path, + FAR const posix_spawn_file_actions_t *file_actions, + FAR const posix_spawnattr_t *attr, + FAR char *const argv[], FAR char *const envp[]) +#endif +{ + struct sched_param param; + pid_t proxy; +#ifdef CONFIG_SCHED_WAITPID + int status; +#endif + int ret; + + DEBUGASSERT(path); + + svdbg("pid=%p path=%s file_actions=%p attr=%p argv=%p\n", + pid, path, file_actions, attr, argv); + + /* If there are no file actions to be performed and there is no change to + * the signal mask, then start the new child task directly from the parent task. + */ + +#ifndef CONFIG_DISABLE_SIGNALS + if ((file_actions == NULL || *file_actions == NULL) && + (attr == NULL || (attr->flags & POSIX_SPAWN_SETSIGMASK) == 0)) +#else + if (file_actions == NULL || *file_actions == NULL) +#endif + { + return spawn_exec(pid, path, attr, argv); + } + + /* Otherwise, we will have to go through an intermediary/proxy task in order + * to perform the I/O redirection. This would be a natural place to fork(). + * However, true fork() behavior requires an MMU and most implementations + * of vfork() are not capable of these operations. + * + * Even without fork(), we can still do the job, but parameter passing is + * messier. Unfortunately, there is no (clean) way to pass binary values + * as a task parameter, so we will use a semaphore-protected global + * structure. + */ + + /* Get exclusive access to the global parameter structure */ + + spawn_semtake(&g_ps_parmsem); + + /* Populate the parameter structure */ + + g_ps_parms.result = ENOSYS; + g_ps_parms.pid = pid; + g_ps_parms.path = path; + g_ps_parms.file_actions = file_actions; + g_ps_parms.attr = attr; + g_ps_parms.argv = argv; + + /* Get the priority of this (parent) task */ + + ret = sched_getparam(0, ¶m); + if (ret < 0) + { + int errcode = errno; + + sdbg("ERROR: sched_getparam failed: %d\n", errcode); + spawn_semgive(&g_ps_parmsem); + return errcode; + } + + /* Disable pre-emption so that the proxy does not run until we waitpid + * is called. This is probably unnecessary since the spawn_proxy has + * the same priority as this thread; it should be schedule behind this + * task in the ready-to-run list. + */ + +#ifdef CONFIG_SCHED_WAITPID + sched_lock(); +#endif + + /* Start the intermediary/proxy task at the same priority as the parent + * task. + */ + + proxy = TASK_CREATE("spawn_proxy", param.sched_priority, + CONFIG_POSIX_SPAWN_STACKSIZE, (main_t)spawn_proxy, + (FAR const char **)NULL); + if (proxy < 0) + { + ret = get_errno(); + sdbg("ERROR: Failed to start spawn_proxy: %d\n", ret); + + goto errout_with_lock; + } + + /* Wait for the proxy to complete its job */ + +#ifdef CONFIG_SCHED_WAITPID + ret = waitpid(proxy, &status, 0); + if (ret < 0) + { + sdbg("ERROR: waitpid() failed: %d\n", errno); + goto errout_with_lock; + } +#else + spawn_semtake(&g_ps_execsem); +#endif + + /* Get the result and relinquish our access to the parameter structure */ + + ret = g_ps_parms.result; + +errout_with_lock: +#ifdef CONFIG_SCHED_WAITPID + sched_unlock(); +#endif + spawn_semgive(&g_ps_parmsem); + return ret; +} diff --git a/nuttx/sched/task_reparent.c b/nuttx/sched/task_reparent.c new file mode 100644 index 000000000..28d371bf1 --- /dev/null +++ b/nuttx/sched/task_reparent.c @@ -0,0 +1,163 @@ +/***************************************************************************** + * sched/task_reparent.c + * + * Copyright (C) 2013 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 <nuttx/config.h> + +#include <errno.h> + +#include "os_internal.h" + +#ifdef CONFIG_SCHED_HAVE_PARENT + +/***************************************************************************** + * Private Functions + *****************************************************************************/ + +/***************************************************************************** + * Public Functions + *****************************************************************************/ + +/***************************************************************************** + * Name: task_reparent + * + * Description: + * Change the parent of a task. + * + * Parameters: + * ppid - PID of the new parent task (0 for grandparent, i.e. the parent + * of the current parent task) + * chpid - PID of the child to be reparented. + * + * Return Value: + * 0 (OK) on success; A negated errno value on failure. + * + *****************************************************************************/ + +int task_reparent(pid_t ppid, pid_t chpid) +{ +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; +#endif + _TCB *ptcb; + _TCB *chtcb; + _TCB *otcb; + pid_t opid; + irqstate_t flags; + int ret; + + /* Disable interrupts so that nothing can change in the relatinoship of + * the three task: Child, current parent, and new parent. + */ + + flags = irqsave(); + + /* Get the child tasks TCB (chtcb) */ + + chtcb = sched_gettcb(chpid); + if (!chtcb) + { + ret = -ECHILD; + goto errout_with_ints; + } + + /* Get the PID of the child task's parent (opid) */ + + opid = chtcb->parent; + + /* Get the TCB of the child task's parent (otcb) */ + + otcb = sched_gettcb(opid); + if (!otcb) + { + ret = -ESRCH; + goto errout_with_ints; + } + + /* If new parent task's PID (ppid) is zero, then new parent is the + * grandparent will be the new parent, i.e., the parent of the current + * parent task. + */ + + if (ppid == 0) + { + ppid = otcb->parent; + } + + /* Get the new parent task's TCB (ptcb) */ + + ptcb = sched_gettcb(ppid); + if (!ptcb) + { + ret = -ESRCH; + goto errout_with_ints; + } + + /* Then reparent the child */ + + chtcb->parent = ppid; /* The task specified by ppid is the new parent */ + +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Remove the child status entry from old parent TCB */ + + child = task_removechild(otcb, chpid); + if (child) + { + /* Add the child status entry to the new parent TCB */ + + task_addchild(ptcb, child); + ret = OK; + } + else + { + ret = -ENOENT; + } +#else + DEBUGASSERT(otcb->nchildren > 0); + + otcb->nchildren--; /* The orignal parent now has one few children */ + ptcb->nchildren++; /* The new parent has one additional child */ + ret = OK; +#endif + +errout_with_ints: + irqrestore(flags); + return ret; +} + +#endif /* CONFIG_SCHED_HAVE_PARENT */ diff --git a/nuttx/sched/task_setup.c b/nuttx/sched/task_setup.c index 92897f0ae..80aefded3 100644 --- a/nuttx/sched/task_setup.c +++ b/nuttx/sched/task_setup.c @@ -153,7 +153,8 @@ static int task_assignpid(FAR _TCB *tcb) * Save the task ID of the parent task in the child task's TCB. * * Parameters: - * tcb - The TCB of the new, child task. + * tcb - The TCB of the new, child task. + * ttype - Type of the new thread: task, pthread, or kernel thread * * Returned Value: * None @@ -165,13 +166,57 @@ static int task_assignpid(FAR _TCB *tcb) ****************************************************************************/ #ifdef CONFIG_SCHED_HAVE_PARENT -static inline void task_saveparent(FAR _TCB *tcb) +static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) { FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head; - DEBUGASSERT(rtcb->nchildren < UINT16_MAX); + /* Save the parent task's ID in the child task's TCB. I am not sure if + * this makes sense for the case of pthreads or not, but I don't think it + * is harmful in any event. + */ + tcb->parent = rtcb->pid; - rtcb->nchildren++; + + /* Exit status only needs to be retained for the case of tasks, however */ + + if (ttype == TCB_FLAG_TTYPE_TASK) + { +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; + + /* Make sure that there is not already a structure for this PID in the + * parent TCB. There should not be. + */ + + child = task_findchild(rtcb, tcb->pid); + DEBUGASSERT(!child); + if (!child) + { + /* Allocate a new status structure */ + + child = task_allocchild(); + } + + /* Did we successfully find/allocate the child status structure? */ + + DEBUGASSERT(child); + if (child) + { + /* Yes.. Initialize the structure */ + + child->ch_flags = ttype; + child->ch_pid = tcb->pid; + child->ch_status = 0; + + /* Add the entry into the TCB list of children */ + + task_addchild(rtcb, child); + } +#else + DEBUGASSERT(rtcb->nchildren < UINT16_MAX); + rtcb->nchildren++; +#endif + } } #else # define task_saveparent(tcb) @@ -235,7 +280,7 @@ static inline void task_dupdspace(FAR _TCB *tcb) * priority - Priority of the new task * entry - Entry point of a new task * main - Application start point of the new task - * type - Type of the new thread: task, pthread, or kernel thread + * ttype - Type of the new thread: task, pthread, or kernel thread * * Return Value: * OK on success; ERROR on failure. @@ -245,7 +290,8 @@ static inline void task_dupdspace(FAR _TCB *tcb) * ****************************************************************************/ -int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main) +int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main, + uint8_t ttype) { int ret; @@ -264,9 +310,17 @@ int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main) tcb->start = start; tcb->entry.main = main; + /* Save the thrad type. This setting will be needed in + * up_initial_state() is called. + */ + + ttype &= TCB_FLAG_TTYPE_MASK; + tcb->flags &= ~TCB_FLAG_TTYPE_MASK; + tcb->flags |= ttype; + /* Save the task ID of the parent task in the TCB */ - task_saveparent(tcb); + task_saveparent(tcb, ttype); /* exec(), pthread_create(), task_create(), and vfork() all * inherit the signal mask of the parent thread. diff --git a/nuttx/sched/task_vfork.c b/nuttx/sched/task_vfork.c index 46b2d8e9f..fece4c596 100644 --- a/nuttx/sched/task_vfork.c +++ b/nuttx/sched/task_vfork.c @@ -136,12 +136,6 @@ FAR _TCB *task_vforksetup(start_t retaddr) (void)env_dup(child); - /* Mark the type of this thread (this setting will be needed in - * task_schedsetup() when up_initial_state() is called. - */ - - child->flags |= TCB_FLAG_TTYPE_TASK; - /* Get the priority of the parent task */ #ifdef CONFIG_PRIORITY_INHERITANCE @@ -153,7 +147,8 @@ FAR _TCB *task_vforksetup(start_t retaddr) /* Initialize the task control block. This calls up_initial_state() */ svdbg("Child priority=%d start=%p\n", priority, retaddr); - ret = task_schedsetup(child, priority, retaddr, parent->entry.main); + ret = task_schedsetup(child, priority, retaddr, parent->entry.main, + TCB_FLAG_TTYPE_TASK); if (ret != OK) { goto errout_with_tcb; |