From 3a26a98db7603c69917fec492d0a3757335fe3ae Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Wed, 14 Mar 2018 15:22:45 +1100 Subject: [PATCH] job/flag: add a one off event object --- CMakeLists.txt | 10 ++++-- job/flag.hpp | 39 ++++++++++++++++++++++ job/flag_linux.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++ job/flag_win32.cpp | 0 test/job/flag.cpp | 38 +++++++++++++++++++++ 5 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 job/flag.hpp create mode 100644 job/flag_linux.cpp create mode 100644 job/flag_win32.cpp create mode 100644 test/job/flag.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 98cd0401..4cec9de9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,7 @@ list ( if (LINUX) list (APPEND UTIL_FILES job/event_linux.cpp + job/flag_linux.cpp ) endif () @@ -123,16 +124,17 @@ if (WINDOWS) exe_win32.cpp io_win32.cpp io_win32.hpp - library_win32.hpp + job/event_win32.cpp + job/flag_win32.cpp library_win32.cpp + library_win32.hpp time_win32.cpp win32/except.cpp win32/except.hpp win32/handle.cpp win32/handle.hpp - win32/registry.hpp win32/registry.cpp - job/event_win32.cpp + win32/registry.hpp ) endif () @@ -265,6 +267,7 @@ list ( io.hpp iterator.hpp job/event.hpp + job/flag.hpp job/queue.cpp job/queue.hpp json/fwd.hpp @@ -475,6 +478,7 @@ if (TESTS) introspection iterator job/event + job/flag job/queue json_types json2/event diff --git a/job/flag.hpp b/job/flag.hpp new file mode 100644 index 00000000..f0a6921a --- /dev/null +++ b/job/flag.hpp @@ -0,0 +1,39 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2018 Danny Robson + */ + +#ifndef CRUFT_UTIL_JOB_FLAG_HPP +#define CRUFT_UTIL_JOB_FLAG_HPP + +#include + +namespace util::job { + /// a fire-once event that users can wait upon. if already fired then + /// waiting will be a noop. + class flag { + public: + flag (); + + void wait (void); + + int notify (void); + int notify (int); + + private: + std::atomic value; + }; +}; + +#endif diff --git a/job/flag_linux.cpp b/job/flag_linux.cpp new file mode 100644 index 00000000..0b4a9303 --- /dev/null +++ b/job/flag_linux.cpp @@ -0,0 +1,82 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2018 Danny Robson + */ + +#include "flag.hpp" +#include "../cast.hpp" + +#include +#include +#include +#include +#include +#include + +using util::job::flag; + + +/////////////////////////////////////////////////////////////////////////////// +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); +} + +/////////////////////////////////////////////////////////////////////////////// +flag::flag (): + value (0) +{ ; } + + +/////////////////////////////////////////////////////////////////////////////// +void +flag::wait (void) +{ + if (value) + return; + + while (!value) { + auto res = sys_futex (&value, FUTEX_WAIT, 0, nullptr, nullptr, 0); + + if (res < 0) { + switch (errno) { + case EAGAIN: return; + case EINTR: continue; + } + + posix::error::throw_code (); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// +int +flag::notify (void) +{ + return notify (std::numeric_limits::max ()); +} + + +//----------------------------------------------------------------------------- +int +flag::notify (int count) +{ + value = 1; + auto res = sys_futex (&value, FUTEX_WAKE, count, nullptr, nullptr, 0); + if (res < 0) + posix::error::throw_code (); + return util::cast::narrow (res); +} diff --git a/job/flag_win32.cpp b/job/flag_win32.cpp new file mode 100644 index 00000000..e69de29b diff --git a/test/job/flag.cpp b/test/job/flag.cpp new file mode 100644 index 00000000..252ed3f0 --- /dev/null +++ b/test/job/flag.cpp @@ -0,0 +1,38 @@ +#include "job/flag.hpp" +#include "tap.hpp" + +#include + + +int +main () +{ + util::TAP::logger tap; + + util::job::flag f; + std::atomic value = 0; + + std::thread t1 ([&] () { + f.wait (); + value = 1; + }); + + std::this_thread::sleep_for (std::chrono::milliseconds (100)); + + tap.expect_eq (value, 0, "value hasn't been set during wait"); + tap.expect_eq (f.notify (), 1, "notify reports one thread woke"); + + t1.join (); + tap.expect_eq (value, 1, "value has been changed after wait"); + + std::thread t2 ([&] () { + f.wait (); + value = 2; + }); + + std::this_thread::sleep_for (std::chrono::milliseconds (100)); + tap.expect_eq (value, 2, "second wait didn't appear to block"); + t2.join (); + + return tap.status (); +} \ No newline at end of file