⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 task.c

📁 yavrtos,一款用于广泛用于AVR单片机的RTOS,文件里是这款OS的源码
💻 C
📖 第 1 页 / 共 2 页
字号:
			while (owned->next != m) owned = owned->next;
			owned->next = m->next;
		}
		if ((!current_task->owned_mutex) && (get_task_state(current_task) == TASK_STATE_WAITING_TO_STOP)) {
			stop_task(current_task, 0);
		}
	} else {
		restore_interrupts(interrupts);
		return 1;
	}
	restore_interrupts(interrupts);
	yield();
	return 0;
}

// (documentation in task.h)
int16_t get_semaphore_value(semaphore_t *s) {
	interrupt_store_t interrupts = disable_interrupts(); // We are reading a 16-bit value!
	int16_t ans = s->value;
	restore_interrupts(interrupts);
	return ans;
}

// (documentation in task.h)
uint8_t wait_for_min_value(semaphore_t *p, int16_t value) {
	interrupt_store_t interrupts;
	if ((!current_task->pri) || (executing_isr())) {
		return 1;
	}
	interrupts = disable_interrupts();
	if (!semaphore_triggered(p->value, value)) {
		current_task->waiting_semaphore = p;
		current_task->waiting_semaphore_min_value = value;
		restore_interrupts(interrupts);
		yield();
	} else {
		restore_interrupts(interrupts);
	}
	return 0;
}

// (documentation in task.h)
uint8_t wait_for_increment_of(semaphore_t *p, uint16_t amount) {
	interrupt_store_t interrupts;
	int16_t v;
	if ((!current_task->pri) || (executing_isr())) {
		return 1;
	}
	interrupts = disable_interrupts();
	v = p->value + amount;
	restore_interrupts(interrupts);
	return wait_for_min_value(p, v);
}

/**
 * \brief \internal Perform a task switch
 *
 * Note that interrupts must be disabled before we enter, and the stack pointer should be set up to use
 * the system stack.
 */
void switch_task() { /*  __attribute__ ((naked)) */
	uint8_t min_priority = 0;
	// ans will contain the next task to run
	task_t *ans = 0;
	// Our round-robin algorithm is to check the task list starting at the current task. Hence the task we will end up
	// with will be the current task only if it is the only task available.
	task_t *checking = current_task;

	// We set the system.interrupted_task flag so that if any ISR executes during the brief re-enabling
	// of interrupts below, they won't attempt a task switch.
	system.interrupted_task = 1;
	__asm__ volatile ("sei\n nop\n nop\n cli\n" ::); // Give any pending interrupts a chance to run
	do {
		if (get_task_state(checking) != TASK_STATE_STOPPED) {
			// First, if the task is waiting on a semaphore, and the semaphore is triggered, then mark it as no
			// longer waiting on the semaphore
			if (checking->waiting_semaphore) {
				if (semaphore_triggered((checking->waiting_semaphore)->value, checking->waiting_semaphore_min_value)) {
					checking->waiting_semaphore = (semaphore_t*)0;
				}
			}
			// Now, if the task is available (i.e. not waiting on a semaphore), and if it's priority is at least the
			// minimum priority of the available tasks found so far, then it is our candidate for the next task to run
			if (checking->pri >= min_priority && !checking->waiting_semaphore) {
				min_priority = checking->pri;
				ans = checking;
			}
		}
		checking = checking->next;
		if (!checking) {
			// If we have reached the end of the task list, then go back to the start
			checking = first_task;
		}
	} while (checking != current_task); // Stop when we have looped through the task list completely
	current_task = ans; // This is why it is so important to have an always-available idle task!!!
	SP = (uint16_t) ans->sp; // Restore the stack pointer
	system.interrupted_task = 0;
	switch (get_task_state(current_task)) {
		case TASK_STATE_RUNNING:
		case TASK_STATE_CLEANING_UP:
		case TASK_STATE_WAITING_TO_STOP:
			restore_cpu_context(); // The task has previously saved its context onto its stack
			__asm__ volatile ("ret\n" ::);
		case TASK_STATE_STARTING:
			set_task_state(current_task, TASK_STATE_RUNNING);
			// Retrieve the init data from the stack, placing it into the registers that mark the value of the argument
			// to the called function. Then, perform a ret, which will start executing task_starter()
			__asm__ volatile ("pop r25\npop r24\nret\n" ::);
			break;
		case TASK_STATE_STOPPING:
			set_task_state(current_task, TASK_STATE_CLEANING_UP);
			break;
		default: ;
	}
	__asm__ volatile ("ret" ::); // This will start executing task_stopper()
}

// (documentation in task.h)
void increment_semaphore_by(semaphore_t *s, uint16_t amount) {
	interrupt_store_t interrupts = disable_interrupts();
	s->value += amount;
	restore_interrupts(interrupts);
	if ((!executing_isr()) && (rtos_started())) {
		yield(); // There may now be a higher-priority task that can run
	}
}

// (documentation in task.h)
int16_t get_current_mbox_version(mailbox_t *m) {
	interrupt_store_t interrupts = disable_interrupts();
	int16_t ans = m->reading_semaphore.value; // Reading a 16-bit value!
	restore_interrupts(interrupts);
	return ans;
}

// (documentation in task.h)
void *read_mbox(mailbox_t *m, int16_t *version) {
	interrupt_store_t interrupts;
	void *ans = 0;
	if ((!rtos_started()) || (executing_isr())) return 0;
	interrupts = disable_interrupts();
	if (!current_task->waiting_on_mbox) { // We can only "lock" one mailbox at a time
		ans = m->data;
		// Now, "lock" the mailbox against writing
		current_task->waiting_on_mbox = m;
		current_task->waiting_on_mbox_version = m->reading_semaphore.value;
		// Return the version number (if required)
		if (version) *version = m->reading_semaphore.value;
	}
	restore_interrupts(interrupts);
	return ans;
}

// (documentation in task.h)
void *read_mbox_min_version(mailbox_t *m, int16_t *version) {
	interrupt_store_t interrupts;
	void *ans = 0;
	if ((!rtos_started()) || (executing_isr()) || (!(current_task->pri))) return 0;
	interrupts = disable_interrupts();
	if (!current_task->waiting_on_mbox) {
		current_task->waiting_on_mbox = m;
		current_task->waiting_on_mbox_version = *version;
		while (!semaphore_triggered(m->reading_semaphore.value, *version)) {
			restore_interrupts(interrupts);
			increment_semaphore_by(&(m->writing_semaphore), 1); // Writers can be waiting for readers!
			wait_for_min_value(&(m->reading_semaphore), *version);
			cli();
		}
		ans = m->data;
		*version = m->reading_semaphore.value;
	}
	restore_interrupts(interrupts);
	return ans;
}

// (documentation in task.h)
mailbox_t *release_mbox_read() {
	interrupt_store_t interrupts;
	mailbox_t *ans = 0;
	if (!rtos_started()) return 0;
	interrupts = disable_interrupts();
	ans = current_task->waiting_on_mbox;
	current_task->waiting_on_mbox = 0; // Writing a 16-bit value!
	restore_interrupts(interrupts);
	if (ans) {
		increment_semaphore_by(&(ans->writing_semaphore), 1);
	}
	return ans;
}

// (documentation in task.h)
void initialise_mbox(mailbox_t *m, void *data, const int16_t version) {
	m->data = data;
	m->reading_semaphore.value = version;
	m->writing_semaphore.value = 0;
}

/**
 * \internal \brief Find out if a mailbox is "empty" (i.e. if there is no-one waiting to read it)
 *
 * Note that interrupts must be disabled before we enter
 */
int8_t mbox_is_empty(mailbox_t *m) {
	task_t *t = first_task;
	while (t) {
		if (t->waiting_on_mbox == m) {
			if (semaphore_triggered(m->reading_semaphore.value, t->waiting_on_mbox_version)) {
				// Task t is waiting for the current version of this mailbox
				return 0;
			}
		}
		t = t->next;
	}
	return 1;
}

// (documentation in task.h)
void write_mbox(mailbox_t *m, void *data, uint8_t wait_for_receivers, uint8_t wait_for_empty_nullify) {
	interrupt_store_t interrupts;
	uint8_t receivers = 0;
	uint8_t wait_for_empty = wait_for_empty_nullify;
	if ((!rtos_started()) || (executing_isr()) || (!(current_task->pri))) return;
	interrupts = disable_interrupts();
	// First, wait for the mailbox to be empty - i.e. wait for everyone that wants the current version
	// of the mailbox to get it - we are not allowed to over-write whats in the mailbox until everyone
	// is finished with the old data that is in there.
	while (!mbox_is_empty(m)) {
		int16_t v = m->writing_semaphore.value + 1; // Need to read 16-bit value with disabled interrupts
		restore_interrupts(interrupts);
		wait_for_min_value(&(m->writing_semaphore), v);
		cli();
	}
	// Now, we wait for the specified number of receivers
	while (wait_for_receivers > receivers) {
		task_t *t = first_task;
		receivers = 0;
		while (t && (wait_for_receivers > receivers)) {
			if ((t->waiting_on_mbox == m) && (!semaphore_triggered(m->reading_semaphore.value, t->waiting_on_mbox_version))) {
				// Task t is waiting on this mailbox
				receivers++;
			}
			t = t->next;
		}
		if (wait_for_receivers > receivers) {
			// We haven't got the required number of receivers yet - wait on the writing semaphore
			int16_t v = m->writing_semaphore.value + 1;
			restore_interrupts(interrupts);
			wait_for_min_value(&(m->writing_semaphore), v);
			cli();
		}
	}
	// The mailbox is empty, and we have the required number of receivers, so put the data into the mailbox,
	// and increment the mailbox version (which doubles as the reading semaphore value)
	m->data = data;
	m->reading_semaphore.value++;
	// Have we been requested to wait for everyone to read the data?
	if (wait_for_empty) {
		// v is the version of the data we have published
		int16_t v = m->reading_semaphore.value;
		while (wait_for_empty) {
			int8_t empty = 1;
			task_t *t = first_task;
			while (t && empty) {
				if (t->waiting_on_mbox == m) {
					if (semaphore_triggered(v, t->waiting_on_mbox_version)) {
						// Task t is waiting to read our data
						empty = 0;
					}
				}
				t = t->next;
			}
			if (empty) {
				wait_for_empty = 0;
				if (wait_for_empty_nullify > 1) {
					// Publish another version of the mailbox data - one with a null pointer
					// (It is safe to do this, as the mailbox is "empty" - i.e. no-one is waiting
					//  for what we've just published).
					m->data = 0;
					m->reading_semaphore.value++;
				}
			} else {
				// There are still tasks waiting to read our data - wait on the writing semaphore
				int16_t val = m->writing_semaphore.value + 1;
				restore_interrupts(interrupts);
				wait_for_min_value(&(m->writing_semaphore), val);
				cli();
			}
		}
	}
	restore_interrupts(interrupts);
	yield();
}

// (documentation in task.h)
int8_t write_mbox_now(mailbox_t *m, void *data) {
	interrupt_store_t interrupts;
	if (!rtos_started()) return 1;
	interrupts = disable_interrupts();
	if (!mbox_is_empty(m)) {
		restore_interrupts(interrupts);
		return 2;
	}
	m->data = data;
	m->reading_semaphore.value++;
	restore_interrupts(interrupts);
	if (!executing_isr()) yield();
	return 0;
}

// (documentation in task.h)
void wait_for_receiver(mailbox_t *m) {
	interrupt_store_t interrupts;
	if ((!rtos_started()) || (executing_isr()) || (!(current_task->pri))) return;
	interrupts = disable_interrupts();
	while (1) {
		task_t *t = first_task;
		int16_t v;
		while (t) {
			if ((t->waiting_on_mbox == m) && (!semaphore_triggered(m->reading_semaphore.value, t->waiting_on_mbox_version))) {
				restore_interrupts(interrupts);
				return;
			}
			t = t->next;
		}
		v = m->writing_semaphore.value + 1;
		restore_interrupts(interrupts);
		wait_for_min_value(&(m->writing_semaphore), v);
		cli();
	}
}

// (documentation in task.h)
interrupt_store_t disable_interrupts() {
	interrupt_store_t ans = (SREG & _BV(SREG_I));
	cli();
	return ans;
}

// (documentation in task.h)
void restore_interrupts(interrupt_store_t interrupts) {
	if (interrupts) {
		sei();
	} else {
		cli();
	}
}

// (documentation in task.h)
void yield() { /*  __attribute__ ((naked)) */
	save_cpu_context();
	current_task->sp = (uint8_t *)SP;
	SP = (uint16_t) system.stack_top;
	switch_task();
}

// (documentation in task.h)
void task_switcher_start(void (*idle)(void*), void *idle_data, uint16_t idle_stacklen, uint16_t system_stacklen) { /*  __attribute__ ((naked)) */
	cli();
	create_task(idle, 0, idle_data, idle_stacklen, 0, 0);
	system.stack_top = malloc(system_stacklen);
	system.stack_top += system_stacklen - 1;
	system.interrupted_task = 0;
	task_stopping_semaphore.value = 0;
	current_task = first_task;
	SP = (uint16_t) system.stack_top;
	switch_task();
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -