summaryrefslogtreecommitdiff
path: root/sos-code-article6.5/sos/kwaitq.c
diff options
context:
space:
mode:
Diffstat (limited to 'sos-code-article6.5/sos/kwaitq.c')
-rw-r--r--sos-code-article6.5/sos/kwaitq.c249
1 files changed, 249 insertions, 0 deletions
diff --git a/sos-code-article6.5/sos/kwaitq.c b/sos-code-article6.5/sos/kwaitq.c
new file mode 100644
index 0000000..b4a451e
--- /dev/null
+++ b/sos-code-article6.5/sos/kwaitq.c
@@ -0,0 +1,249 @@
+/* Copyright (C) 2004 David Decotigny
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA.
+*/
+
+#include <sos/klibc.h>
+#include <sos/list.h>
+#include <sos/assert.h>
+#include <hwcore/irq.h>
+
+#include "kwaitq.h"
+
+
+sos_ret_t sos_kwaitq_init(struct sos_kwaitq *kwq,
+ const char *name)
+{
+ memset(kwq, 0x0, sizeof(struct sos_kwaitq));
+
+#ifdef SOS_KWQ_DEBUG
+ if (! name)
+ name = "<unknown>";
+ strzcpy(kwq->name, name, SOS_KWQ_DEBUG_MAX_NAMELEN);
+#endif
+ list_init_named(kwq->waiting_list,
+ prev_entry_in_kwaitq, next_entry_in_kwaitq);
+
+ return SOS_OK;
+}
+
+
+sos_ret_t sos_kwaitq_dispose(struct sos_kwaitq *kwq)
+{
+ sos_ui32_t flags;
+ sos_ret_t retval;
+
+ sos_disable_IRQs(flags);
+ if (list_is_empty_named(kwq->waiting_list,
+ prev_entry_in_kwaitq, next_entry_in_kwaitq))
+ retval = SOS_OK;
+ else
+ retval = -SOS_EBUSY;
+
+ sos_restore_IRQs(flags);
+ return retval;
+}
+
+
+sos_bool_t sos_kwaitq_is_empty(const struct sos_kwaitq *kwq)
+{
+ sos_ui32_t flags;
+ sos_ret_t retval;
+
+ sos_disable_IRQs(flags);
+ retval = list_is_empty_named(kwq->waiting_list,
+ prev_entry_in_kwaitq, next_entry_in_kwaitq);
+
+ sos_restore_IRQs(flags);
+ return retval;
+}
+
+
+sos_ret_t sos_kwaitq_init_entry(struct sos_kwaitq_entry *kwq_entry)
+{
+ memset(kwq_entry, 0x0, sizeof(struct sos_kwaitq_entry));
+ kwq_entry->thread = sos_thread_get_current();
+ return SOS_OK;
+}
+
+
+/** Internal helper function equivalent to sos_kwaitq_add_entry(), but
+ without interrupt protection scheme, and explicit priority
+ ordering */
+inline static sos_ret_t _kwaitq_add_entry(struct sos_kwaitq *kwq,
+ struct sos_kwaitq_entry *kwq_entry)
+{
+ /* This entry is already added in the kwaitq ! */
+ SOS_ASSERT_FATAL(NULL == kwq_entry->kwaitq);
+
+ /* sos_kwaitq_init_entry() has not been called ?! */
+ SOS_ASSERT_FATAL(NULL != kwq_entry->thread);
+
+ /* (Re-)Initialize wakeup status of the entry */
+ kwq_entry->wakeup_triggered = FALSE;
+ kwq_entry->wakeup_status = SOS_OK;
+
+ /* Add the thread in the list */
+ list_add_tail_named(kwq->waiting_list, kwq_entry,
+ prev_entry_in_kwaitq, next_entry_in_kwaitq);
+
+ /* Update the list of waitqueues for the thread */
+ list_add_tail_named(kwq_entry->thread->kwaitq_list, kwq_entry,
+ prev_entry_for_thread, next_entry_for_thread);
+
+ kwq_entry->kwaitq = kwq;
+
+ return SOS_OK;
+}
+
+
+sos_ret_t sos_kwaitq_add_entry(struct sos_kwaitq *kwq,
+ struct sos_kwaitq_entry *kwq_entry)
+{
+ sos_ui32_t flags;
+ sos_ret_t retval;
+
+ sos_disable_IRQs(flags);
+ retval = _kwaitq_add_entry(kwq, kwq_entry);
+ sos_restore_IRQs(flags);
+
+ return retval;
+}
+
+
+/** Internal helper function equivalent to sos_kwaitq_remove_entry(),
+ but without interrupt protection scheme */
+inline static sos_ret_t
+_kwaitq_remove_entry(struct sos_kwaitq *kwq,
+ struct sos_kwaitq_entry *kwq_entry)
+{
+ SOS_ASSERT_FATAL(kwq_entry->kwaitq == kwq);
+
+ list_delete_named(kwq->waiting_list, kwq_entry,
+ prev_entry_in_kwaitq, next_entry_in_kwaitq);
+
+ list_delete_named(kwq_entry->thread->kwaitq_list, kwq_entry,
+ prev_entry_for_thread, next_entry_for_thread);
+
+ kwq_entry->kwaitq = NULL;
+ return SOS_OK;
+}
+
+
+sos_ret_t sos_kwaitq_remove_entry(struct sos_kwaitq *kwq,
+ struct sos_kwaitq_entry *kwq_entry)
+{
+ sos_ui32_t flags;
+ sos_ret_t retval;
+
+ sos_disable_IRQs(flags);
+ retval = _kwaitq_remove_entry(kwq, kwq_entry);
+ sos_restore_IRQs(flags);
+
+ return retval;
+}
+
+
+sos_ret_t sos_kwaitq_wait(struct sos_kwaitq *kwq,
+ struct sos_time *timeout)
+{
+ sos_ui32_t flags;
+ sos_ret_t retval;
+ struct sos_kwaitq_entry kwq_entry;
+
+ sos_kwaitq_init_entry(& kwq_entry);
+
+ sos_disable_IRQs(flags);
+
+ retval = _kwaitq_add_entry(kwq, & kwq_entry);
+
+ /* Wait for wakeup or timeout */
+ sos_thread_sleep(timeout);
+ /* Woken up ! */
+
+ /* Sleep delay elapsed ? */
+ if (! kwq_entry.wakeup_triggered)
+ {
+ /* Yes (timeout occured, or wakeup on another waitqueue): remove
+ the waitq entry by ourselves */
+ _kwaitq_remove_entry(kwq, & kwq_entry);
+ retval = -SOS_EINTR;
+ }
+ else
+ {
+ retval = kwq_entry.wakeup_status;
+ }
+
+ sos_restore_IRQs(flags);
+
+ /* We were correctly awoken: position return status */
+ return retval;
+}
+
+
+sos_ret_t sos_kwaitq_wakeup(struct sos_kwaitq *kwq,
+ unsigned int nb_threads,
+ sos_ret_t wakeup_status)
+{
+ sos_ui32_t flags;
+
+ sos_disable_IRQs(flags);
+
+ /* Wake up as much threads waiting in waitqueue as possible (up to
+ nb_threads), scanning the list in FIFO order */
+ while (! list_is_empty_named(kwq->waiting_list,
+ prev_entry_in_kwaitq, next_entry_in_kwaitq))
+ {
+ struct sos_kwaitq_entry *kwq_entry
+ = list_get_head_named(kwq->waiting_list,
+ prev_entry_in_kwaitq, next_entry_in_kwaitq);
+
+ /* Enough threads woken up ? */
+ if (nb_threads <= 0)
+ break;
+
+ /*
+ * Ok: wake up the thread for this entry
+ */
+
+ /* Thread already woken up ? */
+ if (SOS_THR_RUNNING == sos_thread_get_state(kwq_entry->thread))
+ {
+ /* Yes => Do nothing because WE are that woken-up thread. In
+ particular: don't call set_ready() here because this
+ would result in an inconsistent configuration (currently
+ running thread marked as "waiting for CPU"...). */
+ continue;
+ }
+ else
+ {
+ /* No => wake it up now. */
+ sos_sched_set_ready(kwq_entry->thread);
+ }
+
+ /* Remove this waitq entry */
+ _kwaitq_remove_entry(kwq, kwq_entry);
+ kwq_entry->wakeup_triggered = TRUE;
+ kwq_entry->wakeup_status = wakeup_status;
+
+ /* Next iteration... */
+ nb_threads --;
+ }
+
+ sos_restore_IRQs(flags);
+
+ return SOS_OK;
+}