/* * Copyright (C) 2012 Philippe Gerum . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include "copperplate/threadobj.h" #include "copperplate/eventobj.h" #include "copperplate/heapobj.h" #include "copperplate/debug.h" #ifdef CONFIG_XENO_COBALT #include "cobalt/internal.h" int eventobj_init(struct eventobj *evobj, unsigned int value, int flags, fnref_type(void (*)(struct eventobj *evobj)) finalizer) { int ret, event_flags = event_scope_attribute; if (flags & EVOBJ_PRIO) event_flags |= COBALT_EVENT_PRIO; ret = cobalt_event_init(&evobj->core.event, value, event_flags); if (ret) return __bt(ret); evobj->finalizer = finalizer; return 0; } int eventobj_destroy(struct eventobj *evobj) { void (*finalizer)(struct eventobj *evobj); int ret; ret = cobalt_event_destroy(&evobj->core.event); if (ret) return ret; fnref_get(finalizer, evobj->finalizer); finalizer(evobj); return 0; } void eventobj_uninit(struct eventobj *evobj) { int ret = cobalt_event_destroy(&evobj->core.event); assert(ret == 0); (void)ret; } int eventobj_wait(struct eventobj *evobj, unsigned int bits, unsigned int *bits_r, int mode, const struct timespec *timeout) { int ret; ret = cobalt_event_wait(&evobj->core.event, bits, bits_r, mode, timeout); if (ret) return ret; return 0; } int eventobj_post(struct eventobj *evobj, unsigned int bits) { int ret; ret = cobalt_event_post(&evobj->core.event, bits); if (ret) return ret; return 0; } int eventobj_clear(struct eventobj *evobj, unsigned int bits, unsigned int *bits_r) { unsigned int oldval; oldval = cobalt_event_clear(&evobj->core.event, bits); if (bits_r) *bits_r = oldval; return 0; } int eventobj_inquire(struct eventobj *evobj, size_t waitsz, struct eventobj_waitentry *waitlist, unsigned int *bits_r) { struct cobalt_threadstat stat; struct cobalt_event_info info; int nrwait, pidsz, n, ret; pid_t *pidlist = NULL; pidsz = sizeof(pid_t) * (waitsz / sizeof(*waitlist)); if (pidsz > 0) { pidlist = pvmalloc(pidsz); if (pidlist == NULL) return -ENOMEM; } nrwait = cobalt_event_inquire(&evobj->core.event, &info, pidlist, pidsz); if (nrwait < 0) goto out; *bits_r = info.value; if (pidlist == NULL) return nrwait; for (n = 0; n < nrwait; n++, waitlist++) { ret = cobalt_thread_stat(pidlist[n], &stat); /* If waiter disappeared, fill in a dummy entry. */ if (ret) { waitlist->pid = -1; strcpy(waitlist->name, "???"); } else { waitlist->pid = pidlist[n]; strcpy(waitlist->name, stat.name); } } out: if (pidlist) pvfree(pidlist); return nrwait; } #else /* CONFIG_XENO_MERCURY */ static void eventobj_finalize(struct syncobj *sobj) { struct eventobj *evobj = container_of(sobj, struct eventobj, core.sobj); void (*finalizer)(struct eventobj *evobj); fnref_get(finalizer, evobj->finalizer); finalizer(evobj); } fnref_register(libcopperplate, eventobj_finalize); int eventobj_init(struct eventobj *evobj, unsigned int value, int flags, fnref_type(void (*)(struct eventobj *evobj)) finalizer) { int sobj_flags = 0, ret; if (flags & EVOBJ_PRIO) sobj_flags = SYNCOBJ_PRIO; ret = syncobj_init(&evobj->core.sobj, CLOCK_COPPERPLATE, sobj_flags, fnref_put(libcopperplate, eventobj_finalize)); if (ret) return __bt(ret); evobj->core.flags = flags; evobj->core.value = value; evobj->finalizer = finalizer; return 0; } int eventobj_destroy(struct eventobj *evobj) { struct syncstate syns; int ret; if (syncobj_lock(&evobj->core.sobj, &syns)) return -EINVAL; ret = syncobj_destroy(&evobj->core.sobj, &syns); if (ret < 0) return ret; return 0; } void eventobj_uninit(struct eventobj *evobj) { syncobj_uninit(&evobj->core.sobj); } int eventobj_wait(struct eventobj *evobj, unsigned int bits, unsigned int *bits_r, int mode, const struct timespec *timeout) { struct eventobj_wait_struct *wait; unsigned int waitval, testval; struct syncstate syns; int ret = 0; ret = syncobj_lock(&evobj->core.sobj, &syns); if (ret) return ret; if (bits == 0) { *bits_r = evobj->core.value; goto done; } waitval = evobj->core.value & bits; testval = mode & EVOBJ_ANY ? waitval : bits; if (waitval && waitval == testval) { *bits_r = waitval; goto done; } /* Have to wait. */ if (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0) { ret = -EWOULDBLOCK; goto done; } wait = threadobj_prepare_wait(struct eventobj_wait_struct); wait->value = bits; wait->mode = mode; ret = syncobj_wait_grant(&evobj->core.sobj, timeout, &syns); if (ret == -EIDRM) { threadobj_finish_wait(); return ret; } if (ret == 0) *bits_r = wait->value; threadobj_finish_wait(); done: syncobj_unlock(&evobj->core.sobj, &syns); return ret; } int eventobj_post(struct eventobj *evobj, unsigned int bits) { struct eventobj_wait_struct *wait; unsigned int waitval, testval; struct threadobj *thobj, *tmp; struct syncstate syns; int ret; ret = syncobj_lock(&evobj->core.sobj, &syns); if (ret) return ret; evobj->core.value |= bits; if (!syncobj_grant_wait_p(&evobj->core.sobj)) goto done; syncobj_for_each_grant_waiter_safe(&evobj->core.sobj, thobj, tmp) { wait = threadobj_get_wait(thobj); waitval = wait->value & bits; testval = wait->mode & EVOBJ_ANY ? waitval : wait->value; if (waitval && waitval == testval) { wait->value = waitval; syncobj_grant_to(&evobj->core.sobj, thobj); } } done: syncobj_unlock(&evobj->core.sobj, &syns); return 0; } int eventobj_clear(struct eventobj *evobj, unsigned int bits, unsigned int *bits_r) { struct syncstate syns; unsigned int oldval; int ret; ret = syncobj_lock(&evobj->core.sobj, &syns); if (ret) return ret; oldval = evobj->core.value; evobj->core.value &= ~bits; syncobj_unlock(&evobj->core.sobj, &syns); if (bits_r) *bits_r = oldval; return 0; } int eventobj_inquire(struct eventobj *evobj, size_t waitsz, struct eventobj_waitentry *waitlist, unsigned int *bits_r) { struct threadobj *thobj; struct syncstate syns; int ret, nrwait; ret = syncobj_lock(&evobj->core.sobj, &syns); if (ret) return ret; nrwait = syncobj_count_grant(&evobj->core.sobj); if (nrwait > 0) { syncobj_for_each_grant_waiter(&evobj->core.sobj, thobj) { waitlist->pid = threadobj_get_pid(thobj); strcpy(waitlist->name, threadobj_get_name(thobj)); waitlist++; } } *bits_r = evobj->core.value; syncobj_unlock(&evobj->core.sobj, &syns); return nrwait; } #endif /* CONFIG_XENO_MERCURY */