/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2018 Danny Robson */ #include "semaphore.hpp" #include "../cast.hpp" #include "../posix/except.hpp" #include #include #include #include #include using cruft::thread::semaphore; /////////////////////////////////////////////////////////////////////////////// static long sys_futex (void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3) { return syscall (SYS_futex, addr1, op | FUTEX_PRIVATE_FLAG, val1, timeout, addr2, val3); } /////////////////////////////////////////////////////////////////////////////// semaphore::semaphore (): semaphore (1) { ; } //----------------------------------------------------------------------------- semaphore::semaphore (int initial): m_value (initial) { ; } /////////////////////////////////////////////////////////////////////////////// int semaphore::acquire (int count) { CHECK_GE (count, 0); do { int now = m_value; // if our value is positive then attempt to decrement it and return, // else retry because someone interfered with us. if (now - count >= 0) { if (m_value.compare_exchange_weak (now, now - count)) return now - count; continue; } // the count doesn't appear to allow us to acquire. sleep until // there's been a modification and retry. if (-1 == sys_futex (&m_value, FUTEX_WAIT, now, nullptr, nullptr, 0)) { switch (errno) { case EAGAIN: break; case EINTR: break; default: posix::error::throw_code (); } } } while (1); } //----------------------------------------------------------------------------- int semaphore::acquire (void) { return acquire (1); } //----------------------------------------------------------------------------- int semaphore::release (int count) { auto res = m_value += count; if (sys_futex (&m_value, FUTEX_WAKE, count, nullptr, nullptr, 0) < 0) posix::error::throw_code (); return res; } //----------------------------------------------------------------------------- int semaphore::release (void) { return release (1); } /////////////////////////////////////////////////////////////////////////////// int semaphore::value (void) const { return m_value; } //----------------------------------------------------------------------------- int semaphore::operator++ (void) { return release (); } //----------------------------------------------------------------------------- int semaphore::operator-- (void) { // we don't need to wake anyone because this will only serve to delay // their wakeup. return --m_value; }