/* * Copyright (C) 2011 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/semobj.h" #include "copperplate/heapobj.h" #include "copperplate/debug.h" #ifdef CONFIG_XENO_COBALT #include "cobalt/internal.h" int semobj_init(struct semobj *smobj, int flags, int value, fnref_type(void (*)(struct semobj *smobj)) finalizer) { int ret, sem_flags; sem_flags = SEM_REPORT|SEM_RAWCLOCK; if (sem_scope_attribute) sem_flags |= SEM_PSHARED; if ((flags & SEMOBJ_PRIO) == 0) sem_flags |= SEM_FIFO; if (flags & SEMOBJ_PULSE) sem_flags |= SEM_PULSE; if (flags & SEMOBJ_WARNDEL) sem_flags |= SEM_WARNDEL; ret = sem_init_np(&smobj->core.sem, sem_flags, value); if (ret) return __bt(-errno); smobj->finalizer = finalizer; return 0; } int semobj_destroy(struct semobj *smobj) { void (*finalizer)(struct semobj *smobj); int ret; ret = __RT(sem_destroy(&smobj->core.sem)); if (ret < 0) return errno == EINVAL ? -EIDRM : -errno; /* * All waiters have been unblocked with EINVAL, and therefore * won't touch this object anymore. We can finalize it * immediately. */ fnref_get(finalizer, smobj->finalizer); finalizer(smobj); return ret; } void semobj_uninit(struct semobj *smobj) { int ret = __RT(sem_destroy(&smobj->core.sem)); assert(ret == 0); (void)ret; } int semobj_post(struct semobj *smobj) { int ret; ret = __RT(sem_post(&smobj->core.sem)); if (ret) return errno == EINVAL ? -EIDRM : -errno; return 0; } int semobj_broadcast(struct semobj *smobj) { int ret; ret = sem_broadcast_np(&smobj->core.sem); if (ret) return errno == EINVAL ? -EIDRM : -errno; return 0; } int semobj_wait(struct semobj *smobj, const struct timespec *timeout) { int ret; if (timeout == NULL) { do ret = __RT(sem_wait(&smobj->core.sem)); while (ret && errno == EINTR); } else if (timeout->tv_sec == 0 && timeout->tv_nsec == 0) ret = __RT(sem_trywait(&smobj->core.sem)); else { do ret = __RT(sem_timedwait(&smobj->core.sem, timeout)); while (ret && errno == EINTR); } if (ret) return errno == EINVAL ? -EIDRM : -errno; return 0; } int semobj_getvalue(struct semobj *smobj, int *sval) { int ret; ret = __RT(sem_getvalue(&smobj->core.sem, sval)); if (ret) return errno == EINVAL ? -EIDRM : -errno; return 0; } int semobj_inquire(struct semobj *smobj, size_t waitsz, struct semobj_waitentry *waitlist, int *val_r) { struct cobalt_threadstat stat; struct cobalt_sem_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_sem_inquire(&smobj->core.sem, &info, pidlist, pidsz); if (nrwait < 0) goto out; *val_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 semobj_finalize(struct syncobj *sobj) { struct semobj *smobj = container_of(sobj, struct semobj, core.sobj); void (*finalizer)(struct semobj *smobj); fnref_get(finalizer, smobj->finalizer); finalizer(smobj); } fnref_register(libcopperplate, semobj_finalize); int semobj_init(struct semobj *smobj, int flags, int value, fnref_type(void (*)(struct semobj *smobj)) finalizer) { int sobj_flags = 0, ret; if (flags & SEMOBJ_PRIO) sobj_flags = SYNCOBJ_PRIO; /* * We need a trampoline for finalizing a semobj, to escalate * from a basic syncobj we receive to the semobj container. */ ret = syncobj_init(&smobj->core.sobj, CLOCK_COPPERPLATE, sobj_flags, fnref_put(libcopperplate, semobj_finalize)); if (ret) return __bt(ret); smobj->core.flags = flags; smobj->core.value = value; smobj->finalizer = finalizer; return 0; } int semobj_destroy(struct semobj *smobj) { struct syncstate syns; if (syncobj_lock(&smobj->core.sobj, &syns)) return -EINVAL; return syncobj_destroy(&smobj->core.sobj, &syns); } void semobj_uninit(struct semobj *smobj) { syncobj_uninit(&smobj->core.sobj); } int semobj_post(struct semobj *smobj) { struct syncstate syns; int ret; ret = syncobj_lock(&smobj->core.sobj, &syns); if (ret) return ret; if (++smobj->core.value <= 0) syncobj_grant_one(&smobj->core.sobj); else if (smobj->core.flags & SEMOBJ_PULSE) smobj->core.value = 0; syncobj_unlock(&smobj->core.sobj, &syns); return 0; } int semobj_broadcast(struct semobj *smobj) { struct syncstate syns; int ret; ret = syncobj_lock(&smobj->core.sobj, &syns); if (ret) return ret; if (smobj->core.value < 0) { smobj->core.value = 0; syncobj_grant_all(&smobj->core.sobj); } syncobj_unlock(&smobj->core.sobj, &syns); return 0; } int semobj_wait(struct semobj *smobj, const struct timespec *timeout) { struct syncstate syns; int ret = 0; ret = syncobj_lock(&smobj->core.sobj, &syns); if (ret) return ret; if (--smobj->core.value >= 0) goto done; if (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0) { smobj->core.value++; ret = -EWOULDBLOCK; goto done; } if (!threadobj_current_p()) { ret = -EPERM; goto done; } ret = syncobj_wait_grant(&smobj->core.sobj, timeout, &syns); if (ret) { /* * -EIDRM means that the semaphore has been deleted, * so we bail out immediately and don't attempt to * access that stale object in any way. */ if (ret == -EIDRM) return ret; smobj->core.value++; /* Fix up semaphore count. */ } done: syncobj_unlock(&smobj->core.sobj, &syns); return ret; } int semobj_getvalue(struct semobj *smobj, int *sval) { struct syncstate syns; if (syncobj_lock(&smobj->core.sobj, &syns)) return -EINVAL; *sval = smobj->core.value; syncobj_unlock(&smobj->core.sobj, &syns); return 0; } int semobj_inquire(struct semobj *smobj, size_t waitsz, struct semobj_waitentry *waitlist, int *val_r) { struct threadobj *thobj; struct syncstate syns; int ret, nrwait; ret = syncobj_lock(&smobj->core.sobj, &syns); if (ret) return ret; nrwait = syncobj_count_grant(&smobj->core.sobj); if (nrwait > 0) { syncobj_for_each_grant_waiter(&smobj->core.sobj, thobj) { waitlist->pid = threadobj_get_pid(thobj); strcpy(waitlist->name, threadobj_get_name(thobj)); waitlist++; } } *val_r = smobj->core.value; syncobj_unlock(&smobj->core.sobj, &syns); return nrwait; } #endif /* CONFIG_XENO_MERCURY */