/* -*- mode:c; c-file-style:"k&r"; c-basic-offset: 4; tab-width:4; indent-tabs-mode:nil; mode:auto-fill; fill-column:78; -*- */ /* vim: set ts=4 sw=4 et tw=78 fo=cqt wm=0: */ /* Copyright (C) 2014 OSCAR lab, Stony Brook University This file is part of Graphene Library OS. Graphene Library OS 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 3 of the License, or (at your option) any later version. Graphene Library OS 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, see . */ /* * db_mutex.c * * This file contains APIs that provide operations of (futex based) mutexes. * Based on "Mutexes and Condition Variables using Futexes" * (http://locklessinc.com/articles/mutex_cv_futex) */ #include "pal_defs.h" #include "pal_linux_defs.h" #include "pal.h" #include "pal_internal.h" #include "pal_linux.h" #include "pal_error.h" #include "pal_debug.h" #include "api.h" #include #include #include #include #define MUTEX_SPINLOCK_TIMES 20 int _DkMutexLockTimeout (struct mutex_handle * mut, int timeout) { int i, c = 0; if (timeout == -1) return -_DkMutexLock(mut); struct atomic_int * m = &mut->value; /* Spin and try to take lock */ for (i = 0 ; i < MUTEX_SPINLOCK_TIMES ; i++) { c = atomic_dec_and_test(m); if (c) goto success; cpu_relax(); } /* The lock is now contended */ int ret; if (timeout == 0) { ret = c ? 0 : -PAL_ERROR_TRYAGAIN; goto out; } unsigned long waittime = timeout; while (!c) { int val = atomic_read(m); if (val == 1) goto again; ret = ocall_futex((int *) &m->counter, FUTEX_WAIT, 2, timeout ? &waittime : NULL); if (ret < 0) goto out; again: /* Upon wakeup, we still need to check whether mutex is unlocked or * someone else took it. * If c==0 upon return from xchg (i.e., the older value of m==0), we * will exit the loop. Else, we sleep again (through a futex call). */ c = atomic_dec_and_test(m); } success: ret = 0; out: return ret; } int _DkMutexLock (struct mutex_handle * mut) { int i, c = 0; int ret; struct atomic_int * m = &mut->value; /* Spin and try to take lock */ for (i = 0; i < MUTEX_SPINLOCK_TIMES; i++) { c = atomic_dec_and_test(m); if (c) goto success; cpu_relax(); } /* The lock is now contended */ while (!c) { int val = atomic_read(m); if (val == 1) goto again; ret = ocall_futex((int *) &m->counter, FUTEX_WAIT, 2, NULL); if (ret < 0) goto out; again: /* Upon wakeup, we still need to check whether mutex is unlocked or * someone else took it. * If c==0 upon return from xchg (i.e., the older value of m==0), we * will exit the loop. Else, we sleep again (through a futex call). */ c = atomic_dec_and_test(m); } success: ret = 0; out: return ret; } int _DkMutexUnlock (struct mutex_handle * mut) { int ret = 0; int must_wake = 0; struct atomic_int * m = &mut->value; /* Unlock, and if not contended then exit. */ if (atomic_read(m) < 0) must_wake = 1; atomic_set(m, 1); if (must_wake) { /* We need to wake someone up */ ret = ocall_futex((int *) &m->counter, FUTEX_WAKE, 1, NULL); if (ret < 0) goto out; } if (ret < 0) { ret = -PAL_ERROR_TRYAGAIN; goto out; } ret = 0; out: return ret; }