From ff5f79a8580403499c95570853a0fe3640963de9 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Mon, 13 Aug 2018 14:50:48 +1000 Subject: [PATCH] build; add (untested) paths for win32 builds --- CMakeLists.txt | 56 +++++-- memory/buffer/paged_win32.cpp | 159 ++++++++++++++++++++ thread/event.hpp | 60 +------- thread/{event_linux.cpp => event_futex.cpp} | 0 thread/event_futex.hpp | 53 +++++++ thread/event_std.cpp | 36 +++++ thread/event_std.hpp | 55 +++++++ thread/flag.hpp | 38 +---- thread/{flag_linux.cpp => flag_futex.cpp} | 0 thread/flag_futex.hpp | 31 ++++ thread/{flag_win32.cpp => flag_std.cpp} | 0 thread/flag_std.hpp | 39 +++++ thread/semaphore.hpp | 51 +------ thread/semaphore_linux.hpp | 44 ++++++ thread/semaphore_win32.hpp | 46 ++++++ 15 files changed, 529 insertions(+), 139 deletions(-) create mode 100644 memory/buffer/paged_win32.cpp rename thread/{event_linux.cpp => event_futex.cpp} (100%) create mode 100644 thread/event_futex.hpp create mode 100644 thread/event_std.cpp create mode 100644 thread/event_std.hpp rename thread/{flag_linux.cpp => flag_futex.cpp} (100%) create mode 100644 thread/flag_futex.hpp rename thread/{flag_win32.cpp => flag_std.cpp} (100%) create mode 100644 thread/flag_std.hpp create mode 100644 thread/semaphore_linux.hpp create mode 100644 thread/semaphore_win32.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 083dd478..53341bf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) ############################################################################### -set (UTIL_FILES) +add_library(cruft) ############################################################################### @@ -44,6 +44,7 @@ if (BACKTRACE_LIB_FOUND) list (APPEND UTIL_FILES backtrace_execinfo.cpp) elseif (HAVE_CAPTURESTACKBACKTRACE) list (APPEND UTIL_FILES backtrace_stackwalk.cpp) + target_link_libraries(cruft DbgHelp) else () list (APPEND UTIL_FILES backtrace_null.cpp) endif () @@ -81,8 +82,6 @@ list ( posix/except.hpp posix/fd.cpp posix/fd.hpp - posix/socket.cpp - posix/socket.hpp ) @@ -91,13 +90,12 @@ if (LINUX) list (APPEND UTIL_FILES thread/event_linux.cpp thread/flag_linux.cpp - thread/semaphore_linux.cpp ) endif () ##----------------------------------------------------------------------------- -if (NOT WINDOWS) +if (NOT WIN32) list ( APPEND UTIL_FILES memory/buffer/circular.cpp @@ -114,13 +112,15 @@ if (NOT WINDOWS) posix/fwd.hpp posix/map.cpp posix/map.hpp + posix/socket.cpp + posix/socket.hpp time_posix.cpp ) endif () ##----------------------------------------------------------------------------- -if (WINDOWS) +if (WIN32) list ( APPEND UTIL_FILES debug_win32.cpp @@ -128,8 +128,6 @@ if (WINDOWS) io_win32.cpp io_win32.hpp thread/event_win32.cpp - thread/flag_win32.cpp - thread/semaphore_win32.cpp library_win32.cpp library_win32.hpp time_win32.cpp @@ -140,6 +138,41 @@ if (WINDOWS) win32/registry.cpp win32/registry.hpp ) + + target_link_libraries (cruft ws2_32) +endif () + + +############################################################################### +list (APPEND UTIL_FILES + thread/event.hpp + thread/semaphore.hpp + thread/flag.hpp +) + +if (LINUX) + list (APPEND UTIL_FILES + thread/event_futex.cpp + thread/event_futex.hpp + thread/semaphore_linux.hpp + thread/semaphore_linux.cpp + thread/flag_futex.cpp + thread/flag_futex.hpp + ) +elseif (WIN32) + list (APPEND UTIL_FILES + thread/event_std.cpp + thread/event_std.hpp + thread/semaphore_win32.hpp + thread/semaphore_win32.cpp + thread/flag_std.cpp + thread/flag_std.hpp + ) +else () + list (APPEND UTIL_FILES + thread/flag_std.cpp + thread/flag_std.hpp + ) endif () @@ -364,7 +397,6 @@ list ( thread/flag.hpp thread/monitor.cpp thread/monitor.hpp - thread/semaphore.hpp thread/ticketlock.cpp thread/ticketlock.hpp thread/spinlock.cpp @@ -420,7 +452,7 @@ DEPENDS ############################################################################### -add_library(cruft ${UTIL_FILES}) +target_sources(cruft PRIVATE ${UTIL_FILES}) add_library(cruft-util ALIAS cruft) target_include_directories(cruft PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}") @@ -437,7 +469,9 @@ target_link_libraries(cruft ${MATH_LIBS}) # HACK: -ldl isn't getting discovered correctly so we add it unconditionally # for the time being. -target_link_libraries(cruft dl) +if (NOT WIN32) + target_link_libraries(cruft dl) +endif () ############################################################################### diff --git a/memory/buffer/paged_win32.cpp b/memory/buffer/paged_win32.cpp new file mode 100644 index 00000000..f1c76327 --- /dev/null +++ b/memory/buffer/paged_win32.cpp @@ -0,0 +1,159 @@ +/* + * 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 2015 Danny Robson + */ + +#include "paged.hpp" + +#include "../../cast.hpp" +#include "../../maths.hpp" +#include "../../pointer.hpp" +#include "../../posix/except.hpp" +#include "../system.hpp" + +#include + +using cruft::memory::buffer::paged; + + +/////////////////////////////////////////////////////////////////////////////// +paged::paged (size_t bytes, size_t _window): + m_window (round_up (_window, pagesize ())) +{ + // reserve the address region with no access permissions + m_begin = reinterpret_cast ( + mmap (nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) + ); + + if (m_begin == MAP_FAILED) + posix::error::throw_code (); + + // remap the initial window with read/write permissions + m_cursor = m_begin + round_up (min (m_window, bytes), pagesize ()); + if (MAP_FAILED == mmap (m_begin, + m_cursor - m_begin, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) + posix::error::throw_code (); + + // record the nominal end address + m_end = m_begin + round_up (bytes, pagesize ()); +} + + +//----------------------------------------------------------------------------- +paged::~paged () +{ + // ignore errors in production; we don't want to double throw. + auto res = munmap (m_begin, m_end - m_begin); + (void)res; + CHECK_ZERO (res); +} + + +/////////////////////////////////////////////////////////////////////////////// +char* +paged::begin (void)& +{ + return m_begin; +} + + +//----------------------------------------------------------------------------- +char* +paged::end (void)& +{ + return m_end; +} + + +/////////////////////////////////////////////////////////////////////////////// +void +paged::access (char *cursor) +{ + if (cursor < m_cursor) + release (cursor); + else + commit (cursor); +} + + +//----------------------------------------------------------------------------- +void +paged::commit (char *cursor) +{ + // bail if it's already mapped + if (cursor <= m_cursor) + return; + + if (cursor > m_end || cursor < m_begin) + throw std::out_of_range ("invalid commit cursor"); + + // bump the request up to page aligned and tack on a little to amortize + // syscall overheads + cursor = align (cursor, pagesize ()) + m_window; + cursor = min (cursor, m_end); + + if (MAP_FAILED == mmap (m_cursor, + cursor - m_cursor, + PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0)) + posix::error::throw_code (); + + m_cursor = cursor; +} + + +//----------------------------------------------------------------------------- +void +paged::release (char *desired) +{ + if (desired > m_end || desired < m_begin) + throw std::out_of_range ("invalid release cursor"); + + align (desired, pagesize ()); + + // bail if the region is alread unmapped, or if it's not sufficiently + // behind the current cursor. + if (desired >= m_cursor || cruft::cast::sign (m_cursor - desired) < m_window) + return; + + desired += m_window; + + if (MAP_FAILED == mmap (desired, + m_end - desired, + PROT_NONE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, + -1, 0)) + posix::error::throw_code (); + + m_cursor = desired; +} + + +/////////////////////////////////////////////////////////////////////////////// +size_t +paged::size (void) const +{ + return m_cursor - m_begin; +} + + +//----------------------------------------------------------------------------- +size_t +paged::capacity (void) const +{ + return m_end - m_begin; +} + + +//----------------------------------------------------------------------------- +size_t +paged::window (void) const +{ + return m_window; +} diff --git a/thread/event.hpp b/thread/event.hpp index ced35913..2982aa85 100644 --- a/thread/event.hpp +++ b/thread/event.hpp @@ -1,53 +1,7 @@ -/* - * 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 - */ - -#ifndef CRUFT_UTIL_THREAD_EVENT_HPP -#define CRUFT_UTIL_THREAD_EVENT_HPP - -#include - -namespace cruft::thread { - /// a reusable synchronisation object that allows threads to wait until - /// notify is called. - /// - /// there is no internal state so it is easy to create races between wait - /// and notify calls. this makes the class mostly suitable for recurring - /// events. - /// - /// the user should ensure no callers are waiting at destruction time - /// otherwise they may remain blocked indefinitely. - /// - /// the address of the object is important so it must _never_ be - /// relocated in any manner if any caller may be waiting. it may be safe - /// to do so if there are no callers waiting (but the relevant functions - /// are deleted for safety anyway). - class event { - public: - event (); - - event (const event&) = delete; - event (event&&) = delete; - event& operator= (const event&) = delete; - event& operator= (event&&) = delete; - - /// block until notified - void wait (void); - - /// wake all threads that are waiting - int notify (void); - - /// wait `count' threads that are waiting - int notify (int count); - - private: - alignas (4) std::atomic value; - }; -} - -#endif - +#include "platform.hpp" + +#if defined(PLATFORM_LINUX) +#include "event_futex.hpp" +#else +#include "event_std.hpp" +#endif \ No newline at end of file diff --git a/thread/event_linux.cpp b/thread/event_futex.cpp similarity index 100% rename from thread/event_linux.cpp rename to thread/event_futex.cpp diff --git a/thread/event_futex.hpp b/thread/event_futex.hpp new file mode 100644 index 00000000..ced35913 --- /dev/null +++ b/thread/event_futex.hpp @@ -0,0 +1,53 @@ +/* + * 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 + */ + +#ifndef CRUFT_UTIL_THREAD_EVENT_HPP +#define CRUFT_UTIL_THREAD_EVENT_HPP + +#include + +namespace cruft::thread { + /// a reusable synchronisation object that allows threads to wait until + /// notify is called. + /// + /// there is no internal state so it is easy to create races between wait + /// and notify calls. this makes the class mostly suitable for recurring + /// events. + /// + /// the user should ensure no callers are waiting at destruction time + /// otherwise they may remain blocked indefinitely. + /// + /// the address of the object is important so it must _never_ be + /// relocated in any manner if any caller may be waiting. it may be safe + /// to do so if there are no callers waiting (but the relevant functions + /// are deleted for safety anyway). + class event { + public: + event (); + + event (const event&) = delete; + event (event&&) = delete; + event& operator= (const event&) = delete; + event& operator= (event&&) = delete; + + /// block until notified + void wait (void); + + /// wake all threads that are waiting + int notify (void); + + /// wait `count' threads that are waiting + int notify (int count); + + private: + alignas (4) std::atomic value; + }; +} + +#endif + diff --git a/thread/event_std.cpp b/thread/event_std.cpp new file mode 100644 index 00000000..783b138e --- /dev/null +++ b/thread/event_std.cpp @@ -0,0 +1,36 @@ +#include "event_std.hpp" + +using cruft::thread::event; + + +/////////////////////////////////////////////////////////////////////////////// +event::event (): + m_value (0) +{ ; } + + +/////////////////////////////////////////////////////////////////////////////// +void +event::wait (void) +{ + int const now = m_value; + std::unique_lock lk (m_mutex); + m_cv.wait (lk, [&,this] () { return now != m_value; }); +} + + +/////////////////////////////////////////////////////////////////////////////// +void +event::notify_one (void) +{ + ++m_value; + m_cv.notify_one (); +} + + +//----------------------------------------------------------------------------- +void event::notify_all() +{ + ++m_value; + m_cv.notify_all (); +} diff --git a/thread/event_std.hpp b/thread/event_std.hpp new file mode 100644 index 00000000..d114fc2e --- /dev/null +++ b/thread/event_std.hpp @@ -0,0 +1,55 @@ +/* + * 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 + */ + +#pragma once + +#include +#include +#include + + +namespace cruft::thread { + /// a reusable synchronisation object that allows threads to wait until + /// notify is called. + /// + /// there is no internal state so it is easy to create races between wait + /// and notify calls. this makes the class mostly suitable for recurring + /// events. + /// + /// the user should ensure no callers are waiting at destruction time + /// otherwise they may remain blocked indefinitely. + /// + /// the address of the object is important so it must _never_ be + /// relocated in any manner if any caller may be waiting. it may be safe + /// to do so if there are no callers waiting (but the relevant functions + /// are deleted for safety anyway). + class event { + public: + event (); + + event (const event&) = delete; + event (event&&) = delete; + event& operator= (const event&) = delete; + event& operator= (event&&) = delete; + + /// block until notified + void wait (void); + + /// wake all threads that are waiting + void notify_one (void); + + /// wait `count' threads that are waiting + void notify_all (void); + + private: + std::atomic m_value; + std::mutex m_mutex; + std::condition_variable m_cv; + }; +} + diff --git a/thread/flag.hpp b/thread/flag.hpp index 1712d9a1..3b686161 100644 --- a/thread/flag.hpp +++ b/thread/flag.hpp @@ -1,31 +1,7 @@ -/* - * 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 - */ - -#ifndef CRUFT_UTIL_THREAD_FLAG_HPP -#define CRUFT_UTIL_THREAD_FLAG_HPP - -#include - -namespace cruft::thread { - /// 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 +#include "platform.hpp" + +#if defined(PLATFORM_LINUX) +#include "flag_futex.hpp" +#else +#include "flag_std.hpp" +#endif diff --git a/thread/flag_linux.cpp b/thread/flag_futex.cpp similarity index 100% rename from thread/flag_linux.cpp rename to thread/flag_futex.cpp diff --git a/thread/flag_futex.hpp b/thread/flag_futex.hpp new file mode 100644 index 00000000..1712d9a1 --- /dev/null +++ b/thread/flag_futex.hpp @@ -0,0 +1,31 @@ +/* + * 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 + */ + +#ifndef CRUFT_UTIL_THREAD_FLAG_HPP +#define CRUFT_UTIL_THREAD_FLAG_HPP + +#include + +namespace cruft::thread { + /// 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/thread/flag_win32.cpp b/thread/flag_std.cpp similarity index 100% rename from thread/flag_win32.cpp rename to thread/flag_std.cpp diff --git a/thread/flag_std.hpp b/thread/flag_std.hpp new file mode 100644 index 00000000..bd46e1ea --- /dev/null +++ b/thread/flag_std.hpp @@ -0,0 +1,39 @@ +/* + * 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 + */ + +#pragma once + +#include +#include +#include + +namespace cruft::thread { + /// a fire-once event that users can wait upon. if already fired then + /// waiting will be a noop. + class flag { + public: + flag (); + + /// blocks indefinitely until the flag has been set. + void wait (void); + + /// wake one thread waiting on the flag. + /// + /// it may be possible, through spurious wakeups, that more threads + /// are release than requested. though this should not be typical. + void notify_one (void); + + /// wake all threads waiting on the flag + void notify_all (void); + + private: + std::atomic fired; + std::mutex m_mutex; + std::condition_variable m_condition; + }; +} diff --git a/thread/semaphore.hpp b/thread/semaphore.hpp index 990907bb..9ee2afbe 100644 --- a/thread/semaphore.hpp +++ b/thread/semaphore.hpp @@ -1,44 +1,7 @@ -/* - * 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 - */ - -#ifndef CRUFT_UTIL_THREAD_SEMAPHORE_HPP -#define CRUFT_UTIL_THREAD_SEMAPHORE_HPP - -#include - -namespace cruft::thread { - /// Explicitly does not conform to BasicLockable. - class semaphore { - public: - semaphore (int initial); - semaphore (); - - semaphore (const semaphore&) = delete; - semaphore& operator= (const semaphore&) = delete; - semaphore (semaphore&&) = delete; - semaphore& operator= (semaphore&&) = delete; - - int acquire (void); - int acquire (int count); - int release (void); - int release (int count); - - auto lock (void) { return acquire (); } - auto unlock (void) { return release (); } - - int value (void) const; - - int operator++ (void); - int operator-- (void); - - private: - std::atomic m_value; - }; -}; - -#endif +#include "platform.hpp" + +#if defined(PLATFORM_LINUX) +#include "semaphore_linux.hpp" +#else +#include "semaphore_win32.hpp" +#endif diff --git a/thread/semaphore_linux.hpp b/thread/semaphore_linux.hpp new file mode 100644 index 00000000..990907bb --- /dev/null +++ b/thread/semaphore_linux.hpp @@ -0,0 +1,44 @@ +/* + * 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 + */ + +#ifndef CRUFT_UTIL_THREAD_SEMAPHORE_HPP +#define CRUFT_UTIL_THREAD_SEMAPHORE_HPP + +#include + +namespace cruft::thread { + /// Explicitly does not conform to BasicLockable. + class semaphore { + public: + semaphore (int initial); + semaphore (); + + semaphore (const semaphore&) = delete; + semaphore& operator= (const semaphore&) = delete; + semaphore (semaphore&&) = delete; + semaphore& operator= (semaphore&&) = delete; + + int acquire (void); + int acquire (int count); + int release (void); + int release (int count); + + auto lock (void) { return acquire (); } + auto unlock (void) { return release (); } + + int value (void) const; + + int operator++ (void); + int operator-- (void); + + private: + std::atomic m_value; + }; +}; + +#endif diff --git a/thread/semaphore_win32.hpp b/thread/semaphore_win32.hpp new file mode 100644 index 00000000..0205ef7e --- /dev/null +++ b/thread/semaphore_win32.hpp @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#pragma once + +#include +#include +#include + + +namespace cruft::thread { + /// Explicitly does not conform to BasicLockable. + class semaphore { + public: + semaphore (int initial); + semaphore (); + + semaphore (const semaphore&) = delete; + semaphore& operator= (const semaphore&) = delete; + semaphore (semaphore&&) = delete; + semaphore& operator= (semaphore&&) = delete; + + int acquire (void); + int acquire (int count); + int release (void); + int release (int count); + + auto lock (void) { return acquire (); } + auto unlock (void) { return release (); } + + int value (void) const; + + int operator++ (void); + int operator-- (void); + + private: + std::atomic m_value; + std::mutex m_mutex; + std::condition_variable m_cv; + }; +};