diff options
| author | Bdale Garbee <bdale@gag.com> | 2013-05-16 00:36:23 -0600 | 
|---|---|---|
| committer | Bdale Garbee <bdale@gag.com> | 2013-05-16 00:36:23 -0600 | 
| commit | 02d111b1b53ef01fc6e9ab6c4bc60b8af1be0067 (patch) | |
| tree | 8356f4a019969ee99a45e264c87d38555cf316cc /src/core/ao_task.c | |
| parent | 7a2e1f05adad990a6b161865267abf07ffec7a7e (diff) | |
| parent | 7699a55aed3a9a7daeb4c6a5a9a280f43edf455f (diff) | |
Merge branch 'branch-1.2' into debian
Diffstat (limited to 'src/core/ao_task.c')
| -rw-r--r-- | src/core/ao_task.c | 372 | 
1 files changed, 356 insertions, 16 deletions
| diff --git a/src/core/ao_task.c b/src/core/ao_task.c index 65654731..0aad6508 100644 --- a/src/core/ao_task.c +++ b/src/core/ao_task.c @@ -16,14 +16,26 @@   */  #include <ao.h> +#include <ao_task.h> +#if HAS_SAMPLE_PROFILE +#include <ao_sample_profile.h> +#endif +#if HAS_STACK_GUARD +#include <ao_mpu.h> +#endif + +#define DEBUG	0  #define AO_NO_TASK_INDEX	0xff  __xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS];  __data uint8_t ao_num_tasks; -__data uint8_t ao_cur_task_index;  __xdata struct ao_task *__data ao_cur_task; +#if !HAS_TASK_QUEUE +static __data uint8_t ao_cur_task_index; +#endif +  #ifdef ao_arch_task_globals  ao_arch_task_globals  #endif @@ -42,6 +54,225 @@ static inline void ao_check_stack(void) {  #define ao_check_stack()  #endif +#if HAS_TASK_QUEUE + +#define SLEEP_HASH_SIZE	17 + +static struct ao_list	run_queue; +static struct ao_list	alarm_queue; +static struct ao_list	sleep_queue[SLEEP_HASH_SIZE]; + +static void +ao_task_to_run_queue(struct ao_task *task) +{ +	ao_list_del(&task->queue); +	ao_list_append(&task->queue, &run_queue); +} + +static struct ao_list * +ao_task_sleep_queue(void *wchan) +{ +	return &sleep_queue[(uintptr_t) wchan % SLEEP_HASH_SIZE]; +} + +static void +ao_task_to_sleep_queue(struct ao_task *task, void *wchan) +{ +	ao_list_del(&task->queue); +	ao_list_append(&task->queue, ao_task_sleep_queue(wchan)); +} + +#if DEBUG +static void +ao_task_validate_alarm_queue(void) +{ +	struct ao_task	*alarm, *prev = NULL; +	int		i; + +	if (ao_list_is_empty(&alarm_queue)) +		return; +	ao_list_for_each_entry(alarm, &alarm_queue, struct ao_task, alarm_queue) { +		if (prev) { +			if ((int16_t) (alarm->alarm - prev->alarm) < 0) { +				ao_panic(1); +			} +		} +		prev = alarm; +	} +	for (i = 0; i < ao_num_tasks; i++) { +		alarm = ao_tasks[i]; +		if (alarm->alarm) { +			if (ao_list_is_empty(&alarm->alarm_queue)) +				ao_panic(2); +		} else { +			if (!ao_list_is_empty(&alarm->alarm_queue)) +				ao_panic(3); +		} +	} +} +#else +#define ao_task_validate_alarm_queue() +#endif + +uint16_t	ao_task_alarm_tick; + +static void +ao_task_to_alarm_queue(struct ao_task *task) +{ +	struct ao_task	*alarm; +	ao_list_for_each_entry(alarm, &alarm_queue, struct ao_task, alarm_queue) { +		if ((int16_t) (alarm->alarm - task->alarm) >= 0) { +			ao_list_insert(&task->alarm_queue, alarm->alarm_queue.prev); +			ao_task_validate_alarm_queue(); +			return; +		} +	} +	ao_list_append(&task->alarm_queue, &alarm_queue); +	ao_task_alarm_tick = ao_list_first_entry(&alarm_queue, struct ao_task, alarm_queue)->alarm; +	ao_task_validate_alarm_queue(); +} + +static void +ao_task_from_alarm_queue(struct ao_task *task) +{ +	ao_list_del(&task->alarm_queue); +	if (ao_list_is_empty(&alarm_queue)) +		ao_task_alarm_tick = 0; +	else +		ao_task_alarm_tick = ao_list_first_entry(&alarm_queue, struct ao_task, alarm_queue)->alarm; +	ao_task_validate_alarm_queue(); +} + +static void +ao_task_init_queue(struct ao_task *task) +{ +	ao_list_init(&task->queue); +	ao_list_init(&task->alarm_queue); +} + +static void +ao_task_exit_queue(struct ao_task *task) +{ +	ao_list_del(&task->queue); +	ao_list_del(&task->alarm_queue); +} + +void +ao_task_check_alarm(uint16_t tick) +{ +	struct ao_task	*alarm, *next; + +	ao_list_for_each_entry_safe(alarm, next, &alarm_queue, struct ao_task, alarm_queue) { +		if ((int16_t) (tick - alarm->alarm) < 0) +			break; +		alarm->alarm = 0; +		ao_task_from_alarm_queue(alarm); +		ao_task_to_run_queue(alarm); +	} +} + +void +ao_task_init(void) +{ +	uint8_t	i; +	ao_list_init(&run_queue); +	ao_list_init(&alarm_queue); +	ao_task_alarm_tick = 0; +	for (i = 0; i < SLEEP_HASH_SIZE; i++) +		ao_list_init(&sleep_queue[i]); +} + +#if DEBUG +static uint8_t +ao_task_validate_queue(struct ao_task *task) +{ +	uint32_t flags; +	struct ao_task *m; +	uint8_t ret = 0; +	struct ao_list *queue; + +	flags = ao_arch_irqsave(); +	if (task->wchan) { +		queue = ao_task_sleep_queue(task->wchan); +		ret |= 2; +	} else { +		queue = &run_queue; +		ret |= 4; +	} +	ao_list_for_each_entry(m, queue, struct ao_task, queue) { +		if (m == task) { +			ret |= 1; +			break; +		} +	} +	ao_arch_irqrestore(flags); +	return ret; +} + +static uint8_t +ao_task_validate_alarm(struct ao_task *task) +{ +	uint32_t	flags; +	struct ao_task	*m; +	uint8_t		ret = 0; + +	flags = ao_arch_irqsave(); +	if (task->alarm == 0) +		return 0xff; +	ao_list_for_each_entry(m, &alarm_queue, struct ao_task, alarm_queue) { +		if (m == task) +			ret |= 1; +		else { +			if (!(ret&1)) { +				if ((int16_t) (m->alarm - task->alarm) > 0) +					ret |= 2; +			} else { +				if ((int16_t) (task->alarm - m->alarm) > 0) +					ret |= 4; +			} +		} +	} +	ao_arch_irqrestore(flags); +	return ret; +} + + +static void +ao_task_validate(void) +{ +	uint8_t		i; +	struct ao_task	*task; +	uint8_t		ret; + +	for (i = 0; i < ao_num_tasks; i++) { +		task = ao_tasks[i]; +		ret = ao_task_validate_queue(task); +		if (!(ret & 1)) { +			if (ret & 2) +				printf ("sleeping task not on sleep queue %s %08x\n", +					task->name, task->wchan); +			else +				printf ("running task not on run queue %s\n", +					task->name); +		} +		ret = ao_task_validate_alarm(task); +		if (ret != 0xff) { +			if (!(ret & 1)) +				printf ("alarm task not on alarm queue %s %d\n", +					task->name, task->alarm); +			if (ret & 2) +				printf ("alarm queue has sooner entries after %s %d\n", +					task->name, task->alarm); +			if (ret & 4) +				printf ("alarm queue has later entries before %s %d\n", +					task->name, task->alarm); +		} +	} +} +#endif /* DEBUG */ + +#endif /* HAS_TASK_QUEUE */ +  void  ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *name) __reentrant  { @@ -56,7 +287,6 @@ ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *nam  		if (t == ao_num_tasks)  			break;  	} -	ao_tasks[ao_num_tasks++] = task;  	task->task_id = task_id;  	task->name = name;  	task->wchan = NULL; @@ -65,22 +295,50 @@ ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *nam  	 * to the start of the task  	 */  	ao_arch_init_stack(task, start); +	ao_arch_critical( +#if HAS_TASK_QUEUE +		ao_task_init_queue(task); +		ao_task_to_run_queue(task); +#endif +		ao_tasks[ao_num_tasks] = task; +		ao_num_tasks++; +		);  } +__data uint8_t	ao_task_minimize_latency; +  /* Task switching function. This must not use any stack variables */  void  ao_yield(void) ao_arch_naked_define  {  	ao_arch_save_regs(); +#if HAS_TASK_QUEUE +	if (ao_cur_task == NULL) +		ao_cur_task = ao_tasks[ao_num_tasks-1]; +#else  	if (ao_cur_task_index == AO_NO_TASK_INDEX)  		ao_cur_task_index = ao_num_tasks-1; +#endif  	else  	{ +#if HAS_SAMPLE_PROFILE +		uint16_t	tick = ao_sample_profile_timer_value(); +		uint16_t	run = tick - ao_cur_task->start; +		if (run > ao_cur_task->max_run) +			ao_cur_task->max_run = run; +		++ao_cur_task->yields; +#endif  		ao_arch_save_stack();  	}  	ao_arch_isr_stack(); +#if !HAS_TASK_QUEUE +	if (ao_task_minimize_latency) +		ao_arch_release_interrupts(); +	else +#endif +		ao_arch_block_interrupts();  #if AO_CHECK_STACK  	in_yield = 1; @@ -88,6 +346,22 @@ ao_yield(void) ao_arch_naked_define  	/* Find a task to run. If there isn't any runnable task,  	 * this loop will run forever, which is just fine  	 */ +#if HAS_TASK_QUEUE +	/* If the current task is running, move it to the +	 * end of the queue to allow other tasks a chance +	 */ +	if (ao_cur_task->wchan == NULL) +		ao_task_to_run_queue(ao_cur_task); +	ao_cur_task = NULL; +	for (;;) { +		ao_arch_memory_barrier(); +		if (!ao_list_is_empty(&run_queue)) +			break; +		/* Wait for interrupts when there's nothing ready */ +		ao_arch_wait_interrupt(); +	} +	ao_cur_task = ao_list_first_entry(&run_queue, struct ao_task, queue); +#else  	{  		__pdata uint8_t	ao_last_task_index = ao_cur_task_index;  		for (;;) { @@ -106,13 +380,19 @@ ao_yield(void) ao_arch_naked_define  			    (int16_t) (ao_time() - ao_cur_task->alarm) >= 0)  				break; -			/* Enter lower power mode when there isn't anything to do */ -			if (ao_cur_task_index == ao_last_task_index) -				ao_arch_cpu_idle(); +			/* Wait for interrupts when there's nothing ready */ +			if (ao_cur_task_index == ao_last_task_index && !ao_task_minimize_latency) +				ao_arch_wait_interrupt();  		}  	} +#endif +#if HAS_SAMPLE_PROFILE +	ao_cur_task->start = ao_sample_profile_timer_value(); +#endif +#if HAS_STACK_GUARD +	ao_mpu_stack_guard(ao_cur_task->stack); +#endif  #if AO_CHECK_STACK -	cli();  	in_yield = 0;  #endif  	ao_arch_restore_stack(); @@ -121,7 +401,15 @@ ao_yield(void) ao_arch_naked_define  uint8_t  ao_sleep(__xdata void *wchan)  { +#if HAS_TASK_QUEUE +	uint32_t flags; +	flags = ao_arch_irqsave(); +#endif  	ao_cur_task->wchan = wchan; +#if HAS_TASK_QUEUE +	ao_task_to_sleep_queue(ao_cur_task, wchan); +	ao_arch_irqrestore(flags); +#endif  	ao_yield();  	if (ao_cur_task->wchan) {  		ao_cur_task->wchan = NULL; @@ -134,28 +422,62 @@ ao_sleep(__xdata void *wchan)  void  ao_wakeup(__xdata void *wchan)  { -	uint8_t	i; +#if HAS_TASK_QUEUE +	struct ao_task	*sleep, *next; +	struct ao_list	*sleep_queue; +	uint32_t	flags; -	ao_check_stack(); +	if (ao_num_tasks == 0) +		return; +	sleep_queue = ao_task_sleep_queue(wchan); +	flags = ao_arch_irqsave(); +	ao_list_for_each_entry_safe(sleep, next, sleep_queue, struct ao_task, queue) { +		if (sleep->wchan == wchan) { +			sleep->wchan = NULL; +			ao_task_to_run_queue(sleep); +		} +	} +	ao_arch_irqrestore(flags); +#else +	uint8_t	i;  	for (i = 0; i < ao_num_tasks; i++)  		if (ao_tasks[i]->wchan == wchan)  			ao_tasks[i]->wchan = NULL; +#endif +	ao_check_stack();  }  void  ao_alarm(uint16_t delay)  { +#if HAS_TASK_QUEUE +	uint32_t flags;  	/* Make sure we sleep *at least* delay ticks, which means adding  	 * one to account for the fact that we may be close to the next tick  	 */ +	flags = ao_arch_irqsave(); +#endif  	if (!(ao_cur_task->alarm = ao_time() + delay + 1))  		ao_cur_task->alarm = 1; +#if HAS_TASK_QUEUE +	ao_task_to_alarm_queue(ao_cur_task); +	ao_arch_irqrestore(flags); +#endif  }  void  ao_clear_alarm(void)  { +#if HAS_TASK_QUEUE +	uint32_t flags; + +	flags = ao_arch_irqsave(); +#endif  	ao_cur_task->alarm = 0; +#if HAS_TASK_QUEUE +	ao_task_from_alarm_queue(ao_cur_task); +	ao_arch_irqrestore(flags); +#endif  }  static __xdata uint8_t ao_forever; @@ -171,17 +493,26 @@ ao_delay(uint16_t ticks)  void  ao_exit(void)  { -	ao_arch_critical( -		uint8_t	i; -		ao_num_tasks--; -		for (i = ao_cur_task_index; i < ao_num_tasks; i++) -			ao_tasks[i] = ao_tasks[i+1]; -		ao_cur_task_index = AO_NO_TASK_INDEX; -		ao_yield(); -		); +	uint8_t	i; +	ao_arch_block_interrupts(); +	ao_num_tasks--; +#if HAS_TASK_QUEUE +	for (i = 0; i < ao_num_tasks; i++) +		if (ao_tasks[i] == ao_cur_task) +			break; +	ao_task_exit_queue(ao_cur_task); +#else +	i = ao_cur_task_index; +	ao_cur_task_index = AO_NO_TASK_INDEX; +#endif +	for (; i < ao_num_tasks; i++) +		ao_tasks[i] = ao_tasks[i+1]; +	ao_cur_task = NULL; +	ao_yield();  	/* we'll never get back here */  } +#if HAS_TASK_INFO  void  ao_task_info(void)  { @@ -194,12 +525,21 @@ ao_task_info(void)  		       task->name,  		       (int) task->wchan);  	} +#if HAS_TASK_QUEUE && DEBUG +	ao_task_validate(); +#endif  } +#endif  void  ao_start_scheduler(void)  { +#if !HAS_TASK_QUEUE  	ao_cur_task_index = AO_NO_TASK_INDEX; +#endif  	ao_cur_task = NULL; +#if HAS_ARCH_START_SCHEDULER +	ao_arch_start_scheduler(); +#endif  	ao_yield();  } | 
