/* * Copyright (C) 2000 Regents of the University of California * * 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 * * Condition variables. * Jim Garlick */ #if !defined(_LINUX_CONDVAR_H) #define _LINUX_CONDVAR_H #if defined(__KERNEL__) #include #include #include #define CV_RET_SIGPENDING 0 #define CV_RET_TIMEOUT (-1) #define CV_RET_NORMAL 1 #define CV_MAX_NAME 16 struct cv_task { struct task_struct *task; /* need to wrap task in this */ struct list_head list; /* to thread as a list */ int blocked; }; typedef struct { struct list_head task_list; /* list of cv_task's */ /* that are waiting on cv */ #if defined(DEBUG_CV) char name[CV_MAX_NAME]; #endif } condvar_t; #if defined(DEBUG_CV) #define LOCK_NAME(c) (c)->name #else #define LOCK_NAME(c) "" #endif #define cv_wait(c,l) debug_cv_wait(c, l, 0, 0, __BASE_FILE__, __LINE__) #define cv_waitsig(c,l) debug_cv_wait(c, l, 0, 1, __BASE_FILE__, __LINE__) #define cv_timedwait(c,l,to) debug_cv_wait(c, l, to, 0, __BASE_FILE__, __LINE__) #define cv_timedwaitsig(c,l,to) debug_cv_wait(c, l, to, 1, __BASE_FILE__, __LINE__) #define cv_wakeup_one(c,l) cv_wakeup(c, l, 0) #define cv_wakeup_all(c,l) cv_wakeup(c, l, 1) extern __inline__ void cv_init(condvar_t *c, char *name) { INIT_LIST_HEAD(&c->task_list); #if defined(DEBUG_CV) strncpy(c->name, name, CV_MAX_NAME); #endif } extern __inline__ void cv_destroy(condvar_t *c) { ASSERT(list_empty(&c->task_list)); } /* * We thread a struct cv_task, allocated on the stack, onto the condvar_t's * task_list, and take it off again when we wake up. * Note: the reason we avoid using TASK_UNINTERRUPTIBLE is that avenrun * computation treats it like TASK_RUNNABLE. The cvt.blocked flag * distinguishes a signal wakeup from a cv_wakeup. */ extern __inline__ int debug_cv_wait(condvar_t *c, mutex_t *l, long tmo, int interruptible, char *file, int line) { struct cv_task cvt; int ret = CV_RET_NORMAL; ASSERT(!in_interrupt()); /* we can block */ ASSERT(mutex_held(l)); /* enter holding lock */ /*DBF("cv_wait:\tsleep %s %d:%d\n", LOCK_NAME(c), MY_PID, MY_CPU);*/ cvt.task = current; cvt.blocked = 1; list_add(&cvt.list, &c->task_list); do { current->state = TASK_INTERRUPTIBLE; mutex_unlock(l); /* drop lock for sleep */ if (tmo) { if (tmo <= jiffies || !schedule_timeout(tmo - jiffies)) ret = CV_RET_TIMEOUT; } else schedule(); debug_mutex_lock(l, file, line); /* pick up lock again */ if (interruptible && signal_pending(current)) ret = CV_RET_SIGPENDING; } while (cvt.blocked && ret == CV_RET_NORMAL); list_del(&cvt.list); /*DBF("cv_wait:\tawake %s %d:%d %s\n", LOCK_NAME(c), MY_PID, MY_CPU, ret == CV_RET_TIMEOUT ? "timeout" : (ret == CV_RET_SIGPENDING) ? "sigpending" : "normal");*/ return ret; /* return holding lock */ } extern __inline__ void cv_wakeup(condvar_t *c, mutex_t *l, int wakeall) { struct list_head *lp; struct cv_task *cvtp; ASSERT(mutex_held(l)); /* already holding lock */ for (lp = c->task_list.next; lp != &c->task_list; lp = lp->next) { cvtp = list_entry(lp, struct cv_task, list); if (cvtp->blocked) { /*DBF("cv_wakeup:\twaking %s pid=%d %d:%d\n", LOCK_NAME(c), p->pid, MY_PID, MY_CPU);*/ cvtp->blocked = 0; /* wake_up_process added to kernel/ksyms.c */ wake_up_process(cvtp->task); if (!wakeall) break; } } } /* return still holding lock */ #endif /* __KERNEL__ */ #endif /* _LINUX_CONDVAR_H */