/* * Copyright (c) 2009 Jakub Jermar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup libc * @{ */ /** @file */ #include #include #include #include #include #include #include #include #include static void optimize_execution_power(void) { /* * When waking up a worker fibril previously blocked in fibril * synchronization, chances are that there is an idle manager fibril * waiting for IPC, that could start executing the awakened worker * fibril right away. We try to detect this and bring the manager * fibril back to fruitful work. */ if (atomic_get(&threads_in_ipc_wait) > 0) ipc_poke(); } void fibril_mutex_initialize(fibril_mutex_t *fm) { fm->counter = 1; list_initialize(&fm->waiters); } void fibril_mutex_lock(fibril_mutex_t *fm) { futex_down(&async_futex); if (fm->counter-- <= 0) { fibril_t *f = (fibril_t *) fibril_get_id(); list_append(&f->link, &fm->waiters); fibril_switch(FIBRIL_TO_MANAGER); } else { futex_up(&async_futex); } } bool fibril_mutex_trylock(fibril_mutex_t *fm) { bool locked = false; futex_down(&async_futex); if (fm->counter > 0) { fm->counter--; locked = true; } futex_up(&async_futex); return locked; } static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm) { assert(fm->counter <= 0); if (fm->counter++ < 0) { link_t *tmp; fibril_t *f; assert(!list_empty(&fm->waiters)); tmp = fm->waiters.next; f = list_get_instance(tmp, fibril_t, link); list_remove(&f->link); fibril_add_ready((fid_t) f); optimize_execution_power(); } } void fibril_mutex_unlock(fibril_mutex_t *fm) { futex_down(&async_futex); _fibril_mutex_unlock_unsafe(fm); futex_up(&async_futex); } void fibril_rwlock_initialize(fibril_rwlock_t *frw) { frw->writers = 0; frw->readers = 0; list_initialize(&frw->waiters); } void fibril_rwlock_read_lock(fibril_rwlock_t *frw) { futex_down(&async_futex); if (frw->writers) { fibril_t *f = (fibril_t *) fibril_get_id(); f->flags &= ~FIBRIL_WRITER; list_append(&f->link, &frw->waiters); fibril_switch(FIBRIL_TO_MANAGER); } else { frw->readers++; futex_up(&async_futex); } } void fibril_rwlock_write_lock(fibril_rwlock_t *frw) { futex_down(&async_futex); if (frw->writers || frw->readers) { fibril_t *f = (fibril_t *) fibril_get_id(); f->flags |= FIBRIL_WRITER; list_append(&f->link, &frw->waiters); fibril_switch(FIBRIL_TO_MANAGER); } else { frw->writers++; futex_up(&async_futex); } } static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw) { futex_down(&async_futex); assert(frw->readers || (frw->writers == 1)); if (frw->readers) { if (--frw->readers) goto out; } else { frw->writers--; } assert(!frw->readers && !frw->writers); while (!list_empty(&frw->waiters)) { link_t *tmp = frw->waiters.next; fibril_t *f = list_get_instance(tmp, fibril_t, link); if (f->flags & FIBRIL_WRITER) { if (frw->readers) break; list_remove(&f->link); fibril_add_ready((fid_t) f); frw->writers++; optimize_execution_power(); break; } else { list_remove(&f->link); fibril_add_ready((fid_t) f); frw->readers++; optimize_execution_power(); } } out: futex_up(&async_futex); } void fibril_rwlock_read_unlock(fibril_rwlock_t *frw) { _fibril_rwlock_common_unlock(frw); } void fibril_rwlock_write_unlock(fibril_rwlock_t *frw) { _fibril_rwlock_common_unlock(frw); } void fibril_condvar_initialize(fibril_condvar_t *fcv) { list_initialize(&fcv->waiters); } int fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm, suseconds_t timeout) { awaiter_t wdata; if (timeout < 0) return ETIMEOUT; wdata.fid = fibril_get_id(); wdata.active = false; wdata.to_event.inlist = timeout > 0; wdata.to_event.occurred = false; link_initialize(&wdata.to_event.link); wdata.wu_event.inlist = true; link_initialize(&wdata.wu_event.link); futex_down(&async_futex); if (timeout) { gettimeofday(&wdata.to_event.expires, NULL); tv_add(&wdata.to_event.expires, timeout); async_insert_timeout(&wdata); } list_append(&wdata.wu_event.link, &fcv->waiters); _fibril_mutex_unlock_unsafe(fm); fibril_switch(FIBRIL_TO_MANAGER); fibril_mutex_lock(fm); /* async_futex not held after fibril_switch() */ futex_down(&async_futex); if (wdata.to_event.inlist) list_remove(&wdata.to_event.link); if (wdata.wu_event.inlist) list_remove(&wdata.wu_event.link); futex_up(&async_futex); return wdata.to_event.occurred ? ETIMEOUT : EOK; } void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm) { int rc; rc = fibril_condvar_wait_timeout(fcv, fm, 0); assert(rc == EOK); } static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once) { link_t *tmp; awaiter_t *wdp; futex_down(&async_futex); while (!list_empty(&fcv->waiters)) { tmp = fcv->waiters.next; wdp = list_get_instance(tmp, awaiter_t, wu_event.link); list_remove(&wdp->wu_event.link); wdp->wu_event.inlist = false; if (!wdp->active) { wdp->active = true; fibril_add_ready(wdp->fid); optimize_execution_power(); if (once) break; } } futex_up(&async_futex); } void fibril_condvar_signal(fibril_condvar_t *fcv) { _fibril_condvar_wakeup_common(fcv, true); } void fibril_condvar_broadcast(fibril_condvar_t *fcv) { _fibril_condvar_wakeup_common(fcv, false); } /** @} */