job/flag: add a one off event object

This commit is contained in:
Danny Robson 2018-03-14 15:22:45 +11:00
parent 1fd58c8967
commit 3a26a98db7
5 changed files with 166 additions and 3 deletions

View File

@ -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

39
job/flag.hpp Normal file
View File

@ -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 <danny@nerdcruft.net>
*/
#ifndef CRUFT_UTIL_JOB_FLAG_HPP
#define CRUFT_UTIL_JOB_FLAG_HPP
#include <atomic>
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<int> value;
};
};
#endif

82
job/flag_linux.cpp Normal file
View File

@ -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 <danny@nerdcruft.net>
*/
#include "flag.hpp"
#include "../cast.hpp"
#include <cerrno>
#include <linux/futex.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <cruft/util/posix/except.hpp>
#include <limits>
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<int>::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<int> (res);
}

0
job/flag_win32.cpp Normal file
View File

38
test/job/flag.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "job/flag.hpp"
#include "tap.hpp"
#include <thread>
int
main ()
{
util::TAP::logger tap;
util::job::flag f;
std::atomic<int> 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 ();
}