/* ??? Copyright 1999-2002 Johan Rydberg, jrydberg@rtmk.org. 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 #include "tm.h" #include "eventcnt.h" #include "vm-slab.h" #include "trace.h" #include "thread.h" /* Kernel memory cache for event counter structures. */ static struct kmem_cache *evc_cache; /* ??? */ void eventcnt_init (void) { evc_cache = kmem_cache_create ("evc cache", sizeof (struct eventcnt), 0); assert (evc_cache); } /* Event counter continue point. Waiting threads begin here. */ static void eventcnt_continue (void) { thread_syscall_return (KERN_SUCCESS); } /* Signal an event. We wake up waiting threads. If there is no waiting threads we increase the event counter. */ void eventcnt_signal (struct eventcnt *evc) { int wakeup_p = 0; SPL_T spl; /* trace_printf ("signal on event %p", evc); */ spl = SPLOFF (); spin_lock (&evc->lock); /* If there's any waiting thread we do not increase the event counter -- but instead we just wake up one thread. */ if (evc->waiting_cnt) { --evc->waiting_cnt; wakeup_p = 1; /* trace_printf ("will wake up threads waiting on event %p", evc); */ } else evc->event_cnt++; spin_unlock (&evc->lock); SPLON (spl); if (wakeup_p) thread_wakeup_one ((int) evc); } /* Wait for an event. If the eventcounter is greater than zero we return directly. If it is zero, we block. */ kern_return_t eventcnt_wait (struct eventcnt *evc) { SPL_T spl; /* trace_printf ("wait of event %p", evc); */ spl = SPLOFF (); spin_lock (&evc->lock); if (evc->event_cnt) { --evc->event_cnt; spin_unlock (&evc->lock); return KERN_SUCCESS; } else evc->waiting_cnt++; /* trace_printf ("blocked on it"); */ assert_wait ((int) evc); spin_unlock (&evc->lock); SPLON (spl); thread_block (eventcnt_continue); /*NOTREACHED*/ return KERN_SUCCESS; } /* Allocating a new event counter. Return pointer to event counter in EVCP. */ kern_return_t eventcnt_allocate (struct task *task, struct eventcnt **evcp) { struct eventcnt *evc; evc = (struct eventcnt *) kmem_cache_alloc (evc_cache); if (! evc) return KERN_RESOURCE_SHORTAGE; memset (evc, 0, sizeof (struct eventcnt)); evc->port_self = ipc_port_create_kernel (); ipc_port_set_kobject (evc->port_self, IPC_KOBJECT_TYPE_EVENTCNT, evc); *evcp = evc; return KERN_SUCCESS; } /* System call for waiting on event to happen on EVC. */ struct eventcnt_wait_args { rtmk_port_t task_name; rtmk_port_t evc_name; }; kern_return_t syscall_eventcnt_wait (void *aux) { struct eventcnt_wait_args *a = (struct eventcnt_wait_args *) aux; struct ipc_entry *entry; struct eventcnt *evc; struct task *task; kern_return_t kr; ipc_object_lock (THREAD_CURRENT()->task->ipc_object); kr = ipc_object_lookup (THREAD_CURRENT()->task->ipc_object, a->task_name, &entry); if (kr != KERN_SUCCESS) { ipc_object_unlock (THREAD_CURRENT()->task->ipc_object); return kr; } if (entry->ie_port->kobject.type != IPC_KOBJECT_TYPE_TASK) { ipc_object_unlock (THREAD_CURRENT()->task->ipc_object); return KERN_INVALID_TASK; } task = (struct task *) entry->ie_port->kobject.port; ipc_object_unlock (THREAD_CURRENT()->task->ipc_object); ipc_object_lock (task->ipc_object); kr = ipc_object_lookup (task->ipc_object, a->evc_name, &entry); if (kr != KERN_SUCCESS) { ipc_object_unlock (task->ipc_object); return kr; } if (entry->ie_port->kobject.type != IPC_KOBJECT_TYPE_EVENTCNT) { ipc_object_unlock (task->ipc_object); return KERN_INVALID_ARGUMENT; } evc = (struct eventcnt *) entry->ie_port->kobject.port; ipc_object_unlock (task->ipc_object); return eventcnt_wait (evc); }