fibril_synch.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009 Jakub Jermar 
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00035 #include <fibril_synch.h>
00036 #include <fibril.h>
00037 #include <async.h>
00038 #include <adt/list.h>
00039 #include <futex.h>
00040 #include <sys/time.h>
00041 #include <errno.h>
00042 #include <assert.h>
00043 #include <stacktrace.h>
00044 #include <stdlib.h>
00045 #include <stdio.h>
00046 #include "private/async.h"
00047 
00048 static void optimize_execution_power(void)
00049 {
00050         /*
00051          * When waking up a worker fibril previously blocked in fibril
00052          * synchronization, chances are that there is an idle manager fibril
00053          * waiting for IPC, that could start executing the awakened worker
00054          * fibril right away. We try to detect this and bring the manager
00055          * fibril back to fruitful work.
00056          */
00057         if (atomic_get(&threads_in_ipc_wait) > 0)
00058                 async_poke();
00059 }
00060 
00061 static void print_deadlock(fibril_owner_info_t *oi)
00062 {
00063         fibril_t *f = (fibril_t *) fibril_get_id();
00064 
00065         printf("Deadlock detected.\n");
00066         stacktrace_print();
00067 
00068         printf("Fibril %p waits for primitive %p.\n", f, oi);
00069 
00070         while (oi && oi->owned_by) {
00071                 printf("Primitive %p is owned by fibril %p.\n",
00072                     oi, oi->owned_by);
00073                 if (oi->owned_by == f)
00074                         break;
00075                 stacktrace_print_fp_pc(context_get_fp(&oi->owned_by->ctx),
00076                     oi->owned_by->ctx.pc);
00077                 printf("Fibril %p waits for primitive %p.\n",
00078                      oi->owned_by, oi->owned_by->waits_for);
00079                 oi = oi->owned_by->waits_for;
00080         }
00081 }
00082 
00083 
00084 static void check_for_deadlock(fibril_owner_info_t *oi)
00085 {
00086         while (oi && oi->owned_by) {
00087                 if (oi->owned_by == (fibril_t *) fibril_get_id()) {
00088                         print_deadlock(oi);
00089                         abort();
00090                 }
00091                 oi = oi->owned_by->waits_for;
00092         }
00093 }
00094 
00095 
00096 void fibril_mutex_initialize(fibril_mutex_t *fm)
00097 {
00098         fm->oi.owned_by = NULL;
00099         fm->counter = 1;
00100         list_initialize(&fm->waiters);
00101 }
00102 
00103 void fibril_mutex_lock(fibril_mutex_t *fm)
00104 {
00105         fibril_t *f = (fibril_t *) fibril_get_id();
00106 
00107         if (fibril_get_sercount() != 0)
00108                 abort();
00109 
00110         futex_down(&async_futex);
00111         if (fm->counter-- <= 0) {
00112                 awaiter_t wdata;
00113 
00114                 wdata.fid = fibril_get_id();
00115                 wdata.active = false;
00116                 wdata.wu_event.inlist = true;
00117                 link_initialize(&wdata.wu_event.link);
00118                 list_append(&wdata.wu_event.link, &fm->waiters);
00119                 check_for_deadlock(&fm->oi);
00120                 f->waits_for = &fm->oi;
00121                 fibril_switch(FIBRIL_TO_MANAGER);
00122         } else {
00123                 fm->oi.owned_by = f;
00124                 futex_up(&async_futex);
00125         }
00126 }
00127 
00128 bool fibril_mutex_trylock(fibril_mutex_t *fm)
00129 {
00130         bool locked = false;
00131         
00132         futex_down(&async_futex);
00133         if (fm->counter > 0) {
00134                 fm->counter--;
00135                 fm->oi.owned_by = (fibril_t *) fibril_get_id();
00136                 locked = true;
00137         }
00138         futex_up(&async_futex);
00139         
00140         return locked;
00141 }
00142 
00143 static void _fibril_mutex_unlock_unsafe(fibril_mutex_t *fm)
00144 {
00145         if (fm->counter++ < 0) {
00146                 link_t *tmp;
00147                 awaiter_t *wdp;
00148                 fibril_t *f;
00149         
00150                 assert(!list_empty(&fm->waiters));
00151                 tmp = fm->waiters.next;
00152                 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
00153                 wdp->active = true;
00154                 wdp->wu_event.inlist = false;
00155 
00156                 f = (fibril_t *) wdp->fid;
00157                 fm->oi.owned_by = f;
00158                 f->waits_for = NULL;
00159 
00160                 list_remove(&wdp->wu_event.link);
00161                 fibril_add_ready(wdp->fid);
00162                 optimize_execution_power();
00163         } else {
00164                 fm->oi.owned_by = NULL;
00165         }
00166 }
00167 
00168 void fibril_mutex_unlock(fibril_mutex_t *fm)
00169 {
00170         assert(fibril_mutex_is_locked(fm));
00171         futex_down(&async_futex);
00172         _fibril_mutex_unlock_unsafe(fm);
00173         futex_up(&async_futex);
00174 }
00175 
00176 bool fibril_mutex_is_locked(fibril_mutex_t *fm)
00177 {
00178         bool locked = false;
00179         
00180         futex_down(&async_futex);
00181         if (fm->counter <= 0) 
00182                 locked = true;
00183         futex_up(&async_futex);
00184         
00185         return locked;
00186 }
00187 
00188 void fibril_rwlock_initialize(fibril_rwlock_t *frw)
00189 {
00190         frw->oi.owned_by = NULL;
00191         frw->writers = 0;
00192         frw->readers = 0;
00193         list_initialize(&frw->waiters);
00194 }
00195 
00196 void fibril_rwlock_read_lock(fibril_rwlock_t *frw)
00197 {
00198         fibril_t *f = (fibril_t *) fibril_get_id();
00199         
00200         if (fibril_get_sercount() != 0)
00201                 abort();
00202 
00203         futex_down(&async_futex);
00204         if (frw->writers) {
00205                 awaiter_t wdata;
00206 
00207                 wdata.fid = (fid_t) f;
00208                 wdata.active = false;
00209                 wdata.wu_event.inlist = true;
00210                 link_initialize(&wdata.wu_event.link);
00211                 f->flags &= ~FIBRIL_WRITER;
00212                 list_append(&wdata.wu_event.link, &frw->waiters);
00213                 check_for_deadlock(&frw->oi);
00214                 f->waits_for = &frw->oi;
00215                 fibril_switch(FIBRIL_TO_MANAGER);
00216         } else {
00217                 /* Consider the first reader the owner. */
00218                 if (frw->readers++ == 0)
00219                         frw->oi.owned_by = f;
00220                 futex_up(&async_futex);
00221         }
00222 }
00223 
00224 void fibril_rwlock_write_lock(fibril_rwlock_t *frw)
00225 {
00226         fibril_t *f = (fibril_t *) fibril_get_id();
00227         
00228         if (fibril_get_sercount() != 0)
00229                 abort();
00230 
00231         futex_down(&async_futex);
00232         if (frw->writers || frw->readers) {
00233                 awaiter_t wdata;
00234 
00235                 wdata.fid = (fid_t) f;
00236                 wdata.active = false;
00237                 wdata.wu_event.inlist = true;
00238                 link_initialize(&wdata.wu_event.link);
00239                 f->flags |= FIBRIL_WRITER;
00240                 list_append(&wdata.wu_event.link, &frw->waiters);
00241                 check_for_deadlock(&frw->oi);
00242                 f->waits_for = &frw->oi;
00243                 fibril_switch(FIBRIL_TO_MANAGER);
00244         } else {
00245                 frw->oi.owned_by = f;
00246                 frw->writers++;
00247                 futex_up(&async_futex);
00248         }
00249 }
00250 
00251 static void _fibril_rwlock_common_unlock(fibril_rwlock_t *frw)
00252 {
00253         futex_down(&async_futex);
00254         if (frw->readers) {
00255                 if (--frw->readers) {
00256                         if (frw->oi.owned_by == (fibril_t *) fibril_get_id()) {
00257                                 /*
00258                                  * If this reader firbril was considered the
00259                                  * owner of this rwlock, clear the ownership
00260                                  * information even if there are still more
00261                                  * readers.
00262                                  *
00263                                  * This is the limitation of the detection
00264                                  * mechanism rooted in the fact that tracking
00265                                  * all readers would require dynamically
00266                                  * allocated memory for keeping linkage info.
00267                                  */
00268                                 frw->oi.owned_by = NULL;
00269                         }
00270                         goto out;
00271                 }
00272         } else {
00273                 frw->writers--;
00274         }
00275         
00276         assert(!frw->readers && !frw->writers);
00277         
00278         frw->oi.owned_by = NULL;
00279         
00280         while (!list_empty(&frw->waiters)) {
00281                 link_t *tmp = frw->waiters.next;
00282                 awaiter_t *wdp;
00283                 fibril_t *f;
00284                 
00285                 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
00286                 f = (fibril_t *) wdp->fid;
00287                 
00288                 f->waits_for = NULL;
00289                 
00290                 if (f->flags & FIBRIL_WRITER) {
00291                         if (frw->readers)
00292                                 break;
00293                         wdp->active = true;
00294                         wdp->wu_event.inlist = false;
00295                         list_remove(&wdp->wu_event.link);
00296                         fibril_add_ready(wdp->fid);
00297                         frw->writers++;
00298                         frw->oi.owned_by = f;
00299                         optimize_execution_power();
00300                         break;
00301                 } else {
00302                         wdp->active = true;
00303                         wdp->wu_event.inlist = false;
00304                         list_remove(&wdp->wu_event.link);
00305                         fibril_add_ready(wdp->fid);
00306                         if (frw->readers++ == 0) {
00307                                 /* Consider the first reader the owner. */
00308                                 frw->oi.owned_by = f;
00309                         }
00310                         optimize_execution_power();
00311                 }
00312         }
00313 out:
00314         futex_up(&async_futex);
00315 }
00316 
00317 void fibril_rwlock_read_unlock(fibril_rwlock_t *frw)
00318 {
00319         assert(fibril_rwlock_is_read_locked(frw));
00320         _fibril_rwlock_common_unlock(frw);
00321 }
00322 
00323 void fibril_rwlock_write_unlock(fibril_rwlock_t *frw)
00324 {
00325         assert(fibril_rwlock_is_write_locked(frw));
00326         _fibril_rwlock_common_unlock(frw);
00327 }
00328 
00329 bool fibril_rwlock_is_read_locked(fibril_rwlock_t *frw)
00330 {
00331         bool locked = false;
00332 
00333         futex_down(&async_futex);
00334         if (frw->readers)
00335                 locked = true;
00336         futex_up(&async_futex);
00337 
00338         return locked;
00339 }
00340 
00341 bool fibril_rwlock_is_write_locked(fibril_rwlock_t *frw)
00342 {
00343         bool locked = false;
00344 
00345         futex_down(&async_futex);
00346         if (frw->writers) {
00347                 assert(frw->writers == 1);
00348                 locked = true;
00349         }
00350         futex_up(&async_futex);
00351 
00352         return locked;
00353 }
00354 
00355 bool fibril_rwlock_is_locked(fibril_rwlock_t *frw)
00356 {
00357         return fibril_rwlock_is_read_locked(frw) ||
00358             fibril_rwlock_is_write_locked(frw);
00359 }
00360 
00361 void fibril_condvar_initialize(fibril_condvar_t *fcv)
00362 {
00363         list_initialize(&fcv->waiters);
00364 }
00365 
00366 int
00367 fibril_condvar_wait_timeout(fibril_condvar_t *fcv, fibril_mutex_t *fm,
00368     suseconds_t timeout)
00369 {
00370         awaiter_t wdata;
00371 
00372         assert(fibril_mutex_is_locked(fm));
00373 
00374         if (timeout < 0)
00375                 return ETIMEOUT;
00376 
00377         wdata.fid = fibril_get_id();
00378         wdata.active = false;
00379         
00380         wdata.to_event.inlist = timeout > 0;
00381         wdata.to_event.occurred = false;
00382         link_initialize(&wdata.to_event.link);
00383 
00384         wdata.wu_event.inlist = true;
00385         link_initialize(&wdata.wu_event.link);
00386 
00387         futex_down(&async_futex);
00388         if (timeout) {
00389                 gettimeofday(&wdata.to_event.expires, NULL);
00390                 tv_add(&wdata.to_event.expires, timeout);
00391                 async_insert_timeout(&wdata);
00392         }
00393         list_append(&wdata.wu_event.link, &fcv->waiters);
00394         _fibril_mutex_unlock_unsafe(fm);
00395         fibril_switch(FIBRIL_TO_MANAGER);
00396         fibril_mutex_lock(fm);
00397 
00398         /* async_futex not held after fibril_switch() */
00399         futex_down(&async_futex);
00400         if (wdata.to_event.inlist)
00401                 list_remove(&wdata.to_event.link);
00402         if (wdata.wu_event.inlist)
00403                 list_remove(&wdata.wu_event.link);
00404         futex_up(&async_futex);
00405         
00406         return wdata.to_event.occurred ? ETIMEOUT : EOK;
00407 }
00408 
00409 void fibril_condvar_wait(fibril_condvar_t *fcv, fibril_mutex_t *fm)
00410 {
00411         int rc;
00412 
00413         rc = fibril_condvar_wait_timeout(fcv, fm, 0);
00414         assert(rc == EOK);
00415 }
00416 
00417 static void _fibril_condvar_wakeup_common(fibril_condvar_t *fcv, bool once)
00418 {
00419         link_t *tmp;
00420         awaiter_t *wdp;
00421 
00422         futex_down(&async_futex);
00423         while (!list_empty(&fcv->waiters)) {
00424                 tmp = fcv->waiters.next;
00425                 wdp = list_get_instance(tmp, awaiter_t, wu_event.link);
00426                 list_remove(&wdp->wu_event.link);
00427                 wdp->wu_event.inlist = false;
00428                 if (!wdp->active) {
00429                         wdp->active = true;
00430                         fibril_add_ready(wdp->fid);
00431                         optimize_execution_power();
00432                         if (once)
00433                                 break;
00434                 }
00435         }
00436         futex_up(&async_futex);
00437 }
00438 
00439 void fibril_condvar_signal(fibril_condvar_t *fcv)
00440 {
00441         _fibril_condvar_wakeup_common(fcv, true);
00442 }
00443 
00444 void fibril_condvar_broadcast(fibril_condvar_t *fcv)
00445 {
00446         _fibril_condvar_wakeup_common(fcv, false);
00447 }
00448 

Generated on Thu Jun 2 07:45:46 2011 for HelenOS/USB by  doxygen 1.4.7