From: bernie Date: Thu, 21 Aug 2008 11:22:37 +0000 (+0000) Subject: proc: Use a global forbid count; X-Git-Tag: 2.0.0~267 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=40c25a4b1d0f1c23478c2d681b74662de968f671;p=bertos.git proc: Use a global forbid count; git-svn-id: https://src.develer.com/svnoss/bertos/trunk@1667 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/kern/preempt.c b/bertos/kern/preempt.c index 7ecd481d..756a38fe 100644 --- a/bertos/kern/preempt.c +++ b/bertos/kern/preempt.c @@ -41,44 +41,15 @@ #include #include #include // CPU_IDLE +#include // IRQ_DISABLE()... #include #include +int preempt_forbid_cnt; Timer preempt_timer; -/** - * Disable preemptive task switching. - * - * The scheduler maintains a per-process nesting counter. Task switching is - * effectively re-enabled only when the number of calls to proc_permit() - * matches the number of calls to proc_forbid(). - * - * Calling functions that could sleep while task switching is disabled - * is dangerous, although supported. Preemptive task switching is - * resumed while the process is sleeping and disabled again as soon as - * it wakes up again. - * - * \sa proc_permit() - */ -void proc_forbid(void) -{ - /* No need to protect against interrupts here. */ - ++CurrentProcess->forbid_cnt; -} - -/** - * Re-enable preemptive task switching. - * - * \sa proc_forbid() - */ -void proc_permit(void) -{ - /* No need to protect against interrupts here. */ - --CurrentProcess->forbid_cnt; -} - void proc_preempt(void) { @@ -97,12 +68,15 @@ void proc_preempt(void) void proc_preempt_timer(UNUSED_ARG(void *, param)) { + /* Abort if task preemption is disabled */ + if (preempt_forbid_cnt) + return; + IRQ_DISABLE; /* if (!CurrentProcess->forbid_cnt) { TRACEMSG("preempting %p:%s", CurrentProcess, CurrentProcess->monitor.name); - LIST_ASSERT_VALID(&ProcReadyList); SCHED_ENQUEUE(CurrentProcess); proc_preempt(); } @@ -115,7 +89,13 @@ void proc_preempt_timer(UNUSED_ARG(void *, param)) void proc_schedule(void) { - TRACE; + ATOMIC(LIST_ASSERT_VALID(&ProcReadyList)); + TRACEMSG("%p:%s", CurrentProcess, proc_currentName()); + ATOMIC(LIST_ASSERT_VALID(&ProcReadyList)); + + /* Sleeping with IRQs disabled or preemption forbidden is illegal */ + ASSERT_IRQ_ENABLED(); + ASSERT(preempt_forbid_cnt == 0); // Will invoke proc_preempt() in interrupt context kill(0, SIGUSR1); @@ -123,14 +103,13 @@ void proc_schedule(void) void proc_yield(void) { - TRACE; + TRACEMSG("%p:%s", CurrentProcess, proc_currentName()); - ASSERT_IRQ_ENABLED(); IRQ_DISABLE; SCHED_ENQUEUE(CurrentProcess); - LIST_ASSERT_VALID(&ProcReadyList); - proc_schedule(); IRQ_ENABLE; + + proc_schedule(); } void proc_entry(void (*user_entry)(void)) @@ -142,10 +121,11 @@ void proc_entry(void (*user_entry)(void)) static cpustack_t idle_stack[CONFIG_PROC_DEFSTACKSIZE / sizeof(cpustack_t)]; -/* +// FIXME: move this to kern/idle.c +/** * The idle process * - * This process never dies and never sleeps. It's also quite apathic + * This process never dies and never sleeps. It's also quite lazy, apathic * and a bit antisocial. * * Having an idle process costs some stack space, but simplifies the @@ -157,7 +137,7 @@ static NORETURN void idle(void) for (;;) { TRACE; - monitor_report(); + //monitor_report(); proc_yield(); // FIXME: CPU_IDLE } } diff --git a/bertos/kern/proc.c b/bertos/kern/proc.c index 16e171de..4b8f3f65 100644 --- a/bertos/kern/proc.c +++ b/bertos/kern/proc.c @@ -90,10 +90,6 @@ static void proc_init_struct(Process *proc) proc->sig_recv = 0; #endif -#if CONFIG_KERN_PREEMPT - proc->forbid_cnt = 0; -#endif - #if CONFIG_KERN_HEAP proc->flags = 0; #endif @@ -223,7 +219,6 @@ struct Process *proc_new_with_name(UNUSED(const char *, name), void (*entry)(voi /* Add to ready list */ ATOMIC(SCHED_ENQUEUE(proc)); - ATOMIC(LIST_ASSERT_VALID(&ProcReadyList)); #if CONFIG_KERN_MONITOR monitor_add(proc, name); @@ -232,7 +227,28 @@ struct Process *proc_new_with_name(UNUSED(const char *, name), void (*entry)(voi return proc; } -/** Rename a process */ +/** + * Return the name of the specified process. + * + * NULL is a legal argument and will return the name "". + */ +const char *proc_name(struct Process *proc) +{ + #if CONFIG_KERN_MONITOR + return proc ? proc->monitor.name : ""; + #else + (void)proc; + return "---"; + #endif +} + +/// Return the name of the currently running process +const char *proc_currentName(void) +{ + return proc_name(proc_current()); +} + +/// Rename a process void proc_rename(struct Process *proc, const char *name) { #if CONFIG_KERN_MONITOR @@ -242,13 +258,12 @@ void proc_rename(struct Process *proc, const char *name) #endif } - /** * Terminate the current process */ void proc_exit(void) { - TRACEMSG("%p:%s", CurrentProcess, CurrentProcess->monitor.name); + TRACEMSG("%p:%s", CurrentProcess, proc_currentName()); #if CONFIG_KERN_MONITOR monitor_remove(CurrentProcess); @@ -297,7 +312,7 @@ struct Process *proc_current(void) /** * Get the pointer to the user data of the current process */ -iptr_t proc_current_user_data(void) +iptr_t proc_currentUserData(void) { return CurrentProcess->user_data; } diff --git a/bertos/kern/proc.h b/bertos/kern/proc.h index 482fb7a3..a2a7b283 100644 --- a/bertos/kern/proc.h +++ b/bertos/kern/proc.h @@ -41,9 +41,16 @@ #include "cfg/cfg_kern.h" #include -#include +#if CONFIG_KERN_PREEMPT + #include // ASSERT() +#endif -/* Fwd decl */ +#include // cpustack_t + +/* + * Forward declaration. The definition of struct Process is private to the + * scheduler and hidden in proc_p.h. + */ struct Process; /* Task scheduling services */ @@ -65,16 +72,72 @@ int proc_testRun(void); int proc_testTearDown(void); struct Process *proc_current(void); -iptr_t proc_current_user_data(void); -void proc_rename(struct Process *proc, const char* name); +iptr_t proc_currentUserData(void); +void proc_rename(struct Process *proc, const char *name); +const char *proc_name(struct Process *proc); +const char *proc_currentName(void); + +/** + * Disable preemptive task switching. + * + * The scheduler maintains a global nesting counter. Task switching is + * effectively re-enabled only when the number of calls to proc_permit() + * matches the number of calls to proc_forbid(). + * + * \note Calling functions that could sleep while task switching is disabled + * is dangerous and unsupported. + * + * \note proc_permit() expands inline to 1-2 asm instructions, so it's a + * very efficient locking primitive in simple but performance-critical + * situations. In all other cases, semaphores offer a more flexible and + * fine-grained locking primitive. + * + * \sa proc_permit() + */ +INLINE void proc_forbid(void) +{ + #if CONFIG_KERN_PREEMPT + // No need to protect against interrupts here. + extern int preempt_forbid_cnt; + ++preempt_forbid_cnt; + + /* + * Make sure preempt_forbid_cnt is flushed to memory so the + * preemption softirq will see the correct value from now on. + */ + MEMORY_BARRIER; + #endif +} + +/** + * Re-enable preemptive task switching. + * + * \sa proc_forbid() + */ +INLINE void proc_permit(void) +{ + #if CONFIG_KERN_PREEMPT + + /* + * This is to ensure any global state changed by the process gets + * flushed to memory before task switching is re-enabled. + */ + MEMORY_BARRIER; + + /* No need to protect against interrupts here. */ + extern int preempt_forbid_cnt; + --preempt_forbid_cnt; + ASSERT(preempt_forbid_cnt >= 0); + + /* + * This ensures preempt_forbid_cnt is flushed to memory immediately + * so the preemption interrupt sees the correct value. + */ + MEMORY_BARRIER; + + #endif +} -#if CONFIG_KERN_PREEMPT - void proc_forbid(void); - void proc_permit(void); -#else - INLINE void proc_forbid(void) { /* nop */ } - INLINE void proc_permit(void) { /* nop */ } -#endif /** * Execute a block of \a CODE atomically with respect to task scheduling. diff --git a/bertos/kern/proc_p.h b/bertos/kern/proc_p.h index fbac38fc..0d6a94f1 100644 --- a/bertos/kern/proc_p.h +++ b/bertos/kern/proc_p.h @@ -62,12 +62,6 @@ typedef struct Process sigmask_t sig_recv; /**< Received signals */ #endif -#if CONFIG_KERN_PREEMPTIVE - int forbid_cnt; /**< Nesting count for proc_forbid()/proc_permit(). */ - bool leaving; /**< XXX: maybe global? */ - ucontext_t context; -#endif - #if CONFIG_KERN_HEAP uint16_t flags; /**< Flags */ #endif @@ -77,6 +71,10 @@ typedef struct Process size_t stack_size; /**< Size of process stack */ #endif +#if CONFIG_KERN_PREEMPTIVE + ucontext_t context; +#endif + #if CONFIG_KERN_MONITOR struct ProcMonitor { @@ -117,7 +115,10 @@ extern REGISTER List ProcReadyList; * \note This macro is *NOT* protected against the scheduler. Access to * this list must be performed with interrupts disabled. */ -#define SCHED_ENQUEUE(proc) ADDTAIL(&ProcReadyList, &(proc)->link) +#define SCHED_ENQUEUE(proc) do { \ + LIST_ASSERT_VALID(&ProcReadyList); \ + ADDTAIL(&ProcReadyList, &(proc)->link); \ + } while (0) /** Schedule to another process *without* adding the current to the ready list. */ void proc_schedule(void);